Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { fetchDetailed, withApiFallback, type FetchResult } from "@lib/wiki-server";
import { fetchAllPaginated } from "@lib/fetch-paginated";
import { DataSourceBanner } from "@components/internal/DataSourceBanner";
import { AgentSessionsTable } from "./sessions-table";
import type {
Expand Down Expand Up @@ -40,16 +41,18 @@ async function loadFromApi(): Promise<FetchResult<AgentSessionRow[]>> {
);
if (!agentResult.ok) return agentResult;

// Fetch session logs (completed sessions with PR/cost info)
const logsResult = await fetchDetailed<{ sessions: SessionRow[] }>(
"/api/sessions?limit=500",
{ revalidate: 60 }
);
// Fetch all session logs (completed sessions with PR/cost info), paginating through all pages
const logsResult = await fetchAllPaginated<SessionRow>({
path: "/api/sessions",
itemsKey: "sessions",
pageSize: 500,
revalidate: 60,
});

// Build a branch → session log map for enrichment
const logsByBranch = new Map<string, SessionRow>();
if (logsResult.ok) {
for (const log of logsResult.data.sessions) {
for (const log of logsResult.data.items) {
if (log.branch) {
logsByBranch.set(log.branch, log);
}
Expand Down
73 changes: 70 additions & 3 deletions apps/web/src/components/ui/data-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import {
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table"
import { Search } from "lucide-react"
import { Search, ChevronLeft, ChevronRight } from "lucide-react"
import {
Table,
TableBody,
Expand Down Expand Up @@ -43,6 +44,8 @@ interface DataTableWithDataProps<TData, TValue> {
defaultSorting?: SortingState
renderExpandedRow?: (row: Row<TData>) => React.ReactNode
getRowClassName?: (row: Row<TData>) => string
/** Number of rows per page. Defaults to 100. Set to 0 to disable pagination. */
pageSize?: number
Comment on lines +47 to +48
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Keep pagination opt-in for the legacy DataTable API.

Defaulting pageSize to 100 changes every existing caller that does not pass this prop. Tables like apps/web/src/app/internal/system-health/open-prs-table.tsx:180-187 and apps/web/src/app/internal/session-insights/insights-table.tsx:72-77 will now paginate after 100 rows without any call-site change, so this is a shared-component regression rather than a targeted dashboard adoption. Prefer keeping the default disabled and passing pageSize explicitly where you want pagination.

[suggested_recommended_refactor]

Proposed change
-  /** Number of rows per page. Defaults to 100. Set to 0 to disable pagination. */
+  /** Number of rows per page. Omit or set to 0 to disable pagination. */
   pageSize?: number
@@
-  pageSize = 100,
+  pageSize = 0,

Also applies to: 176-177

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/ui/data-table.tsx` around lines 47 - 48, The
DataTable prop change made pagination opt-out by default; restore opt-in
behavior by setting pageSize's default to disabled (0 or undefined) so existing
callers without pageSize keep no pagination. Update the DataTable component's
prop default for pageSize (and any related handling inside the DataTable render
logic that reads pageSize) to treat missing value as 0/disabled, and ensure any
internal pagination logic (e.g., where pageSize is used to compute pages)
respects 0 as “no pagination.” This keeps legacy callers unaffected and requires
explicit pageSize where pagination is desired.

}

type DataTableProps<TData, TValue = unknown> =
Expand Down Expand Up @@ -170,11 +173,23 @@ function DataTableWithData<TData, TValue>({
defaultSorting = [],
renderExpandedRow,
getRowClassName,
pageSize = 100,
}: DataTableWithDataProps<TData, TValue>) {
const [sorting, setSorting] = React.useState<SortingState>(defaultSorting)
const [columnFilters, setColumnFilters] =
React.useState<ColumnFiltersState>([])
const [globalFilter, setGlobalFilter] = React.useState("")
const [pagination, setPagination] = React.useState({
pageIndex: 0,
pageSize: pageSize > 0 ? pageSize : data.length,
})

// Reset to page 1 when filter changes
React.useEffect(() => {
setPagination((p) => ({ ...p, pageIndex: 0 }))
}, [globalFilter])

const paginationEnabled = pageSize > 0

const table = useReactTable({
data,
Expand All @@ -184,18 +199,36 @@ function DataTableWithData<TData, TValue>({
getSortedRowModel: getSortedRowModel(),
onColumnFiltersChange: setColumnFilters,
getFilteredRowModel: getFilteredRowModel(),
...(paginationEnabled
? {
getPaginationRowModel: getPaginationRowModel(),
onPaginationChange: setPagination,
}
: {}),
onGlobalFilterChange: setGlobalFilter,
globalFilterFn: "includesString",
state: {
sorting,
columnFilters,
globalFilter,
...(paginationEnabled ? { pagination } : {}),
},
})

const filteredCount = table.getFilteredRowModel().rows.length
const pageCount = table.getPageCount()
const currentPage = table.getState().pagination.pageIndex + 1
const canPrev = table.getCanPreviousPage()
const canNext = table.getCanNextPage()

// Page range info: "Showing 1-100 of 500"
const { pageIndex, pageSize: ps } = table.getState().pagination
const rangeStart = pageIndex * ps + 1
const rangeEnd = Math.min((pageIndex + 1) * ps, filteredCount)

return (
<div className="space-y-4">
{/* Search */}
{/* Search + row count */}
<div className="flex items-center gap-4 pb-4">
<div className="relative flex-1 max-w-md">
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
Expand All @@ -207,7 +240,9 @@ function DataTableWithData<TData, TValue>({
/>
</div>
<span className="text-sm text-muted-foreground whitespace-nowrap">
{table.getFilteredRowModel().rows.length} of {data.length} results
{filteredCount === data.length
? `${data.length} results`
: `${filteredCount} of ${data.length} results`}
</span>
</div>

Expand All @@ -217,6 +252,38 @@ function DataTableWithData<TData, TValue>({
renderExpandedRow={renderExpandedRow}
getRowClassName={getRowClassName}
/>

{/* Pagination controls (only shown when pagination is enabled and there are multiple pages) */}
{paginationEnabled && pageCount > 1 && (
<div className="flex items-center justify-between px-1">
<span className="text-sm text-muted-foreground">
Showing {rangeStart}–{rangeEnd} of {filteredCount}
</span>
<div className="flex items-center gap-1">
<button
onClick={() => table.previousPage()}
disabled={!canPrev}
className="inline-flex items-center gap-1 rounded-md border border-border/60 px-2 py-1 text-xs text-muted-foreground hover:bg-muted/50 disabled:opacity-40 disabled:cursor-not-allowed transition-colors"
aria-label="Previous page"
>
<ChevronLeft className="h-3.5 w-3.5" />
Prev
</button>
<span className="px-2 text-xs text-muted-foreground tabular-nums">
{currentPage} / {pageCount}
</span>
<button
onClick={() => table.nextPage()}
disabled={!canNext}
className="inline-flex items-center gap-1 rounded-md border border-border/60 px-2 py-1 text-xs text-muted-foreground hover:bg-muted/50 disabled:opacity-40 disabled:cursor-not-allowed transition-colors"
aria-label="Next page"
>
Next
<ChevronRight className="h-3.5 w-3.5" />
</button>
</div>
</div>
)}
</div>
)
}
Expand Down
Loading