Added tanstack react table with dynamic pagination

This commit is contained in:
Khalim Conn-Kowlessar 2023-07-24 17:43:23 +01:00
parent 6b9b7fb7f8
commit 52ff79b02d
4 changed files with 179 additions and 40 deletions

View file

@ -5,6 +5,7 @@ import {
SortingState,
flexRender,
getCoreRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
@ -18,10 +19,32 @@ import {
TableRow,
} from "@/app/shadcn_components/ui/table";
import { useState } from "react";
import { DataTablePagination } from "./propertyTablePagination";
import { Property } from "./propertyTableColumns";
import React from "react";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
data: TData[];
columns: ColumnDef<Property>[];
data: Property[];
}
function fetchData(offset: number) {
// Because this is a client component, this will be handled with react query
let properties: Property[] = [];
for (let i = offset; i <= offset + 20; i++) {
properties.push({
id: i,
portfolioId: 1,
address: `${i}23 Fake Street`,
postcode: `AB${i} 2CD`,
loadingStatus: Math.random() < 0.5 ? "complete" : "loading",
status: "assessment",
targetEpc: "C",
cost: 1000,
});
}
return properties;
}
export default function DataTable<TData, TValue>({
@ -29,15 +52,34 @@ export default function DataTable<TData, TValue>({
columns,
}: DataTableProps<TData, TValue>) {
const [sorting, setSorting] = useState<SortingState>([]);
const [tableData, setTableData] = useState<Property[]>(data);
const [offset, setOffset] = useState(0);
const [currentPageIndex, setCurrentPageIndex] = useState(0);
// add page change handlers for DataTablePagination
const loadPaginatedData = () => {
const newData = fetchData(offset);
if (newData) {
console.log("loadPaginatedData");
setTableData([...tableData, ...newData]);
setOffset(offset + 1);
return true;
}
return false;
};
const table = useReactTable({
data,
data: tableData,
columns,
getCoreRowModel: getCoreRowModel(),
onSortingChange: setSorting,
getSortedRowModel: getSortedRowModel(),
getPaginationRowModel: getPaginationRowModel(),
state: {
sorting,
pagination: { pageIndex: currentPageIndex, pageSize: 7 },
},
});
@ -49,7 +91,7 @@ export default function DataTable<TData, TValue>({
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
<TableHead key={header.id} className="h-14 py-4">
{header.isPlaceholder
? null
: flexRender(
@ -85,6 +127,12 @@ export default function DataTable<TData, TValue>({
)}
</TableBody>
</Table>
<DataTablePagination
table={table}
loadPaginatedData={loadPaginatedData}
currentPageIndex={currentPageIndex}
setCurrentPageIndex={setCurrentPageIndex}
/>
</div>
);
}

View file

@ -13,6 +13,7 @@ import { Button } from "@/app/shadcn_components/ui/button";
import { ArrowUpDown, MoreHorizontal } from "lucide-react";
import StatusBadge from "@/app/components/StatusBadge";
import { HomeIcon } from "@heroicons/react/20/solid";
import { formatNumber } from "@/app/utils";
export type Property = {
id: number;
@ -22,6 +23,7 @@ export type Property = {
loadingStatus: string;
status: string;
targetEpc: string;
cost: number;
};
export const columns: ColumnDef<Property>[] = [
@ -80,15 +82,11 @@ export const columns: ColumnDef<Property>[] = [
const cost = parseFloat(row.getValue("cost"));
const loadingStatus = row.original.loadingStatus;
console.log("loadingStatus", loadingStatus);
if (loadingStatus === "loading") {
return <div className="font-medium flex justify-center"></div>;
}
const formatted = new Intl.NumberFormat("en-UK", {
style: "currency",
currency: "GBP",
}).format(cost);
const formatted = "£" + formatNumber(cost);
return (
<div className="text-gray-700 font-medium flex justify-center">
@ -137,7 +135,7 @@ export const columns: ColumnDef<Property>[] = [
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem
// onClick={() => navigator.clipboard.writeText(payment.id)}
className="cursor-pointer"
className="text-gray-700 cursor-pointer"
>
<a
href={`porfolio/${porfolioId}/building-passport/${propertyId}`}
@ -145,6 +143,13 @@ export const columns: ColumnDef<Property>[] = [
Building Passport
</a>
</DropdownMenuItem>
<DropdownMenuItem
className="text-gray-700 cursor-pointer"
// disabled={true}
>
Settings
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
className="text-red-500 focus:text-red-700 cursor-pointer"

View file

@ -0,0 +1,105 @@
import {
ChevronLeftIcon,
ChevronRightIcon,
ChevronDoubleRightIcon,
ChevronDoubleLeftIcon,
} from "@heroicons/react/24/outline";
import { Table } from "@tanstack/react-table";
import { Button } from "@/app/shadcn_components/ui/button";
import { useEffect } from "react";
interface DataTablePaginationProps<TData> {
table: Table<TData>;
loadPaginatedData: () => void;
currentPageIndex: number;
setCurrentPageIndex: (index: number) => void;
}
export function DataTablePagination<TData>({
table,
loadPaginatedData,
currentPageIndex,
setCurrentPageIndex,
}: DataTablePaginationProps<TData>) {
// Check if the user has reached the last page
const requestMoreData = () => {
// Check if the next page is the last one
if (
table.getState().pagination.pageIndex + 1 ===
table.getPageCount() - 1
) {
console.log("requesting more data");
loadPaginatedData();
}
};
const goToFinalPage = () => {
console.log("Go to final page");
// Check if the next page is the last one
loadPaginatedData();
};
return (
<div className="flex items-center justify-end px-2">
<div className="flex items-center space-x-6 lg:space-x-8">
<div className="flex w-[100px] items-center justify-center text-sm font-medium">
Page {table.getState().pagination.pageIndex + 1} of{" "}
{table.getPageCount()}
</div>
<div className="flex items-center space-x-2">
<Button
variant="outline"
className="hidden h-8 w-8 p-0 lg:flex"
onClick={() => {
setCurrentPageIndex(0);
// table.setPageIndex(0);
}}
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Go to first page</span>
<ChevronDoubleLeftIcon className="h-4 w-4" />
</Button>
<Button
variant="outline"
className="h-8 w-8 p-0"
onClick={() => {
setCurrentPageIndex(currentPageIndex - 1);
// table.previousPage();
}}
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Go to previous page</span>
<ChevronLeftIcon className="h-4 w-4" />
</Button>
<Button
variant="outline"
className="h-8 w-8 p-0"
onClick={() => {
requestMoreData();
setCurrentPageIndex(currentPageIndex + 1);
// table.nextPage();
}}
disabled={!table.getCanNextPage()}
>
<span className="sr-only">Go to next page</span>
<ChevronRightIcon className="h-4 w-4" />
</Button>
<Button
variant="outline"
className="hidden h-8 w-8 p-0 lg:flex"
onClick={() => {
goToFinalPage();
setCurrentPageIndex(table.getPageCount() - 1);
}}
disabled={!table.getCanNextPage()}
>
<span className="sr-only">Go to last page</span>
<ChevronDoubleRightIcon className="h-4 w-4" />
</Button>
</div>
</div>
</div>
);
}

View file

@ -10,20 +10,12 @@ import { Toolbar } from "@/app/components/portfolio/Toolbar";
import DataTable from "@/app/portfolio/[slug]/components/propertyTable";
import {
columns,
Payment,
Property,
} from "@/app/portfolio/[slug]/components/propertyTableColumns";
// We enfore caching of data for 60 seconds
export const revalidate = 60;
type Property = {
id: string;
address: string;
postcode: string;
loadingStatus: string;
status: (typeof PortfolioStatus)[number];
};
function EmptyPropertyState() {
return (
<div className="flex justify-center h-1/2">
@ -223,31 +215,20 @@ export default async function Page({
// TODO: TEMP
// let properties: Property[];
let properties: {}[];
let properties: Property[] = [];
if (pageStatus === "loading") {
properties = [
{
id: 1,
portfolioId: portfolioId,
address: "123 Fake Street",
postcode: "AB1 2CD",
loadingStatus: "loading",
for (let i = 1; i <= 20; i++) {
properties.push({
id: i,
portfolioId: 1,
address: `${i}23 Fake Street`,
postcode: `AB${i} 2CD`,
loadingStatus: Math.random() < 0.5 ? "complete" : "loading",
status: "assessment",
targetEpc: "C",
},
{
id: 2,
portfolioId: portfolioId,
address: "223 Fake Street",
postcode: "AB1 2CD",
loadingStatus: "complete",
cost: 1000,
status: "assessment",
targetEpc: "C",
},
];
} else {
properties = [];
});
}
}
return (