Show bridge errors in connector cards
Display SCIM bridge state directly on Google Workspace and Microsoft 365 connector cards so admins can quickly identify failing bridges. When a bridge is failed, render an inline error callout and surface the latest provisioning event error message to make troubleshooting visible without leaving the connector section. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
This commit is contained in:
committed by
Bryan Frimin
parent
4d4f4fa526
commit
f02c3b88b7
@@ -23,6 +23,7 @@ import {
|
|||||||
DialogFooter,
|
DialogFooter,
|
||||||
Google,
|
Google,
|
||||||
IconSettingsGear2,
|
IconSettingsGear2,
|
||||||
|
IconWarning,
|
||||||
Input,
|
Input,
|
||||||
useDialogRef,
|
useDialogRef,
|
||||||
useToast,
|
useToast,
|
||||||
@@ -38,9 +39,18 @@ import { useOrganizationId } from "#/hooks/useOrganizationId";
|
|||||||
const googleWorkspaceConnectorFragment = graphql`
|
const googleWorkspaceConnectorFragment = graphql`
|
||||||
fragment GoogleWorkspaceConnectorFragment on SCIMConfiguration {
|
fragment GoogleWorkspaceConnectorFragment on SCIMConfiguration {
|
||||||
id
|
id
|
||||||
|
events(first: 1) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
errorMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
bridge {
|
bridge {
|
||||||
id
|
id
|
||||||
type
|
type
|
||||||
|
state
|
||||||
excludedUserNames
|
excludedUserNames
|
||||||
connector {
|
connector {
|
||||||
id
|
id
|
||||||
@@ -83,9 +93,26 @@ export function GoogleWorkspaceConnector(props: {
|
|||||||
const connector = bridge?.connector;
|
const connector = bridge?.connector;
|
||||||
const scimConfigurationId = data?.id;
|
const scimConfigurationId = data?.id;
|
||||||
const bridgeId = bridge?.id;
|
const bridgeId = bridge?.id;
|
||||||
|
|
||||||
const organizationId = useOrganizationId();
|
const organizationId = useOrganizationId();
|
||||||
const { __, dateTimeFormat } = useTranslate();
|
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 { toast } = useToast();
|
||||||
const dialogRef = useDialogRef();
|
const dialogRef = useDialogRef();
|
||||||
const excludedUserNamesDialogRef = useDialogRef();
|
const excludedUserNamesDialogRef = useDialogRef();
|
||||||
@@ -225,7 +252,8 @@ export function GoogleWorkspaceConnector(props: {
|
|||||||
|
|
||||||
// Connected state
|
// Connected state
|
||||||
return (
|
return (
|
||||||
<Card padded className="flex items-center gap-3">
|
<Card padded className="space-y-3">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
<div className="w-10 h-10 flex items-center justify-center bg-subtle rounded">
|
<div className="w-10 h-10 flex items-center justify-center bg-subtle rounded">
|
||||||
<Google className="w-6 h-6" />
|
<Google className="w-6 h-6" />
|
||||||
</div>
|
</div>
|
||||||
@@ -235,8 +263,8 @@ export function GoogleWorkspaceConnector(props: {
|
|||||||
{sprintf(__("Connected on %s"), dateTimeFormat(connector.createdAt))}
|
{sprintf(__("Connected on %s"), dateTimeFormat(connector.createdAt))}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Badge variant="success" size="md">
|
<Badge variant={bridgeStatusBadgeVariant} size="md">
|
||||||
{__("Connected")}
|
{bridgeStatusLabel}
|
||||||
</Badge>
|
</Badge>
|
||||||
<Dialog
|
<Dialog
|
||||||
ref={excludedUserNamesDialogRef}
|
ref={excludedUserNamesDialogRef}
|
||||||
@@ -337,6 +365,21 @@ export function GoogleWorkspaceConnector(props: {
|
|||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isBridgeFailed && (
|
||||||
|
<div className="flex items-start gap-2 rounded-lg bg-bg-warning/10 border border-border-warning p-3">
|
||||||
|
<IconWarning size={16} className="text-txt-danger shrink-0 mt-0.5" />
|
||||||
|
<div className="space-y-1">
|
||||||
|
<p className="text-sm font-medium text-txt-danger">
|
||||||
|
{__("Bridge is in an error state")}
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-txt-secondary whitespace-pre-wrap break-all">
|
||||||
|
{bridgeErrorMessage}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
DialogContent,
|
DialogContent,
|
||||||
DialogFooter,
|
DialogFooter,
|
||||||
IconSettingsGear2,
|
IconSettingsGear2,
|
||||||
|
IconWarning,
|
||||||
Input,
|
Input,
|
||||||
Microsoft,
|
Microsoft,
|
||||||
useDialogRef,
|
useDialogRef,
|
||||||
@@ -38,9 +39,18 @@ import { useOrganizationId } from "#/hooks/useOrganizationId";
|
|||||||
const microsoft365ConnectorFragment = graphql`
|
const microsoft365ConnectorFragment = graphql`
|
||||||
fragment Microsoft365ConnectorFragment on SCIMConfiguration {
|
fragment Microsoft365ConnectorFragment on SCIMConfiguration {
|
||||||
id
|
id
|
||||||
|
events(first: 1) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
errorMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
bridge {
|
bridge {
|
||||||
id
|
id
|
||||||
type
|
type
|
||||||
|
state
|
||||||
excludedUserNames
|
excludedUserNames
|
||||||
connector {
|
connector {
|
||||||
id
|
id
|
||||||
@@ -83,9 +93,26 @@ export function Microsoft365Connector(props: {
|
|||||||
const connector = bridge?.connector;
|
const connector = bridge?.connector;
|
||||||
const scimConfigurationId = data?.id;
|
const scimConfigurationId = data?.id;
|
||||||
const bridgeId = bridge?.id;
|
const bridgeId = bridge?.id;
|
||||||
|
|
||||||
const organizationId = useOrganizationId();
|
const organizationId = useOrganizationId();
|
||||||
const { __, dateTimeFormat } = useTranslate();
|
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 { toast } = useToast();
|
||||||
const dialogRef = useDialogRef();
|
const dialogRef = useDialogRef();
|
||||||
const excludedUserNamesDialogRef = useDialogRef();
|
const excludedUserNamesDialogRef = useDialogRef();
|
||||||
@@ -223,7 +250,8 @@ export function Microsoft365Connector(props: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card padded className="flex items-center gap-3">
|
<Card padded className="space-y-3">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
<div className="w-10 h-10 flex items-center justify-center bg-subtle rounded">
|
<div className="w-10 h-10 flex items-center justify-center bg-subtle rounded">
|
||||||
<Microsoft className="w-6 h-6" />
|
<Microsoft className="w-6 h-6" />
|
||||||
</div>
|
</div>
|
||||||
@@ -233,8 +261,8 @@ export function Microsoft365Connector(props: {
|
|||||||
{sprintf(__("Connected on %s"), dateTimeFormat(connector.createdAt))}
|
{sprintf(__("Connected on %s"), dateTimeFormat(connector.createdAt))}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Badge variant="success" size="md">
|
<Badge variant={bridgeStatusBadgeVariant} size="md">
|
||||||
{__("Connected")}
|
{bridgeStatusLabel}
|
||||||
</Badge>
|
</Badge>
|
||||||
<Dialog
|
<Dialog
|
||||||
ref={excludedUserNamesDialogRef}
|
ref={excludedUserNamesDialogRef}
|
||||||
@@ -335,6 +363,21 @@ export function Microsoft365Connector(props: {
|
|||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isBridgeFailed && (
|
||||||
|
<div className="flex items-start gap-2 rounded-lg bg-bg-warning/10 border border-border-warning p-3">
|
||||||
|
<IconWarning size={16} className="text-txt-danger shrink-0 mt-0.5" />
|
||||||
|
<div className="space-y-1">
|
||||||
|
<p className="text-sm font-medium text-txt-danger">
|
||||||
|
{__("Bridge is in an error state")}
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-txt-secondary whitespace-pre-wrap break-all">
|
||||||
|
{bridgeErrorMessage}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user