// /js/load-navbar.js import { getUser, clearAuthData } from './auth.js'; import { loadComponent } from './component-loader.js'; import { config } from './config.js'; // 역할 이름을 한글로 변환하는 맵 const ROLE_NAMES = { admin: '관리자', system: '시스템 관리자', leader: '그룹장', user: '작업자', support: '지원팀', default: '사용자', }; /** * 네비게이션 바 DOM을 사용자 정보와 역할에 맞게 수정하는 프로세서입니다. * @param {Document} doc - 파싱된 HTML 문서 객체 */ async function processNavbarDom(doc) { const currentUser = getUser(); if (!currentUser) return; // 1. 역할 및 페이지 권한 기반 메뉴 필터링 await filterMenuByPageAccess(doc, currentUser); // 2. 사용자 정보 채우기 populateUserInfo(doc, currentUser); } /** * 사용자의 페이지 접근 권한에 따라 메뉴 항목을 필터링합니다. * @param {Document} doc - 파싱된 HTML 문서 객체 * @param {object} currentUser - 현재 사용자 객체 */ async function filterMenuByPageAccess(doc, currentUser) { const userRole = (currentUser.role || '').toLowerCase(); // Admin은 모든 메뉴 표시 + .admin-only 요소 활성화 if (userRole === 'admin' || userRole === 'system admin') { doc.querySelectorAll('.admin-only').forEach(el => el.classList.add('visible')); return; } try { // 사용자의 페이지 접근 권한 조회 const cached = localStorage.getItem('userPageAccess'); let accessiblePages = null; if (cached) { const cacheData = JSON.parse(cached); // 캐시가 5분 이내인 경우 사용 if (Date.now() - cacheData.timestamp < 5 * 60 * 1000) { accessiblePages = cacheData.pages; } } // 캐시가 없으면 API 호출 if (!accessiblePages) { const response = await fetch(`${window.API_BASE_URL}/users/${currentUser.user_id}/page-access`, { method: 'GET', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('token')}` } }); if (!response.ok) { console.error('페이지 권한 조회 실패:', response.status); return; } const data = await response.json(); accessiblePages = data.data.pageAccess || []; // 캐시 저장 localStorage.setItem('userPageAccess', JSON.stringify({ pages: accessiblePages, timestamp: Date.now() })); } // 접근 가능한 페이지 키 목록 const accessiblePageKeys = accessiblePages .filter(p => p.can_access === 1) .map(p => p.page_key); // 메뉴 항목에 data-page-key 속성이 있으면 해당 권한 체크 const menuItems = doc.querySelectorAll('[data-page-key]'); menuItems.forEach(item => { const pageKey = item.getAttribute('data-page-key'); // 대시보드와 프로필 페이지는 모든 사용자 접근 가능 if (pageKey === 'dashboard' || pageKey.startsWith('profile.')) { return; } // 권한이 없으면 메뉴 항목 제거 if (!accessiblePageKeys.includes(pageKey)) { item.remove(); } }); // Admin 전용 메뉴는 무조건 제거 doc.querySelectorAll('.admin-only').forEach(el => el.remove()); } catch (error) { console.error('메뉴 필터링 오류:', error); } } /** * 네비게이션 바에 사용자 정보를 채웁니다. * @param {Document} doc - 파싱된 HTML 문서 객체 * @param {object} user - 현재 사용자 객체 */ function populateUserInfo(doc, user) { const displayName = user.name || user.username; // 대소문자 구분 없이 처리 const roleLower = (user.role || '').toLowerCase(); const roleName = ROLE_NAMES[roleLower] || ROLE_NAMES.default; const elements = { 'userName': displayName, 'userRole': roleName, 'userInitial': displayName.charAt(0), }; for (const id in elements) { const el = doc.getElementById(id); if (el) el.textContent = elements[id]; } // 메인 대시보드 URL 설정 const dashboardBtn = doc.getElementById('dashboardBtn'); if (dashboardBtn) { dashboardBtn.href = '/pages/dashboard.html'; } } /** * 네비게이션 바와 관련된 모든 이벤트를 설정합니다. */ function setupNavbarEvents() { const logoutButton = document.getElementById('logoutBtn'); if (logoutButton) { logoutButton.addEventListener('click', () => { if (confirm('로그아웃 하시겠습니까?')) { clearAuthData(); window.location.href = config.paths.loginPage; } }); } } /** * 현재 날짜와 시간을 업데이트하는 함수 */ function updateDateTime() { const now = new Date(); // 시간 업데이트 const timeElement = document.getElementById('timeValue'); if (timeElement) { timeElement.textContent = now.toLocaleTimeString('ko-KR', { hour12: false }); } // 날짜 업데이트 const dateElement = document.getElementById('dateValue'); if (dateElement) { const days = ['일', '월', '화', '수', '목', '금', '토']; const month = now.getMonth() + 1; const date = now.getDate(); const day = days[now.getDay()]; dateElement.textContent = `${month}월 ${date}일 (${day})`; } } // 날씨 아이콘 매핑 const WEATHER_ICONS = { clear: '☀️', rain: '🌧️', snow: '❄️', heat: '🔥', cold: '🥶', wind: '💨', fog: '🌫️', dust: '😷', cloudy: '⛅', overcast: '☁️' }; // 날씨 조건명 const WEATHER_NAMES = { clear: '맑음', rain: '비', snow: '눈', heat: '폭염', cold: '한파', wind: '강풍', fog: '안개', dust: '미세먼지', cloudy: '구름많음', overcast: '흐림' }; /** * 날씨 정보를 가져와서 업데이트하는 함수 */ async function updateWeather() { try { const token = localStorage.getItem('token'); if (!token) return; const response = await fetch(`${window.API_BASE_URL}/tbm/weather/current`, { method: 'GET', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }); if (!response.ok) { throw new Error('날씨 API 호출 실패'); } const result = await response.json(); if (result.success && result.data) { const { temperature, conditions, weatherData } = result.data; // 온도 표시 const tempElement = document.getElementById('weatherTemp'); if (tempElement && temperature !== null && temperature !== undefined) { tempElement.textContent = `${Math.round(temperature)}°C`; } // 날씨 아이콘 및 설명 const iconElement = document.getElementById('weatherIcon'); const descElement = document.getElementById('weatherDesc'); if (conditions && conditions.length > 0) { const primaryCondition = conditions[0]; if (iconElement) { iconElement.textContent = WEATHER_ICONS[primaryCondition] || '🌤️'; } if (descElement) { descElement.textContent = WEATHER_NAMES[primaryCondition] || '맑음'; } } else { if (iconElement) iconElement.textContent = '☀️'; if (descElement) descElement.textContent = '맑음'; } // 날씨 섹션 표시 const weatherSection = document.getElementById('weatherSection'); if (weatherSection) { weatherSection.style.opacity = '1'; } } } catch (error) { console.warn('날씨 정보 로드 실패:', error.message); // 실패해도 기본값 표시 const descElement = document.getElementById('weatherDesc'); if (descElement) { descElement.textContent = '날씨 정보 없음'; } } } // 메인 로직: DOMContentLoaded 시 실행 document.addEventListener('DOMContentLoaded', async () => { if (getUser()) { // 1. 컴포넌트 로드 및 DOM 수정 await loadComponent('navbar', '#navbar-container', processNavbarDom); // 2. DOM에 삽입된 후에 이벤트 리스너 설정 setupNavbarEvents(); // 3. 실시간 날짜/시간 업데이트 시작 updateDateTime(); setInterval(updateDateTime, 1000); // 4. 날씨 정보 로드 (10분마다 갱신) updateWeather(); setInterval(updateWeather, 10 * 60 * 1000); } });