Fix invitation list connection

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-01-05 13:24:49 +01:00
committed by Bryan Frimin
parent da8a670a68
commit 110cac6847
7 changed files with 104 additions and 44 deletions

View File

@@ -1,5 +1,5 @@
import { usePreloadedQuery, type PreloadedQuery } from "react-relay";
import { graphql } from "relay-runtime";
import { ConnectionHandler, graphql } from "relay-runtime";
import { MemberList } from "./_components/MemberList";
import type { MembersPageQuery } from "/__generated__/iam/MembersPageQuery.graphql";
import { useTranslate } from "@probo/i18n";
@@ -7,6 +7,7 @@ import { Button, Card, TabBadge, TabItem, Tabs } from "@probo/ui";
import { useState } from "react";
import { InvitationList } from "./_components/InvitationList";
import { InviteUserDialog } from "./_components/InviteUserDialog";
import { useOrganizationId } from "/hooks/useOrganizationId";
export const membersPageQuery = graphql`
query MembersPageQuery($organizationId: ID!) {
@@ -37,6 +38,7 @@ export function MembersPage(props: {
}) {
const { queryRef } = props;
const organizationId = useOrganizationId();
const { __ } = useTranslate();
const [activeTab, setActiveTab] = useState<"memberships" | "invitations">(
@@ -51,12 +53,23 @@ export function MembersPage(props: {
throw new Error("node is of invalid type");
}
const [invitationListConnectionId, setInvitationListConnectionId] = useState(
ConnectionHandler.getConnectionID(
organizationId,
"InvitationListFragment_invitations",
{ orderBy: { direction: "ASC", field: "CREATED_AT" } },
),
);
return (
<div className="space-y-2">
<div className="flex items-center justify-between">
<h2 className="text-base font-medium">{__("Workspace members")}</h2>
{organization.canInviteUser && (
<InviteUserDialog viewerMembershipFKey={organization}>
<InviteUserDialog
connectionId={invitationListConnectionId}
viewerMembershipFKey={organization}
>
<Button variant="secondary">{__("Invite member")}</Button>
</InviteUserDialog>
)}
@@ -88,7 +101,10 @@ export function MembersPage(props: {
{activeTab === "memberships" && <MemberList fKey={organization} />}
{activeTab === "invitations" && (
<InvitationList fKey={organization} />
<InvitationList
fKey={organization}
onConnectionIdChange={setInvitationListConnectionId}
/>
)}
</div>
</Card>

View File

@@ -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 (
<SortableTable
{...invitationsPagination}
@@ -67,12 +89,22 @@ export function InvitationList(props: { fKey: InvitationListFragment$key }) {
>
<Thead>
<Tr>
<SortableTh field="FULL_NAME">{__("Name")}</SortableTh>
<SortableTh field="EMAIL">{__("Email")}</SortableTh>
<SortableTh field="ROLE">{__("Role")}</SortableTh>
<SortableTh field="CREATED_AT">{__("Invited")}</SortableTh>
<SortableTh field="FULL_NAME" onOrderChange={handleOrderChange}>
{__("Name")}
</SortableTh>
<SortableTh field="EMAIL" onOrderChange={handleOrderChange}>
{__("Email")}
</SortableTh>
<SortableTh field="ROLE" onOrderChange={handleOrderChange}>
{__("Role")}
</SortableTh>
<SortableTh field="CREATED_AT" onOrderChange={handleOrderChange}>
{__("Invited")}
</SortableTh>
<Th>{__("Status")}</Th>
<SortableTh field="ACCEPTED_AT">{__("Accepted at")}</SortableTh>
<SortableTh field="ACCEPTED_AT" onOrderChange={handleOrderChange}>
{__("Accepted at")}
</SortableTh>
<Th></Th>
</Tr>
</Thead>

View File

@@ -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: {