diff --git a/apps/console/src/pages/iam/organizations/settings/_components/GoogleWorkspaceConnector.tsx b/apps/console/src/pages/iam/organizations/settings/_components/GoogleWorkspaceConnector.tsx index a6d6c26cd..3270efb00 100644 --- a/apps/console/src/pages/iam/organizations/settings/_components/GoogleWorkspaceConnector.tsx +++ b/apps/console/src/pages/iam/organizations/settings/_components/GoogleWorkspaceConnector.tsx @@ -23,6 +23,7 @@ import { DialogFooter, Google, IconSettingsGear2, + IconWarning, Input, useDialogRef, useToast, @@ -38,9 +39,18 @@ import { useOrganizationId } from "#/hooks/useOrganizationId"; const googleWorkspaceConnectorFragment = graphql` fragment GoogleWorkspaceConnectorFragment on SCIMConfiguration { id + events(first: 1) { + edges { + node { + id + errorMessage + } + } + } bridge { id type + state excludedUserNames connector { id @@ -83,9 +93,26 @@ export function GoogleWorkspaceConnector(props: { const connector = bridge?.connector; const scimConfigurationId = data?.id; const bridgeId = bridge?.id; - const organizationId = useOrganizationId(); const { __, dateTimeFormat } = useTranslate(); + const bridgeState = bridge?.state ?? null; + const latestBridgeError = data?.events?.edges?.[0]?.node?.errorMessage ?? null; + const isBridgeFailed = bridgeState === "FAILED"; + const isBridgePending = bridgeState === "PENDING"; + const bridgeStatusBadgeVariant = isBridgeFailed + ? "danger" + : isBridgePending + ? "warning" + : "success"; + const bridgeStatusLabel = isBridgeFailed + ? __("Error") + : isBridgePending + ? __("Syncing") + : __("Connected"); + const bridgeErrorMessage = latestBridgeError + ?? __( + "The bridge is currently failing to sync. Check the provisioning event history for more details.", + ); const { toast } = useToast(); const dialogRef = useDialogRef(); const excludedUserNamesDialogRef = useDialogRef(); @@ -225,118 +252,134 @@ export function GoogleWorkspaceConnector(props: { // Connected state return ( - -
- -
-
-

{__("Google Workspace / Cloud Identity")}

-

- {sprintf(__("Connected on %s"), dateTimeFormat(connector.createdAt))} -

-
- - {__("Connected")} - - - - {__("Settings")} - - )} - title={__("Google Workspace / Cloud Identity Settings")} - className="max-w-lg" - > - -
-
-

{__("Excluded user names")}

-

- {__("Users with these user names will not be synced from Google Workspace / Cloud Identity.")} -

-
-
- setNewUser(e.target.value)} - onKeyDown={(e) => { - if (e.key === "Enter") { - e.preventDefault(); - if (isUpdating) return; - handleAddUser(); - } - }} - placeholder="user@example.com" - className="flex-1" - /> - -
- - {currentExcludedUserNames.length > 0 && ( -
- {currentExcludedUserNames.map((user: string) => ( -
- {user} - -
- ))} + +
+
+ +
+
+

{__("Google Workspace / Cloud Identity")}

+

+ {sprintf(__("Connected on %s"), dateTimeFormat(connector.createdAt))} +

+
+ + {bridgeStatusLabel} + + + + {__("Settings")} + + )} + title={__("Google Workspace / Cloud Identity Settings")} + className="max-w-lg" + > + +
+
+

{__("Excluded user names")}

+

+ {__("Users with these user names will not be synced from Google Workspace / Cloud Identity.")} +

+
+
+ setNewUser(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") { + e.preventDefault(); + if (isUpdating) return; + handleAddUser(); + } + }} + placeholder="user@example.com" + className="flex-1" + /> +
- )} - {currentExcludedUserNames.length === 0 && ( -

- {__("No excluded user names. All Google Workspace / Cloud Identity users will be synced.")} -

- )} + {currentExcludedUserNames.length > 0 && ( +
+ {currentExcludedUserNames.map((user: string) => ( +
+ {user} + +
+ ))} +
+ )} + + {currentExcludedUserNames.length === 0 && ( +

+ {__("No excluded user names. All Google Workspace / Cloud Identity users will be synced.")} +

+ )} +
+
+
+ + {__("Disconnect")} + + )} + title={__("Disconnect Google Workspace / Cloud Identity")} + className="max-w-lg" + > + +

+ {__( + "This will disconnect your Google Workspace / Cloud Identity integration. Users will no longer be automatically synced via SCIM.", + )} +

+

+ {__("This action cannot be undone.")} +

+
+ + + +
+
+ + {isBridgeFailed && ( +
+ +
+

+ {__("Bridge is in an error state")} +

+

+ {bridgeErrorMessage} +

- -
- - {__("Disconnect")} - - )} - title={__("Disconnect Google Workspace / Cloud Identity")} - className="max-w-lg" - > - -

- {__( - "This will disconnect your Google Workspace / Cloud Identity integration. Users will no longer be automatically synced via SCIM.", - )} -

-

- {__("This action cannot be undone.")} -

-
- - - -
+ + )}
); } diff --git a/apps/console/src/pages/iam/organizations/settings/_components/Microsoft365Connector.tsx b/apps/console/src/pages/iam/organizations/settings/_components/Microsoft365Connector.tsx index ee55aa660..c0a32e9c5 100644 --- a/apps/console/src/pages/iam/organizations/settings/_components/Microsoft365Connector.tsx +++ b/apps/console/src/pages/iam/organizations/settings/_components/Microsoft365Connector.tsx @@ -22,6 +22,7 @@ import { DialogContent, DialogFooter, IconSettingsGear2, + IconWarning, Input, Microsoft, useDialogRef, @@ -38,9 +39,18 @@ import { useOrganizationId } from "#/hooks/useOrganizationId"; const microsoft365ConnectorFragment = graphql` fragment Microsoft365ConnectorFragment on SCIMConfiguration { id + events(first: 1) { + edges { + node { + id + errorMessage + } + } + } bridge { id type + state excludedUserNames connector { id @@ -83,9 +93,26 @@ export function Microsoft365Connector(props: { const connector = bridge?.connector; const scimConfigurationId = data?.id; const bridgeId = bridge?.id; - const organizationId = useOrganizationId(); const { __, dateTimeFormat } = useTranslate(); + const bridgeState = bridge?.state ?? null; + const latestBridgeError = data?.events?.edges?.[0]?.node?.errorMessage ?? null; + const isBridgeFailed = bridgeState === "FAILED"; + const isBridgePending = bridgeState === "PENDING"; + const bridgeStatusBadgeVariant = isBridgeFailed + ? "danger" + : isBridgePending + ? "warning" + : "success"; + const bridgeStatusLabel = isBridgeFailed + ? __("Error") + : isBridgePending + ? __("Syncing") + : __("Connected"); + const bridgeErrorMessage = latestBridgeError + ?? __( + "The bridge is currently failing to sync. Check the provisioning event history for more details.", + ); const { toast } = useToast(); const dialogRef = useDialogRef(); const excludedUserNamesDialogRef = useDialogRef(); @@ -223,118 +250,134 @@ export function Microsoft365Connector(props: { } return ( - -
- -
-
-

{__("Microsoft 365")}

-

- {sprintf(__("Connected on %s"), dateTimeFormat(connector.createdAt))} -

-
- - {__("Connected")} - - - - {__("Settings")} - - )} - title={__("Microsoft 365 Settings")} - className="max-w-lg" - > - -
-
-

{__("Excluded user names")}

-

- {__("Users with these user names will not be synced from Microsoft 365.")} -

-
-
- setNewUser(e.target.value)} - onKeyDown={(e) => { - if (e.key === "Enter") { - e.preventDefault(); - if (isUpdating) return; - handleAddUser(); - } - }} - placeholder="user@example.com" - className="flex-1" - /> - -
- - {currentExcludedUserNames.length > 0 && ( -
- {currentExcludedUserNames.map((user: string) => ( -
- {user} - -
- ))} + +
+
+ +
+
+

{__("Microsoft 365")}

+

+ {sprintf(__("Connected on %s"), dateTimeFormat(connector.createdAt))} +

+
+ + {bridgeStatusLabel} + + + + {__("Settings")} + + )} + title={__("Microsoft 365 Settings")} + className="max-w-lg" + > + +
+
+

{__("Excluded user names")}

+

+ {__("Users with these user names will not be synced from Microsoft 365.")} +

+
+
+ setNewUser(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") { + e.preventDefault(); + if (isUpdating) return; + handleAddUser(); + } + }} + placeholder="user@example.com" + className="flex-1" + /> +
- )} - {currentExcludedUserNames.length === 0 && ( -

- {__("No excluded user names. All Microsoft 365 users will be synced.")} -

- )} + {currentExcludedUserNames.length > 0 && ( +
+ {currentExcludedUserNames.map((user: string) => ( +
+ {user} + +
+ ))} +
+ )} + + {currentExcludedUserNames.length === 0 && ( +

+ {__("No excluded user names. All Microsoft 365 users will be synced.")} +

+ )} +
+
+
+ + {__("Disconnect")} + + )} + title={__("Disconnect Microsoft 365")} + className="max-w-lg" + > + +

+ {__( + "This will disconnect your Microsoft 365 integration. Users will no longer be automatically synced via SCIM.", + )} +

+

+ {__("This action cannot be undone.")} +

+
+ + + +
+
+ + {isBridgeFailed && ( +
+ +
+

+ {__("Bridge is in an error state")} +

+

+ {bridgeErrorMessage} +

- -
- - {__("Disconnect")} - - )} - title={__("Disconnect Microsoft 365")} - className="max-w-lg" - > - -

- {__( - "This will disconnect your Microsoft 365 integration. Users will no longer be automatically synced via SCIM.", - )} -

-

- {__("This action cannot be undone.")} -

-
- - - -
+ + )}
); }