diff --git a/apps/console/src/pages/VendorListPage.tsx b/apps/console/src/pages/VendorListPage.tsx index ae19eac83..31756e60a 100644 --- a/apps/console/src/pages/VendorListPage.tsx +++ b/apps/console/src/pages/VendorListPage.tsx @@ -1,13 +1,12 @@ -import { Suspense, useEffect, useState } from "react"; +import { Suspense, useEffect, useState, useTransition } from "react"; import { graphql, PreloadedQuery, usePreloadedQuery, useQueryLoader, useMutation, - loadQuery, - useLazyLoadQuery, } from "react-relay"; +import { useSearchParams } from "react-router"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Badge } from "@/components/ui/badge"; import { CircleUser, Globe, Shield, Store } from "lucide-react"; @@ -17,12 +16,14 @@ import { Link } from "react-router"; import Fuse from "fuse.js"; import type { VendorListPageQuery as VendorListPageQueryType } from "./__generated__/VendorListPageQuery.graphql"; +const ITEMS_PER_PAGE = 2; + const vendorListPageQuery = graphql` - query VendorListPageQuery { + query VendorListPageQuery($count: Int!, $after: CursorKey, $before: CursorKey) { node(id: "AZSfP_xAcAC5IAAAAAAltA") { id ... on Organization { - vendors { + vendors(first: $count, after: $after, before: $before) { edges { node { id @@ -30,6 +31,13 @@ const vendorListPageQuery = graphql` createdAt updatedAt } + cursor + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor } } } @@ -59,20 +67,51 @@ const vendorsList = [ function VendorListContent({ queryRef, + onPageChange, }: { queryRef: PreloadedQuery; + onPageChange: (params: { before?: string; after?: string }) => void; }) { const data = usePreloadedQuery(vendorListPageQuery, queryRef); + const [searchParams, setSearchParams] = useSearchParams(); + const [isPending, startTransition] = useTransition(); const [searchTerm, setSearchTerm] = useState(""); - const [filteredVendors, setFilteredVendors] = useState>([]); + const [filteredVendors, setFilteredVendors] = useState>([]); const [createVendor] = useMutation(createVendorMutation); - const [_, loadQuery] = useQueryLoader(vendorListPageQuery); - const fuse = new Fuse(vendorsList, { + + const vendors = data.node?.vendors?.edges?.map(edge => edge?.node) ?? []; + const pageInfo = data.node?.vendors?.pageInfo; + + const fuse = new Fuse(vendors, { keys: ['name'], threshold: 0.3, }); - const vendors = data.node.vendors?.edges.map(edge => edge.node) ?? []; + const handlePageChange = (direction: 'next' | 'prev') => { + if (!pageInfo) return; + + if (direction === 'next' && !pageInfo.hasNextPage) return; + if (direction === 'prev' && !pageInfo.hasPreviousPage) return; + + startTransition(() => { + const params = direction === 'next' + ? { after: pageInfo.endCursor } + : { before: pageInfo.startCursor }; + + setSearchParams(prev => { + if (direction === 'next') { + prev.set('after', pageInfo.endCursor!); + prev.delete('before'); + } else { + prev.set('before', pageInfo.startCursor!); + prev.delete('after'); + } + return prev; + }); + + onPageChange(params); + }); + }; return (
@@ -129,7 +168,6 @@ function VendorListContent({ onCompleted(response: any) { setSearchTerm(""); setFilteredVendors([]); - loadQuery({}, {fetchPolicy: 'network-only'}); }, }); }} @@ -182,6 +220,23 @@ function VendorListContent({
))} + +
+ + +
); @@ -204,11 +259,31 @@ function VendorListFallback() { } export default function VendorListPage() { + const [searchParams] = useSearchParams(); const [queryRef, loadQuery] = useQueryLoader(vendorListPageQuery); + // Initialize with URL params useEffect(() => { - loadQuery({}); - }, [loadQuery]); + const after = searchParams.get('after'); + const before = searchParams.get('before'); + + loadQuery({ + count: ITEMS_PER_PAGE, + after: after || undefined, + before: before || undefined, + }); + }, [loadQuery, searchParams]); + + const handlePageChange = ({ before, after }: { before?: string; after?: string }) => { + loadQuery( + { + count: ITEMS_PER_PAGE, + before: before || undefined, + after: after || undefined, + }, + { fetchPolicy: 'network-only' } + ); + }; if (!queryRef) { return ; @@ -216,7 +291,7 @@ export default function VendorListPage() { return ( }> - + ); } diff --git a/apps/console/src/pages/__generated__/VendorListPageQuery.graphql.ts b/apps/console/src/pages/__generated__/VendorListPageQuery.graphql.ts index fdc3fa6b2..75ff5e204 100644 --- a/apps/console/src/pages/__generated__/VendorListPageQuery.graphql.ts +++ b/apps/console/src/pages/__generated__/VendorListPageQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -9,12 +9,17 @@ // @ts-nocheck import { ConcreteRequest } from 'relay-runtime'; -export type VendorListPageQuery$variables = Record; +export type VendorListPageQuery$variables = { + after?: any | null | undefined; + before?: any | null | undefined; + count: number; +}; export type VendorListPageQuery$data = { readonly node: { readonly id: string; readonly vendors?: { readonly edges: ReadonlyArray<{ + readonly cursor: any; readonly node: { readonly createdAt: any; readonly id: string; @@ -22,6 +27,12 @@ export type VendorListPageQuery$data = { readonly updatedAt: any; }; }>; + readonly pageInfo: { + readonly endCursor: any | null | undefined; + readonly hasNextPage: boolean; + readonly hasPreviousPage: boolean; + readonly startCursor: any | null | undefined; + }; }; }; }; @@ -31,26 +42,57 @@ export type VendorListPageQuery = { }; const node: ConcreteRequest = (function(){ -var v0 = [ +var v0 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "after" +}, +v1 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "before" +}, +v2 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "count" +}, +v3 = [ { "kind": "Literal", "name": "id", "value": "AZSfP_xAcAC5IAAAAAAltA" } ], -v1 = { +v4 = { "alias": null, "args": null, "kind": "ScalarField", "name": "id", "storageKey": null }, -v2 = { +v5 = { "kind": "InlineFragment", "selections": [ { "alias": null, - "args": null, + "args": [ + { + "kind": "Variable", + "name": "after", + "variableName": "after" + }, + { + "kind": "Variable", + "name": "before", + "variableName": "before" + }, + { + "kind": "Variable", + "name": "first", + "variableName": "count" + } + ], "concreteType": "VendorConnection", "kind": "LinkedField", "name": "vendors", @@ -72,7 +114,7 @@ v2 = { "name": "node", "plural": false, "selections": [ - (v1/*: any*/), + (v4/*: any*/), { "alias": null, "args": null, @@ -96,6 +138,52 @@ v2 = { } ], "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "cursor", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "PageInfo", + "kind": "LinkedField", + "name": "pageInfo", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasNextPage", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasPreviousPage", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "startCursor", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "endCursor", + "storageKey": null } ], "storageKey": null @@ -109,21 +197,25 @@ v2 = { }; return { "fragment": { - "argumentDefinitions": [], + "argumentDefinitions": [ + (v0/*: any*/), + (v1/*: any*/), + (v2/*: any*/) + ], "kind": "Fragment", "metadata": null, "name": "VendorListPageQuery", "selections": [ { "alias": null, - "args": (v0/*: any*/), + "args": (v3/*: any*/), "concreteType": null, "kind": "LinkedField", "name": "node", "plural": false, "selections": [ - (v1/*: any*/), - (v2/*: any*/) + (v4/*: any*/), + (v5/*: any*/) ], "storageKey": "node(id:\"AZSfP_xAcAC5IAAAAAAltA\")" } @@ -133,13 +225,17 @@ return { }, "kind": "Request", "operation": { - "argumentDefinitions": [], + "argumentDefinitions": [ + (v2/*: any*/), + (v0/*: any*/), + (v1/*: any*/) + ], "kind": "Operation", "name": "VendorListPageQuery", "selections": [ { "alias": null, - "args": (v0/*: any*/), + "args": (v3/*: any*/), "concreteType": null, "kind": "LinkedField", "name": "node", @@ -152,24 +248,24 @@ return { "name": "__typename", "storageKey": null }, - (v1/*: any*/), - (v2/*: any*/) + (v4/*: any*/), + (v5/*: any*/) ], "storageKey": "node(id:\"AZSfP_xAcAC5IAAAAAAltA\")" } ] }, "params": { - "cacheID": "1661d7e75c833a569efafa55c204d894", + "cacheID": "813b95f0cae45fec89ee444ce9be7ebb", "id": null, "metadata": {}, "name": "VendorListPageQuery", "operationKind": "query", - "text": "query VendorListPageQuery {\n node(id: \"AZSfP_xAcAC5IAAAAAAltA\") {\n __typename\n id\n ... on Organization {\n vendors {\n edges {\n node {\n id\n name\n createdAt\n updatedAt\n }\n }\n }\n }\n }\n}\n" + "text": "query VendorListPageQuery(\n $count: Int!\n $after: CursorKey\n $before: CursorKey\n) {\n node(id: \"AZSfP_xAcAC5IAAAAAAltA\") {\n __typename\n id\n ... on Organization {\n vendors(first: $count, after: $after, before: $before) {\n edges {\n node {\n id\n name\n createdAt\n updatedAt\n }\n cursor\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n}\n" } }; })(); -(node as any).hash = "e869cdf6203c1a7f42c4c697f574b0c0"; +(node as any).hash = "46f94dbcc31c870cab30e1b8a2c64095"; export default node;