/** * 공통 헤더 로더 * 모든 페이지에서 동일한 헤더를 로드하기 위한 유틸리티 */ class HeaderLoader { constructor() { this.headerLoaded = false; } /** * 헤더 HTML을 로드하고 삽입 */ async loadHeader(targetSelector = '#header-container') { if (this.headerLoaded) { console.log('✅ 헤더가 이미 로드됨'); return; } try { console.log('🔄 헤더 로딩 중...'); const response = await fetch('components/header.html?v=2025012352'); if (!response.ok) { throw new Error(`헤더 로드 실패: ${response.status}`); } const headerHtml = await response.text(); // 헤더 컨테이너 찾기 const container = document.querySelector(targetSelector); if (!container) { throw new Error(`헤더 컨테이너를 찾을 수 없음: ${targetSelector}`); } // 헤더 HTML 삽입 container.innerHTML = headerHtml; // 헤더 로드 후 필요한 함수들 정의 this.initializeHeaderFunctions(); this.headerLoaded = true; console.log('✅ 헤더 로드 완료'); // 헤더 로드 완료 이벤트 발생 document.dispatchEvent(new CustomEvent('headerLoaded')); } catch (error) { console.error('❌ 헤더 로드 오류:', error); this.showFallbackHeader(targetSelector); } } /** * 헤더 로드 후 필요한 함수들 초기화 */ initializeHeaderFunctions() { console.log('🔧 헤더 함수들 초기화 중...'); // handleLogin 함수 정의 window.handleLogin = () => { console.log('🔐 handleLogin 호출됨 - 로그인 페이지로 이동'); const currentUrl = encodeURIComponent(window.location.href); window.location.href = `login.html?redirect=${currentUrl}`; }; // handleLogout 함수 정의 window.handleLogout = async () => { try { console.log('🔄 로그아웃 시작...'); console.log('🔍 window.api 존재 여부:', !!window.api); console.log('🔍 logout 함수 존재 여부:', typeof logout); // 각 페이지의 로그아웃 함수가 있으면 호출 if (typeof logout === 'function') { await logout(); } else { console.log('🔄 직접 로그아웃 처리 시작...'); // API 로그아웃 시도 if (window.api && typeof window.api.logout === 'function') { console.log('🌐 API 로그아웃 호출...'); try { await window.api.logout(); console.log('✅ API 로그아웃 성공'); } catch (apiError) { console.log('⚠️ API 로그아웃 실패:', apiError); } } else { console.log('⚠️ window.api.logout 함수를 찾을 수 없음'); } // 로컬 스토리지 정리 (항상 실행) console.log('🧹 로컬 스토리지 정리...'); localStorage.removeItem('access_token'); localStorage.removeItem('refresh_token'); localStorage.removeItem('user_info'); console.log('✅ 로그아웃 완료'); } // 로그인 페이지로 리다이렉트 const currentUrl = encodeURIComponent(window.location.pathname + window.location.search); window.location.href = `login.html?redirect=${currentUrl}`; } catch (error) { console.error('❌ 로그아웃 실패:', error); // 에러가 발생해도 로컬 데이터는 정리하고 로그인 페이지로 이동 localStorage.removeItem('access_token'); localStorage.removeItem('refresh_token'); localStorage.removeItem('user_info'); window.location.href = 'login.html'; } }; console.log('✅ 헤더 함수들 초기화 완료'); } /** * 사용자 권한에 따른 메뉴 표시/숨김 */ updateMenuPermissions(user) { // 메뉴 요소들 가져오기 const menuItems = { // 문서 관리 관련 'pdf-manager-nav-item': user.can_manage_books || user.is_admin, 'book-documents-nav-item': user.can_manage_books || user.is_admin, 'book-editor-nav-item': user.can_manage_books || user.is_admin, // 노트 관리 관련 'notes-list-nav-item': user.can_manage_notes || user.is_admin, 'notebooks-nav-item': user.can_manage_notes || user.is_admin, 'note-editor-nav-item': user.can_manage_notes || user.is_admin, // 소설 관리 관련 'story-view-nav-item': user.can_manage_novels || user.is_admin, 'story-reader-nav-item': user.can_manage_novels || user.is_admin, 'memo-tree-nav-item': user.can_manage_novels || user.is_admin, // 할일 관리 - 노트 관리 권한 필요 (올바른 ID 사용) 'todos-nav-link': user.can_manage_notes || user.is_admin, // 검색은 모든 사용자 허용 'search-nav-link': true, // 메인 페이지는 모든 사용자 허용 (문서 보기만) 'index-nav-item': true }; // 각 메뉴 아이템의 표시/숨김 처리 Object.entries(menuItems).forEach(([itemId, hasPermission]) => { const menuItem = document.getElementById(itemId); if (menuItem) { if (hasPermission) { menuItem.classList.remove('hidden'); console.log(`✅ ${itemId} 메뉴 표시`); } else { menuItem.classList.add('hidden'); console.log(`❌ ${itemId} 메뉴 숨김`); } } }); // 드롭다운 메뉴의 링크들도 체크 const dropdownLinks = document.querySelectorAll('.user-dropdown a, .user-dropdown button'); dropdownLinks.forEach(link => { const href = link.getAttribute('href') || ''; let hasPermission = true; if (href.includes('pdf-manager') || href.includes('book-')) { hasPermission = user.can_manage_books || user.is_admin; } else if (href.includes('note') || href.includes('notebook')) { hasPermission = user.can_manage_notes || user.is_admin; } else if (href.includes('story') || href.includes('memo-tree')) { hasPermission = user.can_manage_novels || user.is_admin; } if (hasPermission) { link.classList.remove('hidden'); } else { link.classList.add('hidden'); } }); } /** * 헤더 로드 실패 시 폴백 헤더 표시 */ showFallbackHeader(targetSelector) { const container = document.querySelector(targetSelector); if (container) { container.innerHTML = `
`; } } /** * 현재 페이지 정보 가져오기 */ getCurrentPageInfo() { const path = window.location.pathname; const filename = path.split('/').pop().replace('.html', '') || 'index'; const pageInfo = { filename, isDocumentPage: ['index', 'hierarchy'].includes(filename), isMemoPage: ['memo-tree', 'story-view'].includes(filename) }; return pageInfo; } /** * 페이지별 활성 상태 업데이트 */ updateActiveStates() { const pageInfo = this.getCurrentPageInfo(); // 모든 활성 클래스 제거 document.querySelectorAll('.nav-link, .nav-dropdown-item').forEach(item => { item.classList.remove('active'); }); // 현재 페이지에 따라 활성 상태 설정 console.log('현재 페이지:', pageInfo.filename); if (pageInfo.isDocumentPage) { const docLink = document.getElementById('doc-nav-link'); if (docLink) { docLink.classList.add('active'); console.log('문서 관리 메뉴 활성화'); } } if (pageInfo.isMemoPage) { const memoLink = document.getElementById('memo-nav-link'); if (memoLink) { memoLink.classList.add('active'); console.log('메모장 메뉴 활성화'); } } // 특정 페이지 드롭다운 아이템 활성화 const pageItemMap = { 'index': 'index-nav-item', 'hierarchy': 'hierarchy-nav-item', 'memo-tree': 'memo-tree-nav-item', 'story-view': 'story-view-nav-item', 'search': 'search-nav-link', 'notes': 'notes-nav-link', 'notebooks': 'notebooks-nav-item', 'note-editor': 'note-editor-nav-item' }; const itemId = pageItemMap[pageInfo.filename]; if (itemId) { const item = document.getElementById(itemId); if (item) { item.classList.add('active'); console.log(`${pageInfo.filename} 페이지 아이템 활성화`); } } } } // 전역 인스턴스 생성 window.headerLoader = new HeaderLoader(); // DOM 로드 완료 시 자동 초기화 document.addEventListener('DOMContentLoaded', () => { window.headerLoader.loadHeader(); }); // 헤더 로드 완료 후 활성 상태 업데이트 document.addEventListener('headerLoaded', () => { setTimeout(() => { window.headerLoader.updateActiveStates(); // updateUserMenu 함수 정의 (헤더 로더에서 직접 정의) if (typeof window.updateUserMenu === 'undefined') { window.updateUserMenu = (user) => { console.log('🔄 updateUserMenu 호출됨:', user); const loggedInMenu = document.getElementById('logged-in-menu'); const loginButton = document.getElementById('login-button'); const adminMenuSection = document.getElementById('admin-menu-section'); // 사용자 정보 요소들 const userName = document.getElementById('user-name'); const userRole = document.getElementById('user-role'); const dropdownUserName = document.getElementById('dropdown-user-name'); const dropdownUserEmail = document.getElementById('dropdown-user-email'); const dropdownUserRole = document.getElementById('dropdown-user-role'); if (user) { // 로그인된 상태 console.log('✅ 사용자 로그인 상태 - UI 업데이트'); if (loggedInMenu) { loggedInMenu.classList.remove('hidden'); console.log('✅ 로그인 메뉴 표시'); } if (loginButton) { loginButton.classList.add('hidden'); console.log('✅ 로그인 버튼 숨김'); } // 사용자 정보 업데이트 const displayName = user.full_name || user.email || 'User'; const roleText = user.role === 'root' ? '시스템 관리자' : user.role === 'admin' ? '관리자' : '사용자'; if (userName) userName.textContent = displayName; if (userRole) userRole.textContent = roleText; if (dropdownUserName) dropdownUserName.textContent = displayName; if (dropdownUserEmail) dropdownUserEmail.textContent = user.email || ''; if (dropdownUserRole) dropdownUserRole.textContent = roleText; // 사용자별 메뉴 권한 체크 console.log('🔍 사용자 권한 확인:', { role: user.role, is_admin: user.is_admin, can_manage_books: user.can_manage_books, can_manage_notes: user.can_manage_notes, can_manage_novels: user.can_manage_novels }); // 개별 메뉴 권한 체크 window.headerLoader.updateMenuPermissions(user); // 관리자 메뉴 표시/숨김 (전체 관리자만) if (adminMenuSection) { if (user.role === 'root' || user.role === 'admin' || user.is_admin) { console.log('✅ 관리자 메뉴 표시'); adminMenuSection.classList.remove('hidden'); } else { console.log('❌ 관리자 메뉴 숨김'); adminMenuSection.classList.add('hidden'); } } else { console.log('❌ adminMenuSection 요소를 찾을 수 없음'); } } else { // 로그아웃된 상태 console.log('❌ 로그아웃 상태'); if (loggedInMenu) loggedInMenu.classList.add('hidden'); if (loginButton) loginButton.classList.remove('hidden'); if (adminMenuSection) adminMenuSection.classList.add('hidden'); } }; console.log('✅ updateUserMenu 함수 정의 완료'); } // 사용자 메뉴 상태 설정 (현재 로그인 상태 확인) setTimeout(() => { // 전역 사용자 정보가 있으면 사용, 없으면 토큰으로 확인 if (window.currentUser) { window.updateUserMenu(window.currentUser); } else { // 토큰이 있으면 사용자 정보 다시 가져오기 const token = localStorage.getItem('access_token'); if (token) { fetch('/api/auth/me', { headers: { 'Authorization': `Bearer ${token}` } }) .then(response => response.ok ? response.json() : null) .then(user => { if (user) { window.currentUser = user; window.updateUserMenu(user); } else { window.updateUserMenu(null); } }) .catch(() => window.updateUserMenu(null)); } else { window.updateUserMenu(null); } } }, 200); // 전역 함수들이 정의되지 않은 경우 빈 함수로 초기화 if (typeof window.handleLanguageChange === 'undefined') { window.handleLanguageChange = function(lang) { console.log('언어 변경 함수가 아직 로드되지 않았습니다:', lang); }; } }, 100); });