diff --git a/frontend/src/routes/_layout/items.tsx b/frontend/src/routes/_layout/items.tsx index 1f53d27..9946e3b 100644 --- a/frontend/src/routes/_layout/items.tsx +++ b/frontend/src/routes/_layout/items.tsx @@ -1,4 +1,6 @@ +import { z } from "zod" import { + Button, Container, Flex, Heading, @@ -11,85 +13,118 @@ import { Thead, Tr, } from "@chakra-ui/react" -import { useSuspenseQuery } from "@tanstack/react-query" -import { createFileRoute } from "@tanstack/react-router" +import { useQuery, useQueryClient } from "@tanstack/react-query" +import { createFileRoute, useNavigate } from "@tanstack/react-router" -import { Suspense } from "react" -import { ErrorBoundary } from "react-error-boundary" +import { useEffect } from "react" import { ItemsService } from "../../client" import ActionsMenu from "../../components/Common/ActionsMenu" import Navbar from "../../components/Common/Navbar" -export const Route = createFileRoute("/_layout/items")({ - component: Items, +const itemsSearchSchema = z.object({ + page: z.number().catch(1), }) -function ItemsTableBody() { - const { data: items } = useSuspenseQuery({ - queryKey: ["items"], - queryFn: () => ItemsService.readItems({}), +export const Route = createFileRoute("/_layout/items")({ + component: Items, + validateSearch: (search) => itemsSearchSchema.parse(search), +}) + +const PER_PAGE = 5 + +function getItemsQueryOptions({ page }: { page: number }) { + return { + queryFn: () => + ItemsService.readItems({ skip: (page - 1) * PER_PAGE, limit: PER_PAGE }), + queryKey: ["items", { page }], + } +} + +function ItemsTable() { + const queryClient = useQueryClient() + const { page } = Route.useSearch() + const navigate = useNavigate({ from: Route.fullPath }) + const setPage = (page: number) => + navigate({ search: (prev) => ({ ...prev, page }) }) + + const { + data: items, + isPending, + isPlaceholderData, + } = useQuery({ + ...getItemsQueryOptions({ page }), + placeholderData: (prevData) => prevData, }) + const hasNextPage = !isPlaceholderData && items?.data.length === PER_PAGE + const hasPreviousPage = page > 1 + + useEffect(() => { + if (hasNextPage) { + queryClient.prefetchQuery(getItemsQueryOptions({ page: page + 1 })) + } + }, [page, queryClient]) + return ( - - {items.data.map((item) => ( - - {item.id} - {item.title} - - {item.description || "N/A"} - - - - - - ))} - - ) -} -function ItemsTable() { - return ( - - - - - - - - - - - ( + <> + +
IDTitleDescriptionActions
+ + + + + + + + + {isPending ? ( - - - + {new Array(5).fill(null).map((_, index) => ( + + {new Array(4).fill(null).map((_, index) => ( + + ))} + + ))} + + ) : ( + + {items?.data.map((item) => ( + + + + + + + ))} )} - > - - {new Array(5).fill(null).map((_, index) => ( - - {new Array(4).fill(null).map((_, index) => ( - - ))} - - ))} - - } - > - - - -
IDTitleDescriptionActions
Something went wrong: {error.message}
+ + + +
{item.id}{item.title} + {item.description || "N/A"} + + +
- - - -
-
+ + + + + Page {page} + + + ) }