diff --git a/internal/site/src/components/routes/system/package-updates-table.tsx b/internal/site/src/components/routes/system/package-updates-table.tsx index 7041cce28..d9d619524 100644 --- a/internal/site/src/components/routes/system/package-updates-table.tsx +++ b/internal/site/src/components/routes/system/package-updates-table.tsx @@ -6,12 +6,18 @@ import { flexRender, getCoreRowModel, getFilteredRowModel, + getPaginationRowModel, getSortedRowModel, + type PaginationState, type SortingState, useReactTable, } from "@tanstack/react-table" import { ArrowUpDownIcon, + ChevronLeftIcon, + ChevronRightIcon, + ChevronsLeftIcon, + ChevronsRightIcon, GitCompareArrowsIcon, PackageCheckIcon, PackageIcon, @@ -24,11 +30,13 @@ import { Badge, type BadgeProps } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Card, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Separator } from "@/components/ui/separator" import { TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { pb } from "@/lib/api" import { classifyVersionChange, type VersionChange } from "@/lib/package-updates" -import { cn, formatShortDate } from "@/lib/utils" +import { cn, formatShortDate, useBrowserStorage } from "@/lib/utils" import type { PackageUpdate, PackageUpdates } from "@/types" interface PackageUpdateRow extends PackageUpdate { @@ -46,6 +54,8 @@ const changeVariant: Record = { other: "outline", } +const pageSizes = [10, 20, 50, 100, 200] + function changeLabel(change: VersionChange) { switch (change) { case "major": @@ -122,16 +132,6 @@ function getColumns(securityKnown: boolean): ColumnDef[] { ), cell: ({ getValue }) => {getValue() as string}, }, - { - id: "change", - accessorFn: (pkg) => changeRank[pkg.change], - header: ({ column }) => , - cell: ({ row }) => ( - - {changeLabel(row.original.change)} - - ), - }, ] if (securityKnown) { columns.push({ @@ -147,6 +147,17 @@ function getColumns(securityKnown: boolean): ColumnDef[] { ) : null, }) } + // Change is always the last column, pinned to the right edge of the table + columns.push({ + id: "change", + accessorFn: (pkg) => changeRank[pkg.change], + header: ({ column }) => , + cell: ({ row }) => ( + + {changeLabel(row.original.change)} + + ), + }) return columns } @@ -159,6 +170,10 @@ export default function PackageUpdatesTable({ systemId, counts }: { systemId: st const [error, setError] = useState(null) const [sorting, setSorting] = useState([{ id: "name", desc: false }]) const [globalFilter, setGlobalFilter] = useState("") + // Store page size preference in local storage + const [pageSize, setPageSize] = useBrowserStorage("pu-page-size", pageSizes[1], sessionStorage) + const [pageIndex, setPageIndex] = useState(0) + const pagination = useMemo(() => ({ pageIndex, pageSize }), [pageIndex, pageSize]) useEffect(() => { let cancelled = false @@ -190,9 +205,15 @@ export default function PackageUpdatesTable({ systemId, counts }: { systemId: st getCoreRowModel: getCoreRowModel(), getSortedRowModel: getSortedRowModel(), getFilteredRowModel: getFilteredRowModel(), + getPaginationRowModel: getPaginationRowModel(), onSortingChange: setSorting, onGlobalFilterChange: setGlobalFilter, - state: { sorting, globalFilter }, + onPaginationChange: (updater) => { + const next = typeof updater === "function" ? updater(pagination) : updater + setPageIndex(next.pageIndex) + setPageSize(next.pageSize) + }, + state: { sorting, globalFilter, pagination }, globalFilterFn: (row, _columnId, filterValue: string) => { const pkg = row.original const searchString = `${pkg.name} ${pkg.current ?? ""} ${pkg.available} ${changeLabel(pkg.change)}`.toLowerCase() @@ -267,13 +288,13 @@ export default function PackageUpdatesTable({ systemId, counts }: { systemId: st {error ? (

{error}

) : ( -
+
{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( - + {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} ))} @@ -302,6 +323,73 @@ export default function PackageUpdatesTable({ systemId, counts }: { systemId: st
)} + {!error && rows.length > pageSizes[0] && ( +
+
+ + +
+
+ + Page {pageIndex + 1} of {Math.max(table.getPageCount(), 1)} + +
+
+ + + + +
+
+ )} ) }