Major UI overhaul and upload system improvements
- Removed hierarchy view and integrated functionality into index.html - Added book-based document grouping with dedicated book-documents.html page - Implemented comprehensive multi-file upload system with drag-and-drop reordering - Added HTML-PDF matching functionality with download capability - Enhanced upload workflow with 3-step process (File Selection, Book Settings, Order & Match) - Added book conflict resolution (existing book vs new edition) - Improved document order adjustment with one-click sort options - Added modular header component system - Updated API connectivity for Docker environment - Enhanced viewer.html with PDF download functionality - Fixed browser caching issues with version management - Improved mobile responsiveness and modern UI design
This commit is contained in:
211
frontend/components/header.html
Normal file
211
frontend/components/header.html
Normal file
@@ -0,0 +1,211 @@
|
||||
<!-- 공통 헤더 컴포넌트 -->
|
||||
<header class="header-modern fade-in">
|
||||
<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>
|
||||
|
||||
<!-- 메인 네비게이션 - 2가지 기능만 -->
|
||||
<nav class="flex space-x-8">
|
||||
<!-- 문서 관리 시스템 -->
|
||||
<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>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 메모장 시스템 -->
|
||||
<div class="relative" x-data="{ open: false }" @mouseenter="open = true" @mouseleave="open = false">
|
||||
<a href="memo-tree.html" class="nav-link" id="memo-nav-link">
|
||||
<i class="fas fa-sitemap"></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>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- 사용자 메뉴 -->
|
||||
<div class="flex items-center space-x-4">
|
||||
<!-- 로그인/로그아웃 -->
|
||||
<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> 로그아웃
|
||||
</button>
|
||||
</div>
|
||||
<!-- 로그인 버튼 -->
|
||||
<div class="hidden" id="login-button">
|
||||
<button onclick="handleLogin()" class="btn-improved btn-primary-improved">
|
||||
<i class="fas fa-sign-in-alt"></i> 로그인
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 헤더 관련 스타일 -->
|
||||
<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.active {
|
||||
@apply text-blue-600 bg-blue-50 font-medium;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
@apply bg-gray-50;
|
||||
}
|
||||
|
||||
/* 드롭다운 메뉴 스타일 */
|
||||
.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-item {
|
||||
@apply block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 transition-colors duration-150;
|
||||
}
|
||||
|
||||
.nav-dropdown-item.active {
|
||||
@apply text-blue-600 bg-blue-50 font-medium;
|
||||
}
|
||||
|
||||
/* 헤더 모던 스타일 */
|
||||
.header-modern {
|
||||
@apply bg-white border-b border-gray-200 shadow-sm;
|
||||
}
|
||||
</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'].includes(page);
|
||||
},
|
||||
|
||||
isMemoPage() {
|
||||
const page = this.getCurrentPage();
|
||||
return ['memo-tree', 'story-view'].includes(page);
|
||||
}
|
||||
};
|
||||
|
||||
// Alpine.js 전역 함수로 등록 - 즉시 실행
|
||||
if (typeof Alpine !== 'undefined') {
|
||||
// Alpine이 이미 로드된 경우
|
||||
Alpine.store('header', {
|
||||
getCurrentPage: () => headerUtils.getCurrentPage(),
|
||||
isDocumentPage: () => headerUtils.isDocumentPage(),
|
||||
isMemoPage: () => headerUtils.isMemoPage(),
|
||||
});
|
||||
} else {
|
||||
// Alpine 로드 대기
|
||||
document.addEventListener('alpine:init', () => {
|
||||
Alpine.store('header', {
|
||||
getCurrentPage: () => headerUtils.getCurrentPage(),
|
||||
isDocumentPage: () => headerUtils.isDocumentPage(),
|
||||
isMemoPage: () => headerUtils.isMemoPage(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 전역 함수로도 등록 (Alpine 외부에서도 사용 가능)
|
||||
window.getCurrentPage = () => headerUtils.getCurrentPage();
|
||||
window.isDocumentPage = () => headerUtils.isDocumentPage();
|
||||
window.isMemoPage = () => headerUtils.isMemoPage();
|
||||
|
||||
// 로그인 관련 함수들
|
||||
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'));
|
||||
};
|
||||
|
||||
window.handleLogout = () => {
|
||||
// 각 페이지의 로그아웃 함수 호출
|
||||
if (typeof logout === 'function') {
|
||||
logout();
|
||||
} else {
|
||||
console.log('로그아웃 함수를 찾을 수 없습니다.');
|
||||
}
|
||||
};
|
||||
|
||||
// 사용자 상태 업데이트 함수
|
||||
window.updateUserMenu = (user) => {
|
||||
const loggedInMenu = document.getElementById('logged-in-menu');
|
||||
const loginButton = document.getElementById('login-button');
|
||||
const userName = document.getElementById('user-name');
|
||||
|
||||
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';
|
||||
} else {
|
||||
// 로그아웃된 상태
|
||||
if (loggedInMenu) loggedInMenu.classList.add('hidden');
|
||||
if (loginButton) loginButton.classList.remove('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
// 언어 토글 함수 (전역)
|
||||
window.toggleLanguage = () => {
|
||||
console.log('🌐 언어 토글 기능 (미구현)');
|
||||
// 향후 다국어 지원 시 구현
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user