From 8bd7d74a1d5aec0debc81f178a5748f9a566d403 Mon Sep 17 00:00:00 2001 From: tianlilei-c <2816999029@qq.com> Date: Tue, 6 May 2025 19:24:17 +0800 Subject: [PATCH 1/3] feat: integrate pagination into EnhancedDataTable toolbar and apply on LinkedAccountsPage --- frontend/package-lock.json | 231 +++++++++++++++--- frontend/package.json | 2 +- frontend/src/app/linked-accounts/page.tsx | 4 + .../data-table-pagination.tsx | 150 ++++++++++++ .../data-table-toolbar.tsx | 9 + .../enhanced-data-table/data-table.tsx | 27 +- frontend/src/components/ui/pagination.tsx | 117 +++++++++ 7 files changed, 497 insertions(+), 43 deletions(-) create mode 100644 frontend/src/components/ui-extensions/enhanced-data-table/data-table-pagination.tsx create mode 100644 frontend/src/components/ui/pagination.tsx diff --git a/frontend/package-lock.json b/frontend/package-lock.json index aed88f25..42e9ca75 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -21,7 +21,7 @@ "@radix-ui/react-scroll-area": "^1.2.3", "@radix-ui/react-select": "^2.1.5", "@radix-ui/react-separator": "^1.1.1", - "@radix-ui/react-slot": "^1.1.2", + "@radix-ui/react-slot": "^1.2.2", "@radix-ui/react-switch": "^1.1.2", "@radix-ui/react-tabs": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.6", @@ -2690,6 +2690,23 @@ } } }, + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-arrow": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.2.tgz", @@ -2828,9 +2845,8 @@ }, "node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-slot": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", - "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, @@ -2967,6 +2983,23 @@ } } }, + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", @@ -3033,6 +3066,23 @@ } } }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-direction": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", @@ -3223,6 +3273,23 @@ } } }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-popover": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.6.tgz", @@ -3260,6 +3327,23 @@ } } }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-popper": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.2.tgz", @@ -3363,6 +3447,23 @@ } } }, + "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-roving-focus": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.2.tgz", @@ -3468,6 +3569,23 @@ } } }, + "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-separator": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.2.tgz", @@ -3492,12 +3610,11 @@ } }, "node_modules/@radix-ui/react-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", - "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", - "license": "MIT", + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.2.2.tgz", + "integrity": "sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ==", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" + "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", @@ -3509,6 +3626,20 @@ } } }, + "node_modules/@radix-ui/react-slot/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-switch": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.1.3.tgz", @@ -3602,6 +3733,23 @@ } } }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-callback-ref": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", @@ -3807,18 +3955,6 @@ } } }, - "node_modules/@rollup/plugin-commonjs/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/@rollup/pluginutils": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", @@ -3841,18 +3977,6 @@ } } }, - "node_modules/@rollup/pluginutils/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.34.9", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.9.tgz", @@ -5699,6 +5823,17 @@ "node": ">= 8" } }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -10522,6 +10657,18 @@ "node": ">=8.6" } }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -11162,12 +11309,11 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -11800,6 +11946,17 @@ "node": ">=8.10.0" } }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/recharts": { "version": "2.15.1", "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index f534130d..f8270cd4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -27,7 +27,7 @@ "@radix-ui/react-scroll-area": "^1.2.3", "@radix-ui/react-select": "^2.1.5", "@radix-ui/react-separator": "^1.1.1", - "@radix-ui/react-slot": "^1.1.2", + "@radix-ui/react-slot": "^1.2.2", "@radix-ui/react-switch": "^1.1.2", "@radix-ui/react-tabs": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.6", diff --git a/frontend/src/app/linked-accounts/page.tsx b/frontend/src/app/linked-accounts/page.tsx index 65ae7e2c..52759d50 100644 --- a/frontend/src/app/linked-accounts/page.tsx +++ b/frontend/src/app/linked-accounts/page.tsx @@ -396,6 +396,10 @@ export default function LinkedAccountsPage() { searchBarProps={{ placeholder: "Search linked accounts", }} + paginationOptions={{ + initialPageIndex: 0, + initialPageSize: 10, + }} /> )} diff --git a/frontend/src/components/ui-extensions/enhanced-data-table/data-table-pagination.tsx b/frontend/src/components/ui-extensions/enhanced-data-table/data-table-pagination.tsx new file mode 100644 index 00000000..c43e34a9 --- /dev/null +++ b/frontend/src/components/ui-extensions/enhanced-data-table/data-table-pagination.tsx @@ -0,0 +1,150 @@ +import React, { useState, useEffect } from "react"; +import { Table } from "@tanstack/react-table"; +// import { +// Select, +// SelectContent, +// SelectItem, +// SelectTrigger, +// SelectValue, +// } from "@/components/ui/select"; +import { + Pagination, + PaginationContent, + PaginationItem, + PaginationLink, + PaginationPrevious, + PaginationNext, +} from "@/components/ui/pagination"; +import { ChevronsLeft, ChevronsRight } from "lucide-react"; +import { cn } from "@/lib/utils"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; + +export const PaginationFirst = ({ + className, + ...props +}: React.ComponentProps) => ( + + + First + +); + +export const PaginationLast = ({ + className, + ...props +}: React.ComponentProps) => ( + + + Last + +); + +interface DataTablePaginationProps { + table: Table; +} + +export function DataTablePagination({ + table, +}: DataTablePaginationProps) { + const pageCount = table.getPageCount(); + const [pageInput, setPageInput] = useState( + table.getState().pagination.pageIndex + 1, + ); + + useEffect(() => { + const currentPageIndex = table.getState().pagination.pageIndex; + setPageInput(currentPageIndex + 1); + }, [table]); + + const jumpToPage = () => { + const idx = Math.min(Math.max(pageInput, 1), pageCount) - 1; + table.setPageIndex(idx); + }; + + return ( +
+
+ + + + table.firstPage()} + className={ + !table.getCanPreviousPage() + ? "pointer-events-none opacity-50" + : "" + } + /> + + + table.previousPage()} + className={ + !table.getCanPreviousPage() + ? "pointer-events-none opacity-50" + : "" + } + /> + + + + setPageInput(Number(e.target.value))} + onKeyDown={(e) => e.key === "Enter" && jumpToPage()} + className="w-14 text-center" + /> + + + + + table.nextPage()} + className={ + !table.getCanNextPage() + ? "pointer-events-none opacity-50" + : "" + } + /> + + + table.lastPage()} + className={ + !table.getCanNextPage() + ? "pointer-events-none opacity-50" + : "" + } + /> + + + + +
+ Page {table.getState().pagination.pageIndex + 1} of{" "} + {table.getPageCount()} +
+
+
+ ); +} diff --git a/frontend/src/components/ui-extensions/enhanced-data-table/data-table-toolbar.tsx b/frontend/src/components/ui-extensions/enhanced-data-table/data-table-toolbar.tsx index 22749dd6..8d4ee5cc 100644 --- a/frontend/src/components/ui-extensions/enhanced-data-table/data-table-toolbar.tsx +++ b/frontend/src/components/ui-extensions/enhanced-data-table/data-table-toolbar.tsx @@ -5,12 +5,19 @@ import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { X } from "lucide-react"; import { useState } from "react"; +import { DataTablePagination } from "@/components/ui-extensions/enhanced-data-table/data-table-pagination"; + +interface PaginationOptions { + initialPageIndex?: number; + initialPageSize?: number; +} interface EnhancedDataTableToolbarProps { table: Table; placeholder?: string; showSearchInput?: boolean; filterComponent?: React.ReactNode; + paginationOptions?: PaginationOptions; } export function EnhancedDataTableToolbar({ @@ -18,6 +25,7 @@ export function EnhancedDataTableToolbar({ placeholder = "Search...", showSearchInput, filterComponent, + paginationOptions, }: EnhancedDataTableToolbarProps) { const [searchValue, setSearchValue] = useState(""); @@ -59,6 +67,7 @@ export function EnhancedDataTableToolbar({ {filterComponent} + {paginationOptions && } ); } diff --git a/frontend/src/components/ui-extensions/enhanced-data-table/data-table.tsx b/frontend/src/components/ui-extensions/enhanced-data-table/data-table.tsx index e6c48584..ad40cf9c 100644 --- a/frontend/src/components/ui-extensions/enhanced-data-table/data-table.tsx +++ b/frontend/src/components/ui-extensions/enhanced-data-table/data-table.tsx @@ -11,6 +11,8 @@ import { ColumnFiltersState, RowSelectionState, OnChangeFn, + getPaginationRowModel, + PaginationState, } from "@tanstack/react-table"; declare module "@tanstack/react-table" { @@ -44,15 +46,18 @@ interface RowSelectionProps { getRowId: (row: TData) => string; } +interface PaginationOptions { + initialPageIndex?: number; + initialPageSize?: number; +} + interface EnhancedDataTableProps { columns: ColumnDef[]; data: TData[]; - defaultSorting?: { - id: string; - desc: boolean; - }[]; + defaultSorting?: { id: string; desc: boolean }[]; searchBarProps?: SearchBarProps; rowSelectionProps?: RowSelectionProps; + paginationOptions?: PaginationOptions; } export function EnhancedDataTable({ @@ -61,6 +66,7 @@ export function EnhancedDataTable({ defaultSorting = [], searchBarProps, rowSelectionProps, + paginationOptions, }: EnhancedDataTableProps) { const generatedDefaultSorting = useMemo(() => { if (defaultSorting.length > 0) return defaultSorting; @@ -80,6 +86,11 @@ export function EnhancedDataTable({ const [globalFilter, setGlobalFilter] = useState(""); const [columnFilters, setColumnFilters] = useState([]); + const [pagination, setPagination] = useState({ + pageIndex: paginationOptions?.initialPageIndex ?? 0, + pageSize: paginationOptions?.initialPageSize ?? 10, + }); + const hasFilterableColumns = useMemo(() => { return columns.some((column) => column.enableGlobalFilter === true); }, [columns]); @@ -98,6 +109,7 @@ export function EnhancedDataTable({ sorting, globalFilter, columnFilters, + pagination, }; if (!rowSelectionProps) return baseState; @@ -106,7 +118,7 @@ export function EnhancedDataTable({ ...baseState, rowSelection: rowSelectionProps.rowSelection, }; - }, [sorting, globalFilter, columnFilters, rowSelectionProps]); + }, [sorting, globalFilter, columnFilters, rowSelectionProps, pagination]); const table = useReactTable({ data, @@ -114,9 +126,13 @@ export function EnhancedDataTable({ getCoreRowModel: getCoreRowModel(), getSortedRowModel: getSortedRowModel(), getFilteredRowModel: getFilteredRowModel(), + getPaginationRowModel: paginationOptions + ? getPaginationRowModel() + : undefined, onSortingChange: setSorting, onGlobalFilterChange: setGlobalFilter, onColumnFiltersChange: setColumnFilters, + onPaginationChange: setPagination, globalFilterFn: "includesString", state: tableState, enableRowSelection: rowSelectionProps !== undefined, @@ -170,6 +186,7 @@ export function EnhancedDataTable({ placeholder={searchBarProps.placeholder} showSearchInput={hasFilterableColumns} filterComponent={filterComponents} + paginationOptions={paginationOptions} /> )}
diff --git a/frontend/src/components/ui/pagination.tsx b/frontend/src/components/ui/pagination.tsx new file mode 100644 index 00000000..d3311054 --- /dev/null +++ b/frontend/src/components/ui/pagination.tsx @@ -0,0 +1,117 @@ +import * as React from "react" +import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/lib/utils" +import { ButtonProps, buttonVariants } from "@/components/ui/button" + +const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => ( +