From 110cac6847a9311fa820f9b09e25287f6bc7c09f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20R=C3=A9?= Date: Mon, 5 Jan 2026 13:24:49 +0100 Subject: [PATCH] Fix invitation list connection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Émile Ré --- .../iam/InvitationListFragment.graphql.ts | 12 +++-- ...tationListFragment_RefetchQuery.graphql.ts | 8 +-- .../iam/MembersPageQuery.graphql.ts | 13 ++--- apps/console/src/components/SortableTable.tsx | 29 ++++++---- .../organizations/settings/MembersPage.tsx | 22 ++++++-- .../settings/_components/InvitationList.tsx | 54 +++++++++++++++---- .../settings/_components/InviteUserDialog.tsx | 10 ++-- 7 files changed, 104 insertions(+), 44 deletions(-) diff --git a/apps/console/src/__generated__/iam/InvitationListFragment.graphql.ts b/apps/console/src/__generated__/iam/InvitationListFragment.graphql.ts index 6f0ecd6cc..b6150fdd9 100644 --- a/apps/console/src/__generated__/iam/InvitationListFragment.graphql.ts +++ b/apps/console/src/__generated__/iam/InvitationListFragment.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<37b59716fa1aba5ed805ce03278ce01d>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -111,7 +111,13 @@ return { "kind": "RequiredField", "field": { "alias": "invitations", - "args": null, + "args": [ + { + "kind": "Variable", + "name": "orderBy", + "variableName": "order" + } + ], "concreteType": "InvitationConnection", "kind": "LinkedField", "name": "__InvitationListFragment_invitations_connection", @@ -233,6 +239,6 @@ return { }; })(); -(node as any).hash = "6a1b77bc8b0e5f345de9865ec78ef7e9"; +(node as any).hash = "d6f8750bb89b233418c3a66669c4aa23"; export default node; diff --git a/apps/console/src/__generated__/iam/InvitationListFragment_RefetchQuery.graphql.ts b/apps/console/src/__generated__/iam/InvitationListFragment_RefetchQuery.graphql.ts index 4d307741b..f18069ca1 100644 --- a/apps/console/src/__generated__/iam/InvitationListFragment_RefetchQuery.graphql.ts +++ b/apps/console/src/__generated__/iam/InvitationListFragment_RefetchQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<<23d03caf79ac153cc19aae8e87c38a27>> * @lightSyntaxTransform * @nogrep */ @@ -355,7 +355,9 @@ return { { "alias": null, "args": (v13/*: any*/), - "filters": [], + "filters": [ + "orderBy" + ], "handle": "connection", "key": "InvitationListFragment_invitations", "kind": "LinkedHandle", @@ -381,6 +383,6 @@ return { }; })(); -(node as any).hash = "6a1b77bc8b0e5f345de9865ec78ef7e9"; +(node as any).hash = "d6f8750bb89b233418c3a66669c4aa23"; export default node; diff --git a/apps/console/src/__generated__/iam/MembersPageQuery.graphql.ts b/apps/console/src/__generated__/iam/MembersPageQuery.graphql.ts index 4f01330b3..462cb23ff 100644 --- a/apps/console/src/__generated__/iam/MembersPageQuery.graphql.ts +++ b/apps/console/src/__generated__/iam/MembersPageQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<0f7c89bd92b5743593efd542812568a7>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -197,7 +197,10 @@ v17 = { "storageKey": null } ] -}; +}, +v18 = [ + "orderBy" +]; return { "fragment": { "argumentDefinitions": (v0/*: any*/), @@ -411,9 +414,7 @@ return { { "alias": null, "args": (v6/*: any*/), - "filters": [ - "orderBy" - ], + "filters": (v18/*: any*/), "handle": "connection", "key": "MemberListFragment_members", "kind": "LinkedHandle", @@ -499,7 +500,7 @@ return { { "alias": null, "args": (v6/*: any*/), - "filters": [], + "filters": (v18/*: any*/), "handle": "connection", "key": "InvitationListFragment_invitations", "kind": "LinkedHandle", diff --git a/apps/console/src/components/SortableTable.tsx b/apps/console/src/components/SortableTable.tsx index 874e03ca0..948fc3cad 100644 --- a/apps/console/src/components/SortableTable.tsx +++ b/apps/console/src/components/SortableTable.tsx @@ -18,7 +18,7 @@ import { import type { LoadMoreFn } from "react-relay"; import type { OperationType } from "relay-runtime"; -type Order = { +export type Order = { direction: "ASC" | "DESC"; field: string; }; @@ -30,7 +30,7 @@ export const SortableContext = createContext({ direction: "DESC", field: "CREATED_AT", }, - onOrderChange: (() => {}) as (order: Order) => void, + changeOrder: (() => {}) as (order: Order) => void, }); const defaultOrder = { @@ -54,14 +54,14 @@ export function SortableTable({ }) { const { __ } = useTranslate(); const [order, setOrder] = useState(defaultOrder); - const onOrderChange = (o: Order) => { + const changeOrder = (o: Order) => { startTransition(() => { setOrder(o); refetch({ order: o }); }); }; return ( - +
{hasNext && loadNext && ( @@ -83,22 +83,29 @@ export function SortableTable({ export function SortableTh({ children, field, + onOrderChange, ...props -}: ComponentProps & { field: string }) { - const { order, onOrderChange } = useContext(SortableContext); +}: ComponentProps & { + field: string; + onOrderChange?: (order: { direction: "ASC" | "DESC"; field: string }) => void; +}) { + const { order, changeOrder } = useContext(SortableContext); const isCurrentField = order.field === field; const isDesc = order.direction === "DESC"; - const changeOrder = () => { - onOrderChange({ - direction: isDesc && isCurrentField ? "ASC" : "DESC", + const handleChangeOrder = () => { + const newOrder = { + direction: + isDesc && isCurrentField ? ("ASC" as const) : ("DESC" as const), field, - }); + }; + changeOrder(newOrder); + onOrderChange?.(newOrder); }; return ( - {__("Name")} - {__("Email")} - {__("Role")} - {__("Invited")} + + {__("Name")} + + + {__("Email")} + + + {__("Role")} + + + {__("Invited")} + - {__("Accepted at")} + + {__("Accepted at")} + diff --git a/apps/console/src/pages/iam/organizations/settings/_components/InviteUserDialog.tsx b/apps/console/src/pages/iam/organizations/settings/_components/InviteUserDialog.tsx index 7d957a9a2..8ba739499 100644 --- a/apps/console/src/pages/iam/organizations/settings/_components/InviteUserDialog.tsx +++ b/apps/console/src/pages/iam/organizations/settings/_components/InviteUserDialog.tsx @@ -12,7 +12,7 @@ import { } from "@probo/ui"; import { Controller } from "react-hook-form"; import { useFragment } from "react-relay"; -import { ConnectionHandler, graphql } from "relay-runtime"; +import { graphql } from "relay-runtime"; import z from "zod"; import { useFormWithSchema } from "/hooks/useFormWithSchema"; import { useMutationWithToasts } from "/hooks/useMutationWithToasts"; @@ -59,11 +59,12 @@ const schema = z.object({ }); type InviteUserDialogProps = PropsWithChildren<{ + connectionId: string; viewerMembershipFKey: InviteUserDialog_currentRoleFragment$key; }>; export function InviteUserDialog(props: InviteUserDialogProps) { - const { children, viewerMembershipFKey } = props; + const { children, connectionId, viewerMembershipFKey } = props; const organizationId = useOrganizationId(); const { __ } = useTranslate(); @@ -87,11 +88,6 @@ export function InviteUserDialog(props: InviteUserDialogProps) { }); const onSubmit = handleSubmit((data) => { - const connectionId = ConnectionHandler.getConnectionID( - organizationId, - "InvitationListFragment_invitations", - {}, - ); inviteUser({ variables: { input: {
)} @@ -88,7 +101,10 @@ export function MembersPage(props: { {activeTab === "memberships" && } {activeTab === "invitations" && ( - + )} diff --git a/apps/console/src/pages/iam/organizations/settings/_components/InvitationList.tsx b/apps/console/src/pages/iam/organizations/settings/_components/InvitationList.tsx index b0032959b..bf6a339dc 100644 --- a/apps/console/src/pages/iam/organizations/settings/_components/InvitationList.tsx +++ b/apps/console/src/pages/iam/organizations/settings/_components/InvitationList.tsx @@ -1,11 +1,16 @@ import { Tbody, Td, Th, Thead, Tr } from "@probo/ui"; -import { SortableTable, SortableTh } from "/components/SortableTable"; +import { + SortableTable, + SortableTh, + type Order, +} from "/components/SortableTable"; import { useTranslate } from "@probo/i18n"; -import { graphql, usePaginationFragment } from "react-relay"; +import { ConnectionHandler, graphql, usePaginationFragment } from "react-relay"; import { InvitationListItem } from "./InvitationListItem"; import type { InvitationListFragment$key } from "/__generated__/iam/InvitationListFragment.graphql"; import type { InvitationListFragment_RefetchQuery } from "/__generated__/iam/InvitationListFragment_RefetchQuery.graphql"; -import type { ComponentProps } from "react"; +import { type ComponentProps } from "react"; +import { useOrganizationId } from "/hooks/useOrganizationId"; const fragment = graphql` fragment InvitationListFragment on Organization @@ -27,7 +32,10 @@ const fragment = graphql` before: $before orderBy: $order ) - @connection(key: "InvitationListFragment_invitations", filters: []) + @connection( + key: "InvitationListFragment_invitations" + filters: ["orderBy"] + ) @required(action: THROW) { __id totalCount @@ -41,9 +49,13 @@ const fragment = graphql` } `; -export function InvitationList(props: { fKey: InvitationListFragment$key }) { - const { fKey } = props; +export function InvitationList(props: { + fKey: InvitationListFragment$key; + onConnectionIdChange: (connectionId: string) => void; +}) { + const { fKey, onConnectionIdChange } = props; + const organizationId = useOrganizationId(); const { __ } = useTranslate(); const invitationsPagination = usePaginationFragment< @@ -55,6 +67,16 @@ export function InvitationList(props: { fKey: InvitationListFragment$key }) { invitationsPagination.refetch({}, { fetchPolicy: "network-only" }); }; + const handleOrderChange = (order: Order) => { + onConnectionIdChange( + ConnectionHandler.getConnectionID( + organizationId, + "InvitationListFragment_invitations", + { orderBy: order }, + ), + ); + }; + return (
{__("Status")}