import type { ColumnDef, SortingState, ColumnFiltersState, VisibilityState} from "@tanstack/react-table"; import { flexRender, getCoreRowModel, useReactTable, getPaginationRowModel, getSortedRowModel, getFilteredRowModel } from "@tanstack/react-table"; import { Settings2, ChevronDown, ListFilter, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, X } from "lucide-react"; import React from "react"; import { Checkbox } from "@/components/ui/checkbox"; import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Empty, EmptyDescription, EmptyHeader, EmptyTitle, } from "@/components/ui/empty" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Badge } from "./ui/badge"; import { Button } from "./ui/button"; import { Input } from "./ui/input"; interface DataTableFilterOption { columnId: string; title: string; options: { label: string; value: string }[]; } export interface DataTableBulkAction { label: string; onClick: (rows: TData[]) => void; icon?: React.ElementType; variant?: "default" | "destructive"; } interface DataTableProps { columns: ColumnDef[]; data: TData[]; searchKey?: string; filters?: DataTableFilterOption[]; bulkActions?: DataTableBulkAction[]; showNumbering?: boolean; showSelection?: boolean; rowSelection?: any; onRowSelectionChange?: (selection: any) => void; } export function DataTable({ columns, data, filters, bulkActions, showNumbering = true, showSelection = true, rowSelection = {}, onRowSelectionChange, }: DataTableProps) { const [sorting, setSorting] = React.useState([]); const [columnFilters, setColumnFilters] = React.useState([]); const [columnVisibility, setColumnVisibility] = React.useState({}); const [globalFilter, setGlobalFilter] = React.useState(""); const finalColumns = React.useMemo(() => { const computedColumns = [...columns]; if (showNumbering) { const numberingColumn: ColumnDef = { id: "numbering", header: "No.", cell: ({ row }) => row.index + 1, enableHiding: false, }; computedColumns.unshift(numberingColumn); } if (showSelection) { const selectColumn: ColumnDef = { id: "select", header: ({ table }) => ( table.toggleAllPageRowsSelected(!!value)} aria-label="Select all" /> ), cell: ({ row }) => ( row.toggleSelected(!!value)} aria-label="Select row" /> ), enableSorting: false, enableHiding: false, }; computedColumns.unshift(selectColumn); } return computedColumns; }, [columns, showNumbering, showSelection]); const table = useReactTable({ data, columns: finalColumns, getCoreRowModel: getCoreRowModel(), getPaginationRowModel: getPaginationRowModel(), onSortingChange: setSorting, getSortedRowModel: getSortedRowModel(), onColumnFiltersChange: setColumnFilters, getFilteredRowModel: getFilteredRowModel(), onColumnVisibilityChange: setColumnVisibility, onRowSelectionChange: onRowSelectionChange, onGlobalFilterChange: setGlobalFilter, state: { sorting, columnFilters, columnVisibility, rowSelection, globalFilter, }, }); const selectedRows = table.getFilteredSelectedRowModel().rows; return (
setGlobalFilter(event.target.value)} className="max-w-sm" /> {columnFilters.length > 0 && (
{columnFilters.map((filter) => { const filterDef = filters?.find((f) => f.columnId === filter.id); const option = filterDef?.options.find((o) => String(o.value) === String(filter.value)); const label = option ? option.label : String(filter.value); return ( {filterDef?.title || filter.id}: {label} ); })}
)}
{selectedRows.length > 0 && bulkActions && ( {bulkActions.map((action, i) => ( action.onClick(selectedRows.map((r) => r.original))} className={action.variant === "destructive" ? "text-red-600 focus:text-red-600" : ""} > {action.icon && } {action.label} ))} )} {filters && filters.length > 0 && ( Filter Berdasarkan {filters.map((filter) => { const column = table.getColumn(filter.columnId); if (!column) { return null; } return ( {filter.title} e.preventDefault()} onClick={() => column.setFilterValue(undefined)} > Semua {filter.options.map((option) => ( e.preventDefault()} onClick={() => { if (option.value === "true") { column.setFilterValue(true); } else if (option.value === "false") { column.setFilterValue(false); } else { column.setFilterValue(option.value); } }} > {option.label} ))} ); })} )} Tampilan Kolom {table .getAllColumns() .filter((column) => column.getCanHide()) .map((column) => { return ( column.toggleVisibility(!!value)} onSelect={(e) => e.preventDefault()} > {(column.columnDef.meta as any)?.title || (typeof column.columnDef.header === "string" ? column.columnDef.header : String(column.id).replace(/_/g, " "))} ); })}
{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => { return ( {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} ); })} ))} {table.getRowModel().rows?.length ? ( table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => ( {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} )) ) : ( Ooops... Tidak ada data yang ditemukan. )}
{table.getFilteredSelectedRowModel().rows.length} dari{" "} {table.getFilteredRowModel().rows.length} baris dipilih.

Baris per halaman

{table.getFilteredRowModel().rows.length > 0 ? ( <> Menampilkan {table.getState().pagination.pageIndex * table.getState().pagination.pageSize + 1} sampai{" "} {Math.min((table.getState().pagination.pageIndex + 1) * table.getState().pagination.pageSize, table.getFilteredRowModel().rows.length)}{" "} dari {table.getFilteredRowModel().rows.length} data ) : ( "Tidak ada data" )}
); }