117 lines
3.0 KiB
TypeScript
117 lines
3.0 KiB
TypeScript
'use client';
|
|
import { useState } from 'react';
|
|
import {
|
|
useReactTable,
|
|
getCoreRowModel,
|
|
getSortedRowModel,
|
|
ColumnDef,
|
|
SortingState,
|
|
flexRender,
|
|
} from '@tanstack/react-table';
|
|
import { ArrowLeft, ArrowRight, ChevronDown, ChevronUp } from 'lucide-react'; // Icons for sorting
|
|
|
|
export interface SortableTableProps<TData> {
|
|
data: TData[];
|
|
columns: ColumnDef<TData>[];
|
|
}
|
|
|
|
export default function SortableTable<TData>({
|
|
data,
|
|
columns,
|
|
}: SortableTableProps<TData>) {
|
|
const [sorting, setSorting] = useState<SortingState>([]);
|
|
|
|
const table = useReactTable({
|
|
data,
|
|
columns,
|
|
getCoreRowModel: getCoreRowModel(),
|
|
getSortedRowModel: getSortedRowModel(),
|
|
onSortingChange: setSorting,
|
|
state: { sorting },
|
|
});
|
|
|
|
return (
|
|
<div className="overflow-x-auto">
|
|
<table className="table table-zebra w-full">
|
|
<thead>
|
|
{table.getHeaderGroups().map((headerGroup) => (
|
|
<tr key={headerGroup.id}>
|
|
{headerGroup.headers.map((header) => (
|
|
<th
|
|
key={header.id}
|
|
onClick={header.column.getToggleSortingHandler()}
|
|
>
|
|
<div className="flex items-center gap-1 cursor-pointer">
|
|
{flexRender(
|
|
header.column.columnDef.header,
|
|
header.getContext()
|
|
)}
|
|
{header.column.getIsSorted() === 'asc' && (
|
|
<ChevronUp size={16} />
|
|
)}
|
|
{header.column.getIsSorted() === 'desc' && (
|
|
<ChevronDown size={16} />
|
|
)}
|
|
</div>
|
|
</th>
|
|
))}
|
|
</tr>
|
|
))}
|
|
</thead>
|
|
<tbody>
|
|
{table.getRowModel().rows.map((row) => (
|
|
<tr key={row.id}>
|
|
{row.getVisibleCells().map((cell) => (
|
|
<td key={cell.id}>
|
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
</td>
|
|
))}
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export const Pagination = ({
|
|
page,
|
|
totalPages,
|
|
setPage,
|
|
}: {
|
|
page: number;
|
|
totalPages: number;
|
|
setPage: (page: number) => void;
|
|
}) => {
|
|
if (totalPages === 0) return null;
|
|
return (
|
|
<div className="join w-full justify-end">
|
|
<button
|
|
className="join-item btn"
|
|
disabled={page === 0}
|
|
onClick={() => setPage(page - 1)}
|
|
>
|
|
<ArrowLeft size={16} />
|
|
</button>
|
|
<select
|
|
className="select join-item"
|
|
value={page}
|
|
onChange={(e) => setPage(Number(e.target.value))}
|
|
>
|
|
{Array.from({ length: totalPages }).map((_, i) => (
|
|
<option key={i} value={i}>
|
|
{i + 1}
|
|
</option>
|
|
))}
|
|
</select>
|
|
<button
|
|
className="join-item btn"
|
|
disabled={page === totalPages - 1}
|
|
onClick={() => page < totalPages && setPage(page + 1)}
|
|
>
|
|
<ArrowRight size={16} />
|
|
</button>
|
|
</div>
|
|
);
|
|
};
|