diff --git a/frontend/src/pages/DocumentsPage.tsx b/frontend/src/pages/DocumentsPage.tsx index 5af5eac..b23f61f 100644 --- a/frontend/src/pages/DocumentsPage.tsx +++ b/frontend/src/pages/DocumentsPage.tsx @@ -22,6 +22,10 @@ import { Divider, CircularProgress, Alert, + Pagination, + FormControl, + InputLabel, + Select, } from '@mui/material'; import { GridView as GridViewIcon, @@ -38,6 +42,8 @@ import { CalendarToday as DateIcon, Storage as SizeIcon, Visibility as ViewIcon, + ChevronLeft as ChevronLeftIcon, + ChevronRight as ChevronRightIcon, } from '@mui/icons-material'; import { documentService } from '../services/api'; import DocumentThumbnail from '../components/DocumentThumbnail'; @@ -50,9 +56,23 @@ interface Document { mime_type: string; created_at: string; has_ocr_text?: boolean; + ocr_status?: string; + ocr_confidence?: number; tags: string[]; } +interface PaginationInfo { + total: number; + limit: number; + offset: number; + has_more: boolean; +} + +interface DocumentsResponse { + documents: Document[]; + pagination: PaginationInfo; +} + type ViewMode = 'grid' | 'list'; type SortField = 'created_at' | 'original_filename' | 'file_size'; type SortOrder = 'asc' | 'desc'; @@ -60,12 +80,14 @@ type SortOrder = 'asc' | 'desc'; const DocumentsPage: React.FC = () => { const navigate = useNavigate(); const [documents, setDocuments] = useState([]); + const [pagination, setPagination] = useState({ total: 0, limit: 20, offset: 0, has_more: false }); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [viewMode, setViewMode] = useState('grid'); const [searchQuery, setSearchQuery] = useState(''); const [sortBy, setSortBy] = useState('created_at'); const [sortOrder, setSortOrder] = useState('desc'); + const [ocrFilter, setOcrFilter] = useState(''); // Menu states const [sortMenuAnchor, setSortMenuAnchor] = useState(null); @@ -74,13 +96,18 @@ const DocumentsPage: React.FC = () => { useEffect(() => { fetchDocuments(); - }, []); + }, [pagination.limit, pagination.offset, ocrFilter]); const fetchDocuments = async (): Promise => { try { setLoading(true); - const response = await documentService.list(100, 0); - setDocuments(response.data); + const response = await documentService.listWithPagination( + pagination.limit, + pagination.offset, + ocrFilter || undefined + ); + setDocuments(response.data.documents); + setPagination(response.data.pagination); } catch (err) { setError('Failed to load documents'); console.error(err); @@ -179,6 +206,39 @@ const DocumentsPage: React.FC = () => { handleSortMenuClose(); }; + const handlePageChange = (event: React.ChangeEvent, page: number): void => { + const newOffset = (page - 1) * pagination.limit; + setPagination(prev => ({ ...prev, offset: newOffset })); + }; + + const handleOcrFilterChange = (event: React.ChangeEvent): void => { + setOcrFilter(event.target.value); + setPagination(prev => ({ ...prev, offset: 0 })); // Reset to first page when filtering + }; + + const getOcrStatusChip = (doc: Document) => { + if (!doc.ocr_status) return null; + + const statusConfig = { + 'completed': { color: 'success' as const, label: doc.ocr_confidence ? `OCR ${Math.round(doc.ocr_confidence)}%` : 'OCR Done' }, + 'processing': { color: 'warning' as const, label: 'Processing...' }, + 'failed': { color: 'error' as const, label: 'OCR Failed' }, + 'pending': { color: 'default' as const, label: 'Pending' }, + }; + + const config = statusConfig[doc.ocr_status as keyof typeof statusConfig]; + if (!config) return null; + + return ( + + ); + }; + if (loading) { return ( @@ -257,6 +317,22 @@ const DocumentsPage: React.FC = () => { + {/* OCR Filter */} + + OCR Status + + + {/* Sort Button */}