webapp-spa-react/src/routes/transactions.tsx

168 lines
4.3 KiB
TypeScript

import { keepPreviousData, useQuery } from "@tanstack/react-query";
import {
ColumnFiltersState,
createColumnHelper,
flexRender,
getCoreRowModel,
PaginationState,
useReactTable,
} from "@tanstack/react-table";
import { useState } from "react";
import { DebounceInput } from "react-debounce-input";
const PageSize = 30;
async function loader(page = 0, category: string | undefined = "") {
const url = new URL("http://localhost:9000/transactions");
url.search = new URLSearchParams({
limit: String(PageSize),
offset: String(page * PageSize),
...(category !== "" && { category: category }),
}).toString();
return await fetch(url).then((response) => response.json());
}
type Transaction = {
id: number;
date: string;
description: string;
value: number;
category: string;
};
const columnHelper = createColumnHelper<Transaction>();
const columns = [
columnHelper.accessor("date", {
enableColumnFilter: false,
}),
columnHelper.accessor("description", {
enableColumnFilter: false,
}),
columnHelper.accessor("value", {
enableColumnFilter: false,
}),
columnHelper.accessor("category", {}),
];
export default function Transactions() {
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: PageSize,
});
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([
{
id: "category",
value: "",
},
]);
const { data, isPending, isError } = useQuery({
queryKey: ["transactions", pagination.pageIndex, columnFilters],
queryFn: () =>
loader(
pagination.pageIndex,
columnFilters.find((filter) => filter.id == "category")!.value as string
),
placeholderData: keepPreviousData,
});
const table = useReactTable({
columns,
data,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
manualFiltering: true,
// rowCount: , // TODO: get this from the server
pageCount: -1,
onPaginationChange: setPagination,
onColumnFiltersChange: setColumnFilters,
state: {
pagination,
columnFilters,
},
});
if (isPending) {
return <div>Loading...</div>;
}
if (isError) {
return <div>Error loading transactions</div>;
}
return (
<>
<table>
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th key={header.id}>
{header.isPlaceholder ? null : (
<>
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
{header.column.getCanFilter() ? (
<div>
<DebounceInput
debounceTimeout={275}
onChange={(e) =>
header.column.setFilterValue(e.target.value)
}
/>
</div>
) : null}
</>
)}
</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>
<button
onClick={() => table.firstPage()}
disabled={!table.getCanPreviousPage()}
>
First
</button>
<button
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</button>
<button
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</button>
<button
onClick={() => table.lastPage()}
disabled={!table.getCanNextPage()}
>
Last
</button>
</div>
<div>Page {table.getState().pagination.pageIndex + 1}</div>
</>
);
}