From 8b8e6719535f4fa72f2a6cd0efd5e237d8478e95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20R=C3=A9?= Date: Sat, 20 Dec 2025 00:43:54 +0100 Subject: [PATCH] Plug back new org CTA in org dropdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Émile Ré --- .../iam/organizations/OrganizationLayout.tsx | 28 ++++++-- .../organizations/OrganizationLayoutQuery.tsx | 7 +- .../OrganizationLayoutQuery.graphql.ts | 72 ++++++++++++------- .../_components/OrganizationDropdown.tsx | 53 ++++++++++++-- ...nDropdown_organizationFragment.graphql.ts} | 16 ++--- ...nizationDropdown_viewerFragment.graphql.ts | 63 ++++++++++++++++ 6 files changed, 193 insertions(+), 46 deletions(-) rename apps/console/src/pages/iam/organizations/_components/__generated__/{OrganizationDropdownFragment.graphql.ts => OrganizationDropdown_organizationFragment.graphql.ts} (56%) create mode 100644 apps/console/src/pages/iam/organizations/_components/__generated__/OrganizationDropdown_viewerFragment.graphql.ts diff --git a/apps/console/src/pages/iam/organizations/OrganizationLayout.tsx b/apps/console/src/pages/iam/organizations/OrganizationLayout.tsx index 8f72a6a91..77aa480fc 100644 --- a/apps/console/src/pages/iam/organizations/OrganizationLayout.tsx +++ b/apps/console/src/pages/iam/organizations/OrganizationLayout.tsx @@ -4,8 +4,8 @@ import { type PreloadedQuery, } from "react-relay"; import { organizationLayoutQuery } from "./OrganizationLayoutQuery"; -import { Outlet } from "react-router"; -import { Layout, Skeleton } from "@probo/ui"; +import { Link, Outlet } from "react-router"; +import { Badge, Button, IconPeopleAdd, Layout, Skeleton } from "@probo/ui"; import { Sidebar } from "./_components/Sidebar"; import { OrganizationDropdown } from "./_components/OrganizationDropdown"; import { consoleEnvironment } from "/environments"; @@ -13,6 +13,7 @@ import type { OrganizationLayoutQuery } from "./__generated__/OrganizationLayout import { PermissionsProvider } from "/providers/PermissionsProvider"; import { SessionDropdown } from "./_components/SessionDropdown"; import { Suspense } from "react"; +import { useTranslate } from "@probo/i18n"; interface OrganizationLayoutProps { queryRef: PreloadedQuery; @@ -21,7 +22,9 @@ interface OrganizationLayoutProps { export default function OrganizationLayout(props: OrganizationLayoutProps) { const { queryRef } = props; - const data = usePreloadedQuery( + const { __ } = useTranslate(); + + const { organization, viewer } = usePreloadedQuery( organizationLayoutQuery, queryRef, ); @@ -32,10 +35,25 @@ export default function OrganizationLayout(props: OrganizationLayoutProps) { header={ <>
- + + {viewer.pendingInvitations.totalCount > 0 && ( + +
}> - + } diff --git a/apps/console/src/pages/iam/organizations/OrganizationLayoutQuery.tsx b/apps/console/src/pages/iam/organizations/OrganizationLayoutQuery.tsx index c55ee1dc7..5b28eb065 100644 --- a/apps/console/src/pages/iam/organizations/OrganizationLayoutQuery.tsx +++ b/apps/console/src/pages/iam/organizations/OrganizationLayoutQuery.tsx @@ -12,12 +12,13 @@ export const organizationLayoutQuery = graphql` query OrganizationLayoutQuery($organizationId: ID!) { organization: node(id: $organizationId) @required(action: THROW) { ... on Organization { - ...OrganizationDropdownFragment + ...OrganizationDropdown_organizationFragment } } viewer @required(action: THROW) { - pendingInvitations { - totalCount + ...OrganizationDropdown_viewerFragment + pendingInvitations @required(action: THROW) { + totalCount @required(action: THROW) } ...SessionDropdownFragment @arguments(organizationId: $organizationId) } diff --git a/apps/console/src/pages/iam/organizations/__generated__/OrganizationLayoutQuery.graphql.ts b/apps/console/src/pages/iam/organizations/__generated__/OrganizationLayoutQuery.graphql.ts index b13f9886e..6668b65a5 100644 --- a/apps/console/src/pages/iam/organizations/__generated__/OrganizationLayoutQuery.graphql.ts +++ b/apps/console/src/pages/iam/organizations/__generated__/OrganizationLayoutQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<1d82d885a77675a330799121edb5cb69>> + * @generated SignedSource<<09fbc762db2d00eb944a0ef9afe3756d>> * @lightSyntaxTransform * @nogrep */ @@ -15,13 +15,13 @@ export type OrganizationLayoutQuery$variables = { }; export type OrganizationLayoutQuery$data = { readonly organization: { - readonly " $fragmentSpreads": FragmentRefs<"OrganizationDropdownFragment">; + readonly " $fragmentSpreads": FragmentRefs<"OrganizationDropdown_organizationFragment">; }; readonly viewer: { readonly pendingInvitations: { - readonly totalCount: number | null | undefined; - } | null | undefined; - readonly " $fragmentSpreads": FragmentRefs<"SessionDropdownFragment">; + readonly totalCount: number; + }; + readonly " $fragmentSpreads": FragmentRefs<"OrganizationDropdown_viewerFragment" | "SessionDropdownFragment">; }; }; export type OrganizationLayoutQuery = { @@ -47,19 +47,8 @@ v1 = [ v2 = { "alias": null, "args": null, - "concreteType": "InvitationConnection", - "kind": "LinkedField", - "name": "pendingInvitations", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "totalCount", - "storageKey": null - } - ], + "kind": "ScalarField", + "name": "totalCount", "storageKey": null }, v3 = [ @@ -99,7 +88,7 @@ return { { "args": null, "kind": "FragmentSpread", - "name": "OrganizationDropdownFragment" + "name": "OrganizationDropdown_organizationFragment" } ], "type": "Organization", @@ -120,7 +109,31 @@ return { "name": "viewer", "plural": false, "selections": [ - (v2/*: any*/), + { + "args": null, + "kind": "FragmentSpread", + "name": "OrganizationDropdown_viewerFragment" + }, + { + "kind": "RequiredField", + "field": { + "alias": null, + "args": null, + "concreteType": "InvitationConnection", + "kind": "LinkedField", + "name": "pendingInvitations", + "plural": false, + "selections": [ + { + "kind": "RequiredField", + "field": (v2/*: any*/), + "action": "THROW" + } + ], + "storageKey": null + }, + "action": "THROW" + }, { "args": (v3/*: any*/), "kind": "FragmentSpread", @@ -182,7 +195,18 @@ return { "name": "viewer", "plural": false, "selections": [ - (v2/*: any*/), + { + "alias": null, + "args": null, + "concreteType": "InvitationConnection", + "kind": "LinkedField", + "name": "pendingInvitations", + "plural": false, + "selections": [ + (v2/*: any*/) + ], + "storageKey": null + }, { "alias": null, "args": null, @@ -223,16 +247,16 @@ return { ] }, "params": { - "cacheID": "0ebb386f89d19888f18a05c8dc573895", + "cacheID": "ca7f41668eb74024237351d0e076819e", "id": null, "metadata": {}, "name": "OrganizationLayoutQuery", "operationKind": "query", - "text": "query OrganizationLayoutQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n ...OrganizationDropdownFragment\n }\n id\n }\n viewer {\n pendingInvitations {\n totalCount\n }\n ...SessionDropdownFragment_4xMPKw\n id\n }\n}\n\nfragment OrganizationDropdownFragment on Organization {\n name\n}\n\nfragment SessionDropdownFragment_4xMPKw on Identity {\n email\n profileFor(organizationId: $organizationId) {\n firstName\n lastName\n id\n }\n}\n" + "text": "query OrganizationLayoutQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n ...OrganizationDropdown_organizationFragment\n }\n id\n }\n viewer {\n ...OrganizationDropdown_viewerFragment\n pendingInvitations {\n totalCount\n }\n ...SessionDropdownFragment_4xMPKw\n id\n }\n}\n\nfragment OrganizationDropdown_organizationFragment on Organization {\n name\n}\n\nfragment OrganizationDropdown_viewerFragment on Identity {\n pendingInvitations {\n totalCount\n }\n}\n\nfragment SessionDropdownFragment_4xMPKw on Identity {\n email\n profileFor(organizationId: $organizationId) {\n firstName\n lastName\n id\n }\n}\n" } }; })(); -(node as any).hash = "d3d91888eabb36cc450ca585757b21c7"; +(node as any).hash = "81b8eb0af21d649268e30c80d16226de"; export default node; diff --git a/apps/console/src/pages/iam/organizations/_components/OrganizationDropdown.tsx b/apps/console/src/pages/iam/organizations/_components/OrganizationDropdown.tsx index 141d49930..84149a5e9 100644 --- a/apps/console/src/pages/iam/organizations/_components/OrganizationDropdown.tsx +++ b/apps/console/src/pages/iam/organizations/_components/OrganizationDropdown.tsx @@ -1,12 +1,15 @@ import { useFragment, useQueryLoader } from "react-relay"; import { graphql } from "relay-runtime"; -import type { OrganizationDropdownFragment$key } from "./__generated__/OrganizationDropdownFragment.graphql"; import { + Badge, Button, Dropdown, + DropdownItem, DropdownSeparator, IconChevronGrabberVertical, IconMagnifyingGlass, + IconPeopleAdd, + IconPlusLarge, Input, } from "@probo/ui"; import { Suspense, useCallback, useState } from "react"; @@ -16,22 +19,43 @@ import { organizationDropdownMenuQuery, } from "./OrganizationDropdownMenu"; import type { OrganizationDropdownMenuQuery } from "./__generated__/OrganizationDropdownMenuQuery.graphql"; +import { Link } from "react-router"; +import type { OrganizationDropdown_organizationFragment$key } from "./__generated__/OrganizationDropdown_organizationFragment.graphql"; +import type { OrganizationDropdown_viewerFragment$key } from "./__generated__/OrganizationDropdown_viewerFragment.graphql"; -const fragment = graphql` - fragment OrganizationDropdownFragment on Organization { +const organizationFragment = graphql` + fragment OrganizationDropdown_organizationFragment on Organization { name } `; +const viewerFragment = graphql` + fragment OrganizationDropdown_viewerFragment on Identity { + pendingInvitations @required(action: THROW) { + totalCount @required(action: THROW) + } + } +`; export function OrganizationDropdown(props: { - fKey: OrganizationDropdownFragment$key; + organizationFKey: OrganizationDropdown_organizationFragment$key; + viewerFKey: OrganizationDropdown_viewerFragment$key; }) { - const { fKey } = props; + const { organizationFKey, viewerFKey } = props; const { __ } = useTranslate(); const [search, setSearch] = useState(""); - const currentOrganization = useFragment(fragment, fKey); + const currentOrganization = + useFragment( + organizationFragment, + organizationFKey, + ); + const { + pendingInvitations: { totalCount: pendingInvitationCount }, + } = useFragment( + viewerFragment, + viewerFKey, + ); const [queryRef, loadQuery] = useQueryLoader( organizationDropdownMenuQuery, ); @@ -83,6 +107,23 @@ export function OrganizationDropdown(props: { )} + {pendingInvitationCount > 0 && ( + + + + {__("Invitations")} + + {pendingInvitationCount} + + + + )} + + + + {__("Add organization")} + + ); diff --git a/apps/console/src/pages/iam/organizations/_components/__generated__/OrganizationDropdownFragment.graphql.ts b/apps/console/src/pages/iam/organizations/_components/__generated__/OrganizationDropdown_organizationFragment.graphql.ts similarity index 56% rename from apps/console/src/pages/iam/organizations/_components/__generated__/OrganizationDropdownFragment.graphql.ts rename to apps/console/src/pages/iam/organizations/_components/__generated__/OrganizationDropdown_organizationFragment.graphql.ts index cc9adbee3..57e4e86e4 100644 --- a/apps/console/src/pages/iam/organizations/_components/__generated__/OrganizationDropdownFragment.graphql.ts +++ b/apps/console/src/pages/iam/organizations/_components/__generated__/OrganizationDropdown_organizationFragment.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<06f02e43b64c316083067f5b3978a08a>> + * @generated SignedSource<<1d2465517ea2dcfa99fb3dd8757fe980>> * @lightSyntaxTransform * @nogrep */ @@ -10,20 +10,20 @@ import { ReaderFragment } from 'relay-runtime'; import { FragmentRefs } from "relay-runtime"; -export type OrganizationDropdownFragment$data = { +export type OrganizationDropdown_organizationFragment$data = { readonly name: string; - readonly " $fragmentType": "OrganizationDropdownFragment"; + readonly " $fragmentType": "OrganizationDropdown_organizationFragment"; }; -export type OrganizationDropdownFragment$key = { - readonly " $data"?: OrganizationDropdownFragment$data; - readonly " $fragmentSpreads": FragmentRefs<"OrganizationDropdownFragment">; +export type OrganizationDropdown_organizationFragment$key = { + readonly " $data"?: OrganizationDropdown_organizationFragment$data; + readonly " $fragmentSpreads": FragmentRefs<"OrganizationDropdown_organizationFragment">; }; const node: ReaderFragment = { "argumentDefinitions": [], "kind": "Fragment", "metadata": null, - "name": "OrganizationDropdownFragment", + "name": "OrganizationDropdown_organizationFragment", "selections": [ { "alias": null, @@ -37,6 +37,6 @@ const node: ReaderFragment = { "abstractKey": null }; -(node as any).hash = "9f19581f86736e6345912284668a8f25"; +(node as any).hash = "8e6d0c702f821670315b5bde55e2556a"; export default node; diff --git a/apps/console/src/pages/iam/organizations/_components/__generated__/OrganizationDropdown_viewerFragment.graphql.ts b/apps/console/src/pages/iam/organizations/_components/__generated__/OrganizationDropdown_viewerFragment.graphql.ts new file mode 100644 index 000000000..d5612284e --- /dev/null +++ b/apps/console/src/pages/iam/organizations/_components/__generated__/OrganizationDropdown_viewerFragment.graphql.ts @@ -0,0 +1,63 @@ +/** + * @generated SignedSource<> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ReaderFragment } from 'relay-runtime'; +import { FragmentRefs } from "relay-runtime"; +export type OrganizationDropdown_viewerFragment$data = { + readonly pendingInvitations: { + readonly totalCount: number; + }; + readonly " $fragmentType": "OrganizationDropdown_viewerFragment"; +}; +export type OrganizationDropdown_viewerFragment$key = { + readonly " $data"?: OrganizationDropdown_viewerFragment$data; + readonly " $fragmentSpreads": FragmentRefs<"OrganizationDropdown_viewerFragment">; +}; + +const node: ReaderFragment = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "OrganizationDropdown_viewerFragment", + "selections": [ + { + "kind": "RequiredField", + "field": { + "alias": null, + "args": null, + "concreteType": "InvitationConnection", + "kind": "LinkedField", + "name": "pendingInvitations", + "plural": false, + "selections": [ + { + "kind": "RequiredField", + "field": { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "totalCount", + "storageKey": null + }, + "action": "THROW" + } + ], + "storageKey": null + }, + "action": "THROW" + } + ], + "type": "Identity", + "abstractKey": null +}; + +(node as any).hash = "ce947f812c95b74e9f250b8d5821ebb0"; + +export default node;