169 lines
8 KiB
TypeScript
169 lines
8 KiB
TypeScript
import { useLibrary } from './useLibrary'
|
|
import { useAuth } from '../auth/useAuth'
|
|
|
|
export const LibraryHeader = ({ currentTab, onTabChange }: { currentTab: 'library' | 'trash', onTabChange: (tab: 'library' | 'trash') => void }) => {
|
|
const { logout } = useAuth()
|
|
const {
|
|
searchQuery, setSearchQuery, fetchDocuments, fetchTrash,
|
|
selectedIds, clearSelection, selectAll, bulkDelete, bulkRestore, emptyTrash, documents
|
|
} = useLibrary()
|
|
|
|
const handleSearch = (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
if (currentTab === 'library') {
|
|
fetchDocuments(searchQuery)
|
|
}
|
|
}
|
|
|
|
const handleTabChange = (tab: 'library' | 'trash') => {
|
|
onTabChange(tab)
|
|
if (tab === 'library') {
|
|
fetchDocuments()
|
|
} else {
|
|
fetchTrash()
|
|
}
|
|
}
|
|
|
|
const isSelectionMode = selectedIds.size > 0
|
|
const allSelected = documents.length > 0 && selectedIds.size === documents.length
|
|
|
|
return (
|
|
<div className="sticky top-0 z-40 bg-white/80 dark:bg-gray-900/80 backdrop-blur-md border-b border-gray-200 dark:border-gray-800">
|
|
<div className="max-w-7xl mx-auto px-6 h-16 flex items-center justify-between gap-4">
|
|
|
|
{/* Left: Branding & Tabs */}
|
|
<div className="flex items-center gap-8">
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-8 h-8 bg-indigo-600 rounded-lg flex items-center justify-center shadow-lg shadow-indigo-500/30">
|
|
<span className="text-white font-bold text-xl leading-none">P</span>
|
|
</div>
|
|
<h1 className="text-xl font-bold text-gray-900 dark:text-white hidden sm:block">PaperJet</h1>
|
|
</div>
|
|
|
|
<nav className="flex items-center gap-1 bg-gray-100 dark:bg-gray-800 p-1 rounded-lg">
|
|
<button
|
|
onClick={() => handleTabChange('library')}
|
|
className={`px-4 py-1.5 rounded-md text-sm font-medium transition-all ${
|
|
currentTab === 'library'
|
|
? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-white shadow-sm'
|
|
: 'text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white'
|
|
}`}
|
|
>
|
|
Library
|
|
</button>
|
|
<button
|
|
onClick={() => handleTabChange('trash')}
|
|
className={`px-4 py-1.5 rounded-md text-sm font-medium transition-all ${
|
|
currentTab === 'trash'
|
|
? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-white shadow-sm'
|
|
: 'text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white'
|
|
}`}
|
|
>
|
|
Trash
|
|
</button>
|
|
</nav>
|
|
</div>
|
|
|
|
{/* Right: Search & Actions */}
|
|
<div className="flex items-center gap-4 flex-1 justify-end">
|
|
{isSelectionMode ? (
|
|
<div className="flex items-center gap-3 bg-indigo-50 dark:bg-indigo-900/30 px-4 py-1.5 rounded-full border border-indigo-100 dark:border-indigo-800">
|
|
<span className="text-sm font-medium text-indigo-700 dark:text-indigo-300">
|
|
{selectedIds.size} selected
|
|
</span>
|
|
<div className="h-4 w-px bg-indigo-200 dark:bg-indigo-700"></div>
|
|
|
|
<button onClick={allSelected ? clearSelection : selectAll} className="text-sm text-indigo-600 dark:text-indigo-400 hover:text-indigo-800 dark:hover:text-indigo-200 font-medium">
|
|
{allSelected ? 'Deselect All' : 'Select All'}
|
|
</button>
|
|
|
|
{currentTab === 'library' ? (
|
|
<button
|
|
onClick={() => bulkDelete()}
|
|
className="text-sm text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300 font-medium px-2 py-1 hover:bg-red-50 dark:hover:bg-red-900/30 rounded"
|
|
>
|
|
Delete
|
|
</button>
|
|
) : (
|
|
<>
|
|
<button
|
|
onClick={() => bulkRestore()}
|
|
className="text-sm text-green-600 dark:text-green-400 hover:text-green-800 dark:hover:text-green-300 font-medium px-2 py-1 hover:bg-green-50 dark:hover:bg-green-900/30 rounded"
|
|
>
|
|
Restore
|
|
</button>
|
|
<button
|
|
onClick={() => bulkDelete(true)}
|
|
className="text-sm text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300 font-medium px-2 py-1 hover:bg-red-50 dark:hover:bg-red-900/30 rounded"
|
|
>
|
|
Delete Forever
|
|
</button>
|
|
</>
|
|
)}
|
|
</div>
|
|
) : (
|
|
<>
|
|
{currentTab === 'library' && (
|
|
<form onSubmit={handleSearch} className="relative hidden md:block max-w-md w-full">
|
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
<svg className="h-4 w-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
</svg>
|
|
</div>
|
|
<input
|
|
type="text"
|
|
value={searchQuery}
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
placeholder="Search documents..."
|
|
className="block w-full pl-10 pr-3 py-2 border border-gray-200 dark:border-gray-700 rounded-xl leading-5 bg-gray-50 dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent sm:text-sm transition-all"
|
|
/>
|
|
</form>
|
|
)}
|
|
|
|
{currentTab === 'trash' && (
|
|
<button
|
|
onClick={() => emptyTrash()}
|
|
className="text-sm font-medium text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300 transition-colors"
|
|
>
|
|
Empty Trash
|
|
</button>
|
|
)}
|
|
|
|
{currentTab === 'library' && (
|
|
<label className="cursor-pointer inline-flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-xl shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition-all active:scale-95">
|
|
<span>Upload PDF</span>
|
|
<input
|
|
type="file"
|
|
accept="application/pdf"
|
|
multiple
|
|
className="hidden"
|
|
onChange={(e) => {
|
|
// We'll dispatch a custom event that UploadArea listens to, or just click its input
|
|
// For simplicity, find the hidden file input in UploadArea and click it
|
|
const uploader = document.querySelector('input[type="file"][multiple]') as HTMLInputElement
|
|
if (uploader && uploader !== e.target) {
|
|
uploader.files = e.target.files
|
|
const event = new Event('change', { bubbles: true })
|
|
uploader.dispatchEvent(event)
|
|
}
|
|
}}
|
|
/>
|
|
</label>
|
|
)}
|
|
|
|
<button
|
|
onClick={logout}
|
|
className="p-2 text-gray-400 hover:text-gray-500 dark:hover:text-gray-300"
|
|
title="Logout"
|
|
>
|
|
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
|
|
</svg>
|
|
</button>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|