Files
saladeaula.digital/apps/admin.saladeaula.digital/app/components/data-table/data-table.tsx
2025-11-12 15:50:05 -03:00

236 lines
6.4 KiB
TypeScript

'use client'
import {
flexRender,
getCoreRowModel,
useReactTable,
type ColumnDef,
type ColumnSort,
type RowSelectionState,
type SortingState,
type Table,
type VisibilityState
} from '@tanstack/react-table'
import {
createContext,
useContext,
useEffect,
useState,
type ReactNode
} from 'react'
import { useSearchParams } from 'react-router'
import { Card, CardContent } from '@repo/ui/components/ui/card'
import {
Table as Table_,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow
} from '@repo/ui/components/ui/table'
import { cn } from '@repo/ui/lib/utils'
import { DataTablePagination } from './pagination'
interface DataTableProps<TData, TValue> {
children?: ReactNode
columns: ColumnDef<TData, TValue>[]
data: TData[]
setSelectedRows?: (selectedRows: TData[]) => void
pageIndex: number
sort: SortingState
pageSize: number
rowCount: number
hiddenColumn?: string[]
}
interface TableContextProps<TData> {
table: Table<TData>
}
const TableContext = createContext<TableContextProps<any> | null>(null)
export function useDataTable<TData>() {
const ctx = useContext(TableContext)
if (!ctx) {
throw new Error('TableContext is null')
}
return ctx as { table: Table<TData> }
}
export function DataTable<TData, TValue>({
data,
children,
columns,
sort,
pageIndex,
pageSize,
rowCount,
setSelectedRows,
hiddenColumn = []
}: DataTableProps<TData, TValue>) {
const [dataTable, setDataTable] = useState<TData[]>(data)
const columnVisibilityInit = Object.fromEntries(
hiddenColumn.map((column) => [column, false])
)
const [searchParams, setSearchParams] = useSearchParams()
const [columnVisibility, setColumnVisibility] =
useState<VisibilityState>(columnVisibilityInit)
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
const sortParam = searchParams.get('sort')
const sorting = sortParam
? sortParam.split(',').map((s) => {
const [id, dir] = s.split(':')
return { id, desc: dir === 'desc' }
})
: sort
const setPagination = (updater: any) => {
const newState =
typeof updater === 'function' ? updater({ pageIndex, pageSize }) : updater
setRowSelection({})
setSearchParams((searchParams) => {
searchParams.set('p', newState?.pageIndex.toString())
searchParams.set('perPage', newState?.pageSize.toString())
return searchParams
})
}
const setSorting = (updater: any) => {
const newSorting =
typeof updater === 'function' ? updater(sorting) : updater
setSearchParams((searchParams) => {
if (newSorting.length) {
const sort = newSorting
.map((s: ColumnSort) => `${s.id}:${s.desc ? 'desc' : 'asc'}`)
.join(',')
searchParams.set('sort', sort)
}
return searchParams
})
}
useEffect(() => {
setDataTable(data)
}, [data])
const table = useReactTable({
data: dataTable,
columns,
rowCount,
state: {
sorting,
rowSelection,
columnVisibility,
pagination: {
pageIndex,
pageSize
}
},
manualSorting: true,
manualPagination: true,
enableRowSelection: true,
getRowId: (row: any) => row.id,
getCoreRowModel: getCoreRowModel(),
onRowSelectionChange: setRowSelection,
onSortingChange: setSorting,
onColumnVisibilityChange: setColumnVisibility,
onPaginationChange: setPagination,
meta: {
removeRow: (rowId: string) => {
setDataTable((rows) => rows.filter((row: any) => row?.id !== rowId))
}
}
})
useEffect(() => {
const selected = table.getSelectedRowModel().flatRows.map((r) => r.original)
setSelectedRows?.(selected)
}, [rowSelection])
return (
<TableContext value={{ table }}>
<div className="space-y-2.5 max-md:mb-2">
<Card className="relative w-full overflow-auto">
<CardContent>
{children}
<Table_ className="table-auto">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow
key={headerGroup.id}
className="hover:bg-transparent"
>
{headerGroup.headers.map((header) => {
return (
<TableHead
key={header.id}
className={cn(
'p-2.5',
// @ts-ignore
header.column.columnDef.meta?.className
)}
>
{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(
'p-2.5',
// @ts-ignore
cell.column.columnDef.meta?.className
)}
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
Nenhum resultado.
</TableCell>
</TableRow>
)}
</TableBody>
</Table_>
</CardContent>
</Card>
<DataTablePagination table={table} />
</div>
</TableContext>
)
}