use pre and next cursor check from the server

This commit is contained in:
notmd
2023-01-21 23:12:19 +07:00
parent 0cc6b3b8ae
commit c1dd188cbe
3 changed files with 84 additions and 106 deletions
+49 -45
View File
@@ -1,8 +1,6 @@
import {
Box,
Button,
Card,
CardBody,
Flex,
FormControl,
FormLabel,
@@ -47,6 +45,8 @@ export type DataTableProps<T> = {
onNextClick?: () => void;
onPreviousClick?: () => void;
onFilterChange?: (items: FilterItem[]) => void;
disableNext?: boolean;
disablePrevious?: boolean;
};
export const DataTable = <T,>({
@@ -57,6 +57,8 @@ export const DataTable = <T,>({
onNextClick,
onPreviousClick,
onFilterChange,
disableNext,
disablePrevious,
}: DataTableProps<T>) => {
const { getHeaderGroups, getRowModel } = useReactTable<T>({
data,
@@ -75,49 +77,51 @@ export const DataTable = <T,>({
onFilterChange(newValues);
};
return (
<Card>
<CardBody>
<Flex mb="2">
<Button onClick={onPreviousClick}>Previous</Button>
<Spacer />
<Button onClick={onNextClick}>Next</Button>
</Flex>
<TableContainer>
<Table variant="simple">
<TableCaption>{caption}</TableCaption>
<Thead>
{getHeaderGroups().map((headerGroup) => (
<Tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<Th key={header.id}>
<Box display="flex" alignItems="center">
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
{(header.column.columnDef as DataTableColumnDef<T>).filterable && (
<FilterModal
value={filterValues.find((value) => value.id === header.id)?.value ?? ""}
onChange={(value) => handleFilterChange({ id: header.id, value })}
label={flexRender(header.column.columnDef.header, header.getContext())}
></FilterModal>
)}
</Box>
</Th>
))}
</Tr>
))}
</Thead>
<Tbody>
{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>
</TableContainer>
</CardBody>
</Card>
<>
<Flex mb="2">
<Button onClick={onPreviousClick} disabled={disablePrevious}>
Previous
</Button>
<Spacer />
<Button onClick={onNextClick} disabled={disableNext}>
Next
</Button>
</Flex>
<TableContainer>
<Table variant="simple">
<TableCaption>{caption}</TableCaption>
<Thead>
{getHeaderGroups().map((headerGroup) => (
<Tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<Th key={header.id}>
<Box display="flex" alignItems="center">
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
{(header.column.columnDef as DataTableColumnDef<T>).filterable && (
<FilterModal
value={filterValues.find((value) => value.id === header.id)?.value ?? ""}
onChange={(value) => handleFilterChange({ id: header.id, value })}
label={flexRender(header.column.columnDef.header, header.getContext())}
></FilterModal>
)}
</Box>
</Th>
))}
</Tr>
))}
</Thead>
<Tbody>
{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>
</TableContainer>
</>
);
};
+34 -60
View File
@@ -1,4 +1,4 @@
import { IconButton, useToast } from "@chakra-ui/react";
import { Card, CardBody, IconButton } from "@chakra-ui/react";
import { createColumnHelper } from "@tanstack/react-table";
import Link from "next/link";
import { memo, useState } from "react";
@@ -57,11 +57,7 @@ const columns: DataTableColumnDef<User>[] = [
];
export const UserTable = memo(function UserTable() {
const toast = useToast();
const [pagination, setPagination] = useState<Pagination>({ cursor: "", direction: "forward" });
const [response, setResponse] = useState<Omit<FetchUsersResponse<User>, "sort_key" | "order">>({
items: [],
});
const [filterValues, setFilterValues] = useState<FilterItem[]>([]);
const handleFilterValuesChange = (values: FilterItem[]) => {
setFilterValues(values);
@@ -71,68 +67,46 @@ export const UserTable = memo(function UserTable() {
// This follows useSWR's recommendation for simple pagination:
// https://swr.vercel.app/docs/pagination#when-to-use-useswr
const display_name = filterValues.find((value) => value.id === "display_name")?.value ?? "";
useSWR<
FetchUsersResponse<User>
>(`/api/admin/users?direction=${pagination.direction}&cursor=${pagination.cursor}&searchDisplayName=${display_name}&sortKey=display_name`, get, {
onSuccess: (data) => {
// When no more users can be found, trigger a toast to indicate why no
// changes have taken place. We have to maintain a non-empty set of
// users otherwise we can't paginate using a cursor (since we've lost the
// cursor).
if (data.items.length === 0) {
toast({
title: "No more users",
status: "warning",
duration: 1000,
isClosable: true,
});
return;
}
setResponse(data);
},
});
const { data, error } = useSWR<FetchUsersResponse<User>>(
`/api/admin/users?direction=${pagination.direction}&cursor=${pagination.cursor}&searchDisplayName=${display_name}&sortKey=display_name`,
get,
{
keepPreviousData: true,
}
);
const toPreviousPage = () => {
if (response.items.length >= 0) {
setPagination({
cursor: response.prev,
direction: "back",
});
} else {
toast({
title: "Can not paginate when no users are found",
status: "warning",
duration: 1000,
isClosable: true,
});
}
setPagination({
cursor: data.prev,
direction: "back",
});
};
const toNextPage = () => {
if (response.items.length >= 0) {
setPagination({
cursor: response.next,
direction: "forward",
});
} else {
toast({
title: "Can not paginate when no users are found",
status: "warning",
duration: 1000,
isClosable: true,
});
}
setPagination({
cursor: data.next,
direction: "forward",
});
};
return (
<DataTable
data={response.items}
columns={columns}
caption="Users"
onNextClick={toNextPage}
onPreviousClick={toPreviousPage}
filterValues={filterValues}
onFilterChange={handleFilterValuesChange}
></DataTable>
<Card>
<CardBody>
{data && (
<DataTable
data={data.items}
columns={columns}
caption="Users"
onNextClick={toNextPage}
onPreviousClick={toPreviousPage}
disableNext={!data.next}
disablePrevious={!data.prev}
filterValues={filterValues}
onFilterChange={handleFilterValuesChange}
></DataTable>
)}
{error && "Unable to load users."}
</CardBody>
</Card>
);
});
+1 -1
View File
@@ -5,7 +5,7 @@ import prisma from "src/lib/prismadb";
/**
* The number of users to fetch in a single request. Could later be a query parameter.
*/
const PAGE_SIZE = 20;
const PAGE_SIZE = 2;
/**
* Returns a list of user results from the database when the requesting user is