diff --git a/surfsense_web/components/chat/ChatInputGroup.tsx b/surfsense_web/components/chat/ChatInputGroup.tsx index 56d924a78..d1a0c7538 100644 --- a/surfsense_web/components/chat/ChatInputGroup.tsx +++ b/surfsense_web/components/chat/ChatInputGroup.tsx @@ -45,12 +45,19 @@ const DocumentSelector = React.memo( // Calculate skip value for pagination const skip = pageIndex * pageSize; - const { documents, loading, isLoaded, fetchDocuments } = useDocuments( - Number(search_space_id), - true, - skip, - pageSize - ); + const { documents, loading, isLoaded, fetchDocuments, error } = useDocuments( + Number(search_space_id), + true, + skip, + pageSize + ); + + // Refetch documents when pagination changes + useEffect(() => { + if (isOpen) { + fetchDocuments(); + } + }, [skip, pageSize, isOpen, fetchDocuments]); const handleOpenChange = useCallback( (open: boolean) => { @@ -105,6 +112,15 @@ const DocumentSelector = React.memo(

Loading documents...

+ ) : error ? ( +
+
+

Error loading documents: {error}

+ +
+
) : isLoaded ? ( [] = [ checked={ table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && "indeterminate") } - onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)} + onCheckedChange={(value) => { + table.toggleAllPageRowsSelected(!!value); + }} aria-label="Select all" /> ), cell: ({ row }) => ( row.toggleSelected(!!value)} + onCheckedChange={(value) => { + row.toggleSelected(!!value); + }} aria-label="Select row" /> ), @@ -203,28 +207,22 @@ export function DocumentsDataTable({ const selection: Record = {}; initialSelectedDocuments.forEach((selectedDoc) => { - selection[selectedDoc.id] = true; + // Find the document in the current documents array to get the correct row ID + const docInCurrentList = documents.find(doc => doc.id === selectedDoc.id); + if (docInCurrentList) { + selection[docInCurrentList.id.toString()] = true; + } }); return selection; }, [documents, initialSelectedDocuments]); - const [rowSelection, setRowSelection] = useState>({}); + const [rowSelection, setRowSelection] = useState>(() => initialRowSelection); - // Only update row selection when initialRowSelection actually changes and is not empty + // Update row selection when initial selection changes useEffect(() => { - const hasChanges = JSON.stringify(rowSelection) !== JSON.stringify(initialRowSelection); - if (hasChanges && Object.keys(initialRowSelection).length > 0) { - setRowSelection(initialRowSelection); - } + setRowSelection(initialRowSelection); }, [initialRowSelection]); - // Initialize row selection on mount - useEffect(() => { - if (Object.keys(rowSelection).length === 0 && Object.keys(initialRowSelection).length > 0) { - setRowSelection(initialRowSelection); - } - }, []); - const filteredDocuments = useMemo(() => { if (documentTypeFilter === "ALL") return documents; return documents.filter((doc) => doc.document_type === documentTypeFilter); @@ -237,12 +235,14 @@ export function DocumentsDataTable({ onSortingChange: setSorting, onColumnFiltersChange: setColumnFilters, getCoreRowModel: getCoreRowModel(), - getPaginationRowModel: getPaginationRowModel(), getSortedRowModel: getSortedRowModel(), getFilteredRowModel: getFilteredRowModel(), onColumnVisibilityChange: setColumnVisibility, onRowSelectionChange: setRowSelection, - initialState: { pagination: { pageSize: 10 } }, + // Disable internal pagination since we're handling it at the parent level + manualPagination: true, + pageCount: -1, + enableRowSelection: true, state: { sorting, columnFilters, columnVisibility, rowSelection }, }); @@ -250,7 +250,7 @@ export function DocumentsDataTable({ const selectedRows = table.getFilteredSelectedRowModel().rows; const selectedDocuments = selectedRows.map((row) => row.original); onSelectionChange(selectedDocuments); - }, [rowSelection, onSelectionChange, table]); + }, [rowSelection, table]); const handleClearAll = () => setRowSelection({}); @@ -275,6 +275,7 @@ export function DocumentsDataTable({ const selectedCount = table.getFilteredSelectedRowModel().rows.length; const totalFiltered = table.getFilteredRowModel().rows.length; + return (
{/* Header Controls */} diff --git a/surfsense_web/hooks/use-documents.ts b/surfsense_web/hooks/use-documents.ts index 45d3adee8..79c9a9766 100644 --- a/surfsense_web/hooks/use-documents.ts +++ b/surfsense_web/hooks/use-documents.ts @@ -41,10 +41,10 @@ export function useDocuments( const [isLoaded, setIsLoaded] = useState(false); // Memoization flag const fetchDocuments = useCallback(async () => { - if (isLoaded && lazy) return; // Avoid redundant calls in lazy mode - try { setLoading(true); + setError(null); + const response = await fetch( `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/documents?search_space_id=${searchSpaceId}&skip=${skip}&limit=${limit}`, { @@ -56,21 +56,22 @@ export function useDocuments( ); if (!response.ok) { + const errorText = await response.text(); toast.error("Failed to fetch documents"); - throw new Error("Failed to fetch documents"); + throw new Error(`Failed to fetch documents: ${response.status} ${errorText}`); } const data = await response.json(); - setDocuments(data); - setError(null); + setDocuments(data || []); setIsLoaded(true); } catch (err: any) { setError(err.message || "Failed to fetch documents"); console.error("Error fetching documents:", err); + setDocuments([]); } finally { setLoading(false); } - }, [searchSpaceId, skip, limit, isLoaded, lazy]); + }, [searchSpaceId, skip, limit]); useEffect(() => { if (!lazy && searchSpaceId) {