2025-03-27 22:52:35 +07:00
|
|
|
import { ReactElement, ReactNode } from 'react';
|
2025-03-28 00:19:01 +07:00
|
|
|
import {
|
|
|
|
|
useReactTable,
|
|
|
|
|
getCoreRowModel,
|
|
|
|
|
getPaginationRowModel,
|
|
|
|
|
getFilteredRowModel,
|
|
|
|
|
} from '@tanstack/react-table';
|
2025-03-27 15:17:04 +07:00
|
|
|
|
2025-03-27 17:22:04 +07:00
|
|
|
interface DataTableProps<T> {
|
|
|
|
|
data: T[];
|
|
|
|
|
headers: {
|
2025-03-28 00:19:01 +07:00
|
|
|
accessorKey?: keyof T;
|
|
|
|
|
header: string;
|
|
|
|
|
cell?: (info: any) => ReactNode;
|
2025-03-27 17:22:04 +07:00
|
|
|
}[];
|
|
|
|
|
onRowClick?: (item: T) => void;
|
2025-03-27 15:17:04 +07:00
|
|
|
}
|
|
|
|
|
|
2025-03-27 17:22:04 +07:00
|
|
|
export const DataTable = <T extends Record<string, any>>({
|
2025-03-27 15:17:04 +07:00
|
|
|
data,
|
2025-03-27 17:22:04 +07:00
|
|
|
headers,
|
|
|
|
|
onRowClick,
|
|
|
|
|
}: DataTableProps<T>): ReactElement => {
|
2025-03-28 00:19:01 +07:00
|
|
|
const table = useReactTable({
|
|
|
|
|
data,
|
|
|
|
|
columns: headers.map((header) => ({
|
|
|
|
|
accessorKey: header.accessorKey,
|
|
|
|
|
header: header.header,
|
|
|
|
|
cell: header.cell,
|
|
|
|
|
})),
|
|
|
|
|
getCoreRowModel: getCoreRowModel(),
|
|
|
|
|
getPaginationRowModel: getPaginationRowModel(),
|
|
|
|
|
getFilteredRowModel: getFilteredRowModel(),
|
|
|
|
|
});
|
2025-03-27 15:17:04 +07:00
|
|
|
return (
|
|
|
|
|
<div className="w-full overflow-x-auto">
|
2025-03-27 16:29:09 +07:00
|
|
|
<table className="w-full min-w-full text-base">
|
|
|
|
|
<thead className="bg-primary-50 mb-3">
|
2025-03-28 00:19:01 +07:00
|
|
|
{table.getHeaderGroups().map((headerGroup) => (
|
|
|
|
|
<tr key={headerGroup.id}>
|
|
|
|
|
{headerGroup.headers.map((header) => (
|
|
|
|
|
<th key={header.id}>
|
|
|
|
|
{header.isPlaceholder ? null : (
|
|
|
|
|
<div>
|
|
|
|
|
{typeof header.column.columnDef.header === 'function'
|
|
|
|
|
? header.column.columnDef.header(header.getContext())
|
|
|
|
|
: header.column.columnDef.header}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2025-03-27 17:22:04 +07:00
|
|
|
</th>
|
2025-03-28 00:19:01 +07:00
|
|
|
))}
|
|
|
|
|
</tr>
|
|
|
|
|
))}
|
2025-03-27 15:17:04 +07:00
|
|
|
</thead>
|
2025-03-27 16:29:09 +07:00
|
|
|
<tbody className="mt-3">
|
2025-03-28 00:19:01 +07:00
|
|
|
{table.getRowModel().rows.map((row) => (
|
2025-03-27 15:17:04 +07:00
|
|
|
<tr
|
2025-03-28 00:19:01 +07:00
|
|
|
key={row.id}
|
|
|
|
|
onClick={() => onRowClick && onRowClick(row.original)}
|
2025-03-27 15:17:04 +07:00
|
|
|
>
|
2025-03-28 00:19:01 +07:00
|
|
|
{row.getVisibleCells().map((cell) => (
|
|
|
|
|
<td key={cell.id}>{cell.getValue() as ReactNode}</td>
|
|
|
|
|
))}
|
2025-03-27 15:17:04 +07:00
|
|
|
</tr>
|
|
|
|
|
))}
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
2025-03-28 00:19:01 +07:00
|
|
|
<div>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => table.setPageIndex(0)}
|
|
|
|
|
disabled={!table.getCanPreviousPage()}
|
|
|
|
|
>
|
|
|
|
|
{'<<'}
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => table.previousPage()}
|
|
|
|
|
disabled={!table.getCanPreviousPage()}
|
|
|
|
|
>
|
|
|
|
|
{'<'}
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => table.nextPage()}
|
|
|
|
|
disabled={!table.getCanNextPage()}
|
|
|
|
|
>
|
|
|
|
|
{'>'}
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
|
|
|
|
disabled={!table.getCanNextPage()}
|
|
|
|
|
>
|
|
|
|
|
{'>>'}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
2025-03-27 15:17:04 +07:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2025-03-27 16:29:09 +07:00
|
|
|
export default DataTable;
|