108 lines
3.7 KiB
TypeScript
108 lines
3.7 KiB
TypeScript
import { useListSearch } from "@/hooks/useListSearch"
|
|
import { ColumnDef, flexRender, getCoreRowModel, getPaginationRowModel, getSortedRowModel, PaginationState, useReactTable } from "@tanstack/react-table"
|
|
import clsx from "clsx"
|
|
import { useState } from "react"
|
|
import { BsArrowDown, BsArrowUp } from "react-icons/bs"
|
|
import Button from "../Low/Button"
|
|
|
|
interface Props<T> {
|
|
data: T[]
|
|
columns: ColumnDef<any, any>[]
|
|
searchFields: string[][]
|
|
size?: number
|
|
onDownload?: (rows: T[]) => void
|
|
}
|
|
|
|
export default function Table<T>({ data, columns, searchFields, size = 16, onDownload }: Props<T>) {
|
|
const [pagination, setPagination] = useState<PaginationState>({
|
|
pageIndex: 0,
|
|
pageSize: 16,
|
|
})
|
|
|
|
const { rows, renderSearch } = useListSearch<T>(searchFields, data);
|
|
|
|
const table = useReactTable({
|
|
data: rows,
|
|
columns,
|
|
getCoreRowModel: getCoreRowModel(),
|
|
getSortedRowModel: getSortedRowModel(),
|
|
getPaginationRowModel: getPaginationRowModel(),
|
|
onPaginationChange: setPagination,
|
|
state: {
|
|
pagination
|
|
}
|
|
});
|
|
|
|
return (
|
|
<div className="w-full flex flex-col gap-2">
|
|
<div className="w-full flex gap-2 items-end">
|
|
{renderSearch()}
|
|
{onDownload && (
|
|
<Button className="w-full max-w-[200px] mb-1" variant="outline" onClick={() => onDownload(rows)}>
|
|
Download List
|
|
</Button>
|
|
)
|
|
}
|
|
</div>
|
|
|
|
<div className="w-full flex gap-2 justify-between items-center">
|
|
<div className="flex items-center gap-4 w-fit">
|
|
<Button className="w-[200px] h-fit" disabled={!table.getCanPreviousPage()} onClick={() => table.previousPage()}>
|
|
Previous Page
|
|
</Button>
|
|
</div>
|
|
<div className="flex items-center gap-4 w-fit">
|
|
<span className="flex items-center gap-1">
|
|
<div>Page</div>
|
|
<strong>
|
|
{table.getState().pagination.pageIndex + 1} of{' '}
|
|
{table.getPageCount().toLocaleString()}
|
|
</strong>
|
|
<div>| Total: {table.getRowCount().toLocaleString()}</div>
|
|
</span>
|
|
<Button className="w-[200px]" disabled={!table.getCanNextPage()} onClick={() => table.nextPage()}>
|
|
Next Page
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<table className="rounded-xl bg-mti-purple-ultralight/40 w-full">
|
|
<thead>
|
|
{table.getHeaderGroups().map((headerGroup) => (
|
|
<tr key={headerGroup.id}>
|
|
{headerGroup.headers.map((header) => (
|
|
<th className="py-4 px-4 text-left" key={header.id} colSpan={header.colSpan}>
|
|
<div
|
|
className={clsx(header.column.getCanSort() && 'cursor-pointer select-none', 'flex items-center gap-2')}
|
|
onClick={header.column.getToggleSortingHandler()}
|
|
>
|
|
{flexRender(
|
|
header.column.columnDef.header,
|
|
header.getContext()
|
|
)}
|
|
{{
|
|
asc: <BsArrowUp />,
|
|
desc: <BsArrowDown />,
|
|
}[header.column.getIsSorted() as string] ?? null}
|
|
</div>
|
|
</th>
|
|
))}
|
|
</tr>
|
|
))}
|
|
</thead>
|
|
<tbody className="px-2 w-full">
|
|
{table.getRowModel().rows.map((row) => (
|
|
<tr className="odd:bg-white even:bg-mti-purple-ultralight/40 rounded-lg py-2" key={row.id}>
|
|
{row.getVisibleCells().map((cell) => (
|
|
<td className="px-4 py-2 items-center w-fit" key={cell.id}>
|
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
</td>
|
|
))}
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
)
|
|
}
|