-
-
Notifications
You must be signed in to change notification settings - Fork 784
feat: fixed chat Documents table #427
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughThe document selection flow in the chat component has been refactored. ChatInputGroup simplifies the DocumentSelector by removing lazy-load document fetching logic and reducing it to state toggle behavior. DocumentsDataTable now fetches documents server-side using a searchSpaceId prop instead of receiving a pre-loaded documents array, implementing pagination, debounced search, and document-type filtering. Changes
Sequence DiagramsequenceDiagram
participant User
participant ChatInputGroup
participant DocumentsDataTable
participant useDocuments Hook
participant Backend
User->>ChatInputGroup: Opens document selector
ChatInputGroup->>DocumentsDataTable: Renders (passes searchSpaceId)
DocumentsDataTable->>DocumentsDataTable: Initialize: pageIndex=0, search="", filters={}
DocumentsDataTable->>useDocuments Hook: Fetch with searchSpaceId, page, search, filters
useDocuments Hook->>Backend: Request documents + type counts
Backend-->>useDocuments Hook: Return paginated data + counts
useDocuments Hook-->>DocumentsDataTable: Load state complete
DocumentsDataTable->>DocumentsDataTable: Render table + type filter options
User->>DocumentsDataTable: Type in search (debounced)
DocumentsDataTable->>DocumentsDataTable: Trigger debounced effect
DocumentsDataTable->>useDocuments Hook: Re-fetch with new search query
useDocuments Hook->>Backend: Request filtered documents
Backend-->>useDocuments Hook: Return filtered results
DocumentsDataTable->>DocumentsDataTable: Update table, persist selection in map
User->>DocumentsDataTable: Select document
DocumentsDataTable->>DocumentsDataTable: Update selectedDocumentsMap
DocumentsDataTable->>ChatInputGroup: Notify onSelectionChange (parent)
User->>DocumentsDataTable: Navigate to next page
DocumentsDataTable->>DocumentsDataTable: Increment pageIndex
DocumentsDataTable->>useDocuments Hook: Fetch next page
useDocuments Hook->>Backend: Request page N
Backend-->>useDocuments Hook: Return page data
DocumentsDataTable->>DocumentsDataTable: Render new page, restore prior selections
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes The DocumentsDataTable component undergoes substantial refactoring with multiple interacting systems: server-side pagination and filtering logic, debounced search integration, selection persistence across pages via a new map-based structure, and significant UI restructuring (Popover-based type filters, redesigned pagination controls). The shift from client-side to server-side data flow requires careful validation of state management and API contract changes. ChatInputGroup's changes are simpler but must be reviewed in context of the prop signature change to DocumentsDataTable. Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review by RecurseML
🔍 Review performed on 6750c47..c4b9ce6
✨ No bugs found, your code is sparkling clean
✅ Files analyzed, no issues (2)
• surfsense_web/components/chat/ChatInputGroup.tsx
• surfsense_web/components/chat/DocumentsDataTable.tsx
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
surfsense_web/components/chat/DocumentsDataTable.tsx (1)
547-575
: Fix zero‑results pagination text (“1 to 0 of 0”).When total === 0 you display 1→0. Compute safe start/end and clamp pageCount.
Apply this diff:
- pageCount: Math.ceil(total / pageSize), + const pageCount = Math.max(1, Math.ceil(total / pageSize)); + // … + pageCount,- <div className="text-center sm:text-left"> - Showing {pageIndex * pageSize + 1} to {Math.min((pageIndex + 1) * pageSize, total)} of {total} documents - </div> + const shownStart = total === 0 ? 0 : pageIndex * pageSize + 1; + const shownEnd = total === 0 ? 0 : Math.min((pageIndex + 1) * pageSize, total); + // … + <div className="text-center sm:text-left"> + Showing {shownStart} to {shownEnd} of {total} documents + </div>Also applies to: 327-329
🧹 Nitpick comments (4)
surfsense_web/components/chat/DocumentsDataTable.tsx (4)
42-49
: Consider React’s useDeferredValue instead of a custom debounce.For keystroke‑heavy search UIs, useDeferredValue avoids re‑renders without timers. If you need a fixed delay, keep your hook; otherwise:
- function useDebounced<T>(value: T, delay = 300) { - const [debounced, setDebounced] = useState(value); - useEffect(() => { - const t = setTimeout(() => setDebounced(value), delay); - return () => clearTimeout(t); - }, [value, delay]); - return debounced; - } + // import { useDeferredValue } from "react"; + // const deferredSearch = useDeferredValue(search);Then switch debouncedSearch to deferredSearch.
372-377
: Minor: use input type="search" for better UX.Enables native clear buttons and semantics.
- <Input + <Input + type="search" placeholder="Search documents..."
398-407
: Sanitize HTML id/for to avoid invalid characters.Document types may contain spaces/symbols. Create a safe id slug.
- <Checkbox - id={`type-${type}`} + {(() => { + const safe = `type-${type.replace(/[^a-zA-Z0-9_-]/g, "")}`; + return ( + <Checkbox + id={safe} checked={documentTypeFilter.includes(type)} onCheckedChange={(checked) => handleToggleType(type, !!checked)} - /> - )()} + /> + ); + })()} - <Label htmlFor={`type-${type}`} … + <Label htmlFor={safe} …
188-194
: Optional: prevent out‑of‑order fetch updates on rapid input.Debounce helps, but concurrent fetches can still resolve out of order. Track a request id or use AbortController in useDocuments and ignore stale responses.
I can propose a small patch to use-documents.ts adding an AbortController per call if you want.
Also applies to: 202-229
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
surfsense_web/components/chat/ChatInputGroup.tsx
(3 hunks)surfsense_web/components/chat/DocumentsDataTable.tsx
(6 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.rules/require_unique_id_props.mdc)
**/*.{jsx,tsx}
: When mapping arrays to React elements in JSX/TSX, each rendered element must include a unique key prop
Keys used for React list items should be stable, predictable, and unique among siblings
Files:
surfsense_web/components/chat/ChatInputGroup.tsx
surfsense_web/components/chat/DocumentsDataTable.tsx
🧬 Code graph analysis (2)
surfsense_web/components/chat/ChatInputGroup.tsx (1)
surfsense_web/components/chat/DocumentsDataTable.tsx (1)
DocumentsDataTable
(174-579)
surfsense_web/components/chat/DocumentsDataTable.tsx (1)
surfsense_web/hooks/use-documents.ts (2)
Document
(6-14)useDocuments
(42-265)
🔇 Additional comments (4)
surfsense_web/components/chat/ChatInputGroup.tsx (2)
28-28
: Type-only import is correct and preferred here.Reduces bundle surface and avoids runtime import. Nice.
43-45
: Open-state handler simplification LGTM.Removing fetch side-effects from open/close improves separation of concerns; fetching now lives in DocumentsDataTable.
surfsense_web/components/chat/DocumentsDataTable.tsx (2)
501-527
: List keys look good and stable.
- Header groups: key={headerGroup.id}
- Headers: key={header.id}
- Rows: key={row.id}
- Cells: key={cell.id}
- Type filter list: key={type}
Also applies to: 521-525, 398-413
35-40
: Prop contract change verified at all call sites.The only invocation of
<DocumentsDataTable>
atChatInputGroup.tsx:83-88
correctly passessearchSpaceId={Number(search_space_id)}
along with all other required props (onSelectionChange
,onDone
,initialSelectedDocuments
). No usages with the olddocuments
prop pattern remain anywhere in the codebase.
<DocumentsDataTable | ||
searchSpaceId={Number(search_space_id)} | ||
onSelectionChange={handleSelectionChange} | ||
onDone={handleDone} | ||
initialSelectedDocuments={selectedDocuments} | ||
/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Guard against invalid searchSpaceId (NaN) to avoid silent no‑fetch state.
Number(search_space_id) can be NaN; useDocuments then won’t fetch, leaving the dialog empty. Add a parse/guard and disable the trigger or render a message when invalid.
Apply this diff:
- <DocumentsDataTable
- searchSpaceId={Number(search_space_id)}
+ const sid = Number.parseInt(String(search_space_id ?? ''), 10);
+ const hasValidSid = Number.isFinite(sid) && sid > 0;
+ // …
+ {hasValidSid ? (
+ <DocumentsDataTable
+ searchSpaceId={sid}
onSelectionChange={handleSelectionChange}
onDone={handleDone}
initialSelectedDocuments={selectedDocuments}
- />
+ />
+ ) : (
+ <div className="text-sm text-muted-foreground">Invalid or missing search space.</div>
+ )}
Run to check other call sites also pass a valid number:
🏁 Script executed:
#!/bin/bash
rg -nP --type=ts --type=tsx -C2 '<DocumentsDataTable\b' | sed -n '1,200p'
Length of output: 27
I'll fix the ripgrep command to properly search for DocumentsDataTable usage in the codebase.
---
🏁 Script executed:
```shell
#!/bin/bash
# Search for DocumentsDataTable usage without strict type filtering
rg -nP 'DocumentsDataTable' --type-add 'web:*.{ts,tsx,js,jsx}' --type web -C3 | head -100
Length of output: 3023
🏁 Script executed:
#!/bin/bash
# Alternative: search using glob patterns directly
rg -nP 'DocumentsDataTable' -g '*.tsx' -g '*.ts' -C3 | head -100
Length of output: 3023
🏁 Script executed:
#!/bin/bash
# Also find the DocumentsDataTable component definition
rg -nP 'export.*DocumentsDataTable|function DocumentsDataTable|const DocumentsDataTable' -g '*.tsx' -g '*.ts'
Length of output: 93
🏁 Script executed:
#!/bin/bash
# Search for search_space_id usage in ChatInputGroup
fd ChatInputGroup -e tsx -e ts -x cat {} \;
Length of output: 17613
🏁 Script executed:
#!/bin/bash
# Check if useDocuments hook and how it handles invalid IDs
rg -nP 'useDocuments|searchSpaceId' -g '*.tsx' -g '*.ts' -A2 -B2 | head -150
Length of output: 9599
---
🏁 Script executed:
```shell
#!/bin/bash
# Get the full useDocuments hook implementation
cat surfsense_web/hooks/use-documents.ts
Length of output: 7788
🏁 Script executed:
#!/bin/bash
# Get the full DocumentsDataTable implementation to see error handling
cat surfsense_web/components/chat/DocumentsDataTable.tsx
Length of output: 17730
🏁 Script executed:
#!/bin/bash
# Check if search_space_id can be undefined from useParams
rg -nP 'useParams' -g '*.tsx' -g '*.ts' -A5 | head -50
Length of output: 4578
🏁 Script executed:
#!/bin/bash
# Search for any existing NaN or isFinite guards in the codebase
rg -nP 'Number\.isFinite|isNaN|Number\.parseInt' -g '*.tsx' -g '*.ts'
Length of output: 1095
Guard against invalid searchSpaceId (NaN) to prevent silent loading state.
The concern is valid: Number(search_space_id)
can be NaN
if search_space_id
is undefined from useParams()
. The useDocuments
hook checks if (!lazy && searchSpaceId)
at line 113, and since NaN
is falsy, the fetch never occurs. The UI then shows either an indefinite loading spinner or silently displays "No documents found" without indicating the root cause.
One call site confirmed (ChatInputGroup.tsx:84), and the codebase already uses Number.isNaN()
guards elsewhere for similar validation (e.g., connectors/edit pages).
The proposed fix adds validation at the call site with conditional rendering. Alternatively, consider adding the guard inside DocumentsDataTable
itself (lines 210-221) to catch the issue closer to where it manifests, or within the useDocuments
hook to handle it universally.
🤖 Prompt for AI Agents
In surfsense_web/components/chat/ChatInputGroup.tsx around lines 83 to 88,
Number(search_space_id) can be NaN when search_space_id is undefined from
useParams(), causing useDocuments to skip fetching and leave the UI in a silent
loading/empty state; update the call site to validate the param first (e.g.,
parseInt/Number then check Number.isNaN or ensure search_space_id != null and
/^\d+$/.test(search_space_id)) and only render <DocumentsDataTable ... /> when
searchSpaceId is a valid number, otherwise render an appropriate fallback or
early return; alternatively, if you prefer a centralized fix, add the NaN guard
inside DocumentsDataTable (or inside useDocuments) to treat NaN as invalid and
surface an error/empty state instead of silently skipping the fetch.
const table = useReactTable({ | ||
data: filteredDocuments, | ||
data: documents || [], | ||
columns, | ||
getRowId: (row) => row.id.toString(), | ||
onSortingChange: setSorting, | ||
onColumnFiltersChange: setColumnFilters, | ||
getCoreRowModel: getCoreRowModel(), | ||
getPaginationRowModel: getPaginationRowModel(), | ||
getSortedRowModel: getSortedRowModel(), | ||
getFilteredRowModel: getFilteredRowModel(), | ||
onColumnVisibilityChange: setColumnVisibility, | ||
onRowSelectionChange: setRowSelection, | ||
initialState: { pagination: { pageSize: 10 } }, | ||
state: { sorting, columnFilters, columnVisibility, rowSelection }, | ||
manualPagination: true, | ||
pageCount: Math.ceil(total / pageSize), | ||
state: { sorting, rowSelection, pagination: { pageIndex, pageSize } }, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorting UI is currently a no‑op. Wire client sorting or remove toggles.
You track sorting but don’t apply it (no getSortedRowModel, no server sort params). This confuses users.
Two options; pick one:
- Client per‑page sorting (quick fix):
- import {
- type ColumnDef, flexRender, getCoreRowModel, type SortingState, useReactTable,
- } from "@tanstack/react-table";
+ import {
+ type ColumnDef, flexRender, getCoreRowModel, getSortedRowModel, type SortingState, useReactTable,
+ } from "@tanstack/react-table";
const table = useReactTable({
data: documents || [],
columns,
getRowId: (row) => row.id.toString(),
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
+ getSortedRowModel: getSortedRowModel(),
onRowSelectionChange: setRowSelection,
manualPagination: true,
- pageCount: Math.ceil(total / pageSize),
- state: { sorting, rowSelection, pagination: { pageIndex, pageSize } },
+ pageCount,
+ state: { sorting, rowSelection, pagination: { pageIndex, pageSize } },
});
- Or implement server sorting: include sorting in deps and pass to useDocuments (API needs sort field and direction). Until then, consider disabling column sorting to avoid misleading UI.
Also applies to: 202-229
Description
feat: fixed chat Documents table
Screenshots
API Changes
Change Type
Testing Performed
Checklist
High-level PR Summary
This PR refactors the chat Documents table to implement server-side pagination, search, and filtering instead of client-side operations. The changes move data fetching responsibility from the parent
ChatInputGroup
component to theDocumentsDataTable
component itself, replacing eager loading with lazy loading. The refactor includes a new multi-select filter UI using a popover with checkboxes, debounced search functionality, and proper state management to track selected documents across paginated pages.⏱️ Estimated Review Time: 30-90 minutes
💡 Review Order Suggestion
surfsense_web/hooks/use-documents.ts
surfsense_web/components/chat/DocumentsDataTable.tsx
surfsense_web/components/chat/ChatInputGroup.tsx
Summary by CodeRabbit
New Features
Improvements