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:
Hyungi Ahn
2025-08-25 15:58:30 +09:00
parent f95f67364a
commit 4038040faa
21 changed files with 3875 additions and 2603 deletions

View File

@@ -9,6 +9,9 @@
<script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="/static/css/main.css">
<!-- 헤더 로더 -->
<script src="static/js/header-loader.js"></script>
</head>
<body class="bg-gray-50 min-h-screen">
<!-- 메인 앱 -->
@@ -48,87 +51,8 @@
</form>
</div>
</div>
<!-- 헤더 -->
<header class="bg-white shadow-sm border-b">
<div class="max-w-7xl 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-4">
<h1 class="text-xl font-bold text-gray-900">
<i class="fas fa-file-alt mr-2"></i>
Document Server
</h1>
<div class="flex space-x-2">
<span class="px-3 py-1 text-sm bg-blue-100 text-blue-800 rounded-full">📖 그리드 뷰</span>
<a href="hierarchy.html" class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-full hover:bg-gray-200">📚 계층구조 뷰</a>
<a href="memo-tree.html" class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-full hover:bg-gray-200">🌳 트리 메모장</a>
</div>
</div>
<!-- 검색바 -->
<div class="flex-1 max-w-lg mx-8" x-show="isAuthenticated">
<div class="relative">
<input type="text" x-model="searchQuery" @input="searchDocuments"
placeholder="문서, 메모 검색..."
class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
<i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
</div>
</div>
<!-- 사용자 메뉴 -->
<div class="flex items-center space-x-4">
<template x-if="!isAuthenticated">
<button @click="showLoginModal = true"
class="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700">
로그인
</button>
</template>
<template x-if="isAuthenticated">
<div class="flex items-center space-x-4">
<!-- 업로드 버튼 -->
<button @click="showUploadModal = true"
class="bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700">
<i class="fas fa-upload mr-2"></i>
업로드
</button>
<!-- 사용자 드롭다운 -->
<div class="relative" x-data="{ open: false }">
<button @click="open = !open" class="flex items-center text-gray-700 hover:text-gray-900">
<i class="fas fa-user-circle text-2xl"></i>
<span x-text="user?.full_name || user?.email" class="ml-2"></span>
<i class="fas fa-chevron-down ml-1"></i>
</button>
<div x-show="open" @click.away="open = false" x-cloak
class="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 z-50">
<a href="#" @click="showProfile = true" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<i class="fas fa-user mr-2"></i>프로필
</a>
<a href="#" @click="showMyNotes = true" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<i class="fas fa-sticky-note mr-2"></i>내 메모
</a>
<a href="#" @click="showBookmarks = true" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<i class="fas fa-bookmark mr-2"></i>책갈피
</a>
<template x-if="user?.is_admin">
<a href="#" @click="showAdmin = true" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<i class="fas fa-cog mr-2"></i>관리자
</a>
</template>
<hr class="my-1">
<a href="#" @click="logout" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<i class="fas fa-sign-out-alt mr-2"></i>로그아웃
</a>
</div>
</div>
</div>
</template>
</div>
</div>
</div>
</header>
<!-- 공통 헤더 컨테이너 -->
<div id="header-container"></div>
<!-- 메인 컨텐츠 -->
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
@@ -149,35 +73,69 @@
<template x-if="isAuthenticated">
<div>
<!-- 필터 및 정렬 -->
<div class="flex justify-between items-center mb-6">
<div class="flex items-center space-x-4">
<div class="mb-6">
<!-- 첫 번째 줄: 제목과 뷰 모드 -->
<div class="flex justify-between items-center mb-4">
<h2 class="text-2xl font-bold text-gray-900">문서 목록</h2>
<select x-model="selectedTag" @change="loadDocuments"
class="px-3 py-2 border border-gray-300 rounded-md">
<div class="flex items-center space-x-2">
<button @click="viewMode = 'grid'"
:class="viewMode === 'grid' ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-700'"
class="px-3 py-2 rounded-md">
<i class="fas fa-th-large mr-2"></i>그리드
</button>
<button @click="viewMode = 'books'"
:class="viewMode === 'books' ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-700'"
class="px-3 py-2 rounded-md">
<i class="fas fa-list mr-2"></i>목차
</button>
</div>
</div>
<!-- 두 번째 줄: 검색과 필터 -->
<div class="flex items-center space-x-4">
<!-- 검색 입력창 -->
<div class="flex-1 relative">
<input type="text"
x-model="searchQuery"
@input="filterDocuments"
placeholder="문서 제목, 내용, 태그로 검색..."
class="w-full pl-10 pr-4 py-2.5 bg-white border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent shadow-sm transition-all duration-200">
<i class="fas fa-search absolute left-3 top-3.5 text-gray-400"></i>
<!-- 검색 결과 개수 표시 -->
<span x-show="searchQuery && filteredDocuments.length !== documents.length"
class="absolute right-3 top-3 text-sm text-gray-500">
<span x-text="filteredDocuments.length"></span>개 결과
</span>
</div>
<!-- 태그 필터 -->
<select x-model="selectedTag" @change="filterDocuments"
class="px-4 py-2.5 border border-gray-200 rounded-xl bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 min-w-[140px]">
<option value="">모든 태그</option>
<template x-for="tag in tags" :key="tag.id">
<option :value="tag.name" x-text="tag.name"></option>
</template>
</select>
</div>
<div class="flex items-center space-x-2">
<button @click="viewMode = 'grid'"
:class="viewMode === 'grid' ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-700'"
class="px-3 py-2 rounded-md">
<i class="fas fa-th-large"></i>
<!-- 업로드 버튼 -->
<button @click="openUploadPage()"
class="px-6 py-2.5 bg-gradient-to-r from-blue-600 to-indigo-600 text-white rounded-xl hover:from-blue-700 hover:to-indigo-700 transition-all duration-200 shadow-md hover:shadow-lg flex items-center space-x-2">
<i class="fas fa-upload"></i>
<span>업로드</span>
</button>
<button @click="viewMode = 'list'"
:class="viewMode === 'list' ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-700'"
class="px-3 py-2 rounded-md">
<i class="fas fa-list"></i>
<!-- 검색 초기화 버튼 -->
<button x-show="searchQuery || selectedTag"
@click="clearFilters"
class="px-4 py-2.5 bg-gray-100 text-gray-600 rounded-xl hover:bg-gray-200 transition-all duration-200">
<i class="fas fa-times mr-2"></i>초기화
</button>
</div>
</div>
<!-- 문서 그리드/리스트 -->
<!-- 기존 그리드 뷰 (모든 문서 평면적으로) -->
<div x-show="viewMode === 'grid'" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<template x-for="doc in documents" :key="doc.id">
<template x-for="doc in filteredDocuments" :key="doc.id">
<div class="bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow cursor-pointer"
@click="openDocument(doc.id)">
<div class="p-6">
@@ -229,17 +187,66 @@
</template>
</div>
<!-- 서적별 그룹화 뷰 (목차) -->
<div x-show="viewMode === 'books'" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- 서적 카드들 -->
<template x-for="bookGroup in groupedDocuments" :key="bookGroup.book?.id || 'no-book'">
<div class="bg-white rounded-lg shadow-md hover:shadow-lg transition-all duration-200 cursor-pointer border border-gray-200"
@click="openBookDocuments(bookGroup.book)">
<div class="p-6">
<div class="flex items-center mb-4">
<div class="w-12 h-12 bg-gradient-to-br from-blue-500 to-indigo-600 rounded-lg flex items-center justify-center mr-4">
<i class="fas fa-book text-white text-lg"></i>
</div>
<div class="flex-1">
<h3 class="text-lg font-bold text-gray-900 line-clamp-2" x-text="bookGroup.book?.title || '서적 미분류'"></h3>
<p class="text-sm text-gray-600 mt-1" x-show="bookGroup.book?.author" x-text="bookGroup.book.author"></p>
</div>
</div>
<div class="flex items-center justify-between text-sm text-gray-500 mb-3">
<span class="flex items-center">
<i class="fas fa-file-alt mr-2"></i>
<span x-text="bookGroup.documents.length"></span>개 문서
</span>
<i class="fas fa-chevron-right text-gray-400"></i>
</div>
<p class="text-gray-600 text-sm line-clamp-2" x-text="bookGroup.book?.description || '서적 설명이 없습니다'"></p>
</div>
</div>
</template>
</div>
<!-- 빈 상태 -->
<template x-if="documents.length === 0 && !loading">
<template x-if="filteredDocuments.length === 0 && !loading">
<div class="text-center py-16">
<i class="fas fa-folder-open text-6xl text-gray-400 mb-4"></i>
<h3 class="text-xl font-semibold text-gray-900 mb-2">문서가 없습니다</h3>
<p class="text-gray-600 mb-6">첫 번째 문서를 업로드해보세요</p>
<button @click="showUploadModal = true"
class="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700">
<i class="fas fa-upload mr-2"></i>
문서 업로드
</button>
<template x-if="searchQuery || selectedTag">
<!-- 검색 결과 없음 -->
<div>
<i class="fas fa-search text-6xl text-gray-400 mb-4"></i>
<h3 class="text-xl font-semibold text-gray-900 mb-2">검색 결과가 없습니다</h3>
<p class="text-gray-600 mb-6">다른 검색어나 필터를 시도해보세요</p>
<button @click="clearFilters"
class="bg-gray-600 text-white px-6 py-3 rounded-lg hover:bg-gray-700">
<i class="fas fa-times mr-2"></i>
필터 초기화
</button>
</div>
</template>
<template x-if="!searchQuery && !selectedTag">
<!-- 문서 없음 -->
<div>
<i class="fas fa-folder-open text-6xl text-gray-400 mb-4"></i>
<h3 class="text-xl font-semibold text-gray-900 mb-2">문서가 없습니다</h3>
<p class="text-gray-600 mb-6">첫 번째 문서를 업로드해보세요</p>
<button @click="showUploadModal = true"
class="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700">
<i class="fas fa-upload mr-2"></i>
문서 업로드
</button>
</div>
</template>
</div>
</template>
</div>
@@ -478,8 +485,8 @@
</style>
<!-- JavaScript 파일들 -->
<script src="/static/js/api.js?v=2025012225"></script>
<script src="/static/js/auth.js?v=2025012225"></script>
<script src="/static/js/main.js?v=2025012225"></script>
<script src="/static/js/api.js?v=2025012380"></script>
<script src="/static/js/auth.js?v=2025012351"></script>
<script src="/static/js/main.js?v=2025012374"></script>
</body>
</html>