@open-mercato/enterprise 0.6.4-develop.4178.1.aad9ddaa95 → 0.6.4-develop.4210.1.d412061cfe
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -220,11 +220,11 @@ function SsoConfigDetailPage() {
|
|
|
220
220
|
{ id: "scim", label: t("sso.admin.tab.scim", "Provisioning") },
|
|
221
221
|
{ id: "activity", label: t("sso.admin.tab.activity", "Activity") }
|
|
222
222
|
];
|
|
223
|
-
const statusBadge = /* @__PURE__ */ jsx("span", { className: `inline-flex items-center rounded-full px-2 py-1 text-xs font-medium ${config.isActive ? "bg-
|
|
223
|
+
const statusBadge = /* @__PURE__ */ jsx("span", { className: `inline-flex items-center rounded-full px-2 py-1 text-xs font-medium ${config.isActive ? "bg-status-success-bg text-status-success-text" : "bg-status-neutral-bg text-status-neutral-text"}`, children: config.isActive ? t("sso.admin.status.active", "Active") : t("sso.admin.status.inactive", "Inactive") });
|
|
224
224
|
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsxs(PageBody, { children: [
|
|
225
225
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6 max-w-3xl", children: [
|
|
226
|
-
showActivationBanner && !config.isActive && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-
|
|
227
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-
|
|
226
|
+
showActivationBanner && !config.isActive && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-status-info-border bg-status-info-bg p-4", children: [
|
|
227
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-status-info-text mb-3", children: t("sso.admin.banner.created", "Your SSO configuration has been created. Would you like to activate it now?") }),
|
|
228
228
|
activationError && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive mb-3", children: activationError }),
|
|
229
229
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
230
230
|
/* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleToggleActivation, disabled: isActivating, children: isActivating ? t("common.activating", "Activating...") : t("sso.admin.banner.activateNow", "Activate Now") }),
|
|
@@ -261,7 +261,7 @@ function SsoConfigDetailPage() {
|
|
|
261
261
|
type: "button",
|
|
262
262
|
variant: "ghost",
|
|
263
263
|
size: "sm",
|
|
264
|
-
className: `h-auto rounded-none border-b-2 px-4 py-2 hover:bg-transparent ${activeTab === tab.id ? "border-
|
|
264
|
+
className: `h-auto rounded-none border-b-2 px-4 py-2 hover:bg-transparent ${activeTab === tab.id ? "border-accent-indigo text-foreground" : "border-transparent text-muted-foreground"}`,
|
|
265
265
|
onClick: () => setActiveTab(tab.id),
|
|
266
266
|
children: tab.label
|
|
267
267
|
},
|
|
@@ -638,8 +638,8 @@ function ScimProvisioningTab({ configId, jitEnabled, issuer, onProvisioningChang
|
|
|
638
638
|
};
|
|
639
639
|
if (isLoading) return /* @__PURE__ */ jsx(LoadingMessage, { label: t("common.loading", "Loading...") });
|
|
640
640
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
641
|
-
isGoogleProvider && /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-
|
|
642
|
-
!isGoogleProvider && jitEnabled && /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-
|
|
641
|
+
isGoogleProvider && /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-status-info-border bg-status-info-bg p-4", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-status-info-text", children: t("sso.admin.scim.googleNotSupported", "Google Workspace does not support SCIM provisioning. Users are provisioned via Just-In-Time (JIT) on first login.") }) }),
|
|
642
|
+
!isGoogleProvider && jitEnabled && /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-status-warning-border bg-status-warning-bg p-4", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-status-warning-text", children: t("sso.admin.scim.jitActiveWarning", "SCIM provisioning is unavailable while JIT provisioning is enabled. Disable JIT in the General tab to configure SCIM.") }) }),
|
|
643
643
|
isGoogleProvider ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
644
644
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
645
645
|
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium mb-2", children: t("sso.admin.scim.endpointUrl", "SCIM Endpoint URL") }),
|
|
@@ -648,8 +648,8 @@ function ScimProvisioningTab({ configId, jitEnabled, issuer, onProvisioningChang
|
|
|
648
648
|
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: handleCopyEndpoint, children: t("common.copy", "Copy") })
|
|
649
649
|
] })
|
|
650
650
|
] }),
|
|
651
|
-
newlyCreatedToken && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-
|
|
652
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-
|
|
651
|
+
newlyCreatedToken && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-status-warning-border bg-status-warning-bg p-4", children: [
|
|
652
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-status-warning-text mb-2", children: t("sso.admin.scim.tokenCreated", "Your SCIM token has been created. Copy it now \u2014 it will not be shown again.") }),
|
|
653
653
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
654
654
|
/* @__PURE__ */ jsx("code", { className: "flex-1 rounded-md border bg-white px-3 py-2 text-xs font-mono break-all", children: newlyCreatedToken }),
|
|
655
655
|
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: handleCopyToken, children: t("common.copy", "Copy") })
|
|
@@ -694,7 +694,7 @@ function ScimProvisioningTab({ configId, jitEnabled, issuer, onProvisioningChang
|
|
|
694
694
|
"..."
|
|
695
695
|
] })
|
|
696
696
|
] }),
|
|
697
|
-
/* @__PURE__ */ jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${token.isActive ? "bg-
|
|
697
|
+
/* @__PURE__ */ jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${token.isActive ? "bg-status-success-bg text-status-success-text" : "bg-status-neutral-bg text-status-neutral-text"}`, children: token.isActive ? t("sso.admin.scim.tokenActive", "Active") : t("sso.admin.scim.tokenRevoked", "Revoked") })
|
|
698
698
|
] }),
|
|
699
699
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
700
700
|
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: new Date(token.createdAt).toLocaleDateString() }),
|
|
@@ -716,7 +716,7 @@ function ScimProvisioningTab({ configId, jitEnabled, issuer, onProvisioningChang
|
|
|
716
716
|
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-xs text-muted-foreground whitespace-nowrap", children: new Date(log.createdAt).toLocaleString() }),
|
|
717
717
|
/* @__PURE__ */ jsx("td", { className: "px-3 py-2", children: /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded px-1.5 py-0.5 text-xs font-medium bg-muted", children: log.operation }) }),
|
|
718
718
|
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-xs", children: log.resourceType }),
|
|
719
|
-
/* @__PURE__ */ jsx("td", { className: "px-3 py-2", children: /* @__PURE__ */ jsx("span", { className: `text-xs font-medium ${log.responseStatus < 300 ? "text-
|
|
719
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2", children: /* @__PURE__ */ jsx("span", { className: `text-xs font-medium ${log.responseStatus < 300 ? "text-status-success-text" : "text-destructive"}`, children: log.responseStatus }) }),
|
|
720
720
|
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-xs text-muted-foreground truncate max-w-[200px]", children: log.errorMessage || "\u2014" })
|
|
721
721
|
] }, log.id)) })
|
|
722
722
|
] }) })
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/sso/backend/sso/config/%5Bid%5D/page.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport React from 'react'\nimport { useParams, useRouter, useSearchParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { PasswordInput } from '@open-mercato/ui/primitives/password-input'\nimport { FormHeader } from '@open-mercato/ui/backend/forms'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\n\ntype Tab = 'general' | 'domains' | 'roles' | 'scim' | 'activity'\n\ninterface SsoConfigDetail {\n id: string\n name: string | null\n tenantId: string | null\n organizationId: string\n protocol: string\n issuer: string | null\n clientId: string | null\n hasClientSecret: boolean\n allowedDomains: string[]\n jitEnabled: boolean\n autoLinkByEmail: boolean\n isActive: boolean\n ssoRequired: boolean\n appRoleMappings: Record<string, string>\n hasActiveScimTokens: boolean\n createdAt: string\n updatedAt: string\n}\n\ninterface SsoIdentityRow {\n id: string\n userId: string\n idpEmail: string\n idpName: string | null\n provisioningMethod: string\n lastLoginAt: string | null\n createdAt: string\n}\n\nexport default function SsoConfigDetailPage() {\n const params = useParams()\n const configId = (params?.slug && Array.isArray(params.slug))\n ? params.slug[2]\n : (Array.isArray(params?.id) ? params.id[0] : params?.id as string)\n const router = useRouter()\n const searchParams = useSearchParams()\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n\n const [config, setConfig] = React.useState<SsoConfigDetail | null>(null)\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [activeTab, setActiveTab] = React.useState<Tab>('general')\n const [showActivationBanner, setShowActivationBanner] = React.useState(searchParams?.get('created') === '1')\n const [activationError, setActivationError] = React.useState<string | null>(null)\n const [isActivating, setIsActivating] = React.useState(false)\n\n const { runMutation, retryLastMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: `sso-config:${configId ?? 'pending'}`,\n })\n const runMutationWithContext = React.useCallback(\n async <T,>(operation: () => Promise<T>, mutationPayload?: Record<string, unknown>): Promise<T> => {\n return runMutation({\n operation,\n mutationPayload,\n context: { configId, retryLastMutation },\n })\n },\n [configId, retryLastMutation, runMutation],\n )\n\n // General tab form state\n const [name, setName] = React.useState('')\n const [issuer, setIssuer] = React.useState('')\n const [clientId, setClientId] = React.useState('')\n const [newClientSecret, setNewClientSecret] = React.useState('')\n const [showSecretField, setShowSecretField] = React.useState(false)\n const [jitEnabled, setJitEnabled] = React.useState(true)\n const [autoLinkByEmail, setAutoLinkByEmail] = React.useState(true)\n const [isSaving, setIsSaving] = React.useState(false)\n\n // Domains tab state\n const [domainInput, setDomainInput] = React.useState('')\n const [domainError, setDomainError] = React.useState('')\n\n const fetchConfig = React.useCallback(async () => {\n setIsLoading(true)\n const call = await apiCall<SsoConfigDetail>(`/api/sso/config/${configId}`)\n if (call.ok && call.result) {\n const c = call.result\n setConfig(c)\n setName(c.name ?? '')\n setIssuer(c.issuer ?? '')\n setClientId(c.clientId ?? '')\n setJitEnabled(c.jitEnabled)\n setAutoLinkByEmail(c.autoLinkByEmail)\n setError(null)\n } else {\n setError(t('sso.admin.error.loadFailed', 'Failed to load SSO configuration'))\n }\n setIsLoading(false)\n }, [configId, t])\n\n React.useEffect(() => { fetchConfig() }, [fetchConfig])\n\n const handleSave = async () => {\n setIsSaving(true)\n try {\n const payload: Record<string, unknown> = { name, issuer, clientId, jitEnabled, autoLinkByEmail }\n if (newClientSecret) payload.clientSecret = newClientSecret\n\n await runMutationWithContext(\n () => apiCallOrThrow<SsoConfigDetail>(\n `/api/sso/config/${configId}`,\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n },\n { errorMessage: t('sso.admin.error.saveFailed', 'Failed to save SSO configuration') },\n ),\n payload,\n )\n flash(t('sso.admin.saved', 'SSO configuration saved'), 'success')\n setNewClientSecret('')\n setShowSecretField(false)\n fetchConfig()\n } catch {\n // apiCallOrThrow handles the error\n } finally {\n setIsSaving(false)\n }\n }\n\n const handleToggleActivation = async () => {\n if (!config) return\n setActivationError(null)\n setIsActivating(true)\n try {\n await runMutationWithContext(\n () => apiCallOrThrow(\n `/api/sso/config/${configId}/activate`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ active: !config.isActive }),\n },\n { errorMessage: t('sso.admin.error.activationFailed', 'Failed to update activation status') },\n ),\n { active: !config.isActive },\n )\n flash(\n config.isActive\n ? t('sso.admin.deactivated', 'SSO configuration deactivated')\n : t('sso.admin.activated', 'SSO configuration activated'),\n 'success',\n )\n setShowActivationBanner(false)\n fetchConfig()\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n const isNoDomains = message.toLowerCase().includes('no allowed domains')\n if (isNoDomains) {\n setActivationError(t('sso.admin.error.noDomainsForActivation', 'Add at least one allowed email domain before activating'))\n setActiveTab('domains')\n } else {\n setActivationError(message)\n }\n } finally {\n setIsActivating(false)\n }\n }\n\n const handleTestConnection = async () => {\n try {\n const call = await runMutationWithContext(\n () => apiCallOrThrow<{ ok: boolean; error?: string }>(\n `/api/sso/config/${configId}/test`,\n { method: 'POST' },\n { errorMessage: t('sso.admin.error.testFailed', 'Connection test failed') },\n ),\n )\n if (call.result?.ok) {\n flash(t('sso.admin.test.success', 'Discovery successful \u2014 issuer is reachable'), 'success')\n } else {\n flash(call.result?.error || t('sso.admin.test.failed', 'Discovery failed'), 'error')\n }\n } catch {\n // handled by apiCallOrThrow\n }\n }\n\n const handleDelete = async () => {\n if (!config) return\n if (config.isActive) {\n flash(t('sso.admin.error.deleteActive', 'Cannot delete an active SSO configuration \u2014 deactivate it first'), 'error')\n return\n }\n const confirmed = await confirm({\n title: t('sso.admin.delete.title', 'Delete SSO Configuration'),\n text: t('sso.admin.delete.confirm', 'Are you sure? This will remove the SSO configuration.'),\n confirmText: t('common.delete', 'Delete'),\n variant: 'destructive',\n })\n if (!confirmed) return\n\n await runMutationWithContext(\n () => apiCallOrThrow(`/api/sso/config/${configId}`, { method: 'DELETE' }, {\n errorMessage: t('sso.admin.error.deleteFailed', 'Failed to delete SSO configuration'),\n }),\n { id: configId },\n )\n flash(t('sso.admin.delete.success', 'SSO configuration deleted'), 'success')\n router.push('/backend/sso')\n }\n\n const handleAddDomain = async () => {\n const normalized = domainInput.trim().toLowerCase()\n if (!normalized) return\n\n const domainRegex = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/\n if (!domainRegex.test(normalized) || !normalized.includes('.')) {\n setDomainError(t('sso.admin.wizard.domain.invalid', 'Invalid domain format'))\n return\n }\n\n try {\n await runMutationWithContext(\n () => apiCallOrThrow(\n `/api/sso/config/${configId}/domains`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ domain: normalized }),\n },\n { errorMessage: t('sso.admin.error.domainAddFailed', 'Failed to add domain') },\n ),\n { domain: normalized },\n )\n setDomainInput('')\n setDomainError('')\n fetchConfig()\n } catch {\n // handled by apiCallOrThrow\n }\n }\n\n const handleRemoveDomain = async (domain: string) => {\n try {\n await runMutationWithContext(\n () => apiCallOrThrow(\n `/api/sso/config/${configId}/domains?domain=${encodeURIComponent(domain)}`,\n { method: 'DELETE' },\n { errorMessage: t('sso.admin.error.domainRemoveFailed', 'Failed to remove domain') },\n ),\n { domain },\n )\n fetchConfig()\n } catch {\n // handled by apiCallOrThrow\n }\n }\n\n if (isLoading) return <Page><PageBody><LoadingMessage label={t('common.loading', 'Loading...')} /></PageBody></Page>\n if (error || !config) return <Page><PageBody><ErrorMessage label={error || t('common.notFound', 'Not found')} /></PageBody></Page>\n\n const tabs: { id: Tab; label: string }[] = [\n { id: 'general', label: t('sso.admin.tab.general', 'General') },\n { id: 'domains', label: t('sso.admin.tab.domains', 'Domains') },\n { id: 'roles', label: t('sso.admin.tab.roles', 'Role Mappings') },\n { id: 'scim', label: t('sso.admin.tab.scim', 'Provisioning') },\n { id: 'activity', label: t('sso.admin.tab.activity', 'Activity') },\n ]\n\n const statusBadge = (\n <span className={`inline-flex items-center rounded-full px-2 py-1 text-xs font-medium ${config.isActive ? 'bg-green-50 text-green-700' : 'bg-gray-100 text-gray-600'}`}>\n {config.isActive ? t('sso.admin.status.active', 'Active') : t('sso.admin.status.inactive', 'Inactive')}\n </span>\n )\n\n return (\n <Page>\n <PageBody>\n <div className=\"flex flex-col gap-6 max-w-3xl\">\n {/* Activation banner after creation */}\n {showActivationBanner && !config.isActive && (\n <div className=\"rounded-lg border border-blue-200 bg-blue-50 p-4\">\n <p className=\"text-sm font-medium text-blue-900 mb-3\">\n {t('sso.admin.banner.created', 'Your SSO configuration has been created. Would you like to activate it now?')}\n </p>\n {activationError && (\n <p className=\"text-sm text-destructive mb-3\">{activationError}</p>\n )}\n <div className=\"flex items-center gap-2\">\n <Button size=\"sm\" onClick={handleToggleActivation} disabled={isActivating}>\n {isActivating\n ? t('common.activating', 'Activating...')\n : t('sso.admin.banner.activateNow', 'Activate Now')}\n </Button>\n <Button variant=\"outline\" size=\"sm\" onClick={() => setShowActivationBanner(false)}>\n {t('sso.admin.banner.notYet', 'Not Yet')}\n </Button>\n </div>\n </div>\n )}\n\n <FormHeader\n mode=\"detail\"\n backHref=\"/backend/sso\"\n backLabel={t('sso.admin.detail.backToList', 'Back to SSO')}\n title={config.name || config.issuer || t('sso.admin.detail.title', 'SSO Configuration')}\n statusBadge={statusBadge}\n actionsContent={\n <div className=\"flex items-center gap-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={handleTestConnection}>\n {t('sso.admin.action.test', 'Verify Discovery')}\n </Button>\n <Button\n type=\"button\"\n variant={config.isActive ? 'outline' : 'default'}\n size=\"sm\"\n onClick={handleToggleActivation}\n >\n {config.isActive\n ? t('sso.admin.action.deactivate', 'Deactivate')\n : t('sso.admin.action.activate', 'Activate')}\n </Button>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={handleDelete} className=\"text-destructive\">\n {t('common.delete', 'Delete')}\n </Button>\n </div>\n }\n />\n\n {/* Tabs */}\n <div className=\"flex gap-1 border-b\">\n {tabs.map((tab) => (\n <Button\n key={tab.id}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className={`h-auto rounded-none border-b-2 px-4 py-2 hover:bg-transparent ${\n activeTab === tab.id\n ? 'border-primary text-foreground'\n : 'border-transparent text-muted-foreground'\n }`}\n onClick={() => setActiveTab(tab.id)}\n >\n {tab.label}\n </Button>\n ))}\n </div>\n\n {/* Tab content */}\n {activeTab === 'general' && (\n <div className=\"rounded-lg border bg-card p-4\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase text-muted-foreground\">\n {t('sso.admin.section.oidcSettings', 'OIDC Settings')}\n </h2>\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium mb-1\">{t('sso.admin.field.name', 'Configuration Name')}</label>\n <input\n type=\"text\"\n className=\"w-full rounded-md border px-3 py-2 text-sm\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium mb-1\">{t('sso.admin.field.protocol', 'Protocol')}</label>\n <input type=\"text\" className=\"w-full rounded-md border px-3 py-2 text-sm bg-muted\" value={config.protocol.toUpperCase()} disabled />\n </div>\n <div>\n <label className=\"block text-sm font-medium mb-1\">{t('sso.admin.field.issuer', 'Issuer URL')}</label>\n <input\n type=\"url\"\n className=\"w-full rounded-md border px-3 py-2 text-sm\"\n value={issuer}\n onChange={(e) => setIssuer(e.target.value)}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium mb-1\">{t('sso.admin.field.clientId', 'Client ID')}</label>\n <input\n type=\"text\"\n className=\"w-full rounded-md border px-3 py-2 text-sm\"\n value={clientId}\n onChange={(e) => setClientId(e.target.value)}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium mb-1\">{t('sso.admin.field.clientSecret', 'Client Secret')}</label>\n {config.hasClientSecret && !showSecretField ? (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-muted-foreground\">{t('sso.admin.field.secretSet', 'Client secret is configured')}</span>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => setShowSecretField(true)}>\n {t('sso.admin.field.changeSecret', 'Change')}\n </Button>\n </div>\n ) : (\n <PasswordInput\n value={newClientSecret}\n onChange={(e) => setNewClientSecret(e.target.value)}\n placeholder={config.hasClientSecret\n ? t('sso.admin.field.secretPlaceholder', 'Enter new secret to replace existing')\n : t('sso.admin.field.secretRequired', 'Enter client secret')}\n />\n )}\n </div>\n <div className=\"space-y-3 pt-2\">\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={jitEnabled}\n onChange={(e) => setJitEnabled(e.target.checked)}\n disabled={config.hasActiveScimTokens}\n className=\"accent-primary\"\n />\n <div>\n <span className=\"text-sm font-medium\">{t('sso.admin.field.jitEnabled', 'Just-in-Time Provisioning')}</span>\n <span className=\"text-xs text-muted-foreground ml-2\">\n {config.hasActiveScimTokens\n ? t('sso.admin.field.jitDisabledByScim', 'Unavailable \u2014 SCIM directory sync is active. Revoke SCIM tokens to enable JIT.')\n : t('sso.admin.field.jitEnabledDesc', 'Automatically create user accounts on first SSO login')}\n </span>\n </div>\n </label>\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={autoLinkByEmail}\n onChange={(e) => setAutoLinkByEmail(e.target.checked)}\n className=\"accent-primary\"\n />\n <div>\n <span className=\"text-sm font-medium\">{t('sso.admin.field.autoLinkByEmail', 'Auto-link by Email')}</span>\n <span className=\"text-xs text-muted-foreground ml-2\">\n {t('sso.admin.field.autoLinkByEmailDesc', 'Automatically link existing users by matching email address')}\n </span>\n </div>\n </label>\n </div>\n <div className=\"pt-4\">\n <Button onClick={handleSave} disabled={isSaving}>\n {isSaving ? t('common.saving', 'Saving...') : t('common.save', 'Save')}\n </Button>\n </div>\n </div>\n </div>\n )}\n\n {activeTab === 'domains' && (\n <div className=\"rounded-lg border bg-card p-4\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase text-muted-foreground\">\n {t('sso.admin.section.allowedDomains', 'Allowed Domains')}\n </h2>\n <p className=\"text-sm text-muted-foreground mb-4\">\n {t('sso.admin.wizard.domains.description', 'Users with email addresses matching these domains will be redirected to your SSO provider.')}\n </p>\n <div className=\"flex items-center gap-2 mb-4\">\n <input\n type=\"text\"\n className=\"flex-1 rounded-md border px-3 py-2 text-sm\"\n placeholder={t('sso.admin.wizard.domains.placeholder', 'example.com')}\n value={domainInput}\n onChange={(e) => { setDomainInput(e.target.value); setDomainError('') }}\n onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); handleAddDomain() } }}\n />\n <Button type=\"button\" variant=\"outline\" onClick={handleAddDomain}>\n {t('common.add', 'Add')}\n </Button>\n </div>\n {domainError && <p className=\"text-sm text-destructive mb-2\">{domainError}</p>}\n {config.allowedDomains.length > 0 ? (\n <div className=\"space-y-2\">\n {config.allowedDomains.map((domain) => (\n <div key={domain} className=\"flex items-center justify-between p-3 border rounded-md\">\n <code className=\"text-sm font-mono\">{domain}</code>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => handleRemoveDomain(domain)}>\n {t('common.remove', 'Remove')}\n </Button>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-muted-foreground py-4 text-center\">\n {t('sso.admin.domains.empty', 'No domains configured. Add at least one domain before activating SSO.')}\n </p>\n )}\n </div>\n )}\n\n {activeTab === 'roles' && config && (\n <div className=\"rounded-lg border bg-card p-4\">\n <RoleMappingsTab\n configId={configId}\n appRoleMappings={config.appRoleMappings ?? {}}\n onSaved={fetchConfig}\n runMutationWithContext={runMutationWithContext}\n />\n </div>\n )}\n\n {activeTab === 'scim' && (\n <div className=\"rounded-lg border bg-card p-4\">\n <ScimProvisioningTab configId={configId} jitEnabled={config.jitEnabled} issuer={config.issuer ?? undefined} onProvisioningChange={fetchConfig} runMutationWithContext={runMutationWithContext} />\n </div>\n )}\n\n {activeTab === 'activity' && (\n <div className=\"rounded-lg border bg-card p-4\">\n <SsoActivityTab configId={configId} />\n </div>\n )}\n </div>\n {ConfirmDialogElement}\n </PageBody>\n </Page>\n )\n}\n\nfunction RoleMappingsTab({\n configId,\n appRoleMappings,\n onSaved,\n runMutationWithContext,\n}: {\n configId: string\n appRoleMappings: Record<string, string>\n onSaved: () => void\n runMutationWithContext: <T>(operation: () => Promise<T>, mutationPayload?: Record<string, unknown>) => Promise<T>\n}) {\n const t = useT()\n const [mappings, setMappings] = React.useState<Record<string, string>>(appRoleMappings)\n const [idpRoleInput, setIdpRoleInput] = React.useState('')\n const [localRoleInput, setLocalRoleInput] = React.useState('')\n const [roleOptions, setRoleOptions] = React.useState<{ value: string; label: string }[]>([])\n const [isSaving, setIsSaving] = React.useState(false)\n const [inputError, setInputError] = React.useState('')\n\n React.useEffect(() => {\n setMappings(appRoleMappings)\n }, [appRoleMappings])\n\n React.useEffect(() => {\n const loadRoles = async () => {\n const call = await apiCall<{ items?: Array<{ id?: string | null; name?: string | null }> }>(\n '/api/auth/roles?page=1&pageSize=50',\n undefined,\n { fallback: { items: [] } },\n )\n if (call.ok && Array.isArray(call.result?.items)) {\n const options = call.result.items\n .map((item) => {\n const name = typeof item?.name === 'string' ? item.name.trim() : ''\n if (!name || name === 'superadmin') return null\n return { value: name, label: name }\n })\n .filter((opt): opt is { value: string; label: string } => !!opt)\n setRoleOptions(options)\n if (options.length > 0 && !localRoleInput) {\n setLocalRoleInput(options[0].value)\n }\n }\n }\n loadRoles()\n }, [])\n\n const handleAdd = () => {\n const trimmed = idpRoleInput.trim()\n if (!trimmed) {\n setInputError(t('sso.admin.roles.error.emptyIdpRole', 'IdP role name is required'))\n return\n }\n if (!localRoleInput) {\n setInputError(t('sso.admin.roles.error.emptyLocalRole', 'Select a local role'))\n return\n }\n if (mappings[trimmed]) {\n setInputError(t('sso.admin.roles.error.duplicate', 'This IdP role is already mapped'))\n return\n }\n setMappings({ ...mappings, [trimmed]: localRoleInput })\n setIdpRoleInput('')\n setInputError('')\n }\n\n const handleRemove = (idpRole: string) => {\n const updated = { ...mappings }\n delete updated[idpRole]\n setMappings(updated)\n }\n\n const handleSave = async () => {\n setIsSaving(true)\n try {\n await runMutationWithContext(\n () => apiCallOrThrow(\n `/api/sso/config/${configId}`,\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ appRoleMappings: mappings }),\n },\n { errorMessage: t('sso.admin.roles.error.saveFailed', 'Failed to save role mappings') },\n ),\n { appRoleMappings: mappings },\n )\n flash(t('sso.admin.roles.saved', 'Role mappings saved'), 'success')\n onSaved()\n } catch {\n // handled by apiCallOrThrow\n } finally {\n setIsSaving(false)\n }\n }\n\n const mappingEntries = Object.entries(mappings)\n\n return (\n <div>\n <p className=\"text-sm text-muted-foreground mb-4\">\n {t('sso.admin.roles.description', 'Map IdP app role names to local roles. On each SSO login, SSO-sourced roles are synced \u2014 roles no longer sent by the IdP are removed, while manually-assigned roles are preserved.')}\n </p>\n <div className=\"flex items-end gap-2 mb-4\">\n <div className=\"flex-1\">\n <label className=\"block text-xs font-medium mb-1\">{t('sso.admin.roles.idpRole', 'IdP Role Name')}</label>\n <input\n type=\"text\"\n className=\"w-full rounded-md border px-3 py-2 text-sm\"\n placeholder={t('sso.admin.roles.idpRolePlaceholder', 'e.g. OpenMercato.Admin')}\n value={idpRoleInput}\n onChange={(e) => { setIdpRoleInput(e.target.value); setInputError('') }}\n onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); handleAdd() } }}\n />\n </div>\n <div className=\"flex-1\">\n <label className=\"block text-xs font-medium mb-1\">{t('sso.admin.roles.localRole', 'Local Role')}</label>\n <select\n className=\"w-full rounded-md border px-3 py-2 text-sm bg-background\"\n value={localRoleInput}\n onChange={(e) => setLocalRoleInput(e.target.value)}\n >\n {roleOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n </div>\n <Button variant=\"outline\" onClick={handleAdd}>\n {t('common.add', 'Add')}\n </Button>\n </div>\n {inputError && <p className=\"text-sm text-destructive mb-2\">{inputError}</p>}\n\n {mappingEntries.length > 0 ? (\n <div className=\"space-y-2 mb-4\">\n {mappingEntries.map(([idpRole, localRole]) => (\n <div key={idpRole} className=\"flex items-center justify-between p-3 border rounded-md\">\n <div className=\"flex items-center gap-2\">\n <code className=\"text-sm font-mono\">{idpRole}</code>\n <span className=\"text-muted-foreground text-sm\">→</span>\n <span className=\"text-sm font-medium\">{localRole}</span>\n </div>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => handleRemove(idpRole)}>\n {t('common.remove', 'Remove')}\n </Button>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-muted-foreground py-4 text-center mb-4\">\n {t('sso.admin.roles.empty', 'No role mappings configured. IdP role names will be matched directly against local role names.')}\n </p>\n )}\n\n <Button onClick={handleSave} disabled={isSaving}>\n {isSaving ? t('common.saving', 'Saving...') : t('common.save', 'Save')}\n </Button>\n </div>\n )\n}\n\ninterface ScimTokenRow {\n id: string\n ssoConfigId: string\n name: string\n tokenPrefix: string\n isActive: boolean\n createdAt: string\n}\n\ninterface ScimLogRow {\n id: string\n operation: string\n resourceType: string\n resourceId: string | null\n scimExternalId: string | null\n responseStatus: number\n errorMessage: string | null\n createdAt: string\n}\n\nconst googleIssuerHost = 'accounts.google.com'\n\nfunction isGoogleIssuer(issuer?: string): boolean {\n if (!issuer) return false\n\n const normalizedIssuer = issuer.trim()\n if (!normalizedIssuer) return false\n\n try {\n return new URL(normalizedIssuer).hostname.toLowerCase() === googleIssuerHost\n } catch {\n return normalizedIssuer.toLowerCase() === googleIssuerHost\n }\n}\n\nfunction ScimProvisioningTab({ configId, jitEnabled, issuer, onProvisioningChange, runMutationWithContext }: { configId: string; jitEnabled: boolean; issuer?: string; onProvisioningChange: () => void; runMutationWithContext: <T>(operation: () => Promise<T>, mutationPayload?: Record<string, unknown>) => Promise<T> }) {\n const isGoogleProvider = isGoogleIssuer(issuer)\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n\n const [tokens, setTokens] = React.useState<ScimTokenRow[]>([])\n const [logs, setLogs] = React.useState<ScimLogRow[]>([])\n const [isLoading, setIsLoading] = React.useState(true)\n const [showCreateForm, setShowCreateForm] = React.useState(false)\n const [tokenName, setTokenName] = React.useState('')\n const [isCreating, setIsCreating] = React.useState(false)\n const [newlyCreatedToken, setNewlyCreatedToken] = React.useState<string | null>(null)\n\n const fetchData = React.useCallback(async () => {\n setIsLoading(true)\n const [tokensCall, logsCall] = await Promise.all([\n apiCall<{ items: ScimTokenRow[] }>(`/api/sso/scim/tokens?ssoConfigId=${configId}`),\n apiCall<{ items: ScimLogRow[] }>(`/api/sso/scim/logs?ssoConfigId=${configId}`),\n ])\n if (tokensCall.ok && tokensCall.result) setTokens(tokensCall.result.items)\n if (logsCall.ok && logsCall.result) setLogs(logsCall.result.items)\n setIsLoading(false)\n }, [configId])\n\n React.useEffect(() => { fetchData() }, [fetchData])\n\n const handleCreateToken = async () => {\n if (!tokenName.trim()) return\n setIsCreating(true)\n try {\n const result = await runMutationWithContext(\n () => apiCallOrThrow<{ id: string; token: string; prefix: string; name: string }>(\n '/api/sso/scim/tokens',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ ssoConfigId: configId, name: tokenName.trim() }),\n },\n { errorMessage: t('sso.admin.scim.error.createFailed', 'Failed to create SCIM token') },\n ),\n { ssoConfigId: configId, name: tokenName.trim() },\n )\n if (result.result) {\n setNewlyCreatedToken(result.result.token)\n setShowCreateForm(false)\n setTokenName('')\n fetchData()\n onProvisioningChange()\n }\n } catch {\n // handled by apiCallOrThrow\n } finally {\n setIsCreating(false)\n }\n }\n\n const handleRevokeToken = async (tokenId: string) => {\n const confirmed = await confirm({\n title: t('sso.admin.scim.revoke.title', 'Revoke Token'),\n text: t('sso.admin.scim.revoke.confirm', 'Are you sure? This token will no longer authenticate SCIM requests.'),\n confirmText: t('sso.admin.scim.revoke.action', 'Revoke'),\n variant: 'destructive',\n })\n if (!confirmed) return\n\n try {\n await runMutationWithContext(\n () => apiCallOrThrow(`/api/sso/scim/tokens/${tokenId}`, { method: 'DELETE' }, {\n errorMessage: t('sso.admin.scim.error.revokeFailed', 'Failed to revoke token'),\n }),\n { tokenId },\n )\n flash(t('sso.admin.scim.revoked', 'Token revoked'), 'success')\n fetchData()\n onProvisioningChange()\n } catch {\n // handled\n }\n }\n\n const handleCopyEndpoint = () => {\n const url = `${window.location.origin}/api/sso/scim/v2`\n navigator.clipboard.writeText(url).then(() => {\n flash(t('sso.admin.scim.endpointCopied', 'SCIM endpoint URL copied'), 'success')\n })\n }\n\n const handleCopyToken = () => {\n if (!newlyCreatedToken) return\n navigator.clipboard.writeText(newlyCreatedToken).then(() => {\n flash(t('sso.admin.scim.tokenCopied', 'Token copied to clipboard'), 'success')\n })\n }\n\n if (isLoading) return <LoadingMessage label={t('common.loading', 'Loading...')} />\n\n return (\n <div className=\"space-y-6\">\n {/* Google provider banner \u2014 SCIM not supported */}\n {isGoogleProvider && (\n <div className=\"rounded-lg border border-blue-200 bg-blue-50 p-4\">\n <p className=\"text-sm text-blue-900\">\n {t('sso.admin.scim.googleNotSupported', 'Google Workspace does not support SCIM provisioning. Users are provisioned via Just-In-Time (JIT) on first login.')}\n </p>\n </div>\n )}\n\n {/* JIT active banner */}\n {!isGoogleProvider && jitEnabled && (\n <div className=\"rounded-lg border border-amber-200 bg-amber-50 p-4\">\n <p className=\"text-sm text-amber-900\">\n {t('sso.admin.scim.jitActiveWarning', 'SCIM provisioning is unavailable while JIT provisioning is enabled. Disable JIT in the General tab to configure SCIM.')}\n </p>\n </div>\n )}\n\n {isGoogleProvider ? null : <>\n {/* SCIM Endpoint URL */}\n <div>\n <h3 className=\"text-sm font-medium mb-2\">{t('sso.admin.scim.endpointUrl', 'SCIM Endpoint URL')}</h3>\n <div className=\"flex items-center gap-2\">\n <code className=\"flex-1 rounded-md border bg-muted px-3 py-2 text-sm font-mono\">\n {typeof window !== 'undefined' ? `${window.location.origin}/api/sso/scim/v2` : '/api/sso/scim/v2'}\n </code>\n <Button variant=\"outline\" size=\"sm\" onClick={handleCopyEndpoint}>\n {t('common.copy', 'Copy')}\n </Button>\n </div>\n </div>\n\n {/* Newly created token banner */}\n {newlyCreatedToken && (\n <div className=\"rounded-lg border border-amber-200 bg-amber-50 p-4\">\n <p className=\"text-sm font-medium text-amber-900 mb-2\">\n {t('sso.admin.scim.tokenCreated', 'Your SCIM token has been created. Copy it now \u2014 it will not be shown again.')}\n </p>\n <div className=\"flex items-center gap-2 mb-2\">\n <code className=\"flex-1 rounded-md border bg-white px-3 py-2 text-xs font-mono break-all\">\n {newlyCreatedToken}\n </code>\n <Button variant=\"outline\" size=\"sm\" onClick={handleCopyToken}>\n {t('common.copy', 'Copy')}\n </Button>\n </div>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => setNewlyCreatedToken(null)}>\n {t('common.dismiss', 'Dismiss')}\n </Button>\n </div>\n )}\n\n {/* Token management */}\n <div>\n <div className=\"flex items-center justify-between mb-3\">\n <h3 className=\"text-sm font-medium\">{t('sso.admin.scim.tokens', 'Bearer Tokens')}</h3>\n {!showCreateForm && (\n <Button variant=\"outline\" size=\"sm\" onClick={() => setShowCreateForm(true)} disabled={jitEnabled}>\n {t('sso.admin.scim.generateToken', 'Generate Token')}\n </Button>\n )}\n </div>\n\n {showCreateForm && (\n <div className=\"flex items-center gap-2 mb-4\">\n <input\n type=\"text\"\n className=\"flex-1 rounded-md border px-3 py-2 text-sm\"\n placeholder={t('sso.admin.scim.tokenNamePlaceholder', 'Token name (e.g., Entra ID Production)')}\n value={tokenName}\n onChange={(e) => setTokenName(e.target.value)}\n onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); handleCreateToken() } }}\n autoFocus\n />\n <Button size=\"sm\" onClick={handleCreateToken} disabled={isCreating || !tokenName.trim()}>\n {isCreating ? t('common.creating', 'Creating...') : t('common.create', 'Create')}\n </Button>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => { setShowCreateForm(false); setTokenName('') }}>\n {t('common.cancel', 'Cancel')}\n </Button>\n </div>\n )}\n\n {tokens.length === 0 ? (\n <div className=\"text-center py-8 border rounded-md\">\n <p className=\"text-sm text-muted-foreground\">\n {t('sso.admin.scim.noTokens', 'SCIM provisioning is not configured. Generate a bearer token to enable your identity provider to sync users automatically.')}\n </p>\n </div>\n ) : (\n <div className=\"space-y-2\">\n {tokens.map((token) => (\n <div key={token.id} className=\"flex items-center justify-between p-3 border rounded-md\">\n <div className=\"flex items-center gap-3\">\n <div>\n <span className=\"text-sm font-medium\">{token.name}</span>\n <span className=\"text-xs text-muted-foreground ml-2 font-mono\">{token.tokenPrefix}...</span>\n </div>\n <span className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${\n token.isActive ? 'bg-green-50 text-green-700' : 'bg-gray-100 text-gray-500'\n }`}>\n {token.isActive ? t('sso.admin.scim.tokenActive', 'Active') : t('sso.admin.scim.tokenRevoked', 'Revoked')}\n </span>\n </div>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs text-muted-foreground\">\n {new Date(token.createdAt).toLocaleDateString()}\n </span>\n {token.isActive && (\n <Button variant=\"ghost\" size=\"sm\" onClick={() => handleRevokeToken(token.id)} className=\"text-destructive\">\n {t('sso.admin.scim.revoke.action', 'Revoke')}\n </Button>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Provisioning log */}\n {logs.length > 0 && (\n <div>\n <h3 className=\"text-sm font-medium mb-3\">{t('sso.admin.scim.recentActivity', 'Recent Provisioning Activity')}</h3>\n <div className=\"border rounded-md overflow-hidden\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted/50\">\n <tr>\n <th className=\"text-left px-3 py-2 font-medium\">{t('sso.admin.scim.log.time', 'Time')}</th>\n <th className=\"text-left px-3 py-2 font-medium\">{t('sso.admin.scim.log.operation', 'Operation')}</th>\n <th className=\"text-left px-3 py-2 font-medium\">{t('sso.admin.scim.log.resource', 'Resource')}</th>\n <th className=\"text-left px-3 py-2 font-medium\">{t('sso.admin.scim.log.status', 'Status')}</th>\n <th className=\"text-left px-3 py-2 font-medium\">{t('sso.admin.scim.log.error', 'Error')}</th>\n </tr>\n </thead>\n <tbody>\n {logs.map((log) => (\n <tr key={log.id} className=\"border-t\">\n <td className=\"px-3 py-2 text-xs text-muted-foreground whitespace-nowrap\">\n {new Date(log.createdAt).toLocaleString()}\n </td>\n <td className=\"px-3 py-2\">\n <span className=\"inline-flex items-center rounded px-1.5 py-0.5 text-xs font-medium bg-muted\">\n {log.operation}\n </span>\n </td>\n <td className=\"px-3 py-2 text-xs\">{log.resourceType}</td>\n <td className=\"px-3 py-2\">\n <span className={`text-xs font-medium ${log.responseStatus < 300 ? 'text-green-700' : 'text-red-600'}`}>\n {log.responseStatus}\n </span>\n </td>\n <td className=\"px-3 py-2 text-xs text-muted-foreground truncate max-w-[200px]\">\n {log.errorMessage || '\u2014'}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n )}\n\n </>}\n\n {ConfirmDialogElement}\n </div>\n )\n}\n\nfunction SsoActivityTab({ configId }: { configId: string }) {\n const t = useT()\n const [identities, setIdentities] = React.useState<SsoIdentityRow[]>([])\n const [isLoading, setIsLoading] = React.useState(true)\n\n React.useEffect(() => {\n const load = async () => {\n setIsLoading(true)\n // For now, just show a placeholder \u2014 the identities list API is an M3 deliverable\n setIdentities([])\n setIsLoading(false)\n }\n load()\n }, [configId])\n\n if (isLoading) return <LoadingMessage label={t('common.loading', 'Loading...')} />\n\n if (identities.length === 0) {\n return (\n <div className=\"text-center py-8\">\n <p className=\"text-sm text-muted-foreground\">\n {t('sso.admin.activity.empty', 'No SSO login activity yet. Activity will appear here once users start logging in via SSO.')}\n </p>\n </div>\n )\n }\n\n return (\n <div className=\"space-y-2\">\n {identities.map((identity) => (\n <div key={identity.id} className=\"flex items-center justify-between p-3 border rounded-md\">\n <div>\n <span className=\"text-sm font-medium\">{identity.idpEmail}</span>\n {identity.idpName && <span className=\"text-sm text-muted-foreground ml-2\">({identity.idpName})</span>}\n </div>\n <span className=\"text-xs text-muted-foreground\">\n {identity.lastLoginAt ? new Date(identity.lastLoginAt).toLocaleString() : '\u2014'}\n </span>\n </div>\n ))}\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport React from 'react'\nimport { useParams, useRouter, useSearchParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { PasswordInput } from '@open-mercato/ui/primitives/password-input'\nimport { FormHeader } from '@open-mercato/ui/backend/forms'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\n\ntype Tab = 'general' | 'domains' | 'roles' | 'scim' | 'activity'\n\ninterface SsoConfigDetail {\n id: string\n name: string | null\n tenantId: string | null\n organizationId: string\n protocol: string\n issuer: string | null\n clientId: string | null\n hasClientSecret: boolean\n allowedDomains: string[]\n jitEnabled: boolean\n autoLinkByEmail: boolean\n isActive: boolean\n ssoRequired: boolean\n appRoleMappings: Record<string, string>\n hasActiveScimTokens: boolean\n createdAt: string\n updatedAt: string\n}\n\ninterface SsoIdentityRow {\n id: string\n userId: string\n idpEmail: string\n idpName: string | null\n provisioningMethod: string\n lastLoginAt: string | null\n createdAt: string\n}\n\nexport default function SsoConfigDetailPage() {\n const params = useParams()\n const configId = (params?.slug && Array.isArray(params.slug))\n ? params.slug[2]\n : (Array.isArray(params?.id) ? params.id[0] : params?.id as string)\n const router = useRouter()\n const searchParams = useSearchParams()\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n\n const [config, setConfig] = React.useState<SsoConfigDetail | null>(null)\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [activeTab, setActiveTab] = React.useState<Tab>('general')\n const [showActivationBanner, setShowActivationBanner] = React.useState(searchParams?.get('created') === '1')\n const [activationError, setActivationError] = React.useState<string | null>(null)\n const [isActivating, setIsActivating] = React.useState(false)\n\n const { runMutation, retryLastMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: `sso-config:${configId ?? 'pending'}`,\n })\n const runMutationWithContext = React.useCallback(\n async <T,>(operation: () => Promise<T>, mutationPayload?: Record<string, unknown>): Promise<T> => {\n return runMutation({\n operation,\n mutationPayload,\n context: { configId, retryLastMutation },\n })\n },\n [configId, retryLastMutation, runMutation],\n )\n\n // General tab form state\n const [name, setName] = React.useState('')\n const [issuer, setIssuer] = React.useState('')\n const [clientId, setClientId] = React.useState('')\n const [newClientSecret, setNewClientSecret] = React.useState('')\n const [showSecretField, setShowSecretField] = React.useState(false)\n const [jitEnabled, setJitEnabled] = React.useState(true)\n const [autoLinkByEmail, setAutoLinkByEmail] = React.useState(true)\n const [isSaving, setIsSaving] = React.useState(false)\n\n // Domains tab state\n const [domainInput, setDomainInput] = React.useState('')\n const [domainError, setDomainError] = React.useState('')\n\n const fetchConfig = React.useCallback(async () => {\n setIsLoading(true)\n const call = await apiCall<SsoConfigDetail>(`/api/sso/config/${configId}`)\n if (call.ok && call.result) {\n const c = call.result\n setConfig(c)\n setName(c.name ?? '')\n setIssuer(c.issuer ?? '')\n setClientId(c.clientId ?? '')\n setJitEnabled(c.jitEnabled)\n setAutoLinkByEmail(c.autoLinkByEmail)\n setError(null)\n } else {\n setError(t('sso.admin.error.loadFailed', 'Failed to load SSO configuration'))\n }\n setIsLoading(false)\n }, [configId, t])\n\n React.useEffect(() => { fetchConfig() }, [fetchConfig])\n\n const handleSave = async () => {\n setIsSaving(true)\n try {\n const payload: Record<string, unknown> = { name, issuer, clientId, jitEnabled, autoLinkByEmail }\n if (newClientSecret) payload.clientSecret = newClientSecret\n\n await runMutationWithContext(\n () => apiCallOrThrow<SsoConfigDetail>(\n `/api/sso/config/${configId}`,\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n },\n { errorMessage: t('sso.admin.error.saveFailed', 'Failed to save SSO configuration') },\n ),\n payload,\n )\n flash(t('sso.admin.saved', 'SSO configuration saved'), 'success')\n setNewClientSecret('')\n setShowSecretField(false)\n fetchConfig()\n } catch {\n // apiCallOrThrow handles the error\n } finally {\n setIsSaving(false)\n }\n }\n\n const handleToggleActivation = async () => {\n if (!config) return\n setActivationError(null)\n setIsActivating(true)\n try {\n await runMutationWithContext(\n () => apiCallOrThrow(\n `/api/sso/config/${configId}/activate`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ active: !config.isActive }),\n },\n { errorMessage: t('sso.admin.error.activationFailed', 'Failed to update activation status') },\n ),\n { active: !config.isActive },\n )\n flash(\n config.isActive\n ? t('sso.admin.deactivated', 'SSO configuration deactivated')\n : t('sso.admin.activated', 'SSO configuration activated'),\n 'success',\n )\n setShowActivationBanner(false)\n fetchConfig()\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n const isNoDomains = message.toLowerCase().includes('no allowed domains')\n if (isNoDomains) {\n setActivationError(t('sso.admin.error.noDomainsForActivation', 'Add at least one allowed email domain before activating'))\n setActiveTab('domains')\n } else {\n setActivationError(message)\n }\n } finally {\n setIsActivating(false)\n }\n }\n\n const handleTestConnection = async () => {\n try {\n const call = await runMutationWithContext(\n () => apiCallOrThrow<{ ok: boolean; error?: string }>(\n `/api/sso/config/${configId}/test`,\n { method: 'POST' },\n { errorMessage: t('sso.admin.error.testFailed', 'Connection test failed') },\n ),\n )\n if (call.result?.ok) {\n flash(t('sso.admin.test.success', 'Discovery successful \u2014 issuer is reachable'), 'success')\n } else {\n flash(call.result?.error || t('sso.admin.test.failed', 'Discovery failed'), 'error')\n }\n } catch {\n // handled by apiCallOrThrow\n }\n }\n\n const handleDelete = async () => {\n if (!config) return\n if (config.isActive) {\n flash(t('sso.admin.error.deleteActive', 'Cannot delete an active SSO configuration \u2014 deactivate it first'), 'error')\n return\n }\n const confirmed = await confirm({\n title: t('sso.admin.delete.title', 'Delete SSO Configuration'),\n text: t('sso.admin.delete.confirm', 'Are you sure? This will remove the SSO configuration.'),\n confirmText: t('common.delete', 'Delete'),\n variant: 'destructive',\n })\n if (!confirmed) return\n\n await runMutationWithContext(\n () => apiCallOrThrow(`/api/sso/config/${configId}`, { method: 'DELETE' }, {\n errorMessage: t('sso.admin.error.deleteFailed', 'Failed to delete SSO configuration'),\n }),\n { id: configId },\n )\n flash(t('sso.admin.delete.success', 'SSO configuration deleted'), 'success')\n router.push('/backend/sso')\n }\n\n const handleAddDomain = async () => {\n const normalized = domainInput.trim().toLowerCase()\n if (!normalized) return\n\n const domainRegex = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/\n if (!domainRegex.test(normalized) || !normalized.includes('.')) {\n setDomainError(t('sso.admin.wizard.domain.invalid', 'Invalid domain format'))\n return\n }\n\n try {\n await runMutationWithContext(\n () => apiCallOrThrow(\n `/api/sso/config/${configId}/domains`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ domain: normalized }),\n },\n { errorMessage: t('sso.admin.error.domainAddFailed', 'Failed to add domain') },\n ),\n { domain: normalized },\n )\n setDomainInput('')\n setDomainError('')\n fetchConfig()\n } catch {\n // handled by apiCallOrThrow\n }\n }\n\n const handleRemoveDomain = async (domain: string) => {\n try {\n await runMutationWithContext(\n () => apiCallOrThrow(\n `/api/sso/config/${configId}/domains?domain=${encodeURIComponent(domain)}`,\n { method: 'DELETE' },\n { errorMessage: t('sso.admin.error.domainRemoveFailed', 'Failed to remove domain') },\n ),\n { domain },\n )\n fetchConfig()\n } catch {\n // handled by apiCallOrThrow\n }\n }\n\n if (isLoading) return <Page><PageBody><LoadingMessage label={t('common.loading', 'Loading...')} /></PageBody></Page>\n if (error || !config) return <Page><PageBody><ErrorMessage label={error || t('common.notFound', 'Not found')} /></PageBody></Page>\n\n const tabs: { id: Tab; label: string }[] = [\n { id: 'general', label: t('sso.admin.tab.general', 'General') },\n { id: 'domains', label: t('sso.admin.tab.domains', 'Domains') },\n { id: 'roles', label: t('sso.admin.tab.roles', 'Role Mappings') },\n { id: 'scim', label: t('sso.admin.tab.scim', 'Provisioning') },\n { id: 'activity', label: t('sso.admin.tab.activity', 'Activity') },\n ]\n\n const statusBadge = (\n <span className={`inline-flex items-center rounded-full px-2 py-1 text-xs font-medium ${config.isActive ? 'bg-status-success-bg text-status-success-text' : 'bg-status-neutral-bg text-status-neutral-text'}`}>\n {config.isActive ? t('sso.admin.status.active', 'Active') : t('sso.admin.status.inactive', 'Inactive')}\n </span>\n )\n\n return (\n <Page>\n <PageBody>\n <div className=\"flex flex-col gap-6 max-w-3xl\">\n {/* Activation banner after creation */}\n {showActivationBanner && !config.isActive && (\n <div className=\"rounded-lg border border-status-info-border bg-status-info-bg p-4\">\n <p className=\"text-sm font-medium text-status-info-text mb-3\">\n {t('sso.admin.banner.created', 'Your SSO configuration has been created. Would you like to activate it now?')}\n </p>\n {activationError && (\n <p className=\"text-sm text-destructive mb-3\">{activationError}</p>\n )}\n <div className=\"flex items-center gap-2\">\n <Button size=\"sm\" onClick={handleToggleActivation} disabled={isActivating}>\n {isActivating\n ? t('common.activating', 'Activating...')\n : t('sso.admin.banner.activateNow', 'Activate Now')}\n </Button>\n <Button variant=\"outline\" size=\"sm\" onClick={() => setShowActivationBanner(false)}>\n {t('sso.admin.banner.notYet', 'Not Yet')}\n </Button>\n </div>\n </div>\n )}\n\n <FormHeader\n mode=\"detail\"\n backHref=\"/backend/sso\"\n backLabel={t('sso.admin.detail.backToList', 'Back to SSO')}\n title={config.name || config.issuer || t('sso.admin.detail.title', 'SSO Configuration')}\n statusBadge={statusBadge}\n actionsContent={\n <div className=\"flex items-center gap-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={handleTestConnection}>\n {t('sso.admin.action.test', 'Verify Discovery')}\n </Button>\n <Button\n type=\"button\"\n variant={config.isActive ? 'outline' : 'default'}\n size=\"sm\"\n onClick={handleToggleActivation}\n >\n {config.isActive\n ? t('sso.admin.action.deactivate', 'Deactivate')\n : t('sso.admin.action.activate', 'Activate')}\n </Button>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={handleDelete} className=\"text-destructive\">\n {t('common.delete', 'Delete')}\n </Button>\n </div>\n }\n />\n\n {/* Tabs */}\n <div className=\"flex gap-1 border-b\">\n {tabs.map((tab) => (\n <Button\n key={tab.id}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className={`h-auto rounded-none border-b-2 px-4 py-2 hover:bg-transparent ${\n activeTab === tab.id\n ? 'border-accent-indigo text-foreground'\n : 'border-transparent text-muted-foreground'\n }`}\n onClick={() => setActiveTab(tab.id)}\n >\n {tab.label}\n </Button>\n ))}\n </div>\n\n {/* Tab content */}\n {activeTab === 'general' && (\n <div className=\"rounded-lg border bg-card p-4\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase text-muted-foreground\">\n {t('sso.admin.section.oidcSettings', 'OIDC Settings')}\n </h2>\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium mb-1\">{t('sso.admin.field.name', 'Configuration Name')}</label>\n <input\n type=\"text\"\n className=\"w-full rounded-md border px-3 py-2 text-sm\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium mb-1\">{t('sso.admin.field.protocol', 'Protocol')}</label>\n <input type=\"text\" className=\"w-full rounded-md border px-3 py-2 text-sm bg-muted\" value={config.protocol.toUpperCase()} disabled />\n </div>\n <div>\n <label className=\"block text-sm font-medium mb-1\">{t('sso.admin.field.issuer', 'Issuer URL')}</label>\n <input\n type=\"url\"\n className=\"w-full rounded-md border px-3 py-2 text-sm\"\n value={issuer}\n onChange={(e) => setIssuer(e.target.value)}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium mb-1\">{t('sso.admin.field.clientId', 'Client ID')}</label>\n <input\n type=\"text\"\n className=\"w-full rounded-md border px-3 py-2 text-sm\"\n value={clientId}\n onChange={(e) => setClientId(e.target.value)}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium mb-1\">{t('sso.admin.field.clientSecret', 'Client Secret')}</label>\n {config.hasClientSecret && !showSecretField ? (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-muted-foreground\">{t('sso.admin.field.secretSet', 'Client secret is configured')}</span>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => setShowSecretField(true)}>\n {t('sso.admin.field.changeSecret', 'Change')}\n </Button>\n </div>\n ) : (\n <PasswordInput\n value={newClientSecret}\n onChange={(e) => setNewClientSecret(e.target.value)}\n placeholder={config.hasClientSecret\n ? t('sso.admin.field.secretPlaceholder', 'Enter new secret to replace existing')\n : t('sso.admin.field.secretRequired', 'Enter client secret')}\n />\n )}\n </div>\n <div className=\"space-y-3 pt-2\">\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={jitEnabled}\n onChange={(e) => setJitEnabled(e.target.checked)}\n disabled={config.hasActiveScimTokens}\n className=\"accent-primary\"\n />\n <div>\n <span className=\"text-sm font-medium\">{t('sso.admin.field.jitEnabled', 'Just-in-Time Provisioning')}</span>\n <span className=\"text-xs text-muted-foreground ml-2\">\n {config.hasActiveScimTokens\n ? t('sso.admin.field.jitDisabledByScim', 'Unavailable \u2014 SCIM directory sync is active. Revoke SCIM tokens to enable JIT.')\n : t('sso.admin.field.jitEnabledDesc', 'Automatically create user accounts on first SSO login')}\n </span>\n </div>\n </label>\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={autoLinkByEmail}\n onChange={(e) => setAutoLinkByEmail(e.target.checked)}\n className=\"accent-primary\"\n />\n <div>\n <span className=\"text-sm font-medium\">{t('sso.admin.field.autoLinkByEmail', 'Auto-link by Email')}</span>\n <span className=\"text-xs text-muted-foreground ml-2\">\n {t('sso.admin.field.autoLinkByEmailDesc', 'Automatically link existing users by matching email address')}\n </span>\n </div>\n </label>\n </div>\n <div className=\"pt-4\">\n <Button onClick={handleSave} disabled={isSaving}>\n {isSaving ? t('common.saving', 'Saving...') : t('common.save', 'Save')}\n </Button>\n </div>\n </div>\n </div>\n )}\n\n {activeTab === 'domains' && (\n <div className=\"rounded-lg border bg-card p-4\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase text-muted-foreground\">\n {t('sso.admin.section.allowedDomains', 'Allowed Domains')}\n </h2>\n <p className=\"text-sm text-muted-foreground mb-4\">\n {t('sso.admin.wizard.domains.description', 'Users with email addresses matching these domains will be redirected to your SSO provider.')}\n </p>\n <div className=\"flex items-center gap-2 mb-4\">\n <input\n type=\"text\"\n className=\"flex-1 rounded-md border px-3 py-2 text-sm\"\n placeholder={t('sso.admin.wizard.domains.placeholder', 'example.com')}\n value={domainInput}\n onChange={(e) => { setDomainInput(e.target.value); setDomainError('') }}\n onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); handleAddDomain() } }}\n />\n <Button type=\"button\" variant=\"outline\" onClick={handleAddDomain}>\n {t('common.add', 'Add')}\n </Button>\n </div>\n {domainError && <p className=\"text-sm text-destructive mb-2\">{domainError}</p>}\n {config.allowedDomains.length > 0 ? (\n <div className=\"space-y-2\">\n {config.allowedDomains.map((domain) => (\n <div key={domain} className=\"flex items-center justify-between p-3 border rounded-md\">\n <code className=\"text-sm font-mono\">{domain}</code>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => handleRemoveDomain(domain)}>\n {t('common.remove', 'Remove')}\n </Button>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-muted-foreground py-4 text-center\">\n {t('sso.admin.domains.empty', 'No domains configured. Add at least one domain before activating SSO.')}\n </p>\n )}\n </div>\n )}\n\n {activeTab === 'roles' && config && (\n <div className=\"rounded-lg border bg-card p-4\">\n <RoleMappingsTab\n configId={configId}\n appRoleMappings={config.appRoleMappings ?? {}}\n onSaved={fetchConfig}\n runMutationWithContext={runMutationWithContext}\n />\n </div>\n )}\n\n {activeTab === 'scim' && (\n <div className=\"rounded-lg border bg-card p-4\">\n <ScimProvisioningTab configId={configId} jitEnabled={config.jitEnabled} issuer={config.issuer ?? undefined} onProvisioningChange={fetchConfig} runMutationWithContext={runMutationWithContext} />\n </div>\n )}\n\n {activeTab === 'activity' && (\n <div className=\"rounded-lg border bg-card p-4\">\n <SsoActivityTab configId={configId} />\n </div>\n )}\n </div>\n {ConfirmDialogElement}\n </PageBody>\n </Page>\n )\n}\n\nfunction RoleMappingsTab({\n configId,\n appRoleMappings,\n onSaved,\n runMutationWithContext,\n}: {\n configId: string\n appRoleMappings: Record<string, string>\n onSaved: () => void\n runMutationWithContext: <T>(operation: () => Promise<T>, mutationPayload?: Record<string, unknown>) => Promise<T>\n}) {\n const t = useT()\n const [mappings, setMappings] = React.useState<Record<string, string>>(appRoleMappings)\n const [idpRoleInput, setIdpRoleInput] = React.useState('')\n const [localRoleInput, setLocalRoleInput] = React.useState('')\n const [roleOptions, setRoleOptions] = React.useState<{ value: string; label: string }[]>([])\n const [isSaving, setIsSaving] = React.useState(false)\n const [inputError, setInputError] = React.useState('')\n\n React.useEffect(() => {\n setMappings(appRoleMappings)\n }, [appRoleMappings])\n\n React.useEffect(() => {\n const loadRoles = async () => {\n const call = await apiCall<{ items?: Array<{ id?: string | null; name?: string | null }> }>(\n '/api/auth/roles?page=1&pageSize=50',\n undefined,\n { fallback: { items: [] } },\n )\n if (call.ok && Array.isArray(call.result?.items)) {\n const options = call.result.items\n .map((item) => {\n const name = typeof item?.name === 'string' ? item.name.trim() : ''\n if (!name || name === 'superadmin') return null\n return { value: name, label: name }\n })\n .filter((opt): opt is { value: string; label: string } => !!opt)\n setRoleOptions(options)\n if (options.length > 0 && !localRoleInput) {\n setLocalRoleInput(options[0].value)\n }\n }\n }\n loadRoles()\n }, [])\n\n const handleAdd = () => {\n const trimmed = idpRoleInput.trim()\n if (!trimmed) {\n setInputError(t('sso.admin.roles.error.emptyIdpRole', 'IdP role name is required'))\n return\n }\n if (!localRoleInput) {\n setInputError(t('sso.admin.roles.error.emptyLocalRole', 'Select a local role'))\n return\n }\n if (mappings[trimmed]) {\n setInputError(t('sso.admin.roles.error.duplicate', 'This IdP role is already mapped'))\n return\n }\n setMappings({ ...mappings, [trimmed]: localRoleInput })\n setIdpRoleInput('')\n setInputError('')\n }\n\n const handleRemove = (idpRole: string) => {\n const updated = { ...mappings }\n delete updated[idpRole]\n setMappings(updated)\n }\n\n const handleSave = async () => {\n setIsSaving(true)\n try {\n await runMutationWithContext(\n () => apiCallOrThrow(\n `/api/sso/config/${configId}`,\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ appRoleMappings: mappings }),\n },\n { errorMessage: t('sso.admin.roles.error.saveFailed', 'Failed to save role mappings') },\n ),\n { appRoleMappings: mappings },\n )\n flash(t('sso.admin.roles.saved', 'Role mappings saved'), 'success')\n onSaved()\n } catch {\n // handled by apiCallOrThrow\n } finally {\n setIsSaving(false)\n }\n }\n\n const mappingEntries = Object.entries(mappings)\n\n return (\n <div>\n <p className=\"text-sm text-muted-foreground mb-4\">\n {t('sso.admin.roles.description', 'Map IdP app role names to local roles. On each SSO login, SSO-sourced roles are synced \u2014 roles no longer sent by the IdP are removed, while manually-assigned roles are preserved.')}\n </p>\n <div className=\"flex items-end gap-2 mb-4\">\n <div className=\"flex-1\">\n <label className=\"block text-xs font-medium mb-1\">{t('sso.admin.roles.idpRole', 'IdP Role Name')}</label>\n <input\n type=\"text\"\n className=\"w-full rounded-md border px-3 py-2 text-sm\"\n placeholder={t('sso.admin.roles.idpRolePlaceholder', 'e.g. OpenMercato.Admin')}\n value={idpRoleInput}\n onChange={(e) => { setIdpRoleInput(e.target.value); setInputError('') }}\n onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); handleAdd() } }}\n />\n </div>\n <div className=\"flex-1\">\n <label className=\"block text-xs font-medium mb-1\">{t('sso.admin.roles.localRole', 'Local Role')}</label>\n <select\n className=\"w-full rounded-md border px-3 py-2 text-sm bg-background\"\n value={localRoleInput}\n onChange={(e) => setLocalRoleInput(e.target.value)}\n >\n {roleOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n </div>\n <Button variant=\"outline\" onClick={handleAdd}>\n {t('common.add', 'Add')}\n </Button>\n </div>\n {inputError && <p className=\"text-sm text-destructive mb-2\">{inputError}</p>}\n\n {mappingEntries.length > 0 ? (\n <div className=\"space-y-2 mb-4\">\n {mappingEntries.map(([idpRole, localRole]) => (\n <div key={idpRole} className=\"flex items-center justify-between p-3 border rounded-md\">\n <div className=\"flex items-center gap-2\">\n <code className=\"text-sm font-mono\">{idpRole}</code>\n <span className=\"text-muted-foreground text-sm\">→</span>\n <span className=\"text-sm font-medium\">{localRole}</span>\n </div>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => handleRemove(idpRole)}>\n {t('common.remove', 'Remove')}\n </Button>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-muted-foreground py-4 text-center mb-4\">\n {t('sso.admin.roles.empty', 'No role mappings configured. IdP role names will be matched directly against local role names.')}\n </p>\n )}\n\n <Button onClick={handleSave} disabled={isSaving}>\n {isSaving ? t('common.saving', 'Saving...') : t('common.save', 'Save')}\n </Button>\n </div>\n )\n}\n\ninterface ScimTokenRow {\n id: string\n ssoConfigId: string\n name: string\n tokenPrefix: string\n isActive: boolean\n createdAt: string\n}\n\ninterface ScimLogRow {\n id: string\n operation: string\n resourceType: string\n resourceId: string | null\n scimExternalId: string | null\n responseStatus: number\n errorMessage: string | null\n createdAt: string\n}\n\nconst googleIssuerHost = 'accounts.google.com'\n\nfunction isGoogleIssuer(issuer?: string): boolean {\n if (!issuer) return false\n\n const normalizedIssuer = issuer.trim()\n if (!normalizedIssuer) return false\n\n try {\n return new URL(normalizedIssuer).hostname.toLowerCase() === googleIssuerHost\n } catch {\n return normalizedIssuer.toLowerCase() === googleIssuerHost\n }\n}\n\nfunction ScimProvisioningTab({ configId, jitEnabled, issuer, onProvisioningChange, runMutationWithContext }: { configId: string; jitEnabled: boolean; issuer?: string; onProvisioningChange: () => void; runMutationWithContext: <T>(operation: () => Promise<T>, mutationPayload?: Record<string, unknown>) => Promise<T> }) {\n const isGoogleProvider = isGoogleIssuer(issuer)\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n\n const [tokens, setTokens] = React.useState<ScimTokenRow[]>([])\n const [logs, setLogs] = React.useState<ScimLogRow[]>([])\n const [isLoading, setIsLoading] = React.useState(true)\n const [showCreateForm, setShowCreateForm] = React.useState(false)\n const [tokenName, setTokenName] = React.useState('')\n const [isCreating, setIsCreating] = React.useState(false)\n const [newlyCreatedToken, setNewlyCreatedToken] = React.useState<string | null>(null)\n\n const fetchData = React.useCallback(async () => {\n setIsLoading(true)\n const [tokensCall, logsCall] = await Promise.all([\n apiCall<{ items: ScimTokenRow[] }>(`/api/sso/scim/tokens?ssoConfigId=${configId}`),\n apiCall<{ items: ScimLogRow[] }>(`/api/sso/scim/logs?ssoConfigId=${configId}`),\n ])\n if (tokensCall.ok && tokensCall.result) setTokens(tokensCall.result.items)\n if (logsCall.ok && logsCall.result) setLogs(logsCall.result.items)\n setIsLoading(false)\n }, [configId])\n\n React.useEffect(() => { fetchData() }, [fetchData])\n\n const handleCreateToken = async () => {\n if (!tokenName.trim()) return\n setIsCreating(true)\n try {\n const result = await runMutationWithContext(\n () => apiCallOrThrow<{ id: string; token: string; prefix: string; name: string }>(\n '/api/sso/scim/tokens',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ ssoConfigId: configId, name: tokenName.trim() }),\n },\n { errorMessage: t('sso.admin.scim.error.createFailed', 'Failed to create SCIM token') },\n ),\n { ssoConfigId: configId, name: tokenName.trim() },\n )\n if (result.result) {\n setNewlyCreatedToken(result.result.token)\n setShowCreateForm(false)\n setTokenName('')\n fetchData()\n onProvisioningChange()\n }\n } catch {\n // handled by apiCallOrThrow\n } finally {\n setIsCreating(false)\n }\n }\n\n const handleRevokeToken = async (tokenId: string) => {\n const confirmed = await confirm({\n title: t('sso.admin.scim.revoke.title', 'Revoke Token'),\n text: t('sso.admin.scim.revoke.confirm', 'Are you sure? This token will no longer authenticate SCIM requests.'),\n confirmText: t('sso.admin.scim.revoke.action', 'Revoke'),\n variant: 'destructive',\n })\n if (!confirmed) return\n\n try {\n await runMutationWithContext(\n () => apiCallOrThrow(`/api/sso/scim/tokens/${tokenId}`, { method: 'DELETE' }, {\n errorMessage: t('sso.admin.scim.error.revokeFailed', 'Failed to revoke token'),\n }),\n { tokenId },\n )\n flash(t('sso.admin.scim.revoked', 'Token revoked'), 'success')\n fetchData()\n onProvisioningChange()\n } catch {\n // handled\n }\n }\n\n const handleCopyEndpoint = () => {\n const url = `${window.location.origin}/api/sso/scim/v2`\n navigator.clipboard.writeText(url).then(() => {\n flash(t('sso.admin.scim.endpointCopied', 'SCIM endpoint URL copied'), 'success')\n })\n }\n\n const handleCopyToken = () => {\n if (!newlyCreatedToken) return\n navigator.clipboard.writeText(newlyCreatedToken).then(() => {\n flash(t('sso.admin.scim.tokenCopied', 'Token copied to clipboard'), 'success')\n })\n }\n\n if (isLoading) return <LoadingMessage label={t('common.loading', 'Loading...')} />\n\n return (\n <div className=\"space-y-6\">\n {/* Google provider banner \u2014 SCIM not supported */}\n {isGoogleProvider && (\n <div className=\"rounded-lg border border-status-info-border bg-status-info-bg p-4\">\n <p className=\"text-sm text-status-info-text\">\n {t('sso.admin.scim.googleNotSupported', 'Google Workspace does not support SCIM provisioning. Users are provisioned via Just-In-Time (JIT) on first login.')}\n </p>\n </div>\n )}\n\n {/* JIT active banner */}\n {!isGoogleProvider && jitEnabled && (\n <div className=\"rounded-lg border border-status-warning-border bg-status-warning-bg p-4\">\n <p className=\"text-sm text-status-warning-text\">\n {t('sso.admin.scim.jitActiveWarning', 'SCIM provisioning is unavailable while JIT provisioning is enabled. Disable JIT in the General tab to configure SCIM.')}\n </p>\n </div>\n )}\n\n {isGoogleProvider ? null : <>\n {/* SCIM Endpoint URL */}\n <div>\n <h3 className=\"text-sm font-medium mb-2\">{t('sso.admin.scim.endpointUrl', 'SCIM Endpoint URL')}</h3>\n <div className=\"flex items-center gap-2\">\n <code className=\"flex-1 rounded-md border bg-muted px-3 py-2 text-sm font-mono\">\n {typeof window !== 'undefined' ? `${window.location.origin}/api/sso/scim/v2` : '/api/sso/scim/v2'}\n </code>\n <Button variant=\"outline\" size=\"sm\" onClick={handleCopyEndpoint}>\n {t('common.copy', 'Copy')}\n </Button>\n </div>\n </div>\n\n {/* Newly created token banner */}\n {newlyCreatedToken && (\n <div className=\"rounded-lg border border-status-warning-border bg-status-warning-bg p-4\">\n <p className=\"text-sm font-medium text-status-warning-text mb-2\">\n {t('sso.admin.scim.tokenCreated', 'Your SCIM token has been created. Copy it now \u2014 it will not be shown again.')}\n </p>\n <div className=\"flex items-center gap-2 mb-2\">\n <code className=\"flex-1 rounded-md border bg-white px-3 py-2 text-xs font-mono break-all\">\n {newlyCreatedToken}\n </code>\n <Button variant=\"outline\" size=\"sm\" onClick={handleCopyToken}>\n {t('common.copy', 'Copy')}\n </Button>\n </div>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => setNewlyCreatedToken(null)}>\n {t('common.dismiss', 'Dismiss')}\n </Button>\n </div>\n )}\n\n {/* Token management */}\n <div>\n <div className=\"flex items-center justify-between mb-3\">\n <h3 className=\"text-sm font-medium\">{t('sso.admin.scim.tokens', 'Bearer Tokens')}</h3>\n {!showCreateForm && (\n <Button variant=\"outline\" size=\"sm\" onClick={() => setShowCreateForm(true)} disabled={jitEnabled}>\n {t('sso.admin.scim.generateToken', 'Generate Token')}\n </Button>\n )}\n </div>\n\n {showCreateForm && (\n <div className=\"flex items-center gap-2 mb-4\">\n <input\n type=\"text\"\n className=\"flex-1 rounded-md border px-3 py-2 text-sm\"\n placeholder={t('sso.admin.scim.tokenNamePlaceholder', 'Token name (e.g., Entra ID Production)')}\n value={tokenName}\n onChange={(e) => setTokenName(e.target.value)}\n onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); handleCreateToken() } }}\n autoFocus\n />\n <Button size=\"sm\" onClick={handleCreateToken} disabled={isCreating || !tokenName.trim()}>\n {isCreating ? t('common.creating', 'Creating...') : t('common.create', 'Create')}\n </Button>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => { setShowCreateForm(false); setTokenName('') }}>\n {t('common.cancel', 'Cancel')}\n </Button>\n </div>\n )}\n\n {tokens.length === 0 ? (\n <div className=\"text-center py-8 border rounded-md\">\n <p className=\"text-sm text-muted-foreground\">\n {t('sso.admin.scim.noTokens', 'SCIM provisioning is not configured. Generate a bearer token to enable your identity provider to sync users automatically.')}\n </p>\n </div>\n ) : (\n <div className=\"space-y-2\">\n {tokens.map((token) => (\n <div key={token.id} className=\"flex items-center justify-between p-3 border rounded-md\">\n <div className=\"flex items-center gap-3\">\n <div>\n <span className=\"text-sm font-medium\">{token.name}</span>\n <span className=\"text-xs text-muted-foreground ml-2 font-mono\">{token.tokenPrefix}...</span>\n </div>\n <span className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${\n token.isActive ? 'bg-status-success-bg text-status-success-text' : 'bg-status-neutral-bg text-status-neutral-text'\n }`}>\n {token.isActive ? t('sso.admin.scim.tokenActive', 'Active') : t('sso.admin.scim.tokenRevoked', 'Revoked')}\n </span>\n </div>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs text-muted-foreground\">\n {new Date(token.createdAt).toLocaleDateString()}\n </span>\n {token.isActive && (\n <Button variant=\"ghost\" size=\"sm\" onClick={() => handleRevokeToken(token.id)} className=\"text-destructive\">\n {t('sso.admin.scim.revoke.action', 'Revoke')}\n </Button>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Provisioning log */}\n {logs.length > 0 && (\n <div>\n <h3 className=\"text-sm font-medium mb-3\">{t('sso.admin.scim.recentActivity', 'Recent Provisioning Activity')}</h3>\n <div className=\"border rounded-md overflow-hidden\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted/50\">\n <tr>\n <th className=\"text-left px-3 py-2 font-medium\">{t('sso.admin.scim.log.time', 'Time')}</th>\n <th className=\"text-left px-3 py-2 font-medium\">{t('sso.admin.scim.log.operation', 'Operation')}</th>\n <th className=\"text-left px-3 py-2 font-medium\">{t('sso.admin.scim.log.resource', 'Resource')}</th>\n <th className=\"text-left px-3 py-2 font-medium\">{t('sso.admin.scim.log.status', 'Status')}</th>\n <th className=\"text-left px-3 py-2 font-medium\">{t('sso.admin.scim.log.error', 'Error')}</th>\n </tr>\n </thead>\n <tbody>\n {logs.map((log) => (\n <tr key={log.id} className=\"border-t\">\n <td className=\"px-3 py-2 text-xs text-muted-foreground whitespace-nowrap\">\n {new Date(log.createdAt).toLocaleString()}\n </td>\n <td className=\"px-3 py-2\">\n <span className=\"inline-flex items-center rounded px-1.5 py-0.5 text-xs font-medium bg-muted\">\n {log.operation}\n </span>\n </td>\n <td className=\"px-3 py-2 text-xs\">{log.resourceType}</td>\n <td className=\"px-3 py-2\">\n <span className={`text-xs font-medium ${log.responseStatus < 300 ? 'text-status-success-text' : 'text-destructive'}`}>\n {log.responseStatus}\n </span>\n </td>\n <td className=\"px-3 py-2 text-xs text-muted-foreground truncate max-w-[200px]\">\n {log.errorMessage || '\u2014'}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n )}\n\n </>}\n\n {ConfirmDialogElement}\n </div>\n )\n}\n\nfunction SsoActivityTab({ configId }: { configId: string }) {\n const t = useT()\n const [identities, setIdentities] = React.useState<SsoIdentityRow[]>([])\n const [isLoading, setIsLoading] = React.useState(true)\n\n React.useEffect(() => {\n const load = async () => {\n setIsLoading(true)\n // For now, just show a placeholder \u2014 the identities list API is an M3 deliverable\n setIdentities([])\n setIsLoading(false)\n }\n load()\n }, [configId])\n\n if (isLoading) return <LoadingMessage label={t('common.loading', 'Loading...')} />\n\n if (identities.length === 0) {\n return (\n <div className=\"text-center py-8\">\n <p className=\"text-sm text-muted-foreground\">\n {t('sso.admin.activity.empty', 'No SSO login activity yet. Activity will appear here once users start logging in via SSO.')}\n </p>\n </div>\n )\n }\n\n return (\n <div className=\"space-y-2\">\n {identities.map((identity) => (\n <div key={identity.id} className=\"flex items-center justify-between p-3 border rounded-md\">\n <div>\n <span className=\"text-sm font-medium\">{identity.idpEmail}</span>\n {identity.idpName && <span className=\"text-sm text-muted-foreground ml-2\">({identity.idpName})</span>}\n </div>\n <span className=\"text-xs text-muted-foreground\">\n {identity.lastLoginAt ? new Date(identity.lastLoginAt).toLocaleString() : '\u2014'}\n </span>\n </div>\n ))}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/enterprise",
|
|
3
|
-
"version": "0.6.4-develop.
|
|
3
|
+
"version": "0.6.4-develop.4210.1.d412061cfe",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"@open-mercato/core": "0.6.4-develop.
|
|
68
|
-
"@open-mercato/ui": "0.6.4-develop.
|
|
67
|
+
"@open-mercato/core": "0.6.4-develop.4210.1.d412061cfe",
|
|
68
|
+
"@open-mercato/ui": "0.6.4-develop.4210.1.d412061cfe",
|
|
69
69
|
"@simplewebauthn/browser": "^13.3.0",
|
|
70
70
|
"@simplewebauthn/server": "^13.3.0",
|
|
71
71
|
"@simplewebauthn/types": "^12.0.0",
|
|
@@ -75,12 +75,12 @@
|
|
|
75
75
|
"qrcode": "^1.5.4"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
|
-
"@open-mercato/shared": "0.6.4-develop.
|
|
78
|
+
"@open-mercato/shared": "0.6.4-develop.4210.1.d412061cfe",
|
|
79
79
|
"react": "^19.0.0",
|
|
80
80
|
"react-dom": "^19.0.0"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
|
-
"@open-mercato/shared": "0.6.4-develop.
|
|
83
|
+
"@open-mercato/shared": "0.6.4-develop.4210.1.d412061cfe",
|
|
84
84
|
"@types/jest": "^30.0.0",
|
|
85
85
|
"@types/react": "^19.2.15",
|
|
86
86
|
"@types/react-dom": "^19.2.3",
|
|
@@ -281,7 +281,7 @@ export default function SsoConfigDetailPage() {
|
|
|
281
281
|
]
|
|
282
282
|
|
|
283
283
|
const statusBadge = (
|
|
284
|
-
<span className={`inline-flex items-center rounded-full px-2 py-1 text-xs font-medium ${config.isActive ? 'bg-
|
|
284
|
+
<span className={`inline-flex items-center rounded-full px-2 py-1 text-xs font-medium ${config.isActive ? 'bg-status-success-bg text-status-success-text' : 'bg-status-neutral-bg text-status-neutral-text'}`}>
|
|
285
285
|
{config.isActive ? t('sso.admin.status.active', 'Active') : t('sso.admin.status.inactive', 'Inactive')}
|
|
286
286
|
</span>
|
|
287
287
|
)
|
|
@@ -292,8 +292,8 @@ export default function SsoConfigDetailPage() {
|
|
|
292
292
|
<div className="flex flex-col gap-6 max-w-3xl">
|
|
293
293
|
{/* Activation banner after creation */}
|
|
294
294
|
{showActivationBanner && !config.isActive && (
|
|
295
|
-
<div className="rounded-lg border border-
|
|
296
|
-
<p className="text-sm font-medium text-
|
|
295
|
+
<div className="rounded-lg border border-status-info-border bg-status-info-bg p-4">
|
|
296
|
+
<p className="text-sm font-medium text-status-info-text mb-3">
|
|
297
297
|
{t('sso.admin.banner.created', 'Your SSO configuration has been created. Would you like to activate it now?')}
|
|
298
298
|
</p>
|
|
299
299
|
{activationError && (
|
|
@@ -350,7 +350,7 @@ export default function SsoConfigDetailPage() {
|
|
|
350
350
|
size="sm"
|
|
351
351
|
className={`h-auto rounded-none border-b-2 px-4 py-2 hover:bg-transparent ${
|
|
352
352
|
activeTab === tab.id
|
|
353
|
-
? 'border-
|
|
353
|
+
? 'border-accent-indigo text-foreground'
|
|
354
354
|
: 'border-transparent text-muted-foreground'
|
|
355
355
|
}`}
|
|
356
356
|
onClick={() => setActiveTab(tab.id)}
|
|
@@ -825,8 +825,8 @@ function ScimProvisioningTab({ configId, jitEnabled, issuer, onProvisioningChang
|
|
|
825
825
|
<div className="space-y-6">
|
|
826
826
|
{/* Google provider banner — SCIM not supported */}
|
|
827
827
|
{isGoogleProvider && (
|
|
828
|
-
<div className="rounded-lg border border-
|
|
829
|
-
<p className="text-sm text-
|
|
828
|
+
<div className="rounded-lg border border-status-info-border bg-status-info-bg p-4">
|
|
829
|
+
<p className="text-sm text-status-info-text">
|
|
830
830
|
{t('sso.admin.scim.googleNotSupported', 'Google Workspace does not support SCIM provisioning. Users are provisioned via Just-In-Time (JIT) on first login.')}
|
|
831
831
|
</p>
|
|
832
832
|
</div>
|
|
@@ -834,8 +834,8 @@ function ScimProvisioningTab({ configId, jitEnabled, issuer, onProvisioningChang
|
|
|
834
834
|
|
|
835
835
|
{/* JIT active banner */}
|
|
836
836
|
{!isGoogleProvider && jitEnabled && (
|
|
837
|
-
<div className="rounded-lg border border-
|
|
838
|
-
<p className="text-sm text-
|
|
837
|
+
<div className="rounded-lg border border-status-warning-border bg-status-warning-bg p-4">
|
|
838
|
+
<p className="text-sm text-status-warning-text">
|
|
839
839
|
{t('sso.admin.scim.jitActiveWarning', 'SCIM provisioning is unavailable while JIT provisioning is enabled. Disable JIT in the General tab to configure SCIM.')}
|
|
840
840
|
</p>
|
|
841
841
|
</div>
|
|
@@ -857,8 +857,8 @@ function ScimProvisioningTab({ configId, jitEnabled, issuer, onProvisioningChang
|
|
|
857
857
|
|
|
858
858
|
{/* Newly created token banner */}
|
|
859
859
|
{newlyCreatedToken && (
|
|
860
|
-
<div className="rounded-lg border border-
|
|
861
|
-
<p className="text-sm font-medium text-
|
|
860
|
+
<div className="rounded-lg border border-status-warning-border bg-status-warning-bg p-4">
|
|
861
|
+
<p className="text-sm font-medium text-status-warning-text mb-2">
|
|
862
862
|
{t('sso.admin.scim.tokenCreated', 'Your SCIM token has been created. Copy it now — it will not be shown again.')}
|
|
863
863
|
</p>
|
|
864
864
|
<div className="flex items-center gap-2 mb-2">
|
|
@@ -922,7 +922,7 @@ function ScimProvisioningTab({ configId, jitEnabled, issuer, onProvisioningChang
|
|
|
922
922
|
<span className="text-xs text-muted-foreground ml-2 font-mono">{token.tokenPrefix}...</span>
|
|
923
923
|
</div>
|
|
924
924
|
<span className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${
|
|
925
|
-
token.isActive ? 'bg-
|
|
925
|
+
token.isActive ? 'bg-status-success-bg text-status-success-text' : 'bg-status-neutral-bg text-status-neutral-text'
|
|
926
926
|
}`}>
|
|
927
927
|
{token.isActive ? t('sso.admin.scim.tokenActive', 'Active') : t('sso.admin.scim.tokenRevoked', 'Revoked')}
|
|
928
928
|
</span>
|
|
@@ -971,7 +971,7 @@ function ScimProvisioningTab({ configId, jitEnabled, issuer, onProvisioningChang
|
|
|
971
971
|
</td>
|
|
972
972
|
<td className="px-3 py-2 text-xs">{log.resourceType}</td>
|
|
973
973
|
<td className="px-3 py-2">
|
|
974
|
-
<span className={`text-xs font-medium ${log.responseStatus < 300 ? 'text-
|
|
974
|
+
<span className={`text-xs font-medium ${log.responseStatus < 300 ? 'text-status-success-text' : 'text-destructive'}`}>
|
|
975
975
|
{log.responseStatus}
|
|
976
976
|
</span>
|
|
977
977
|
</td>
|