Spaces:
Running
Running
| "use client"; | |
| import { | |
| ColumnDef, | |
| flexRender, | |
| getCoreRowModel, | |
| getSortedRowModel, | |
| getPaginationRowModel, | |
| SortingState, | |
| useReactTable, | |
| } from "@tanstack/react-table"; | |
| import { | |
| Table, | |
| TableBody, | |
| TableCell, | |
| TableHead, | |
| TableHeader, | |
| TableRow, | |
| } from "@/components/ui/table"; | |
| import { Button } from "@/components/ui/button"; | |
| import { useState } from "react"; | |
| import { ChevronLeft, ChevronRight, ChevronsUpDown, ChevronUp, ChevronDown } from "lucide-react"; | |
| interface DataTableProps<TData, TValue> { | |
| columns: ColumnDef<TData, TValue>[]; | |
| data: TData[]; | |
| onRowClick?: (row: TData) => void; | |
| } | |
| export function DataTable<TData, TValue>({ | |
| columns, | |
| data, | |
| onRowClick, | |
| }: DataTableProps<TData, TValue>) { | |
| const [sorting, setSorting] = useState<SortingState>([]); | |
| const table = useReactTable({ | |
| data, | |
| columns, | |
| getCoreRowModel: getCoreRowModel(), | |
| getSortedRowModel: getSortedRowModel(), | |
| getPaginationRowModel: getPaginationRowModel(), | |
| onSortingChange: setSorting, | |
| state: { sorting }, | |
| initialState: { pagination: { pageSize: 15 } }, | |
| }); | |
| return ( | |
| <div> | |
| <div className="rounded-md border"> | |
| <Table> | |
| <TableHeader> | |
| {table.getHeaderGroups().map((headerGroup) => ( | |
| <TableRow key={headerGroup.id}> | |
| {headerGroup.headers.map((header) => ( | |
| <TableHead | |
| key={header.id} | |
| className={header.column.getCanSort() ? "cursor-pointer select-none" : ""} | |
| onClick={header.column.getToggleSortingHandler()} | |
| > | |
| <div className="flex items-center gap-1"> | |
| {header.isPlaceholder | |
| ? null | |
| : flexRender(header.column.columnDef.header, header.getContext())} | |
| {header.column.getCanSort() && ( | |
| <span className="ml-1"> | |
| {header.column.getIsSorted() === "asc" ? ( | |
| <ChevronUp className="h-3 w-3" /> | |
| ) : header.column.getIsSorted() === "desc" ? ( | |
| <ChevronDown className="h-3 w-3" /> | |
| ) : ( | |
| <ChevronsUpDown className="h-3 w-3 text-muted-foreground/50" /> | |
| )} | |
| </span> | |
| )} | |
| </div> | |
| </TableHead> | |
| ))} | |
| </TableRow> | |
| ))} | |
| </TableHeader> | |
| <TableBody> | |
| {table.getRowModel().rows?.length ? ( | |
| table.getRowModel().rows.map((row) => ( | |
| <TableRow | |
| key={row.id} | |
| className={onRowClick ? "cursor-pointer" : ""} | |
| onClick={() => onRowClick?.(row.original)} | |
| > | |
| {row.getVisibleCells().map((cell) => ( | |
| <TableCell key={cell.id}> | |
| {flexRender(cell.column.columnDef.cell, cell.getContext())} | |
| </TableCell> | |
| ))} | |
| </TableRow> | |
| )) | |
| ) : ( | |
| <TableRow> | |
| <TableCell colSpan={columns.length} className="h-24 text-center"> | |
| No results. | |
| </TableCell> | |
| </TableRow> | |
| )} | |
| </TableBody> | |
| </Table> | |
| </div> | |
| <div className="flex items-center justify-between py-4"> | |
| <p className="text-sm text-muted-foreground"> | |
| {table.getFilteredRowModel().rows.length} row(s) | |
| </p> | |
| <div className="flex gap-2"> | |
| <Button | |
| variant="outline" | |
| size="sm" | |
| onClick={() => table.previousPage()} | |
| disabled={!table.getCanPreviousPage()} | |
| > | |
| <ChevronLeft className="h-4 w-4" /> | |
| </Button> | |
| <Button | |
| variant="outline" | |
| size="sm" | |
| onClick={() => table.nextPage()} | |
| disabled={!table.getCanNextPage()} | |
| > | |
| <ChevronRight className="h-4 w-4" /> | |
| </Button> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |