Refactor people pagination

Signed-off-by: gearnode <bryan@frimin.fr>
This commit is contained in:
gearnode
2025-02-19 13:43:03 +01:00
parent b27f69756e
commit adb1dbbb39
2 changed files with 98 additions and 44 deletions

View File

@@ -15,7 +15,7 @@ import { Link } from "react-router";
import { Helmet } from "react-helmet-async"; import { Helmet } from "react-helmet-async";
import type { PeopleListPageQuery as PeopleListPageQueryType } from "./__generated__/PeopleListPageQuery.graphql"; import type { PeopleListPageQuery as PeopleListPageQueryType } from "./__generated__/PeopleListPageQuery.graphql";
const ITEMS_PER_PAGE = 20; const ITEMS_PER_PAGE = 25;
const PeopleListPageQuery = graphql` const PeopleListPageQuery = graphql`
query PeopleListPageQuery( query PeopleListPageQuery(
@@ -59,10 +59,61 @@ const deletePeopleMutation = graphql`
} }
`; `;
function LoadAboveButton({
pageInfo,
isPending,
onPageChange,
}: {
pageInfo: {
hasNextPage: boolean;
hasPreviousPage: boolean;
} | null | undefined;
isPending: boolean;
onPageChange: (direction: "prev") => void;
}) {
return (
<div className="flex justify-center">
<Button
variant="outline"
onClick={() => onPageChange("prev")}
disabled={isPending || !pageInfo?.hasPreviousPage}
className="w-full"
>
{isPending ? "Loading..." : "Load above"}
</Button>
</div>
);
}
function LoadBelowButton({
pageInfo,
isPending,
onPageChange,
}: {
pageInfo: {
hasNextPage: boolean;
hasPreviousPage: boolean;
} | null | undefined;
isPending: boolean;
onPageChange: (direction: "next") => void;
}) {
return (
<div className="flex justify-center">
<Button
variant="outline"
onClick={() => onPageChange("next")}
disabled={isPending || !pageInfo?.hasNextPage}
className="w-full"
>
{isPending ? "Loading..." : "Load below"}
</Button>
</div>
);
}
function PeopleListPageContent({ function PeopleListPageContent({
queryRef, queryRef,
onPageChange, onPageChange,
loadQuery,
}: { }: {
queryRef: PreloadedQuery<PeopleListPageQueryType>; queryRef: PreloadedQuery<PeopleListPageQueryType>;
onPageChange: (params: { onPageChange: (params: {
@@ -71,27 +122,38 @@ function PeopleListPageContent({
last?: number; last?: number;
before?: string; before?: string;
}) => void; }) => void;
loadQuery: LoadQueryType;
}) { }) {
const data = usePreloadedQuery(PeopleListPageQuery, queryRef); const data = usePreloadedQuery(PeopleListPageQuery, queryRef);
const [searchParams, setSearchParams] = useSearchParams();
const peoples = data.node.peoples?.edges.map((edge) => edge?.node) ?? []; const peoples = data.node.peoples?.edges.map((edge) => edge?.node) ?? [];
const pageInfo = data.node.peoples?.pageInfo; const pageInfo = data.node.peoples?.pageInfo;
const [isPending, startTransition] = useTransition(); const [isPending, startTransition] = useTransition();
const [deletePeople] = useMutation(deletePeopleMutation); const [deletePeople] = useMutation(deletePeopleMutation);
const handlePageChange = (direction: "prev" | "next") => { const handlePageChange = (direction: "prev" | "next") => {
if (!pageInfo) return;
if (direction === "next" && !pageInfo.hasNextPage) return;
if (direction === "prev" && !pageInfo.hasPreviousPage) return;
startTransition(() => { startTransition(() => {
if (direction === "prev") { const params =
onPageChange({ direction === "next"
last: ITEMS_PER_PAGE, ? { first: ITEMS_PER_PAGE, after: pageInfo.endCursor }
before: pageInfo?.startCursor, : { last: ITEMS_PER_PAGE, before: pageInfo.startCursor };
});
} else { setSearchParams((prev) => {
onPageChange({ if (direction === "next") {
first: ITEMS_PER_PAGE, prev.set("after", pageInfo.endCursor!);
after: pageInfo?.endCursor, prev.delete("before");
}); } else {
} prev.set("before", pageInfo.startCursor!);
prev.delete("after");
}
return prev;
});
onPageChange(params);
}); });
}; };
@@ -110,6 +172,13 @@ function PeopleListPageContent({
</Button> </Button>
</div> </div>
</div> </div>
<LoadAboveButton
pageInfo={pageInfo}
isPending={isPending}
onPageChange={() => handlePageChange("prev")}
/>
<div className="space-y-2"> <div className="space-y-2">
{peoples.map((person) => ( {peoples.map((person) => (
<Link <Link
@@ -164,15 +233,12 @@ function PeopleListPageContent({
}, },
}, },
onCompleted() { onCompleted() {
loadQuery( onPageChange({
{ first: ITEMS_PER_PAGE,
first: ITEMS_PER_PAGE, after: undefined,
after: undefined, last: undefined,
last: undefined, before: undefined,
before: undefined, });
},
{ fetchPolicy: "network-only" },
);
}, },
}); });
} }
@@ -184,22 +250,11 @@ function PeopleListPageContent({
</Link> </Link>
))} ))}
<div className="flex gap-2 justify-end mt-4"> <LoadBelowButton
<Button pageInfo={pageInfo}
variant="outline" isPending={isPending}
onClick={() => handlePageChange("prev")} onPageChange={() => handlePageChange("next")}
disabled={isPending || !pageInfo?.hasPreviousPage} />
>
{isPending ? "Loading..." : "Previous"}
</Button>
<Button
variant="outline"
onClick={() => handlePageChange("next")}
disabled={isPending || !pageInfo?.hasNextPage}
>
{isPending ? "Loading..." : "Next"}
</Button>
</div>
</div> </div>
</div> </div>
); );
@@ -300,7 +355,6 @@ export default function PeopleListPage() {
<PeopleListPageContent <PeopleListPageContent
queryRef={queryRef} queryRef={queryRef}
onPageChange={handlePageChange} onPageChange={handlePageChange}
loadQuery={loadQuery}
/> />
</Suspense> </Suspense>
</> </>

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<048f9d727dd07032be161da7019c6ebe>> * @generated SignedSource<<98591652ea931191ef7210e363e0adad>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -88,6 +88,7 @@ return {
}, },
"name": "VendorListPage_vendors", "name": "VendorListPage_vendors",
"selections": [ "selections": [
(v1/*: any*/),
{ {
"alias": "vendors", "alias": "vendors",
"args": null, "args": null,
@@ -195,14 +196,13 @@ return {
} }
], ],
"storageKey": null "storageKey": null
}, }
(v1/*: any*/)
], ],
"type": "Organization", "type": "Organization",
"abstractKey": null "abstractKey": null
}; };
})(); })();
(node as any).hash = "753e6b32cc21645852de27cf6f23c769"; (node as any).hash = "f61c6a2996a9fe95b71d9c0c2fe0530c";
export default node; export default node;