MIF_E31221222/sigap-website/components/admin/users/data-table.tsx

253 lines
9.2 KiB
TypeScript

"use client"
import { useState } from "react"
import {
type ColumnDef,
flexRender,
getCoreRowModel,
useReactTable,
getSortedRowModel,
type SortingState,
getFilteredRowModel,
type ColumnFiltersState,
getPaginationRowModel,
} from "@tanstack/react-table"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, Filter } from "lucide-react"
import { Skeleton } from "@/components/ui/skeleton"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[]
data: TData[]
loading?: boolean
onRowClick?: (row: TData) => void
pageSize?: number
}
export function DataTable<TData, TValue>({
columns,
data,
loading = false,
onRowClick,
pageSize = 5,
}: DataTableProps<TData, TValue>) {
const [sorting, setSorting] = useState<SortingState>([])
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
const [columnVisibility, setColumnVisibility] = useState({})
const [pagination, setPagination] = useState({
pageIndex: 0,
pageSize: pageSize,
})
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
onSortingChange: setSorting,
getSortedRowModel: getSortedRowModel(),
onColumnFiltersChange: setColumnFilters,
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
getPaginationRowModel: getPaginationRowModel(),
onPaginationChange: setPagination,
state: {
sorting,
columnFilters,
columnVisibility,
pagination,
},
})
if (loading) {
return (
<div className="border rounded-md">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{Array.from({ length: pagination.pageSize }).map((_, index) => (
<TableRow key={index}>
{columns.map((_, colIndex) => (
<TableCell key={colIndex}>
<Skeleton className="h-6 w-full" />
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</div>
)
}
return (
<div className="border rounded-md">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<TableHead key={header.id} className="relative">
{header.isPlaceholder ? null : (
<div className="flex items-center gap-2">
<div
className={
header.column.getCanSort() ? "cursor-pointer select-none flex items-center gap-1" : ""
}
onClick={header.column.getToggleSortingHandler()}
>
{flexRender(header.column.columnDef.header, header.getContext())}
{{
asc: " 🔼",
desc: " 🔽",
}[header.column.getIsSorted() as string] ?? null}
</div>
{header.column.getCanFilter() && (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="h-6 w-6 ml-auto"
onClick={(e) => e.stopPropagation()}
>
<Filter className="h-3 w-3" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<div className="p-2">
<Input
placeholder={`Filter ${header.column.id}...`}
value={(header.column.getFilterValue() as string) ?? ""}
onChange={(e) => header.column.setFilterValue(e.target.value)}
className="h-8"
/>
</div>
</DropdownMenuContent>
</DropdownMenu>
)}
</div>
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
className="cursor-pointer hover:bg-muted/50"
onClick={() => onRowClick && 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>
{/* Pagination Controls */}
<div className="flex items-center justify-between px-4 py-2 border-t">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<div>
Showing {table.getState().pagination.pageIndex * table.getState().pagination.pageSize + 1} to{" "}
{Math.min(
(table.getState().pagination.pageIndex + 1) * table.getState().pagination.pageSize,
table.getFilteredRowModel().rows.length,
)}{" "}
of {table.getFilteredRowModel().rows.length} entries
</div>
</div>
<div className="flex items-center space-x-2">
<div className="flex items-center space-x-2">
<p className="text-sm font-medium">Rows per page</p>
<Select
value={`${table.getState().pagination.pageSize}`}
onValueChange={(value) => {
table.setPageSize(Number(value))
}}
>
<SelectTrigger className="h-8 w-[70px]">
<SelectValue placeholder={table.getState().pagination.pageSize} />
</SelectTrigger>
<SelectContent side="top">
{[5, 10, 20, 30, 40, 50].map((pageSize) => (
<SelectItem key={pageSize} value={`${pageSize}`}>
{pageSize}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex items-center space-x-2">
<Button
variant="outline"
className="hidden h-8 w-8 p-0 lg:flex"
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Go to first page</span>
<ChevronsLeft className="h-4 w-4" />
</Button>
<Button
variant="outline"
className="h-8 w-8 p-0"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Go to previous page</span>
<ChevronLeft className="h-4 w-4" />
</Button>
<div className="flex items-center gap-1 text-sm font-medium">
Page {table.getState().pagination.pageIndex + 1} of {table.getPageCount()}
</div>
<Button
variant="outline"
className="h-8 w-8 p-0"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
<span className="sr-only">Go to next page</span>
<ChevronRight className="h-4 w-4" />
</Button>
<Button
variant="outline"
className="hidden h-8 w-8 p-0 lg:flex"
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table.getCanNextPage()}
>
<span className="sr-only">Go to last page</span>
<ChevronsRight className="h-4 w-4" />
</Button>
</div>
</div>
</div>
</div>
)
}