- FastAPI 라우터에서 슬래시 문제로 인한 307 리다이렉트 수정 - Nginx 프록시 설정에서 경로 중복 문제 해결 - 계정 관리 시스템 구현 (로그인, 사용자 관리, 권한 설정) - 노트북 연결 기능 수정 (notebook_id 필드 추가) - 메모 트리 UI 개선 (수평 레이아웃, 드래그 기능 제거) - 헤더 UI 개선 및 고정 위치 설정 - 백업/복원 스크립트 추가 - PDF 미리보기 토큰 인증 지원
621 lines
30 KiB
HTML
621 lines
30 KiB
HTML
<!-- 공통 헤더 컴포넌트 -->
|
|
<header class="header-modern fade-in fixed top-0 left-0 right-0 z-50">
|
|
<div class="max-w-full mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div class="flex justify-between items-center h-16">
|
|
<!-- 로고 -->
|
|
<div class="flex items-center space-x-2">
|
|
<i class="fas fa-book text-blue-600 text-xl"></i>
|
|
<h1 class="text-xl font-bold text-gray-900">Document Server</h1>
|
|
</div>
|
|
|
|
<!-- 메인 네비게이션 -->
|
|
<nav class="hidden md:flex items-center space-x-1 relative">
|
|
<!-- 문서 관리 -->
|
|
<div class="relative" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false">
|
|
<button class="nav-link-modern" id="doc-nav-link">
|
|
<i class="fas fa-folder-open text-blue-600"></i>
|
|
<span>문서 관리</span>
|
|
<i class="fas fa-chevron-down text-xs ml-1 transition-transform duration-200" :class="{ 'rotate-180': open }"></i>
|
|
</button>
|
|
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0 transform scale-95" x-transition:enter-end="opacity-100 transform scale-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100 transform scale-100" x-transition:leave-end="opacity-0 transform scale-95" class="nav-dropdown-wide">
|
|
<div class="grid grid-cols-2 gap-2">
|
|
<a href="index.html" class="nav-dropdown-card" id="index-nav-item">
|
|
<div class="flex items-center space-x-3">
|
|
<div class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
|
|
<i class="fas fa-th-large text-blue-600"></i>
|
|
</div>
|
|
<div>
|
|
<div class="font-semibold text-gray-900">문서 관리</div>
|
|
<div class="text-xs text-gray-500">HTML 문서 관리</div>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
<a href="pdf-manager.html" class="nav-dropdown-card" id="pdf-manager-nav-item">
|
|
<div class="flex items-center space-x-3">
|
|
<div class="w-10 h-10 bg-red-100 rounded-lg flex items-center justify-center">
|
|
<i class="fas fa-file-pdf text-red-600"></i>
|
|
</div>
|
|
<div>
|
|
<div class="font-semibold text-gray-900">PDF 관리</div>
|
|
<div class="text-xs text-gray-500">PDF 파일 관리</div>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 통합 검색 -->
|
|
<a href="search.html" class="nav-link-modern" id="search-nav-link">
|
|
<i class="fas fa-search text-green-600"></i>
|
|
<span>통합 검색</span>
|
|
</a>
|
|
|
|
<!-- 소설 관리 -->
|
|
<div class="relative" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false">
|
|
<button class="nav-link-modern" id="novel-nav-link">
|
|
<i class="fas fa-feather-alt text-purple-600"></i>
|
|
<span>소설 관리</span>
|
|
<i class="fas fa-chevron-down text-xs ml-1 transition-transform duration-200" :class="{ 'rotate-180': open }"></i>
|
|
</button>
|
|
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0 transform scale-95" x-transition:enter-end="opacity-100 transform scale-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100 transform scale-100" x-transition:leave-end="opacity-0 transform scale-95" class="nav-dropdown-wide">
|
|
<div class="grid grid-cols-2 gap-2">
|
|
<a href="memo-tree.html" class="nav-dropdown-card" id="memo-tree-nav-item">
|
|
<div class="flex items-center space-x-3">
|
|
<div class="w-10 h-10 bg-purple-100 rounded-lg flex items-center justify-center">
|
|
<i class="fas fa-sitemap text-purple-600"></i>
|
|
</div>
|
|
<div>
|
|
<div class="font-semibold text-gray-900">트리 뷰</div>
|
|
<div class="text-xs text-gray-500">계층형 메모 관리</div>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
<a href="story-view.html" class="nav-dropdown-card" id="story-view-nav-item">
|
|
<div class="flex items-center space-x-3">
|
|
<div class="w-10 h-10 bg-orange-100 rounded-lg flex items-center justify-center">
|
|
<i class="fas fa-book-open text-orange-600"></i>
|
|
</div>
|
|
<div>
|
|
<div class="font-semibold text-gray-900">스토리 뷰</div>
|
|
<div class="text-xs text-gray-500">스토리 읽기 모드</div>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 노트 관리 -->
|
|
<div class="relative" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false">
|
|
<button class="nav-link-modern" id="notes-nav-link">
|
|
<i class="fas fa-sticky-note text-yellow-600"></i>
|
|
<span>노트 관리</span>
|
|
<i class="fas fa-chevron-down text-xs ml-1 transition-transform duration-200" :class="{ 'rotate-180': open }"></i>
|
|
</button>
|
|
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0 transform scale-95" x-transition:enter-end="opacity-100 transform scale-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100 transform scale-100" x-transition:leave-end="opacity-0 transform scale-95" class="nav-dropdown-wide">
|
|
<div class="grid grid-cols-3 gap-2">
|
|
<a href="notebooks.html" class="nav-dropdown-card" id="notebooks-nav-item">
|
|
<div class="flex flex-col items-center text-center space-y-2">
|
|
<div class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
|
|
<i class="fas fa-book text-blue-600"></i>
|
|
</div>
|
|
<div>
|
|
<div class="font-semibold text-gray-900 text-sm">노트북 관리</div>
|
|
<div class="text-xs text-gray-500">그룹 관리</div>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
<a href="notes.html" class="nav-dropdown-card" id="notes-list-nav-item">
|
|
<div class="flex flex-col items-center text-center space-y-2">
|
|
<div class="w-10 h-10 bg-green-100 rounded-lg flex items-center justify-center">
|
|
<i class="fas fa-list text-green-600"></i>
|
|
</div>
|
|
<div>
|
|
<div class="font-semibold text-gray-900 text-sm">노트 목록</div>
|
|
<div class="text-xs text-gray-500">전체 보기</div>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
<a href="note-editor.html" class="nav-dropdown-card" id="note-editor-nav-item">
|
|
<div class="flex flex-col items-center text-center space-y-2">
|
|
<div class="w-10 h-10 bg-purple-100 rounded-lg flex items-center justify-center">
|
|
<i class="fas fa-edit text-purple-600"></i>
|
|
</div>
|
|
<div>
|
|
<div class="font-semibold text-gray-900 text-sm">새 노트 작성</div>
|
|
<div class="text-xs text-gray-500">노트 만들기</div>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- 모바일 메뉴 버튼 -->
|
|
<div class="md:hidden">
|
|
<button x-data="{ open: false }" @click="open = !open" class="mobile-menu-btn">
|
|
<i class="fas fa-bars"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- 사용자 메뉴 -->
|
|
<div class="flex items-center space-x-4">
|
|
|
|
|
|
<!-- 사용자 계정 메뉴 -->
|
|
<div class="flex items-center space-x-3" id="user-menu">
|
|
<!-- 로그인된 사용자 드롭다운 -->
|
|
<div class="hidden relative" id="logged-in-menu" x-data="{ open: false }" @click.away="open = false">
|
|
<button @click="open = !open" class="user-menu-btn">
|
|
<div class="w-9 h-9 bg-gradient-to-br from-blue-500 to-blue-600 rounded-full flex items-center justify-center shadow-sm">
|
|
<i class="fas fa-user text-white text-sm"></i>
|
|
</div>
|
|
<div class="hidden sm:block text-left">
|
|
<div class="text-sm font-semibold text-gray-900" id="user-name">User</div>
|
|
<div class="text-xs text-gray-500" id="user-role">사용자</div>
|
|
</div>
|
|
<i class="fas fa-chevron-down text-xs ml-1 transition-transform duration-200" :class="{ 'rotate-180': open }"></i>
|
|
</button>
|
|
|
|
<!-- 드롭다운 메뉴 -->
|
|
<div x-show="open" x-transition:enter="transition ease-out duration-150" x-transition:enter-start="opacity-0 transform scale-95" x-transition:enter-end="opacity-100 transform scale-100" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100 transform scale-100" x-transition:leave-end="opacity-0 transform scale-95" class="user-dropdown">
|
|
<!-- 사용자 정보 -->
|
|
<div class="px-4 py-4 border-b border-gray-100 bg-gradient-to-r from-blue-50 to-indigo-50">
|
|
<div class="flex items-center space-x-3">
|
|
<div class="w-12 h-12 bg-gradient-to-br from-blue-500 to-blue-600 rounded-full flex items-center justify-center shadow-md">
|
|
<i class="fas fa-user text-white"></i>
|
|
</div>
|
|
<div>
|
|
<div class="font-semibold text-gray-900" id="dropdown-user-name">User</div>
|
|
<div class="text-sm text-gray-600" id="dropdown-user-email">user@example.com</div>
|
|
<div class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800 mt-1" id="dropdown-user-role">사용자</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 메뉴 항목들 -->
|
|
<div class="py-2">
|
|
<a href="profile.html" class="user-menu-item">
|
|
<i class="fas fa-user-edit text-blue-500"></i>
|
|
<div>
|
|
<div class="font-medium">프로필 관리</div>
|
|
<div class="text-xs text-gray-500">개인 정보 수정</div>
|
|
</div>
|
|
</a>
|
|
<a href="account-settings.html" class="user-menu-item">
|
|
<i class="fas fa-cog text-gray-500"></i>
|
|
<div>
|
|
<div class="font-medium">계정 설정</div>
|
|
<div class="text-xs text-gray-500">환경 설정 및 보안</div>
|
|
</div>
|
|
</a>
|
|
|
|
<!-- 관리자 메뉴 (관리자만 표시) -->
|
|
<div class="hidden" id="admin-menu-section">
|
|
<div class="border-t border-gray-100 my-2"></div>
|
|
<div class="px-4 py-2">
|
|
<div class="text-xs font-semibold text-gray-500 uppercase tracking-wider">관리자 메뉴</div>
|
|
</div>
|
|
<a href="user-management.html" class="user-menu-item">
|
|
<i class="fas fa-users text-indigo-500"></i>
|
|
<div>
|
|
<div class="font-medium">사용자 관리</div>
|
|
<div class="text-xs text-gray-500">계정 및 권한 관리</div>
|
|
</div>
|
|
</a>
|
|
<a href="system-settings.html" class="user-menu-item">
|
|
<i class="fas fa-server text-green-500"></i>
|
|
<div>
|
|
<div class="font-medium">시스템 설정</div>
|
|
<div class="text-xs text-gray-500">시스템 전체 설정</div>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
|
|
<!-- 로그아웃 -->
|
|
<div class="border-t border-gray-100 my-2"></div>
|
|
<button onclick="handleLogout()" class="user-menu-item text-red-600 hover:bg-red-50 w-full">
|
|
<i class="fas fa-sign-out-alt text-red-500"></i>
|
|
<div>
|
|
<div class="font-medium">로그아웃</div>
|
|
<div class="text-xs text-gray-500">계정에서 로그아웃</div>
|
|
</div>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 로그인 버튼 -->
|
|
<div class="" id="login-button">
|
|
<button id="login-btn" class="login-btn-modern">
|
|
<i class="fas fa-sign-in-alt"></i>
|
|
<span>로그인</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- 헤더 관련 스타일 -->
|
|
<style>
|
|
/* 모던 네비게이션 링크 스타일 */
|
|
.nav-link-modern {
|
|
@apply flex items-center space-x-2 px-4 py-2 text-sm font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50 rounded-lg transition-all duration-200 cursor-pointer;
|
|
border: 1px solid transparent;
|
|
}
|
|
|
|
.nav-link-modern:hover {
|
|
@apply bg-gradient-to-r from-gray-50 to-gray-100 shadow-sm;
|
|
border-color: rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.nav-link-modern.active {
|
|
@apply text-blue-700 bg-blue-50 border-blue-200;
|
|
box-shadow: 0 1px 3px rgba(59, 130, 246, 0.1);
|
|
}
|
|
|
|
/* 와이드 드롭다운 메뉴 스타일 */
|
|
.nav-dropdown-wide {
|
|
position: absolute !important;
|
|
top: 100% !important;
|
|
left: 50% !important;
|
|
transform: translateX(-50%) !important;
|
|
margin-top: 0.5rem !important;
|
|
z-index: 9999 !important;
|
|
min-width: 400px;
|
|
background: rgba(255, 255, 255, 0.98);
|
|
backdrop-filter: blur(15px);
|
|
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
border-radius: 0.75rem;
|
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
|
padding: 1rem;
|
|
pointer-events: auto;
|
|
}
|
|
|
|
.nav-dropdown-card {
|
|
@apply block p-4 bg-white border border-gray-100 rounded-lg hover:border-gray-200 hover:shadow-md transition-all duration-200 cursor-pointer;
|
|
}
|
|
|
|
.nav-dropdown-card:hover {
|
|
@apply bg-gradient-to-br from-gray-50 to-blue-50 transform -translate-y-1;
|
|
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.nav-dropdown-card.active {
|
|
@apply border-blue-200 bg-blue-50;
|
|
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);
|
|
}
|
|
|
|
/* 드롭다운 컨테이너 안정성 */
|
|
nav > div.relative {
|
|
position: relative !important;
|
|
display: inline-block !important;
|
|
}
|
|
|
|
/* 애니메이션 중 위치 고정 */
|
|
.nav-dropdown-wide[x-show] {
|
|
position: absolute !important;
|
|
top: 100% !important;
|
|
left: 50% !important;
|
|
transform: translateX(-50%) !important;
|
|
}
|
|
|
|
/* 모바일 메뉴 버튼 */
|
|
.mobile-menu-btn {
|
|
@apply p-2 text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-lg transition-colors duration-200;
|
|
}
|
|
|
|
/* 사용자 메뉴 스타일 */
|
|
.user-menu-btn {
|
|
@apply flex items-center space-x-3 px-3 py-2 text-gray-700 hover:text-gray-900 hover:bg-gray-50 rounded-lg transition-all duration-200 cursor-pointer;
|
|
border: 1px solid transparent;
|
|
}
|
|
|
|
.user-menu-btn:hover {
|
|
@apply shadow-sm;
|
|
border-color: rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.user-dropdown {
|
|
@apply absolute right-0 mt-2 w-80 bg-white border border-gray-200 rounded-xl shadow-xl py-0 z-50;
|
|
backdrop-filter: blur(10px);
|
|
background: rgba(255, 255, 255, 0.98);
|
|
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
|
}
|
|
|
|
.user-menu-item {
|
|
@apply flex items-center space-x-3 px-4 py-3 text-sm text-gray-700 hover:bg-gray-50 transition-all duration-150 cursor-pointer;
|
|
}
|
|
|
|
.user-menu-item:hover {
|
|
@apply bg-gradient-to-r from-gray-50 to-blue-50 text-gray-900;
|
|
transform: translateX(2px);
|
|
}
|
|
|
|
.user-menu-item i {
|
|
@apply w-5 h-5 flex items-center justify-center;
|
|
}
|
|
|
|
/* 로그인 버튼 모던 스타일 */
|
|
.login-btn-modern {
|
|
@apply flex items-center space-x-2 px-4 py-2 bg-gradient-to-r from-blue-600 to-blue-700 text-white font-medium rounded-lg shadow-sm hover:shadow-md transition-all duration-200;
|
|
}
|
|
|
|
.login-btn-modern:hover {
|
|
@apply from-blue-700 to-blue-800 transform -translate-y-0.5;
|
|
box-shadow: 0 10px 25px rgba(59, 130, 246, 0.3);
|
|
}
|
|
|
|
.login-btn-modern:active {
|
|
@apply transform translate-y-0;
|
|
}
|
|
|
|
/* 헤더 모던 스타일 */
|
|
.header-modern {
|
|
@apply bg-white/95 backdrop-blur-md border-b border-gray-200/50 shadow-lg;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
/* 헤더 호버 효과 */
|
|
.header-modern:hover {
|
|
@apply bg-white shadow-xl;
|
|
}
|
|
|
|
/* 언어 전환 스타일 */
|
|
.lang-ko .lang-en,
|
|
.lang-ko [lang="en"],
|
|
.lang-ko .english {
|
|
display: none !important;
|
|
}
|
|
|
|
.lang-en .lang-ko,
|
|
.lang-en [lang="ko"],
|
|
.lang-en .korean {
|
|
display: none !important;
|
|
}
|
|
</style>
|
|
|
|
<!-- 헤더 관련 JavaScript 함수들 -->
|
|
<script>
|
|
// 헤더 관련 유틸리티 함수들
|
|
window.headerUtils = {
|
|
getCurrentPage() {
|
|
const path = window.location.pathname;
|
|
const filename = path.split('/').pop().replace('.html', '');
|
|
return filename || 'index';
|
|
},
|
|
|
|
isDocumentPage() {
|
|
const page = this.getCurrentPage();
|
|
return ['index', 'hierarchy', 'pdf-manager'].includes(page);
|
|
},
|
|
|
|
isNovelPage() {
|
|
const page = this.getCurrentPage();
|
|
return ['memo-tree', 'story-view', 'story-reader'].includes(page);
|
|
},
|
|
|
|
isNotePage() {
|
|
const page = this.getCurrentPage();
|
|
return ['notes', 'note-editor'].includes(page);
|
|
}
|
|
};
|
|
|
|
// Alpine.js 전역 함수로 등록 - 즉시 실행
|
|
if (typeof Alpine !== 'undefined') {
|
|
// Alpine이 이미 로드된 경우
|
|
Alpine.store('header', {
|
|
getCurrentPage: () => headerUtils.getCurrentPage(),
|
|
isDocumentPage: () => headerUtils.isDocumentPage(),
|
|
isNovelPage: () => headerUtils.isNovelPage(),
|
|
isNotePage: () => headerUtils.isNotePage(),
|
|
});
|
|
} else {
|
|
// Alpine 로드 대기
|
|
document.addEventListener('alpine:init', () => {
|
|
Alpine.store('header', {
|
|
getCurrentPage: () => headerUtils.getCurrentPage(),
|
|
isDocumentPage: () => headerUtils.isDocumentPage(),
|
|
isNovelPage: () => headerUtils.isNovelPage(),
|
|
isNotePage: () => headerUtils.isNotePage(),
|
|
});
|
|
});
|
|
}
|
|
|
|
// 전역 함수로도 등록 (Alpine 외부에서도 사용 가능)
|
|
window.getCurrentPage = () => headerUtils.getCurrentPage();
|
|
window.isDocumentPage = () => headerUtils.isDocumentPage();
|
|
window.isMemoPage = () => headerUtils.isMemoPage();
|
|
|
|
// 로그인 관련 함수들
|
|
window.handleLogin = () => {
|
|
console.log('🔐 handleLogin 호출됨 - 로그인 페이지로 이동');
|
|
const currentUrl = encodeURIComponent(window.location.href);
|
|
window.location.href = `login.html?redirect=${currentUrl}`;
|
|
};
|
|
|
|
window.handleLogout = () => {
|
|
// 각 페이지의 로그아웃 함수 호출
|
|
if (typeof logout === 'function') {
|
|
logout();
|
|
} else {
|
|
console.log('로그아웃 함수를 찾을 수 없습니다.');
|
|
}
|
|
};
|
|
|
|
// 사용자 상태 업데이트 함수
|
|
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');
|
|
|
|
console.log('🔍 요소 찾기:', {
|
|
loggedInMenu: !!loggedInMenu,
|
|
loginButton: !!loginButton,
|
|
adminMenuSection: !!adminMenuSection
|
|
});
|
|
|
|
// 사용자 정보 요소들
|
|
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 = getRoleText(user.role);
|
|
|
|
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;
|
|
|
|
// 관리자 메뉴 표시/숨김
|
|
if (adminMenuSection) {
|
|
if (user.role === 'root' || user.role === 'admin' || user.is_admin) {
|
|
adminMenuSection.classList.remove('hidden');
|
|
} else {
|
|
adminMenuSection.classList.add('hidden');
|
|
}
|
|
}
|
|
} else {
|
|
// 로그아웃된 상태
|
|
if (loggedInMenu) loggedInMenu.classList.add('hidden');
|
|
if (loginButton) loginButton.classList.remove('hidden');
|
|
if (adminMenuSection) adminMenuSection.classList.add('hidden');
|
|
}
|
|
};
|
|
|
|
// 역할 텍스트 변환 함수
|
|
function getRoleText(role) {
|
|
const roleMap = {
|
|
'root': '시스템 관리자',
|
|
'admin': '관리자',
|
|
'user': '사용자'
|
|
};
|
|
return roleMap[role] || '사용자';
|
|
}
|
|
|
|
// 언어 토글 함수 (전역)
|
|
// 통합 언어 변경 함수
|
|
window.handleLanguageChange = (lang) => {
|
|
console.log('🌐 언어 변경 요청:', lang);
|
|
localStorage.setItem('preferred_language', lang);
|
|
|
|
// HTML lang 속성 변경
|
|
document.documentElement.lang = lang;
|
|
|
|
// body에 언어 클래스 추가/제거
|
|
document.body.classList.remove('lang-ko', 'lang-en');
|
|
document.body.classList.add(`lang-${lang}`);
|
|
|
|
// 뷰어 페이지인 경우 뷰어의 언어 전환 함수 호출
|
|
if (window.documentViewerInstance && typeof window.documentViewerInstance.toggleLanguage === 'function') {
|
|
window.documentViewerInstance.toggleLanguage();
|
|
}
|
|
|
|
// 문서 내용에서 언어별 요소 처리
|
|
toggleDocumentLanguage(lang);
|
|
|
|
// 헤더 언어 표시 업데이트
|
|
updateLanguageDisplay(lang);
|
|
|
|
console.log(`✅ 언어가 ${lang === 'ko' ? '한국어' : 'English'}로 설정되었습니다.`);
|
|
};
|
|
|
|
// 문서 내용 언어 전환
|
|
function toggleDocumentLanguage(lang) {
|
|
// 언어별 요소 숨기기/보이기
|
|
const koElements = document.querySelectorAll('[lang="ko"], .lang-ko, .korean');
|
|
const enElements = document.querySelectorAll('[lang="en"], .lang-en, .english');
|
|
|
|
if (lang === 'ko') {
|
|
koElements.forEach(el => el.style.display = '');
|
|
enElements.forEach(el => el.style.display = 'none');
|
|
} else {
|
|
koElements.forEach(el => el.style.display = 'none');
|
|
enElements.forEach(el => el.style.display = '');
|
|
}
|
|
|
|
console.log(`🔄 문서 언어 전환: ${koElements.length}개 한국어, ${enElements.length}개 영어 요소 처리`);
|
|
}
|
|
|
|
// 헤더 언어 표시 업데이트
|
|
function updateLanguageDisplay(lang) {
|
|
const langSpan = document.querySelector('.nav-link span:contains("한국어"), .nav-link span:contains("English")');
|
|
if (langSpan) {
|
|
langSpan.textContent = lang === 'ko' ? '한국어' : 'English';
|
|
}
|
|
}
|
|
|
|
// 기존 setLanguage 함수 (호환성 유지)
|
|
window.setLanguage = window.handleLanguageChange;
|
|
|
|
window.toggleLanguage = () => {
|
|
console.log('🌐 언어 토글 기능 (미구현)');
|
|
// 향후 다국어 지원 시 구현
|
|
};
|
|
|
|
// 헤더 로드 완료 후 이벤트 바인딩
|
|
document.addEventListener('headerLoaded', () => {
|
|
console.log('🔧 헤더 로드 완료 - 이벤트 바인딩 시작');
|
|
|
|
// 로그인 버튼 이벤트 리스너 추가
|
|
const loginBtn = document.getElementById('login-btn');
|
|
if (loginBtn) {
|
|
loginBtn.addEventListener('click', () => {
|
|
console.log('🔐 로그인 버튼 클릭됨');
|
|
if (typeof window.handleLogin === 'function') {
|
|
window.handleLogin();
|
|
} else {
|
|
console.error('❌ handleLogin 함수를 찾을 수 없습니다');
|
|
}
|
|
});
|
|
console.log('✅ 로그인 버튼 이벤트 리스너 등록 완료');
|
|
}
|
|
|
|
|
|
|
|
// 언어 설정 적용
|
|
const savedLang = localStorage.getItem('preferred_language') || 'ko';
|
|
console.log('💾 저장된 언어 설정 적용:', savedLang);
|
|
|
|
// 약간의 지연 후 적용 (DOM 완전 로드 대기)
|
|
setTimeout(() => {
|
|
handleLanguageChange(savedLang);
|
|
}, 100);
|
|
});
|
|
|
|
// DOMContentLoaded 백업 (헤더가 직접 로드된 경우)
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
setTimeout(() => {
|
|
if (typeof window.handleLanguageChange === 'function') {
|
|
const savedLang = localStorage.getItem('preferred_language') || 'ko';
|
|
console.log('💾 DOMContentLoaded - 언어 설정 적용:', savedLang);
|
|
handleLanguageChange(savedLang);
|
|
}
|
|
}, 200);
|
|
});
|
|
</script>
|