import { useState, useRef } from 'react'; import { Link } from 'react-router-dom'; import { useGetDocuments, useCreateDocument } from '../generated/anthoLumeAPIV1'; import type { Document } from '../generated/model'; import { ActivityIcon, DownloadIcon, Search2Icon, UploadIcon } from '../icons'; import { LoadingState, Pagination, TextInput, IconInput, SegmentedControl } from '../components'; import { useToasts } from '../components/ToastContext'; import { useMutationWithToast } from '../hooks/useMutationWithToast'; import { formatDuration } from '../utils/formatters'; import { cn } from '../utils/cn'; import { useDebouncedState } from '../hooks/useDebouncedState'; import { usePaginatedList } from '../hooks/usePaginatedList'; import { useLocalSetting, type DocumentsViewMode } from '../utils/localSettings'; const DOCUMENTS_PAGE_SIZE = 9; interface DocumentItemProps { doc: Document; layout: 'grid' | 'list'; } function DocumentItem({ doc, layout }: DocumentItemProps) { const percentage = doc.percentage || 0; const totalTimeSeconds = doc.total_time_seconds || 0; const documentPath = `/documents/${doc.id}`; const title = doc.title || 'Unknown'; const actions = (
{doc.filepath ? ( ) : ( )}
); const fields = [ { label: 'Title', value: title, link: true }, { label: 'Author', value: doc.author || 'Unknown' }, { label: 'Progress', value: `${percentage}%` }, { label: 'Time Read', value: formatDuration(totalTimeSeconds) }, ]; const fieldList = (
{fields.map(field => (

{field.label}

{field.link ? ( {field.value} ) : (

{field.value}

)}
))}
); if (layout === 'grid') { return (
{title}
{fieldList} {actions}
); } return (
{fieldList} {actions}
); } function EmptyDocuments({ className }: { className?: string }) { return (
No documents found.
); } export default function DocumentsPage() { const [search, setSearch, debouncedSearch] = useDebouncedState('', 300); const { page, setPage } = usePaginatedList(debouncedSearch); const limit = DOCUMENTS_PAGE_SIZE; const [uploadMode, setUploadMode] = useState(false); const [viewMode, setViewMode] = useLocalSetting('documentsViewMode', 'grid'); const fileInputRef = useRef(null); const { showWarning } = useToasts(); const toastMutationOptions = useMutationWithToast(); const { data, isLoading, refetch } = useGetDocuments({ page, limit, search: debouncedSearch }); const createMutation = useCreateDocument(); const documentsResponse = data; const docs = documentsResponse?.documents; const previousPage = documentsResponse?.previous_page; const nextPage = documentsResponse?.next_page; const uploadDocument = (file: File | undefined) => { if (!file) return; if (!file.name.endsWith('.epub')) { showWarning('Please upload an EPUB file'); return; } createMutation.mutate( { data: { document_file: file } }, toastMutationOptions({ success: 'Document uploaded successfully!', error: 'Failed to upload document', onSuccess: () => { setUploadMode(false); refetch(); }, }) ); }; const handleCancelUpload = () => { setUploadMode(false); if (fileInputRef.current) { fileInputRef.current.value = ''; } }; return (
}> setSearch(e.target.value)} placeholder="Search Author / Title" name="search" />
ariaLabel="Document view mode" value={viewMode} onChange={setViewMode} options={[ { value: 'grid', label: 'Grid' }, { value: 'list', label: 'List' }, ]} />
{viewMode === 'grid' ? (
{isLoading ? ( ) : docs && docs.length > 0 ? ( docs.map(doc => ) ) : ( )}
) : (
{isLoading ? ( ) : docs && docs.length > 0 ? ( docs.map(doc => ) ) : ( )}
)}
{uploadMode && (
)}
); }