166 lines
4.7 KiB
TypeScript
166 lines
4.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;
|
|
isDownloadLoading?: boolean;
|
|
searchPlaceholder?: string;
|
|
isLoading?: boolean;
|
|
}
|
|
|
|
export default function Table<T>({
|
|
data,
|
|
columns,
|
|
searchFields,
|
|
size = 16,
|
|
onDownload,
|
|
isDownloadLoading,
|
|
searchPlaceholder,
|
|
isLoading,
|
|
}: Props<T>) {
|
|
const [pagination, setPagination] = useState<PaginationState>({
|
|
pageIndex: 0,
|
|
pageSize: size,
|
|
});
|
|
|
|
const { rows, renderSearch } = useListSearch<T>(
|
|
searchFields,
|
|
data,
|
|
searchPlaceholder
|
|
);
|
|
|
|
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
|
|
isLoading={isDownloadLoading}
|
|
className="w-full max-w-[200px] mb-1"
|
|
variant="outline"
|
|
onClick={() => onDownload(rows)}
|
|
>
|
|
Download
|
|
</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>
|
|
{isLoading ? (
|
|
<div className="min-h-screen flex justify-center items-start">
|
|
<span className="loading loading-infinity w-32" />
|
|
</div>
|
|
) : (
|
|
rows.length === 0 && (
|
|
<div className="w-full flex justify-center items-start">
|
|
<span className="text-xl text-gray-500">No data found...</span>
|
|
</div>
|
|
)
|
|
)}
|
|
</div>
|
|
);
|
|
}
|