import { useState, useRef, useEffect } from 'react'; import { Link, useNavigate } from 'react-router-dom'; import { useGetDocuments, useCreateDocument } from '../generated/anthoLumeAPIV1'; import type { Document, DocumentsResponse } from '../generated/model'; import { ActivityIcon, DownloadIcon, Search2Icon, UploadIcon } from '../icons'; import { LoadingState, Pagination } from '../components'; import { useToasts } from '../components/ToastContext'; import { formatDuration } from '../utils/formatters'; import { useDebounce } from '../hooks/useDebounce'; import { getErrorMessage } from '../utils/errors'; import { getDocumentsViewMode, setDocumentsViewMode, type DocumentsViewMode, } from '../utils/localSettings'; interface DocumentCardProps { doc: Document; } function DocumentCard({ doc }: DocumentCardProps) { const navigate = useNavigate(); const percentage = doc.percentage || 0; const totalTimeSeconds = doc.total_time_seconds || 0; return (
navigate(`/documents/${doc.id}`)} onKeyDown={event => { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); navigate(`/documents/${doc.id}`); } }} >
{doc.title}

Title

{doc.title || 'Unknown'}

Author

{doc.author || 'Unknown'}

Progress

{percentage}%

Time Read

{formatDuration(totalTimeSeconds)}

e.stopPropagation()}> {doc.filepath ? ( e.stopPropagation()}> ) : ( )}
); } interface DocumentListItemProps { doc: Document; } function DocumentListItem({ doc }: DocumentListItemProps) { const navigate = useNavigate(); const percentage = doc.percentage || 0; const totalTimeSeconds = doc.total_time_seconds || 0; return (
navigate(`/documents/${doc.id}`)} onKeyDown={event => { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); navigate(`/documents/${doc.id}`); } }} >

Title

{doc.title || 'Unknown'}

Author

{doc.author || 'Unknown'}

Progress

{percentage}%

Time Read

{formatDuration(totalTimeSeconds)}

e.stopPropagation()}> {doc.filepath ? ( e.stopPropagation()}> ) : ( )}
); } export default function DocumentsPage() { const [search, setSearch] = useState(''); const [page, setPage] = useState(1); const [limit] = useState(9); const [uploadMode, setUploadMode] = useState(false); const [viewMode, setViewMode] = useState(getDocumentsViewMode); const fileInputRef = useRef(null); const { showInfo, showWarning, showError } = useToasts(); const debouncedSearch = useDebounce(search, 300); useEffect(() => { setDocumentsViewMode(viewMode); }, [viewMode]); useEffect(() => { setPage(1); }, [debouncedSearch]); const { data, isLoading, refetch } = useGetDocuments({ page, limit, search: debouncedSearch }); const createMutation = useCreateDocument(); const docs = (data?.data as DocumentsResponse | undefined)?.documents; const previousPage = (data?.data as DocumentsResponse | undefined)?.previous_page; const nextPage = (data?.data as DocumentsResponse | undefined)?.next_page; const handleFileChange = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; if (!file.name.endsWith('.epub')) { showWarning('Please upload an EPUB file'); return; } try { await createMutation.mutateAsync({ data: { document_file: file, }, }); showInfo('Document uploaded successfully!'); setUploadMode(false); refetch(); } catch (error) { showError('Failed to upload document: ' + getErrorMessage(error)); } }; const handleCancelUpload = () => { setUploadMode(false); if (fileInputRef.current) { fileInputRef.current.value = ''; } }; const getViewModeButtonClasses = (mode: DocumentsViewMode) => `rounded px-3 py-1 text-sm font-medium transition-colors ${ viewMode === mode ? 'bg-content text-content-inverse' : 'text-content-muted hover:bg-surface-muted' }`; return (
setSearch(e.target.value)} className="w-full flex-1 appearance-none rounded-none border border-border bg-surface px-4 py-2 text-base text-content shadow-sm placeholder:text-content-subtle focus:border-transparent focus:outline-none focus:ring-2 focus:ring-primary-600" placeholder="Search Author / Title" name="search" />
{viewMode === 'grid' ? (
{isLoading ? ( ) : docs && docs.length > 0 ? ( docs.map(doc => ) ) : (
No documents found.
)}
) : (
{isLoading ? ( ) : docs && docs.length > 0 ? ( docs.map(doc => ) ) : (
No documents found.
)}
)}
setUploadMode(!uploadMode)} />
); }