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) {