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:
@@ -15,66 +15,108 @@
|
||||
<body class="bg-gray-50 min-h-screen">
|
||||
<div x-data="documentViewer" x-init="init()">
|
||||
<!-- 헤더 -->
|
||||
<header class="bg-white shadow-sm border-b sticky top-0 z-40">
|
||||
<header class="bg-white shadow-sm border-b sticky top-0 z-40 backdrop-blur-sm bg-white/95">
|
||||
<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 justify-between items-center h-12 border-b border-gray-100">
|
||||
<!-- 뒤로가기 및 문서 정보 -->
|
||||
<div class="flex items-center space-x-4">
|
||||
<button @click="goBack" class="text-gray-600 hover:text-gray-900">
|
||||
<i class="fas fa-arrow-left text-xl"></i>
|
||||
<button @click="goBack" class="w-8 h-8 bg-gray-100 hover:bg-gray-200 rounded-lg flex items-center justify-center transition-all duration-200 text-gray-600 hover:text-gray-900">
|
||||
<i class="fas fa-arrow-left text-sm"></i>
|
||||
</button>
|
||||
<div>
|
||||
<h1 class="text-lg font-semibold text-gray-900" x-text="document?.title || '로딩 중...'"></h1>
|
||||
<p class="text-sm text-gray-500" x-show="document" x-text="document?.uploader_name"></p>
|
||||
<h1 class="text-lg font-bold text-gray-900" x-text="document?.title || '로딩 중...'"></h1>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 업로더 정보 -->
|
||||
<div x-show="document">
|
||||
<p class="text-sm text-gray-500 font-medium" x-text="document?.uploader_name"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 도구 모음 -->
|
||||
<div class="flex items-center space-x-2">
|
||||
<!-- 두 번째 줄: 도구 모음 -->
|
||||
<div class="flex justify-between items-center h-14 py-2">
|
||||
<!-- 왼쪽: 도구들 -->
|
||||
<div class="flex items-center space-x-3">
|
||||
<!-- 하이라이트 색상 선택 -->
|
||||
<div class="flex items-center space-x-1 bg-gray-100 rounded-lg p-1">
|
||||
<div class="flex items-center space-x-1 bg-white rounded-xl shadow-sm border p-2">
|
||||
<span class="text-xs font-medium text-gray-600 mr-2">하이라이트</span>
|
||||
<button @click="createHighlightWithColor('#FFFF00')"
|
||||
:class="selectedHighlightColor === '#FFFF00' ? 'ring-2 ring-blue-500' : ''"
|
||||
class="w-8 h-8 bg-yellow-300 rounded border-2 border-white"
|
||||
:class="selectedHighlightColor === '#FFFF00' ? 'ring-2 ring-yellow-400 scale-110' : 'hover:scale-105'"
|
||||
class="w-6 h-6 bg-yellow-300 rounded-full border-2 border-white shadow-sm transition-all duration-200"
|
||||
title="노란색 하이라이트"></button>
|
||||
<button @click="createHighlightWithColor('#90EE90')"
|
||||
:class="selectedHighlightColor === '#90EE90' ? 'ring-2 ring-blue-500' : ''"
|
||||
class="w-8 h-8 bg-green-300 rounded border-2 border-white"
|
||||
:class="selectedHighlightColor === '#90EE90' ? 'ring-2 ring-green-400 scale-110' : 'hover:scale-105'"
|
||||
class="w-6 h-6 bg-green-300 rounded-full border-2 border-white shadow-sm transition-all duration-200"
|
||||
title="초록색 하이라이트"></button>
|
||||
<button @click="createHighlightWithColor('#FFB6C1')"
|
||||
:class="selectedHighlightColor === '#FFB6C1' ? 'ring-2 ring-blue-500' : ''"
|
||||
class="w-8 h-8 bg-pink-300 rounded border-2 border-white"
|
||||
:class="selectedHighlightColor === '#FFB6C1' ? 'ring-2 ring-pink-400 scale-110' : 'hover:scale-105'"
|
||||
class="w-6 h-6 bg-pink-300 rounded-full border-2 border-white shadow-sm transition-all duration-200"
|
||||
title="분홍색 하이라이트"></button>
|
||||
<button @click="createHighlightWithColor('#87CEEB')"
|
||||
:class="selectedHighlightColor === '#87CEEB' ? 'ring-2 ring-blue-500' : ''"
|
||||
class="w-8 h-8 bg-blue-300 rounded border-2 border-white"
|
||||
:class="selectedHighlightColor === '#87CEEB' ? 'ring-2 ring-blue-400 scale-110' : 'hover:scale-105'"
|
||||
class="w-6 h-6 bg-blue-300 rounded-full border-2 border-white shadow-sm transition-all duration-200"
|
||||
title="파란색 하이라이트"></button>
|
||||
</div>
|
||||
|
||||
<!-- 메모 패널 토글 -->
|
||||
<button @click="showNotesPanel = !showNotesPanel"
|
||||
:class="showNotesPanel ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-700'"
|
||||
class="px-3 py-2 rounded-md hover:bg-blue-700 transition-colors">
|
||||
<i class="fas fa-sticky-note mr-1"></i>
|
||||
메모 (<span x-text="notes.length"></span>)
|
||||
</button>
|
||||
<!-- 메모 & 책갈피 버튼 그룹 -->
|
||||
<div class="flex items-center bg-white rounded-xl shadow-sm border overflow-hidden">
|
||||
<!-- 메모 버튼 -->
|
||||
<button @click="showNotesPanel = !showNotesPanel"
|
||||
:class="showNotesPanel ? 'bg-blue-50 text-blue-700 border-r border-blue-200' : 'text-gray-600 hover:bg-gray-50 border-r border-gray-200'"
|
||||
class="px-3 py-1.5 transition-all duration-200 flex flex-col items-center space-y-0.5 relative min-w-[50px]">
|
||||
<div class="relative">
|
||||
<i class="fas fa-sticky-note text-sm"></i>
|
||||
<span x-show="notes.length > 0"
|
||||
class="absolute -top-1 -right-1 bg-blue-500 text-white text-xs rounded-full w-3 h-3 flex items-center justify-center font-medium text-xs"
|
||||
x-text="notes.length"></span>
|
||||
</div>
|
||||
<span class="font-medium text-xs">메모</span>
|
||||
</button>
|
||||
|
||||
<!-- 책갈피 패널 토글 -->
|
||||
<button @click="showBookmarksPanel = !showBookmarksPanel"
|
||||
:class="showBookmarksPanel ? 'bg-green-600 text-white' : 'bg-gray-200 text-gray-700'"
|
||||
class="px-3 py-2 rounded-md hover:bg-green-700 transition-colors">
|
||||
<i class="fas fa-bookmark mr-1"></i>
|
||||
책갈피 (<span x-text="bookmarks.length"></span>)
|
||||
</button>
|
||||
<!-- 책갈피 버튼 -->
|
||||
<button @click="showBookmarksPanel = !showBookmarksPanel"
|
||||
:class="showBookmarksPanel ? 'bg-amber-50 text-amber-700' : 'text-gray-600 hover:bg-gray-50'"
|
||||
class="px-3 py-1.5 transition-all duration-200 flex flex-col items-center space-y-0.5 relative min-w-[50px]">
|
||||
<div class="relative">
|
||||
<i class="fas fa-bookmark text-sm"></i>
|
||||
<span x-show="bookmarks.length > 0"
|
||||
class="absolute -top-1 -right-1 bg-amber-500 text-white text-xs rounded-full w-3 h-3 flex items-center justify-center font-medium text-xs"
|
||||
x-text="bookmarks.length"></span>
|
||||
</div>
|
||||
<span class="font-medium text-xs">책갈피</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 검색 -->
|
||||
<!-- 중앙: 검색 -->
|
||||
<div class="flex-1 flex justify-center">
|
||||
<div class="relative">
|
||||
<input type="text" x-model="searchQuery" @input="searchInDocument"
|
||||
placeholder="문서 내 검색..."
|
||||
class="w-64 pl-8 pr-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
<i class="fas fa-search absolute left-2 top-3 text-gray-400"></i>
|
||||
class="w-64 pl-10 pr-4 py-2 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-2.5 text-gray-400"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 오른쪽: 한영 전환 -->
|
||||
<div class="flex items-center">
|
||||
<!-- PDF 다운로드 버튼 (매칭된 PDF가 있는 경우) -->
|
||||
<button x-show="document.matched_pdf_id"
|
||||
@click="downloadMatchedPDF()"
|
||||
class="bg-red-600 text-white px-4 py-2 rounded-xl hover:bg-red-700 transition-all duration-200 flex items-center space-x-2 shadow-sm"
|
||||
title="매칭된 PDF 다운로드">
|
||||
<i class="fas fa-file-pdf text-sm"></i>
|
||||
<span class="font-medium text-sm">PDF 원본</span>
|
||||
</button>
|
||||
|
||||
<button @click="toggleLanguage()"
|
||||
class="bg-blue-600 text-white px-4 py-2 rounded-xl hover:bg-blue-700 transition-all duration-200 flex items-center space-x-2 shadow-sm"
|
||||
id="language-toggle-btn">
|
||||
<i class="fas fa-globe text-sm"></i>
|
||||
<span class="font-medium text-sm">언어전환</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
@@ -82,7 +124,8 @@
|
||||
<!-- 메인 컨텐츠 -->
|
||||
<div class="flex max-w-7xl mx-auto">
|
||||
<!-- 문서 뷰어 -->
|
||||
<main class="flex-1 bg-white shadow-sm" :class="(showNotesPanel || showBookmarksPanel) ? 'mr-80' : ''">
|
||||
<main class="flex-1 bg-white shadow-sm" :class="(showNotesPanel || showBookmarksPanel) ? 'mr-70' : ''"
|
||||
:style="(showNotesPanel || showBookmarksPanel) ? 'margin-right: 280px;' : ''">
|
||||
<div class="p-8">
|
||||
<!-- 로딩 상태 -->
|
||||
<div x-show="loading" class="text-center py-16">
|
||||
@@ -112,24 +155,35 @@
|
||||
|
||||
<!-- 사이드 패널 -->
|
||||
<aside x-show="showNotesPanel || showBookmarksPanel"
|
||||
class="fixed right-0 top-16 w-80 h-screen bg-white shadow-lg border-l overflow-hidden flex flex-col">
|
||||
class="fixed right-0 bg-white shadow-lg border-l overflow-hidden flex flex-col"
|
||||
style="height: calc(100vh - 7rem); top: 7rem; width: 280px;">
|
||||
|
||||
<!-- 패널 탭 -->
|
||||
<div class="flex border-b">
|
||||
<div class="flex bg-gray-50 p-1 rounded-t-lg">
|
||||
<button @click="activePanel = 'notes'; showNotesPanel = true; showBookmarksPanel = false"
|
||||
:class="activePanel === 'notes' ? 'bg-blue-50 text-blue-600 border-b-2 border-blue-600' : 'text-gray-600'"
|
||||
class="flex-1 px-4 py-3 text-sm font-medium">
|
||||
<i class="fas fa-sticky-note mr-2"></i>
|
||||
메모 (<span x-text="notes.length"></span>)
|
||||
:class="activePanel === 'notes' ? 'bg-white text-blue-600 shadow-sm' : 'text-gray-600 hover:text-gray-800'"
|
||||
class="flex-1 px-4 py-2.5 text-sm font-medium rounded-lg transition-all duration-200 flex items-center justify-center space-x-2">
|
||||
<div class="relative">
|
||||
<i class="fas fa-sticky-note"></i>
|
||||
<span x-show="notes.length > 0"
|
||||
class="absolute -top-1.5 -right-1.5 bg-blue-500 text-white text-xs rounded-full w-4 h-4 flex items-center justify-center font-medium"
|
||||
x-text="notes.length"></span>
|
||||
</div>
|
||||
<span>메모</span>
|
||||
</button>
|
||||
<button @click="activePanel = 'bookmarks'; showBookmarksPanel = true; showNotesPanel = false"
|
||||
:class="activePanel === 'bookmarks' ? 'bg-green-50 text-green-600 border-b-2 border-green-600' : 'text-gray-600'"
|
||||
class="flex-1 px-4 py-3 text-sm font-medium">
|
||||
<i class="fas fa-bookmark mr-2"></i>
|
||||
책갈피 (<span x-text="bookmarks.length"></span>)
|
||||
:class="activePanel === 'bookmarks' ? 'bg-white text-amber-600 shadow-sm' : 'text-gray-600 hover:text-gray-800'"
|
||||
class="flex-1 px-4 py-2.5 text-sm font-medium rounded-lg transition-all duration-200 flex items-center justify-center space-x-2">
|
||||
<div class="relative">
|
||||
<i class="fas fa-bookmark"></i>
|
||||
<span x-show="bookmarks.length > 0"
|
||||
class="absolute -top-1.5 -right-1.5 bg-amber-500 text-white text-xs rounded-full w-4 h-4 flex items-center justify-center font-medium"
|
||||
x-text="bookmarks.length"></span>
|
||||
</div>
|
||||
<span>책갈피</span>
|
||||
</button>
|
||||
<button @click="showNotesPanel = false; showBookmarksPanel = false"
|
||||
class="px-3 py-3 text-gray-400 hover:text-gray-600">
|
||||
class="px-3 py-2.5 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-lg transition-all duration-200">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -144,38 +198,47 @@
|
||||
|
||||
<div class="flex-1 overflow-y-auto">
|
||||
<template x-if="filteredNotes.length === 0">
|
||||
<div class="p-4 text-center text-gray-500">
|
||||
<i class="fas fa-sticky-note text-3xl mb-2"></i>
|
||||
<p>메모가 없습니다</p>
|
||||
<p class="text-sm">텍스트를 선택하고 메모를 추가해보세요</p>
|
||||
<div class="p-8 text-center text-gray-500">
|
||||
<div class="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<i class="fas fa-sticky-note text-2xl text-blue-500"></i>
|
||||
</div>
|
||||
<h3 class="font-semibold text-gray-700 mb-2">메모가 없습니다</h3>
|
||||
<p class="text-sm text-gray-500">텍스트를 선택하고 메모를 추가해보세요</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="space-y-2 p-2">
|
||||
<template x-for="note in filteredNotes" :key="note.id">
|
||||
<div class="bg-gray-50 rounded-lg p-3 cursor-pointer hover:bg-gray-100"
|
||||
<div class="bg-white rounded-lg p-3 cursor-pointer hover:shadow-md border border-gray-100 transition-all duration-200 group"
|
||||
@click="scrollToHighlight(note.highlight.id)">
|
||||
<!-- 선택된 텍스트 -->
|
||||
<div class="flex justify-between items-start mb-2">
|
||||
<div class="flex-1">
|
||||
<p class="text-sm text-gray-600 line-clamp-2" x-text="note.highlight.selected_text"></p>
|
||||
<div class="flex-1 bg-blue-50 rounded-md p-1.5 mr-2">
|
||||
<p class="text-xs text-blue-800 line-clamp-2 font-medium" x-text="note.highlight.selected_text"></p>
|
||||
</div>
|
||||
<div class="flex space-x-1 ml-2">
|
||||
<button @click.stop="editNote(note)" class="text-blue-500 hover:text-blue-700">
|
||||
<div class="flex space-x-1 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
|
||||
<button @click.stop="editNote(note)"
|
||||
class="w-6 h-6 bg-blue-100 text-blue-600 rounded-md hover:bg-blue-200 flex items-center justify-center transition-colors duration-200">
|
||||
<i class="fas fa-edit text-xs"></i>
|
||||
</button>
|
||||
<button @click.stop="deleteNote(note.id)" class="text-red-500 hover:text-red-700">
|
||||
<button @click.stop="deleteNote(note.id)"
|
||||
class="w-6 h-6 bg-red-100 text-red-600 rounded-md hover:bg-red-200 flex items-center justify-center transition-colors duration-200">
|
||||
<i class="fas fa-trash text-xs"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-sm text-gray-800" x-text="note.content"></p>
|
||||
<div class="flex justify-between items-center mt-2">
|
||||
|
||||
<!-- 메모 내용 -->
|
||||
<p class="text-sm text-gray-800 mb-2 leading-relaxed" x-text="note.content"></p>
|
||||
|
||||
<!-- 태그와 날짜 -->
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<template x-for="tag in note.tags || []" :key="tag">
|
||||
<span class="px-2 py-1 bg-blue-100 text-blue-800 text-xs rounded-full" x-text="tag"></span>
|
||||
<span class="px-2 py-1 bg-blue-100 text-blue-700 text-xs rounded-full font-medium" x-text="tag"></span>
|
||||
</template>
|
||||
</div>
|
||||
<span class="text-xs text-gray-500" x-text="formatDate(note.created_at)"></span>
|
||||
<span class="text-xs text-gray-500 font-medium" x-text="formatDate(note.created_at)"></span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -186,7 +249,7 @@
|
||||
<!-- 책갈피 패널 -->
|
||||
<div x-show="activePanel === 'bookmarks'" class="flex-1 overflow-hidden flex flex-col">
|
||||
<div class="p-4 border-b">
|
||||
<button @click="addBookmark" class="w-full bg-green-600 text-white py-2 px-4 rounded-md hover:bg-green-700">
|
||||
<button @click="addBookmark" class="w-full bg-gradient-to-r from-amber-500 to-orange-500 text-white py-3 px-4 rounded-xl hover:from-amber-600 hover:to-orange-600 transition-all duration-200 shadow-sm font-medium">
|
||||
<i class="fas fa-plus mr-2"></i>
|
||||
현재 위치에 책갈피 추가
|
||||
</button>
|
||||
@@ -194,29 +257,43 @@
|
||||
|
||||
<div class="flex-1 overflow-y-auto">
|
||||
<template x-if="bookmarks.length === 0">
|
||||
<div class="p-4 text-center text-gray-500">
|
||||
<i class="fas fa-bookmark text-3xl mb-2"></i>
|
||||
<p>책갈피가 없습니다</p>
|
||||
<div class="p-8 text-center text-gray-500">
|
||||
<div class="w-16 h-16 bg-amber-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<i class="fas fa-bookmark text-2xl text-amber-500"></i>
|
||||
</div>
|
||||
<h3 class="font-semibold text-gray-700 mb-2">책갈피가 없습니다</h3>
|
||||
<p class="text-sm text-gray-500">위의 버튼을 눌러 책갈피를 추가해보세요</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="space-y-2 p-2">
|
||||
<template x-for="bookmark in bookmarks" :key="bookmark.id">
|
||||
<div class="bg-gray-50 rounded-lg p-3 cursor-pointer hover:bg-gray-100"
|
||||
<div class="bg-white rounded-lg p-3 cursor-pointer hover:shadow-md border border-gray-100 transition-all duration-200 group"
|
||||
@click="scrollToBookmark(bookmark)">
|
||||
<div class="flex justify-between items-start mb-2">
|
||||
<h4 class="font-medium text-gray-900" x-text="bookmark.title"></h4>
|
||||
<div class="flex space-x-1">
|
||||
<button @click.stop="editBookmark(bookmark)" class="text-blue-500 hover:text-blue-700">
|
||||
<div class="flex items-start space-x-2 flex-1">
|
||||
<div class="w-6 h-6 bg-gradient-to-br from-amber-400 to-orange-500 rounded-md flex items-center justify-center flex-shrink-0">
|
||||
<i class="fas fa-bookmark text-white text-xs"></i>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<h4 class="font-semibold text-gray-900 text-sm truncate" x-text="bookmark.title"></h4>
|
||||
<p class="text-xs text-gray-600 line-clamp-2" x-text="bookmark.description"></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex space-x-1 opacity-0 group-hover:opacity-100 transition-opacity duration-200 ml-2">
|
||||
<button @click.stop="editBookmark(bookmark)"
|
||||
class="w-6 h-6 bg-blue-100 text-blue-600 rounded-md hover:bg-blue-200 flex items-center justify-center transition-colors duration-200">
|
||||
<i class="fas fa-edit text-xs"></i>
|
||||
</button>
|
||||
<button @click.stop="deleteBookmark(bookmark.id)" class="text-red-500 hover:text-red-700">
|
||||
<button @click.stop="deleteBookmark(bookmark.id)"
|
||||
class="w-6 h-6 bg-red-100 text-red-600 rounded-md hover:bg-red-200 flex items-center justify-center transition-colors duration-200">
|
||||
<i class="fas fa-trash text-xs"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-sm text-gray-600" x-text="bookmark.description"></p>
|
||||
<span class="text-xs text-gray-500" x-text="formatDate(bookmark.created_at)"></span>
|
||||
<div class="flex justify-end">
|
||||
<span class="text-xs text-gray-500 font-medium" x-text="formatDate(bookmark.created_at)"></span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
@@ -313,12 +390,20 @@
|
||||
</div>
|
||||
|
||||
<!-- 스크립트 -->
|
||||
<script src="/static/js/api.js?v=2025012225"></script>
|
||||
<script src="/static/js/api.js?v=2025012380"></script>
|
||||
<script src="/static/js/viewer.js?v=2025012225"></script>
|
||||
|
||||
<style>
|
||||
[x-cloak] { display: none !important; }
|
||||
|
||||
/* 기존 언어 전환 버튼 숨기기 */
|
||||
.language-toggle,
|
||||
button[onclick*="toggleLanguage"],
|
||||
*[class*="language"],
|
||||
*[class*="translate"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
Reference in New Issue
Block a user