Fix: 업로드 및 API 연결 문제 해결
- FastAPI 라우터에서 슬래시 문제로 인한 307 리다이렉트 수정 - Nginx 프록시 설정에서 경로 중복 문제 해결 - 계정 관리 시스템 구현 (로그인, 사용자 관리, 권한 설정) - 노트북 연결 기능 수정 (notebook_id 필드 추가) - 메모 트리 UI 개선 (수평 레이아웃, 드래그 기능 제거) - 헤더 UI 개선 및 고정 위치 설정 - 백업/복원 스크립트 추가 - PDF 미리보기 토큰 인증 지원
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<!-- 공통 헤더 컴포넌트 -->
|
||||
<header class="header-modern fade-in">
|
||||
<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">
|
||||
<!-- 로고 -->
|
||||
@@ -8,107 +8,230 @@
|
||||
<h1 class="text-xl font-bold text-gray-900">Document Server</h1>
|
||||
</div>
|
||||
|
||||
<!-- 메인 네비게이션 - 3가지 기능 -->
|
||||
<nav class="flex space-x-6">
|
||||
<!-- 문서 관리 시스템 -->
|
||||
<!-- 메인 네비게이션 -->
|
||||
<nav class="hidden md:flex items-center space-x-1 relative">
|
||||
<!-- 문서 관리 -->
|
||||
<div class="relative" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false">
|
||||
<a href="index.html" class="nav-link" id="doc-nav-link">
|
||||
<i class="fas fa-folder-open"></i>
|
||||
<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"></i>
|
||||
</a>
|
||||
<div x-show="open" x-transition class="nav-dropdown">
|
||||
<a href="index.html" class="nav-dropdown-item" id="index-nav-item">
|
||||
<i class="fas fa-th-large mr-2 text-blue-500"></i>문서 관리
|
||||
</a>
|
||||
<a href="pdf-manager.html" class="nav-dropdown-item" id="pdf-manager-nav-item">
|
||||
<i class="fas fa-file-pdf mr-2 text-red-500"></i>PDF 관리
|
||||
</a>
|
||||
<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" id="search-nav-link">
|
||||
<i class="fas fa-search"></i>
|
||||
<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">
|
||||
<a href="memo-tree.html" class="nav-link" id="novel-nav-link">
|
||||
<i class="fas fa-feather-alt"></i>
|
||||
<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"></i>
|
||||
</a>
|
||||
<div x-show="open" x-transition class="nav-dropdown">
|
||||
<a href="memo-tree.html" class="nav-dropdown-item" id="memo-tree-nav-item">
|
||||
<i class="fas fa-sitemap mr-2 text-purple-500"></i>트리 뷰
|
||||
</a>
|
||||
<a href="story-view.html" class="nav-dropdown-item" id="story-view-nav-item">
|
||||
<i class="fas fa-book-open mr-2 text-orange-500"></i>스토리 뷰
|
||||
</a>
|
||||
<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">
|
||||
<a href="notes.html" class="nav-link" id="notes-nav-link">
|
||||
<i class="fas fa-sticky-note"></i>
|
||||
<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"></i>
|
||||
</a>
|
||||
<div x-show="open" x-transition class="nav-dropdown">
|
||||
<a href="notebooks.html" class="nav-dropdown-item" id="notebooks-nav-item">
|
||||
<i class="fas fa-book mr-2 text-blue-500"></i>노트북 관리
|
||||
</a>
|
||||
<a href="notes.html" class="nav-dropdown-item" id="notes-list-nav-item">
|
||||
<i class="fas fa-list mr-2 text-green-500"></i>노트 목록
|
||||
</a>
|
||||
<a href="note-editor.html" class="nav-dropdown-item" id="note-editor-nav-item">
|
||||
<i class="fas fa-edit mr-2 text-purple-500"></i>새 노트 작성
|
||||
</a>
|
||||
<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">
|
||||
<!-- PDF 관리 버튼 -->
|
||||
<a href="pdf-manager.html" class="nav-link" title="PDF 관리">
|
||||
<i class="fas fa-file-pdf text-red-500"></i>
|
||||
<span class="hidden sm:inline">PDF</span>
|
||||
</a>
|
||||
|
||||
|
||||
<!-- 언어 전환 버튼 -->
|
||||
<div class="relative" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false">
|
||||
<button class="nav-link" title="언어 설정">
|
||||
<i class="fas fa-globe"></i>
|
||||
<span class="hidden sm:inline">한국어</span>
|
||||
<i class="fas fa-chevron-down text-xs ml-1"></i>
|
||||
</button>
|
||||
<div x-show="open" x-transition class="nav-dropdown">
|
||||
<button class="nav-dropdown-item" onclick="handleLanguageChange('ko')">
|
||||
<i class="fas fa-flag mr-2 text-blue-500"></i>한국어
|
||||
</button>
|
||||
<button class="nav-dropdown-item" onclick="handleLanguageChange('en')">
|
||||
<i class="fas fa-flag mr-2 text-red-500"></i>English
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 로그인/로그아웃 -->
|
||||
<!-- 사용자 계정 메뉴 -->
|
||||
<div class="flex items-center space-x-3" id="user-menu">
|
||||
<!-- 로그인된 사용자 -->
|
||||
<div class="hidden" id="logged-in-menu">
|
||||
<span class="text-sm text-gray-600" id="user-name">User</span>
|
||||
<button onclick="handleLogout()" class="btn-improved btn-secondary-improved text-sm">
|
||||
<i class="fas fa-sign-out-alt"></i> 로그아웃
|
||||
<!-- 로그인된 사용자 드롭다운 -->
|
||||
<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="hidden" id="login-button">
|
||||
<button onclick="handleLogin()" class="btn-improved btn-primary-improved">
|
||||
<i class="fas fa-sign-in-alt"></i> 로그인
|
||||
<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>
|
||||
@@ -119,35 +242,128 @@
|
||||
|
||||
<!-- 헤더 관련 스타일 -->
|
||||
<style>
|
||||
/* 네비게이션 링크 스타일 */
|
||||
.nav-link {
|
||||
@apply text-gray-600 hover:text-blue-600 flex items-center space-x-1 py-2 px-3 rounded-lg transition-all duration-200;
|
||||
/* 모던 네비게이션 링크 스타일 */
|
||||
.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.active {
|
||||
@apply text-blue-600 bg-blue-50 font-medium;
|
||||
.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:hover {
|
||||
@apply bg-gray-50;
|
||||
.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 {
|
||||
@apply absolute top-full left-0 mt-1 bg-white border border-gray-200 rounded-lg shadow-lg py-2 min-w-44 z-50;
|
||||
/* 와이드 드롭다운 메뉴 스타일 */
|
||||
.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-item {
|
||||
@apply block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 transition-colors duration-150;
|
||||
.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-item.active {
|
||||
@apply text-blue-600 bg-blue-50 font-medium;
|
||||
.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 border-b border-gray-200 shadow-sm;
|
||||
@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;
|
||||
}
|
||||
|
||||
/* 언어 전환 스타일 */
|
||||
@@ -218,34 +434,9 @@
|
||||
|
||||
// 로그인 관련 함수들
|
||||
window.handleLogin = () => {
|
||||
console.log('🔐 handleLogin 호출됨');
|
||||
|
||||
// Alpine.js 컨텍스트에서 함수 찾기
|
||||
const bodyElement = document.querySelector('body');
|
||||
if (bodyElement && bodyElement._x_dataStack) {
|
||||
const alpineData = bodyElement._x_dataStack[0];
|
||||
if (alpineData && typeof alpineData.openLoginModal === 'function') {
|
||||
console.log('✅ Alpine 컨텍스트에서 openLoginModal 호출');
|
||||
alpineData.openLoginModal();
|
||||
return;
|
||||
}
|
||||
if (alpineData && alpineData.showLoginModal !== undefined) {
|
||||
console.log('✅ Alpine 컨텍스트에서 showLoginModal 설정');
|
||||
alpineData.showLoginModal = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 전역 함수로 시도
|
||||
if (typeof window.openLoginModal === 'function') {
|
||||
console.log('✅ 전역 openLoginModal 호출');
|
||||
window.openLoginModal();
|
||||
return;
|
||||
}
|
||||
|
||||
// 직접 이벤트 발생
|
||||
console.log('🔄 커스텀 이벤트로 로그인 모달 열기');
|
||||
document.dispatchEvent(new CustomEvent('open-login-modal'));
|
||||
console.log('🔐 handleLogin 호출됨 - 로그인 페이지로 이동');
|
||||
const currentUrl = encodeURIComponent(window.location.href);
|
||||
window.location.href = `login.html?redirect=${currentUrl}`;
|
||||
};
|
||||
|
||||
window.handleLogout = () => {
|
||||
@@ -259,22 +450,73 @@
|
||||
|
||||
// 사용자 상태 업데이트 함수
|
||||
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) {
|
||||
// 로그인된 상태
|
||||
if (loggedInMenu) loggedInMenu.classList.remove('hidden');
|
||||
if (loginButton) loginButton.classList.add('hidden');
|
||||
if (userName) userName.textContent = user.username || user.full_name || user.email || '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) => {
|
||||
@@ -335,9 +577,27 @@
|
||||
// 향후 다국어 지원 시 구현
|
||||
};
|
||||
|
||||
// 헤더 로드 완료 후 언어 설정 적용
|
||||
// 헤더 로드 완료 후 이벤트 바인딩
|
||||
document.addEventListener('headerLoaded', () => {
|
||||
console.log('🔧 헤더 로드 완료 - 언어 전환 함수 등록');
|
||||
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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user