Add pagination on people

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Jonathan
2025-06-17 17:14:03 +02:00
committed by Bryan Frimin
parent ab54d13b87
commit fdc2b107a3
6 changed files with 56 additions and 21 deletions

View File

@@ -1,4 +1,12 @@
import { IconChevronTriangleDownSmall, Table, Th } from "@probo/ui"; import { useTranslate } from "@probo/i18n";
import {
Button,
IconChevronDown,
IconChevronTriangleDownSmall,
Spinner,
Table,
Th,
} from "@probo/ui";
import clsx from "clsx"; import clsx from "clsx";
import { import {
createContext, createContext,
@@ -28,10 +36,17 @@ const defaultOrder = {
export function SortableTable({ export function SortableTable({
refetch, refetch,
hasNext,
loadNext,
isLoading,
...props ...props
}: ComponentProps<typeof Table> & { }: ComponentProps<typeof Table> & {
refetch: (o: { order: Order }) => void; refetch: (o: { order: Order }) => void;
hasNext: boolean;
loadNext: () => void;
isLoading: boolean;
}) { }) {
const { __ } = useTranslate();
const [order, setOrder] = useState(defaultOrder); const [order, setOrder] = useState(defaultOrder);
const onOrderChange = (o: Order) => { const onOrderChange = (o: Order) => {
startTransition(() => { startTransition(() => {
@@ -41,7 +56,20 @@ export function SortableTable({
}; };
return ( return (
<SortableContext value={{ order, onOrderChange }}> <SortableContext value={{ order, onOrderChange }}>
<div className="space-y-4">
<Table {...props} /> <Table {...props} />
{hasNext && (
<Button
variant="tertiary"
onClick={() => loadNext()}
className="mt-3 mx-auto"
disabled={isLoading}
icon={isLoading ? Spinner : IconChevronDown}
>
{__("Show more")}
</Button>
)}
</div>
</SortableContext> </SortableContext>
); );
} }

View File

@@ -3,6 +3,7 @@ import type { PeopleGraphQuery } from "./__generated__/PeopleGraphQuery.graphql"
import { import {
useLazyLoadQuery, useLazyLoadQuery,
useMutation, useMutation,
usePaginationFragment,
usePreloadedQuery, usePreloadedQuery,
useRefetchableFragment, useRefetchableFragment,
type PreloadedQuery, type PreloadedQuery,
@@ -91,15 +92,15 @@ export function usePeopleQuery(
queryRef: PreloadedQuery<PeopleGraphPaginatedQuery> queryRef: PreloadedQuery<PeopleGraphPaginatedQuery>
) { ) {
const data = usePreloadedQuery(paginatedPeopleQuery, queryRef); const data = usePreloadedQuery(paginatedPeopleQuery, queryRef);
const [dataFragment, refetch] = useRefetchableFragment( const pagination = usePaginationFragment(
paginatedPeopleFragment, paginatedPeopleFragment,
data.organization as PeopleGraphPaginatedFragment$key data.organization as PeopleGraphPaginatedFragment$key
); );
const people = dataFragment?.peoples?.edges.map((edge) => edge.node); const people = pagination.data.peoples?.edges.map((edge) => edge.node);
return { return {
...pagination,
people, people,
refetch, connectionId: pagination.data.peoples.__id,
connectionId: dataFragment.peoples.__id,
}; };
} }

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<e4fcd65491a7570cf56382a3f977e06d>> * @generated SignedSource<<452165e3b8b1cdef4b8e62d53c8a73ed>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -58,7 +58,7 @@ return {
"name": "before" "name": "before"
}, },
{ {
"defaultValue": 50, "defaultValue": 1,
"kind": "LocalArgument", "kind": "LocalArgument",
"name": "first" "name": "first"
}, },
@@ -247,6 +247,6 @@ return {
}; };
})(); })();
(node as any).hash = "c163b7909a337efd22088852b9908045"; (node as any).hash = "e1720a7dc0b7914edb8a07f63d5cf95a";
export default node; export default node;

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<43878b22fe8a689f41bcd506711ddb5f>> * @generated SignedSource<<56d7fa0e53727e6a52fc6ab7bc805ceb>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -57,7 +57,7 @@ v4 = [
{ {
"kind": "Literal", "kind": "Literal",
"name": "first", "name": "first",
"value": 50 "value": 1
} }
]; ];
return { return {
@@ -233,7 +233,7 @@ return {
] ]
} }
], ],
"storageKey": "peoples(first:50)" "storageKey": "peoples(first:1)"
}, },
{ {
"alias": null, "alias": null,
@@ -256,12 +256,12 @@ return {
] ]
}, },
"params": { "params": {
"cacheID": "b154e94ac7e783a492bc5aed7d44229d", "cacheID": "838b4fd1617a05fcb681c1e533cc0a9f",
"id": null, "id": null,
"metadata": {}, "metadata": {},
"name": "PeopleGraphPaginatedQuery", "name": "PeopleGraphPaginatedQuery",
"operationKind": "query", "operationKind": "query",
"text": "query PeopleGraphPaginatedQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n id\n ...PeopleGraphPaginatedFragment\n }\n id\n }\n}\n\nfragment PeopleGraphPaginatedFragment on Organization {\n peoples(first: 50) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n kind\n additionalEmailAddresses\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n" "text": "query PeopleGraphPaginatedQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n id\n ...PeopleGraphPaginatedFragment\n }\n id\n }\n}\n\nfragment PeopleGraphPaginatedFragment on Organization {\n peoples(first: 1) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n kind\n additionalEmailAddresses\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n"
} }
}; };
})(); })();

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<bc0fdc02fb8b596386e290ae0cf0b8b8>> * @generated SignedSource<<b6d154d843de3da0a803639f428f7152>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -46,7 +46,7 @@ v1 = {
"name": "before" "name": "before"
}, },
v2 = { v2 = {
"defaultValue": 50, "defaultValue": 1,
"kind": "LocalArgument", "kind": "LocalArgument",
"name": "first" "name": "first"
}, },
@@ -329,16 +329,16 @@ return {
] ]
}, },
"params": { "params": {
"cacheID": "a17ff68897dbcf3daa33b073bc83f1bb", "cacheID": "e26cb4d1b42b5500dd12ee294ccce2e5",
"id": null, "id": null,
"metadata": {}, "metadata": {},
"name": "PeopleListQuery", "name": "PeopleListQuery",
"operationKind": "query", "operationKind": "query",
"text": "query PeopleListQuery(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 50\n $last: Int = null\n $order: PeopleOrder = null\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...PeopleGraphPaginatedFragment_16fISc\n id\n }\n}\n\nfragment PeopleGraphPaginatedFragment_16fISc on Organization {\n peoples(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n kind\n additionalEmailAddresses\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n" "text": "query PeopleListQuery(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 1\n $last: Int = null\n $order: PeopleOrder = null\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...PeopleGraphPaginatedFragment_16fISc\n id\n }\n}\n\nfragment PeopleGraphPaginatedFragment_16fISc on Organization {\n peoples(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n kind\n additionalEmailAddresses\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n"
} }
}; };
})(); })();
(node as any).hash = "c163b7909a337efd22088852b9908045"; (node as any).hash = "e1720a7dc0b7914edb8a07f63d5cf95a";
export default node; export default node;

View File

@@ -32,7 +32,8 @@ export default function PeopleListPage({
queryRef: PreloadedQuery<PeopleGraphPaginatedQuery>; queryRef: PreloadedQuery<PeopleGraphPaginatedQuery>;
}) { }) {
const { __ } = useTranslate(); const { __ } = useTranslate();
const { people, refetch, connectionId } = usePeopleQuery(queryRef); const { people, refetch, connectionId, hasNext, loadNext, isLoadingNext } =
usePeopleQuery(queryRef);
usePageTitle(__("Members")); usePageTitle(__("Members"));
@@ -48,7 +49,12 @@ export default function PeopleListPage({
<Button icon={IconPlusLarge}>{__("Add member")}</Button> <Button icon={IconPlusLarge}>{__("Add member")}</Button>
</CreatePeopleDialog> </CreatePeopleDialog>
</PageHeader> </PageHeader>
<SortableTable refetch={refetch}> <SortableTable
refetch={refetch}
hasNext={hasNext}
loadNext={loadNext}
isLoading={isLoadingNext}
>
<Thead> <Thead>
<Tr> <Tr>
<SortableTh field="FULL_NAME">{__("Name")}</SortableTh> <SortableTh field="FULL_NAME">{__("Name")}</SortableTh>