mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-20 23:21:06 +02:00
researcher page work
This commit is contained in:
parent
50562be76f
commit
df665a0424
3 changed files with 49 additions and 31 deletions
|
|
@ -45,12 +45,19 @@ const DocumentSelector = React.memo(
|
||||||
// Calculate skip value for pagination
|
// Calculate skip value for pagination
|
||||||
const skip = pageIndex * pageSize;
|
const skip = pageIndex * pageSize;
|
||||||
|
|
||||||
const { documents, loading, isLoaded, fetchDocuments } = useDocuments(
|
const { documents, loading, isLoaded, fetchDocuments, error } = useDocuments(
|
||||||
Number(search_space_id),
|
Number(search_space_id),
|
||||||
true,
|
true,
|
||||||
skip,
|
skip,
|
||||||
pageSize
|
pageSize
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Refetch documents when pagination changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (isOpen) {
|
||||||
|
fetchDocuments();
|
||||||
|
}
|
||||||
|
}, [skip, pageSize, isOpen, fetchDocuments]);
|
||||||
|
|
||||||
const handleOpenChange = useCallback(
|
const handleOpenChange = useCallback(
|
||||||
(open: boolean) => {
|
(open: boolean) => {
|
||||||
|
|
@ -105,6 +112,15 @@ const DocumentSelector = React.memo(
|
||||||
<p className="text-sm text-muted-foreground">Loading documents...</p>
|
<p className="text-sm text-muted-foreground">Loading documents...</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
) : error ? (
|
||||||
|
<div className="flex items-center justify-center h-full">
|
||||||
|
<div className="text-center space-y-2">
|
||||||
|
<p className="text-sm text-destructive">Error loading documents: {error}</p>
|
||||||
|
<Button onClick={fetchDocuments} variant="outline" size="sm">
|
||||||
|
Retry
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
) : isLoaded ? (
|
) : isLoaded ? (
|
||||||
<DocumentsDataTable
|
<DocumentsDataTable
|
||||||
documents={documents}
|
documents={documents}
|
||||||
|
|
|
||||||
|
|
@ -66,14 +66,18 @@ const columns: ColumnDef<Document>[] = [
|
||||||
checked={
|
checked={
|
||||||
table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && "indeterminate")
|
table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && "indeterminate")
|
||||||
}
|
}
|
||||||
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
onCheckedChange={(value) => {
|
||||||
|
table.toggleAllPageRowsSelected(!!value);
|
||||||
|
}}
|
||||||
aria-label="Select all"
|
aria-label="Select all"
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={row.getIsSelected()}
|
checked={row.getIsSelected()}
|
||||||
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
onCheckedChange={(value) => {
|
||||||
|
row.toggleSelected(!!value);
|
||||||
|
}}
|
||||||
aria-label="Select row"
|
aria-label="Select row"
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
|
@ -203,28 +207,22 @@ export function DocumentsDataTable({
|
||||||
|
|
||||||
const selection: Record<string, boolean> = {};
|
const selection: Record<string, boolean> = {};
|
||||||
initialSelectedDocuments.forEach((selectedDoc) => {
|
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;
|
return selection;
|
||||||
}, [documents, initialSelectedDocuments]);
|
}, [documents, initialSelectedDocuments]);
|
||||||
|
|
||||||
const [rowSelection, setRowSelection] = useState<Record<string, boolean>>({});
|
const [rowSelection, setRowSelection] = useState<Record<string, boolean>>(() => initialRowSelection);
|
||||||
|
|
||||||
// Only update row selection when initialRowSelection actually changes and is not empty
|
// Update row selection when initial selection changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const hasChanges = JSON.stringify(rowSelection) !== JSON.stringify(initialRowSelection);
|
setRowSelection(initialRowSelection);
|
||||||
if (hasChanges && Object.keys(initialRowSelection).length > 0) {
|
|
||||||
setRowSelection(initialRowSelection);
|
|
||||||
}
|
|
||||||
}, [initialRowSelection]);
|
}, [initialRowSelection]);
|
||||||
|
|
||||||
// Initialize row selection on mount
|
|
||||||
useEffect(() => {
|
|
||||||
if (Object.keys(rowSelection).length === 0 && Object.keys(initialRowSelection).length > 0) {
|
|
||||||
setRowSelection(initialRowSelection);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const filteredDocuments = useMemo(() => {
|
const filteredDocuments = useMemo(() => {
|
||||||
if (documentTypeFilter === "ALL") return documents;
|
if (documentTypeFilter === "ALL") return documents;
|
||||||
return documents.filter((doc) => doc.document_type === documentTypeFilter);
|
return documents.filter((doc) => doc.document_type === documentTypeFilter);
|
||||||
|
|
@ -237,12 +235,14 @@ export function DocumentsDataTable({
|
||||||
onSortingChange: setSorting,
|
onSortingChange: setSorting,
|
||||||
onColumnFiltersChange: setColumnFilters,
|
onColumnFiltersChange: setColumnFilters,
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
getPaginationRowModel: getPaginationRowModel(),
|
|
||||||
getSortedRowModel: getSortedRowModel(),
|
getSortedRowModel: getSortedRowModel(),
|
||||||
getFilteredRowModel: getFilteredRowModel(),
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
onColumnVisibilityChange: setColumnVisibility,
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
onRowSelectionChange: setRowSelection,
|
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 },
|
state: { sorting, columnFilters, columnVisibility, rowSelection },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -250,7 +250,7 @@ export function DocumentsDataTable({
|
||||||
const selectedRows = table.getFilteredSelectedRowModel().rows;
|
const selectedRows = table.getFilteredSelectedRowModel().rows;
|
||||||
const selectedDocuments = selectedRows.map((row) => row.original);
|
const selectedDocuments = selectedRows.map((row) => row.original);
|
||||||
onSelectionChange(selectedDocuments);
|
onSelectionChange(selectedDocuments);
|
||||||
}, [rowSelection, onSelectionChange, table]);
|
}, [rowSelection, table]);
|
||||||
|
|
||||||
const handleClearAll = () => setRowSelection({});
|
const handleClearAll = () => setRowSelection({});
|
||||||
|
|
||||||
|
|
@ -275,6 +275,7 @@ export function DocumentsDataTable({
|
||||||
const selectedCount = table.getFilteredSelectedRowModel().rows.length;
|
const selectedCount = table.getFilteredSelectedRowModel().rows.length;
|
||||||
const totalFiltered = table.getFilteredRowModel().rows.length;
|
const totalFiltered = table.getFilteredRowModel().rows.length;
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full space-y-3 md:space-y-4">
|
<div className="flex flex-col h-full space-y-3 md:space-y-4">
|
||||||
{/* Header Controls */}
|
{/* Header Controls */}
|
||||||
|
|
|
||||||
|
|
@ -41,10 +41,10 @@ export function useDocuments(
|
||||||
const [isLoaded, setIsLoaded] = useState(false); // Memoization flag
|
const [isLoaded, setIsLoaded] = useState(false); // Memoization flag
|
||||||
|
|
||||||
const fetchDocuments = useCallback(async () => {
|
const fetchDocuments = useCallback(async () => {
|
||||||
if (isLoaded && lazy) return; // Avoid redundant calls in lazy mode
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/documents?search_space_id=${searchSpaceId}&skip=${skip}&limit=${limit}`,
|
`${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) {
|
if (!response.ok) {
|
||||||
|
const errorText = await response.text();
|
||||||
toast.error("Failed to fetch documents");
|
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();
|
const data = await response.json();
|
||||||
setDocuments(data);
|
setDocuments(data || []);
|
||||||
setError(null);
|
|
||||||
setIsLoaded(true);
|
setIsLoaded(true);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
setError(err.message || "Failed to fetch documents");
|
setError(err.message || "Failed to fetch documents");
|
||||||
console.error("Error fetching documents:", err);
|
console.error("Error fetching documents:", err);
|
||||||
|
setDocuments([]);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [searchSpaceId, skip, limit, isLoaded, lazy]);
|
}, [searchSpaceId, skip, limit]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!lazy && searchSpaceId) {
|
if (!lazy && searchSpaceId) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue