| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- import { useEffect, useState } from 'react'
- import { getRouteApi } from '@tanstack/react-router'
- import {
- type SortingState,
- type VisibilityState,
- flexRender,
- getCoreRowModel,
- getFacetedRowModel,
- getFacetedUniqueValues,
- getFilteredRowModel,
- getPaginationRowModel,
- getSortedRowModel,
- useReactTable,
- } from '@tanstack/react-table'
- import { cn } from '@/lib/utils'
- import { useTableUrlState } from '@/hooks/use-table-url-state'
- import {
- Table,
- TableBody,
- TableCell,
- TableHead,
- TableHeader,
- TableRow,
- } from '@/components/ui/table'
- import { DataTablePagination, DataTableToolbar } from '@/components/data-table'
- import { priorities, statuses } from '../data/data'
- import { type Task } from '../data/schema'
- import { DataTableBulkActions } from './data-table-bulk-actions'
- import { tasksColumns as columns } from './tasks-columns'
- const route = getRouteApi('/_authenticated/tasks/')
- type DataTableProps = {
- data: Task[]
- }
- export function TasksTable({ data }: DataTableProps) {
- // Local UI-only states
- const [rowSelection, setRowSelection] = useState({})
- const [sorting, setSorting] = useState<SortingState>([])
- const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({})
- // Local state management for table (uncomment to use local-only state, not synced with URL)
- // const [globalFilter, onGlobalFilterChange] = useState('')
- // const [columnFilters, onColumnFiltersChange] = useState<ColumnFiltersState>([])
- // const [pagination, onPaginationChange] = useState<PaginationState>({ pageIndex: 0, pageSize: 10 })
- // Synced with URL states (updated to match route search schema defaults)
- const {
- globalFilter,
- onGlobalFilterChange,
- columnFilters,
- onColumnFiltersChange,
- pagination,
- onPaginationChange,
- ensurePageInRange,
- } = useTableUrlState({
- search: route.useSearch(),
- navigate: route.useNavigate(),
- pagination: { defaultPage: 1, defaultPageSize: 10 },
- globalFilter: { enabled: true, key: 'filter' },
- columnFilters: [
- { columnId: 'status', searchKey: 'status', type: 'array' },
- { columnId: 'priority', searchKey: 'priority', type: 'array' },
- ],
- })
- // eslint-disable-next-line react-hooks/incompatible-library
- const table = useReactTable({
- data,
- columns,
- state: {
- sorting,
- columnVisibility,
- rowSelection,
- columnFilters,
- globalFilter,
- pagination,
- },
- enableRowSelection: true,
- onRowSelectionChange: setRowSelection,
- onSortingChange: setSorting,
- onColumnVisibilityChange: setColumnVisibility,
- globalFilterFn: (row, _columnId, filterValue) => {
- const id = String(row.getValue('id')).toLowerCase()
- const title = String(row.getValue('title')).toLowerCase()
- const searchValue = String(filterValue).toLowerCase()
- return id.includes(searchValue) || title.includes(searchValue)
- },
- getCoreRowModel: getCoreRowModel(),
- getFilteredRowModel: getFilteredRowModel(),
- getPaginationRowModel: getPaginationRowModel(),
- getSortedRowModel: getSortedRowModel(),
- getFacetedRowModel: getFacetedRowModel(),
- getFacetedUniqueValues: getFacetedUniqueValues(),
- onPaginationChange,
- onGlobalFilterChange,
- onColumnFiltersChange,
- })
- const pageCount = table.getPageCount()
- useEffect(() => {
- ensurePageInRange(pageCount)
- }, [pageCount, ensurePageInRange])
- return (
- <div
- className={cn(
- 'max-sm:has-[div[role="toolbar"]]:mb-16', // Add margin bottom to the table on mobile when the toolbar is visible
- 'flex flex-1 flex-col gap-4'
- )}
- >
- <DataTableToolbar
- table={table}
- searchPlaceholder='Filter by title or ID...'
- filters={[
- {
- columnId: 'status',
- title: 'Status',
- options: statuses,
- },
- {
- columnId: 'priority',
- title: 'Priority',
- options: priorities,
- },
- ]}
- />
- <div className='overflow-hidden rounded-md border'>
- <Table>
- <TableHeader>
- {table.getHeaderGroups().map((headerGroup) => (
- <TableRow key={headerGroup.id}>
- {headerGroup.headers.map((header) => {
- return (
- <TableHead
- key={header.id}
- colSpan={header.colSpan}
- className={cn(
- header.column.columnDef.meta?.className,
- header.column.columnDef.meta?.thClassName
- )}
- >
- {header.isPlaceholder
- ? null
- : flexRender(
- header.column.columnDef.header,
- header.getContext()
- )}
- </TableHead>
- )
- })}
- </TableRow>
- ))}
- </TableHeader>
- <TableBody>
- {table.getRowModel().rows?.length ? (
- table.getRowModel().rows.map((row) => (
- <TableRow
- key={row.id}
- data-state={row.getIsSelected() && 'selected'}
- >
- {row.getVisibleCells().map((cell) => (
- <TableCell
- key={cell.id}
- className={cn(
- cell.column.columnDef.meta?.className,
- cell.column.columnDef.meta?.tdClassName
- )}
- >
- {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>
- <DataTablePagination table={table} className='mt-auto' />
- <DataTableBulkActions table={table} />
- </div>
- )
- }
|