From ec3d8fad40a2789a90cd9d4c63c89386eab5e54b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20R=C3=A9?= Date: Fri, 13 Feb 2026 14:11:11 +0400 Subject: [PATCH] Fix console app graphQL queries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Émile Ré --- .../MembershipsDropdownMenuQuery.graphql.ts | 38 +++++++------- ...PeopleListFragment_RefetchQuery.graphql.ts | 32 ++++++------ .../iam/PeopleListItemFragment.graphql.ts | 32 ++++++------ .../iam/PeoplePageQuery.graphql.ts | 32 ++++++------ .../iam/PersonPageQuery.graphql.ts | 12 +++-- .../pages/iam/memberships/MembershipsPage.tsx | 30 ++++++----- .../_components/InvitationCard.tsx | 18 +++---- .../_components/MembershipCard.tsx | 23 ++++++--- .../organizations/ViewerMembershipLayout.tsx | 14 ++--- .../_components/MembershipsDropdownMenu.tsx | 21 ++++---- .../MembershipsDropdownMenuItem.tsx | 21 +++++--- .../_components/ViewerMembershipDropdown.tsx | 13 +++-- .../iam/organizations/people/PeoplePage.tsx | 18 +++---- .../iam/organizations/people/PersonPage.tsx | 10 ++-- .../people/_components/PeopleList.tsx | 22 ++++---- .../people/_components/PeopleListItem.tsx | 51 ++++++++++--------- .../people/_components/PersonForm.tsx | 2 +- .../settings/_components/EditMemberDialog.tsx | 12 ++--- .../_components/SCIMEventListItem.tsx | 11 ++-- pkg/coredata/membership_profile.go | 14 ++--- pkg/server/api/connect/v1/v1_resolver.go | 10 ++-- pkg/server/api/console/v1/v1_resolver.go | 2 +- 22 files changed, 229 insertions(+), 209 deletions(-) diff --git a/apps/console/src/__generated__/iam/MembershipsDropdownMenuQuery.graphql.ts b/apps/console/src/__generated__/iam/MembershipsDropdownMenuQuery.graphql.ts index 5f3830d9e..70517b5fa 100644 --- a/apps/console/src/__generated__/iam/MembershipsDropdownMenuQuery.graphql.ts +++ b/apps/console/src/__generated__/iam/MembershipsDropdownMenuQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -19,7 +19,7 @@ export type MembershipsDropdownMenuQuery$data = { readonly id: string; readonly membership: { readonly " $fragmentSpreads": FragmentRefs<"MembershipsDropdownMenuItemFragment">; - } | null | undefined; + }; readonly organization: { readonly name: string; readonly " $fragmentSpreads": FragmentRefs<"MembershipsDropdownMenuItem_organizationFragment">; @@ -134,20 +134,24 @@ return { "action": "THROW" }, { - "alias": null, - "args": null, - "concreteType": "Membership", - "kind": "LinkedField", - "name": "membership", - "plural": false, - "selections": [ - { - "args": null, - "kind": "FragmentSpread", - "name": "MembershipsDropdownMenuItemFragment" - } - ], - "storageKey": null + "kind": "RequiredField", + "field": { + "alias": null, + "args": null, + "concreteType": "Membership", + "kind": "LinkedField", + "name": "membership", + "plural": false, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "MembershipsDropdownMenuItemFragment" + } + ], + "storageKey": null + }, + "action": "THROW" } ], "storageKey": null @@ -289,6 +293,6 @@ return { }; })(); -(node as any).hash = "14dbd79243ffb6104ff7315b78aa7990"; +(node as any).hash = "11d7ec7c24e0e539fd42e428d71998e1"; export default node; diff --git a/apps/console/src/__generated__/iam/PeopleListFragment_RefetchQuery.graphql.ts b/apps/console/src/__generated__/iam/PeopleListFragment_RefetchQuery.graphql.ts index 34f786b59..471c0e223 100644 --- a/apps/console/src/__generated__/iam/PeopleListFragment_RefetchQuery.graphql.ts +++ b/apps/console/src/__generated__/iam/PeopleListFragment_RefetchQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<5916f4df5b8113a300774131759cb218>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -285,6 +285,19 @@ return { "kind": "ScalarField", "name": "permission", "storageKey": "permission(action:\"iam:membership:update\")" + }, + { + "alias": "canDelete", + "args": [ + { + "kind": "Literal", + "name": "action", + "value": "iam:membership-profile:delete" + } + ], + "kind": "ScalarField", + "name": "permission", + "storageKey": "permission(action:\"iam:membership-profile:delete\")" } ], "storageKey": null @@ -328,19 +341,6 @@ return { "name": "permission", "storageKey": "permission(action:\"iam:membership-profile:update\")" }, - { - "alias": "canDelete", - "args": [ - { - "kind": "Literal", - "name": "action", - "value": "iam:membership-profile:delete" - } - ], - "kind": "ScalarField", - "name": "permission", - "storageKey": "permission(action:\"iam:membership-profile:delete\")" - }, (v11/*: any*/) ], "storageKey": null @@ -430,12 +430,12 @@ return { ] }, "params": { - "cacheID": "3ed525921615d7910d784222c189566a", + "cacheID": "25aa17e466fdec627ba3d24d5230301b", "id": null, "metadata": {}, "name": "PeopleListFragment_RefetchQuery", "operationKind": "query", - "text": "query PeopleListFragment_RefetchQuery(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 20\n $last: Int = null\n $order: ProfileOrder = {direction: ASC, field: FULL_NAME}\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...PeopleListFragment_16fISc\n id\n }\n}\n\nfragment PeopleListFragment_16fISc on Organization {\n profiles(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n totalCount\n edges {\n node {\n id\n ...PeopleListItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment PeopleListItemFragment on Profile {\n id\n fullName\n kind\n position\n membership {\n id\n role\n source\n state\n canUpdate: permission(action: \"iam:membership:update\")\n }\n identity {\n email\n id\n }\n createdAt\n canUpdate: permission(action: \"iam:membership-profile:update\")\n canDelete: permission(action: \"iam:membership-profile:delete\")\n}\n" + "text": "query PeopleListFragment_RefetchQuery(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 20\n $last: Int = null\n $order: ProfileOrder = {direction: ASC, field: FULL_NAME}\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...PeopleListFragment_16fISc\n id\n }\n}\n\nfragment PeopleListFragment_16fISc on Organization {\n profiles(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n totalCount\n edges {\n node {\n id\n ...PeopleListItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment PeopleListItemFragment on Profile {\n id\n fullName\n kind\n position\n membership {\n id\n role\n source\n state\n canUpdate: permission(action: \"iam:membership:update\")\n canDelete: permission(action: \"iam:membership-profile:delete\")\n }\n identity {\n email\n id\n }\n createdAt\n canUpdate: permission(action: \"iam:membership-profile:update\")\n}\n" } }; })(); diff --git a/apps/console/src/__generated__/iam/PeopleListItemFragment.graphql.ts b/apps/console/src/__generated__/iam/PeopleListItemFragment.graphql.ts index 1bb81efc7..4be288a77 100644 --- a/apps/console/src/__generated__/iam/PeopleListItemFragment.graphql.ts +++ b/apps/console/src/__generated__/iam/PeopleListItemFragment.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<<6e9b0a4fbf6ce65f11f2367f2ec5b6d4>> * @lightSyntaxTransform * @nogrep */ @@ -15,7 +15,6 @@ export type MembershipState = "ACTIVE" | "INACTIVE"; export type ProfileKind = "CONTRACTOR" | "EMPLOYEE" | "SERVICE_ACCOUNT"; import { FragmentRefs } from "relay-runtime"; export type PeopleListItemFragment$data = { - readonly canDelete: boolean; readonly canUpdate: boolean; readonly createdAt: string; readonly fullName: string; @@ -25,6 +24,7 @@ export type PeopleListItemFragment$data = { }; readonly kind: ProfileKind; readonly membership: { + readonly canDelete: boolean; readonly canUpdate: boolean; readonly id: string; readonly role: MembershipRole; @@ -119,6 +119,19 @@ return { "kind": "ScalarField", "name": "permission", "storageKey": "permission(action:\"iam:membership:update\")" + }, + { + "alias": "canDelete", + "args": [ + { + "kind": "Literal", + "name": "action", + "value": "iam:membership-profile:delete" + } + ], + "kind": "ScalarField", + "name": "permission", + "storageKey": "permission(action:\"iam:membership-profile:delete\")" } ], "storageKey": null @@ -166,19 +179,6 @@ return { "kind": "ScalarField", "name": "permission", "storageKey": "permission(action:\"iam:membership-profile:update\")" - }, - { - "alias": "canDelete", - "args": [ - { - "kind": "Literal", - "name": "action", - "value": "iam:membership-profile:delete" - } - ], - "kind": "ScalarField", - "name": "permission", - "storageKey": "permission(action:\"iam:membership-profile:delete\")" } ], "type": "Profile", @@ -186,6 +186,6 @@ return { }; })(); -(node as any).hash = "5f45ee7c58d50b0a6569c3bd22ffc83f"; +(node as any).hash = "89702fbdf02294212269adb22efb65fe"; export default node; diff --git a/apps/console/src/__generated__/iam/PeoplePageQuery.graphql.ts b/apps/console/src/__generated__/iam/PeoplePageQuery.graphql.ts index 998c2ee14..90cf540cd 100644 --- a/apps/console/src/__generated__/iam/PeoplePageQuery.graphql.ts +++ b/apps/console/src/__generated__/iam/PeoplePageQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<53070e63046901877444d5de41cc8624>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -396,6 +396,19 @@ return { "kind": "ScalarField", "name": "permission", "storageKey": "permission(action:\"iam:membership:update\")" + }, + { + "alias": "canDelete", + "args": [ + { + "kind": "Literal", + "name": "action", + "value": "iam:membership-profile:delete" + } + ], + "kind": "ScalarField", + "name": "permission", + "storageKey": "permission(action:\"iam:membership-profile:delete\")" } ], "storageKey": null @@ -427,19 +440,6 @@ return { "name": "permission", "storageKey": "permission(action:\"iam:membership-profile:update\")" }, - { - "alias": "canDelete", - "args": [ - { - "kind": "Literal", - "name": "action", - "value": "iam:membership-profile:delete" - } - ], - "kind": "ScalarField", - "name": "permission", - "storageKey": "permission(action:\"iam:membership-profile:delete\")" - }, (v2/*: any*/) ], "storageKey": null @@ -552,12 +552,12 @@ return { ] }, "params": { - "cacheID": "d13993ce884669894f65925a714aeaa6", + "cacheID": "4303ec6c2fa72181d747bb9053446aaa", "id": null, "metadata": {}, "name": "PeoplePageQuery", "operationKind": "query", - "text": "query PeoplePageQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n canInviteUser: permission(action: \"iam:invitation:create\")\n profiles(first: 20, orderBy: {direction: ASC, field: FULL_NAME}) {\n totalCount\n }\n ...PeopleListFragment_8lnpd\n invitations(first: 20, orderBy: {direction: DESC, field: CREATED_AT}) {\n totalCount\n __typename\n }\n ...InvitationListFragment_1PypFi\n }\n id\n }\n}\n\nfragment InvitationListFragment_1PypFi on Organization {\n invitations(first: 20, orderBy: {direction: DESC, field: CREATED_AT}) {\n edges {\n node {\n id\n ...InvitationListItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment InvitationListItemFragment on Invitation {\n id\n fullName\n email\n role\n status\n createdAt\n acceptedAt\n canDelete: permission(action: \"iam:invitation:delete\")\n}\n\nfragment PeopleListFragment_8lnpd on Organization {\n profiles(first: 20, orderBy: {direction: ASC, field: FULL_NAME}) {\n totalCount\n edges {\n node {\n id\n ...PeopleListItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment PeopleListItemFragment on Profile {\n id\n fullName\n kind\n position\n membership {\n id\n role\n source\n state\n canUpdate: permission(action: \"iam:membership:update\")\n }\n identity {\n email\n id\n }\n createdAt\n canUpdate: permission(action: \"iam:membership-profile:update\")\n canDelete: permission(action: \"iam:membership-profile:delete\")\n}\n" + "text": "query PeoplePageQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n canInviteUser: permission(action: \"iam:invitation:create\")\n profiles(first: 20, orderBy: {direction: ASC, field: FULL_NAME}) {\n totalCount\n }\n ...PeopleListFragment_8lnpd\n invitations(first: 20, orderBy: {direction: DESC, field: CREATED_AT}) {\n totalCount\n __typename\n }\n ...InvitationListFragment_1PypFi\n }\n id\n }\n}\n\nfragment InvitationListFragment_1PypFi on Organization {\n invitations(first: 20, orderBy: {direction: DESC, field: CREATED_AT}) {\n edges {\n node {\n id\n ...InvitationListItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment InvitationListItemFragment on Invitation {\n id\n fullName\n email\n role\n status\n createdAt\n acceptedAt\n canDelete: permission(action: \"iam:invitation:delete\")\n}\n\nfragment PeopleListFragment_8lnpd on Organization {\n profiles(first: 20, orderBy: {direction: ASC, field: FULL_NAME}) {\n totalCount\n edges {\n node {\n id\n ...PeopleListItemFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment PeopleListItemFragment on Profile {\n id\n fullName\n kind\n position\n membership {\n id\n role\n source\n state\n canUpdate: permission(action: \"iam:membership:update\")\n canDelete: permission(action: \"iam:membership-profile:delete\")\n }\n identity {\n email\n id\n }\n createdAt\n canUpdate: permission(action: \"iam:membership-profile:update\")\n}\n" } }; })(); diff --git a/apps/console/src/__generated__/iam/PersonPageQuery.graphql.ts b/apps/console/src/__generated__/iam/PersonPageQuery.graphql.ts index ec6c7a476..e3ff4cad0 100644 --- a/apps/console/src/__generated__/iam/PersonPageQuery.graphql.ts +++ b/apps/console/src/__generated__/iam/PersonPageQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<405fdde1b279cf7336ca395457041b93>> + * @generated SignedSource<<2e75f19fcdaf2b78a187c33458c9cfe0>> * @lightSyntaxTransform * @nogrep */ @@ -23,7 +23,7 @@ export type PersonPageQuery$data = { }; readonly membership: { readonly id: string; - } | null | undefined; + }; readonly " $fragmentSpreads": FragmentRefs<"PersonFormFragment">; } | { // This will never be '%other', but we need some @@ -126,7 +126,11 @@ return { "kind": "InlineFragment", "selections": [ (v3/*: any*/), - (v5/*: any*/), + { + "kind": "RequiredField", + "field": (v5/*: any*/), + "action": "THROW" + }, { "kind": "RequiredField", "field": { @@ -265,6 +269,6 @@ return { }; })(); -(node as any).hash = "24863216f5484c1fae7ff6ad002a7f8c"; +(node as any).hash = "5dce91fda9224f6f3928d4549485d3bd"; export default node; diff --git a/apps/console/src/pages/iam/memberships/MembershipsPage.tsx b/apps/console/src/pages/iam/memberships/MembershipsPage.tsx index 971e77e97..e6b1f3dc5 100644 --- a/apps/console/src/pages/iam/memberships/MembershipsPage.tsx +++ b/apps/console/src/pages/iam/memberships/MembershipsPage.tsx @@ -18,19 +18,22 @@ import { MembershipCard } from "./_components/MembershipCard"; export const membershipsPageQuery = graphql` query MembershipsPageQuery { viewer @required(action: THROW) { - memberships( + profiles( first: 1000 orderBy: { direction: ASC, field: ORGANIZATION_NAME } ) - @connection(key: "MembershipsPage_memberships") + @connection(key: "MembershipsPage_profiles") @required(action: THROW) { __id edges @required(action: THROW) { node { id + membership @required(action: THROW) { ...MembershipCardFragment + } organization @required(action: THROW) { name + ...MembershipCard_organizationFragment } } } @@ -64,7 +67,7 @@ export function MembershipsPage(props: { const { queryRef } = props; const { viewer: { - memberships: { __id: membershipConnectionId, edges: initialMemberships }, + profiles: { edges: initialProfiles }, pendingInvitations: { __id: pendingInvitationsConnectionId, edges: invitations, @@ -72,14 +75,14 @@ export function MembershipsPage(props: { }, } = usePreloadedQuery(membershipsPageQuery, queryRef); - const memberships = useMemo(() => { + const profiles = useMemo(() => { if (!search.trim()) { - return initialMemberships; + return initialProfiles; } - return initialMemberships.filter(({ node }) => + return initialProfiles.filter(({ node }) => node.organization.name.toLowerCase().includes(search.toLowerCase()), ); - }, [initialMemberships, search]); + }, [initialProfiles, search]); return ( <> @@ -98,14 +101,13 @@ export function MembershipsPage(props: { pendingInvitationsConnectionId={ pendingInvitationsConnectionId } - membershipConnectionId={membershipConnectionId} key={node.id} fKey={node} /> ))} )} - {initialMemberships.length > 0 && ( + {initialProfiles.length > 0 && (

{__("Your organizations")} @@ -118,15 +120,19 @@ export function MembershipsPage(props: { onValueChange={setSearch} />

- {memberships.length === 0 + {profiles.length === 0 ? (
{__("No organizations found")}
) : ( - memberships.map(({ node }) => ( - + profiles.map(({ node }) => ( + )) )} diff --git a/apps/console/src/pages/iam/memberships/_components/InvitationCard.tsx b/apps/console/src/pages/iam/memberships/_components/InvitationCard.tsx index 776ccb673..230fab119 100644 --- a/apps/console/src/pages/iam/memberships/_components/InvitationCard.tsx +++ b/apps/console/src/pages/iam/memberships/_components/InvitationCard.tsx @@ -22,21 +22,17 @@ const fragment = graphql` const acceptMutation = graphql` mutation InvitationCardMutation( $input: AcceptInvitationInput! - $membershipConnections: [ID!]! + # FIXME: profiles + # $membershipConnections: [ID!]! $pendingInvitationConnections: [ID!]! ) { acceptInvitation(input: $input) { invitation { id @deleteEdge(connections: $pendingInvitationConnections) } - membershipEdge @prependEdge(connections: $membershipConnections) { - node { - id - ...MembershipCardFragment - organization { - name - } - } + membership { + id + ...MembershipCardFragment } } } @@ -44,12 +40,11 @@ const acceptMutation = graphql` interface InvitationCardProps { pendingInvitationsConnectionId: DataID; - membershipConnectionId: DataID; fKey: InvitationCardFragment$key; } export function InvitationCard(props: InvitationCardProps) { - const { pendingInvitationsConnectionId, membershipConnectionId, fKey } + const { pendingInvitationsConnectionId, fKey } = props; const { __ } = useTranslate(); @@ -67,7 +62,6 @@ export function InvitationCard(props: InvitationCardProps) { invitationId: invitation.id, }, pendingInvitationConnections: [pendingInvitationsConnectionId], - membershipConnections: [membershipConnectionId], }, onCompleted: (_, e) => { if (e) { diff --git a/apps/console/src/pages/iam/memberships/_components/MembershipCard.tsx b/apps/console/src/pages/iam/memberships/_components/MembershipCard.tsx index a09af7836..ed0e5834e 100644 --- a/apps/console/src/pages/iam/memberships/_components/MembershipCard.tsx +++ b/apps/console/src/pages/iam/memberships/_components/MembershipCard.tsx @@ -13,6 +13,7 @@ import { useFragment } from "react-relay"; import { Link } from "react-router"; import { graphql } from "relay-runtime"; +import type { MembershipCard_organizationFragment$key } from "#/__generated__/iam/MembershipCard_organizationFragment.graphql"; import type { MembershipCardFragment$key } from "#/__generated__/iam/MembershipCardFragment.graphql"; const fragment = graphql` @@ -21,26 +22,34 @@ const fragment = graphql` id expiresAt } - organization @required(action: THROW) { - id - name - logoUrl - } + } +`; + +const organizationFragment = graphql` + fragment MembershipCard_organizationFragment on Organization { + id + name + logoUrl } `; interface MembershipCardProps { fKey: MembershipCardFragment$key; + organizationFragmentRef: MembershipCard_organizationFragment$key; } export function MembershipCard(props: MembershipCardProps) { - const { fKey } = props; + const { fKey, organizationFragmentRef } = props; const { __ } = useTranslate(); - const { lastSession, organization } = useFragment( + const { lastSession } = useFragment( fragment, fKey, ); + const organization = useFragment( + organizationFragment, + organizationFragmentRef, + ); const isExpired = lastSession && parseDate(lastSession.expiresAt) < new Date(); const isAssuming = !!lastSession && !isExpired; diff --git a/apps/console/src/pages/iam/organizations/ViewerMembershipLayout.tsx b/apps/console/src/pages/iam/organizations/ViewerMembershipLayout.tsx index 05b1181c7..d946a1181 100644 --- a/apps/console/src/pages/iam/organizations/ViewerMembershipLayout.tsx +++ b/apps/console/src/pages/iam/organizations/ViewerMembershipLayout.tsx @@ -23,10 +23,10 @@ export const viewerMembershipLayoutQuery = graphql` ...MembershipsDropdown_organizationFragment ...ViewerMembershipDropdownFragment ...SidebarFragment @skip(if: $hideSidebar) - viewerMembership @required(action: THROW) { - role - profile @required(action: THROW) { - fullName + viewer @required(action: THROW) { + fullName + membership @required(action: THROW) { + role } } } @@ -91,11 +91,11 @@ export function ViewerMembershipLayout(props: { - + diff --git a/apps/console/src/pages/iam/organizations/_components/MembershipsDropdownMenu.tsx b/apps/console/src/pages/iam/organizations/_components/MembershipsDropdownMenu.tsx index 70d896768..17cb5a3ac 100644 --- a/apps/console/src/pages/iam/organizations/_components/MembershipsDropdownMenu.tsx +++ b/apps/console/src/pages/iam/organizations/_components/MembershipsDropdownMenu.tsx @@ -8,7 +8,7 @@ import { MembershipsDropdownMenuItem } from "./MembershipsDropdownMenuItem"; export const membershipsDropdownMenuQuery = graphql` query MembershipsDropdownMenuQuery { viewer @required(action: THROW) { - memberships( + profiles( first: 1000 orderBy: { direction: ASC, field: ORGANIZATION_NAME } ) @required(action: THROW) { @@ -17,8 +17,11 @@ export const membershipsDropdownMenuQuery = graphql` id organization @required(action: THROW) { name + ...MembershipsDropdownMenuItem_organizationFragment + } + membership @required(action: THROW) { + ...MembershipsDropdownMenuItemFragment } - ...MembershipsDropdownMenuItemFragment } } } @@ -36,27 +39,27 @@ export function MembershipsDropdownMenu(props: MembershipsDropdownMenuProps) { const { viewer: { - memberships: { edges: initialMemberships }, + profiles: { edges: initialProfiles }, }, } = usePreloadedQuery( membershipsDropdownMenuQuery, queryRef, ); - const memberships = useMemo(() => { + const profiles = useMemo(() => { if (!search) { - return initialMemberships; + return initialProfiles; } - return initialMemberships.filter(({ node: { organization } }) => + return initialProfiles.filter(({ node: { organization } }) => organization.name.toLowerCase().includes(search.toLowerCase()), ); - }, [initialMemberships, search]); + }, [initialProfiles, search]); return ( <> - {memberships.map(({ node }) => ( - + {profiles.map(({ node }) => ( + ))} ); diff --git a/apps/console/src/pages/iam/organizations/_components/MembershipsDropdownMenuItem.tsx b/apps/console/src/pages/iam/organizations/_components/MembershipsDropdownMenuItem.tsx index 2614f89a5..38f895521 100644 --- a/apps/console/src/pages/iam/organizations/_components/MembershipsDropdownMenuItem.tsx +++ b/apps/console/src/pages/iam/organizations/_components/MembershipsDropdownMenuItem.tsx @@ -10,6 +10,7 @@ import { useFragment } from "react-relay"; import { Link } from "react-router"; import { graphql } from "relay-runtime"; +import type { MembershipsDropdownMenuItem_organizationFragment$key } from "#/__generated__/iam/MembershipsDropdownMenuItem_organizationFragment.graphql"; import type { MembershipsDropdownMenuItemFragment$key } from "#/__generated__/iam/MembershipsDropdownMenuItemFragment.graphql"; const fragment = graphql` @@ -19,21 +20,27 @@ const fragment = graphql` id expiresAt } - organization @required(action: THROW) { - id - logoUrl - name - } + } +`; + +const organizationFragment = graphql` + fragment MembershipsDropdownMenuItem_organizationFragment on Organization { + id + name + logoUrl } `; export function MembershipsDropdownMenuItem(props: { fKey: MembershipsDropdownMenuItemFragment$key; + organizationFragmentRef: MembershipsDropdownMenuItem_organizationFragment$key; }) { - const { fKey } = props; + const { fKey, organizationFragmentRef } = props; - const { id, lastSession, organization } + const { id, lastSession } = useFragment(fragment, fKey); + const organization + = useFragment(organizationFragment, organizationFragmentRef); const isAssuming = !!lastSession; const isExpired diff --git a/apps/console/src/pages/iam/organizations/_components/ViewerMembershipDropdown.tsx b/apps/console/src/pages/iam/organizations/_components/ViewerMembershipDropdown.tsx index a0c5a7136..e9f043d48 100644 --- a/apps/console/src/pages/iam/organizations/_components/ViewerMembershipDropdown.tsx +++ b/apps/console/src/pages/iam/organizations/_components/ViewerMembershipDropdown.tsx @@ -14,18 +14,17 @@ import { useFragment, useMutation } from "react-relay"; import { graphql } from "relay-runtime"; import type { ViewerMembershipDropdownFragment$key } from "#/__generated__/iam/ViewerMembershipDropdownFragment.graphql"; +import type { ViewerMembershipDropdownSignOutMutation } from "#/__generated__/iam/ViewerMembershipDropdownSignOutMutation.graphql"; import { useOrganizationId } from "#/hooks/useOrganizationId"; export const fragment = graphql` fragment ViewerMembershipDropdownFragment on Organization { - viewerMembership @required(action: THROW) { + viewer @required(action: THROW) { + fullName identity @required(action: THROW) { email canListAPIKeys: permission(action: "iam:personal-api-key:list") } - profile @required(action: THROW) { - fullName - } } } `; @@ -48,12 +47,12 @@ export function ViewerMembershipDropdown(props: { const { toast } = useToast(); const { - viewerMembership: { + viewer: { + fullName, identity: { canListAPIKeys, email }, - profile: { fullName }, }, } = useFragment(fragment, fKey); - const [signOut] = useMutation(signOutMutation); + const [signOut] = useMutation(signOutMutation); const handleLogout: React.MouseEventHandler = (e) => { e.preventDefault(); diff --git a/apps/console/src/pages/iam/organizations/people/PeoplePage.tsx b/apps/console/src/pages/iam/organizations/people/PeoplePage.tsx index 50ec100a3..88ee3e697 100644 --- a/apps/console/src/pages/iam/organizations/people/PeoplePage.tsx +++ b/apps/console/src/pages/iam/organizations/people/PeoplePage.tsx @@ -17,7 +17,7 @@ export const peoplePageQuery = graphql` __typename ... on Organization { canInviteUser: permission(action: "iam:invitation:create") - members(first: 20, orderBy: { direction: ASC, field: FULL_NAME }) + profiles(first: 20, orderBy: { direction: ASC, field: FULL_NAME }) @required(action: THROW) { totalCount } @@ -50,8 +50,8 @@ export function PeoplePage(props: { const organizationId = useOrganizationId(); const { __ } = useTranslate(); - const [activeTab, setActiveTab] = useState<"memberships" | "invitations">( - "memberships", + const [activeTab, setActiveTab] = useState<"people" | "invitations">( + "people", ); const { organization } = usePreloadedQuery( @@ -85,12 +85,12 @@ export function PeoplePage(props: { setActiveTab("memberships")} + active={activeTab === "people"} + onClick={() => setActiveTab("people")} > - {__("Members")} - {(organization.members.totalCount ?? 0) > 0 && ( - {organization.members.totalCount} + {__("People")} + {(organization.profiles.totalCount ?? 0) > 0 && ( + {organization.profiles.totalCount} )}
- {activeTab === "memberships" && } + {activeTab === "people" && } {activeTab === "invitations" && ( }) const navigate = useNavigate(); const { person } = usePreloadedQuery(personPageQuery, queryRef); - if (person.__typename !== "MembershipProfile") { + if (person.__typename !== "Profile") { throw new Error("invalid type for node"); } @@ -65,7 +67,7 @@ export function PersonPage(props: { queryRef: PreloadedQuery }) return removeMembership({ variables: { input: { - membershipId: person.membershipId, + membershipId: person.membership.id, organizationId: organizationId, }, }, diff --git a/apps/console/src/pages/iam/organizations/people/_components/PeopleList.tsx b/apps/console/src/pages/iam/organizations/people/_components/PeopleList.tsx index 8586748ac..5298620a2 100644 --- a/apps/console/src/pages/iam/organizations/people/_components/PeopleList.tsx +++ b/apps/console/src/pages/iam/organizations/people/_components/PeopleList.tsx @@ -15,20 +15,20 @@ const fragment = graphql` @argumentDefinitions( first: { type: "Int", defaultValue: 20 } order: { - type: "MembershipOrder" + type: "ProfileOrder" defaultValue: { direction: ASC, field: FULL_NAME } } after: { type: "CursorKey", defaultValue: null } before: { type: "CursorKey", defaultValue: null } last: { type: "Int", defaultValue: null } ) { - members( + profiles( first: $first after: $after last: $last before: $before orderBy: $order - ) @connection(key: "PeopleListFragment_members", filters: ["orderBy"]) @required(action: THROW) { + ) @connection(key: "PeopleListFragment_profiles", filters: ["orderBy"]) @required(action: THROW) { __id totalCount edges @required(action: THROW) { @@ -51,7 +51,7 @@ export function PeopleList(props: { fKey: PeopleListFragment$key }) { PeopleListFragment$key >(fragment, fKey); - const refetchMemberships = () => { + const refetchPeople = () => { peoplePagination.refetch({}, { fetchPolicy: "network-only" }); }; @@ -77,21 +77,21 @@ export function PeopleList(props: { fKey: PeopleListFragment$key }) { - {peoplePagination.data.members.totalCount === 0 + {peoplePagination.data.profiles.totalCount === 0 ? ( - {__("No members")} + {__("No people")} ) : ( - peoplePagination.data.members.edges.map(({ node: membership }) => ( + peoplePagination.data.profiles.edges.map(({ node: profile }) => ( )) )} diff --git a/apps/console/src/pages/iam/organizations/people/_components/PeopleListItem.tsx b/apps/console/src/pages/iam/organizations/people/_components/PeopleListItem.tsx index b9b37524e..99afcd4c9 100644 --- a/apps/console/src/pages/iam/organizations/people/_components/PeopleListItem.tsx +++ b/apps/console/src/pages/iam/organizations/people/_components/PeopleListItem.tsx @@ -22,23 +22,24 @@ import { useOrganizationId } from "#/hooks/useOrganizationId"; import { EditMemberDialog } from "../../settings/_components/EditMemberDialog"; const fragment = graphql` - fragment PeopleListItemFragment on Membership { + fragment PeopleListItemFragment on Profile { id - role - source - state - profile @required(action: THROW) { + fullName + kind + position + membership @required(action: THROW) { id - fullName - kind - position + role + source + state + canUpdate: permission(action: "iam:membership:update") + canDelete: permission(action: "iam:membership-profile:delete") } identity @required(action: THROW) { email } createdAt - canUpdate: permission(action: "iam:membership:update") - canDelete: permission(action: "iam:membership:delete") + canUpdate: permission(action: "iam:membership-profile:update") } `; @@ -65,9 +66,9 @@ export function MemberListItem(props: { const confirm = useConfirm(); const [dialogOpen, setDialogOpen] = useState(false); - const membership = useFragment(fragment, fKey); + const profile = useFragment(fragment, fKey); - const isInactive = membership.state === "INACTIVE"; + const isInactive = profile.membership.state === "INACTIVE"; const [removeMembership, isRemoving] = useMutationWithToasts( removeMemberMutation, @@ -83,7 +84,7 @@ export function MemberListItem(props: { return removeMembership({ variables: { input: { - membershipId: membership.id, + membershipId: profile.membership.id, organizationId: organizationId, }, connections: [connectionId], @@ -93,7 +94,7 @@ export function MemberListItem(props: { { message: sprintf( __("Are you sure you want to remove %s?"), - membership.profile.fullName, + profile.fullName, ), }, ); @@ -106,33 +107,33 @@ export function MemberListItem(props: { isRemoving && "opacity-60 pointer-events-none", isInactive && "opacity-50", )} - to={`/organizations/${organizationId}/people/${membership.profile.id}`} + to={`/organizations/${organizationId}/people/${profile.id}`} >
- {membership.profile.fullName} + {profile.fullName} {isInactive && {__("Inactive")}}
- {membership.identity.email} - {membership.source} + {profile.identity.email} + {profile.membership.source}
- {membership.profile.kind} + {profile.kind} - {membership.role} + {profile.membership.role} - {new Date(membership.createdAt).toLocaleDateString()} - {membership.profile.position} + {new Date(profile.createdAt).toLocaleDateString()} + {profile.position} {!isInactive && (
e.stopPropagation()} > - {membership.canUpdate && ( + {profile.membership.canUpdate && (