@open-mercato/enterprise 0.6.1-develop.3278.1.a4d26475e4 → 0.6.1-develop.3287.1.450f4ffb56

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.
@@ -5,7 +5,7 @@ import { z } from "zod";
5
5
  import { CrudForm } from "@open-mercato/ui/backend/CrudForm";
6
6
  import { readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
7
7
  import { createCrudFormError } from "@open-mercato/ui/backend/utils/serverErrors";
8
- import { Input } from "@open-mercato/ui/primitives/input";
8
+ import { PasswordInput } from "@open-mercato/ui/primitives/password-input";
9
9
  import { flash } from "@open-mercato/ui/backend/FlashMessages";
10
10
  import { useT } from "@open-mercato/shared/lib/i18n/context";
11
11
  import { getPasswordPolicy, getPasswordRequirements, buildPasswordSchema, validatePassword } from "@open-mercato/shared/lib/auth/passwordPolicy";
@@ -17,10 +17,9 @@ function PasswordInputField({
17
17
  autoFocus
18
18
  }) {
19
19
  return /* @__PURE__ */ jsx(
20
- Input,
20
+ PasswordInput,
21
21
  {
22
22
  className: "max-w-xl",
23
- type: "password",
24
23
  value: typeof value === "string" ? value : "",
25
24
  onChange: (event) => setValue(event.target.value),
26
25
  disabled,
@@ -36,10 +35,9 @@ function TrackedPasswordInput({
36
35
  onValueChange
37
36
  }) {
38
37
  return /* @__PURE__ */ jsx(
39
- Input,
38
+ PasswordInput,
40
39
  {
41
40
  className: "max-w-xl",
42
- type: "password",
43
41
  value: typeof value === "string" ? value : "",
44
42
  onChange: (event) => {
45
43
  const nextValue = event.target.value;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/security/components/PasswordChangeForm.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { CrudForm, type CrudField, type CrudCustomFieldRenderProps } from '@open-mercato/ui/backend/CrudForm'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { getPasswordPolicy, getPasswordRequirements, buildPasswordSchema, validatePassword } from '@open-mercato/shared/lib/auth/passwordPolicy'\nimport { PasswordStrengthStatus } from './PasswordStrengthStatus'\n\ntype PasswordChangeValues = {\n currentPassword: string\n newPassword: string\n confirmPassword: string\n}\n\ntype ChangePasswordResponse = {\n ok: boolean\n}\n\nfunction PasswordInputField({\n value,\n setValue,\n disabled,\n autoFocus,\n}: CrudCustomFieldRenderProps) {\n return (\n <Input\n className=\"max-w-xl\"\n type=\"password\"\n value={typeof value === 'string' ? value : ''}\n onChange={(event) => setValue(event.target.value)}\n disabled={disabled}\n autoFocus={autoFocus}\n />\n )\n}\n\ntype TrackedPasswordInputProps = CrudCustomFieldRenderProps & {\n onValueChange: (value: string) => void\n}\n\nfunction TrackedPasswordInput({\n value,\n setValue,\n disabled,\n autoFocus,\n onValueChange,\n}: TrackedPasswordInputProps) {\n return (\n <Input\n className=\"max-w-xl\"\n type=\"password\"\n value={typeof value === 'string' ? value : ''}\n onChange={(event) => {\n const nextValue = event.target.value\n onValueChange(nextValue)\n setValue(nextValue)\n }}\n disabled={disabled}\n autoFocus={autoFocus}\n />\n )\n}\n\nexport default function PasswordChangeForm() {\n const t = useT()\n const [formKey, setFormKey] = React.useState(0)\n const [newPasswordValue, setNewPasswordValue] = React.useState('')\n const formId = 'security-password-change-form'\n\n const passwordPolicy = React.useMemo(() => getPasswordPolicy(), [])\n const passwordRequirements = React.useMemo(() => getPasswordRequirements(passwordPolicy), [passwordPolicy])\n\n const schema = React.useMemo(\n () =>\n z\n .object({\n currentPassword: z.string().min(1, t('security.profile.password.form.errors.currentRequired', 'Current password is required.')),\n newPassword: buildPasswordSchema({\n policy: passwordPolicy,\n message: t('security.profile.password.form.errors.passwordPolicy', 'Password does not meet the requirements.'),\n }),\n confirmPassword: z.string().min(1, t('security.profile.password.form.errors.confirmRequired', 'Please confirm the new password.')),\n })\n .superRefine((values, ctx) => {\n if (values.newPassword !== values.confirmPassword) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: t('security.profile.password.form.errors.passwordMismatch', 'Passwords do not match.'),\n path: ['confirmPassword'],\n })\n }\n }),\n [passwordPolicy, t],\n )\n\n const passwordValidation = React.useMemo(\n () => validatePassword(newPasswordValue, passwordPolicy),\n [newPasswordValue, passwordPolicy],\n )\n\n const hasNewPasswordInput = newPasswordValue.trim().length > 0\n const fields = React.useMemo<CrudField[]>(\n () => [\n {\n id: 'currentPassword',\n label: t('security.profile.password.form.currentPassword', 'Current password'),\n type: 'custom',\n required: true,\n component: PasswordInputField,\n },\n {\n id: 'newPassword',\n label: t('security.profile.password.form.newPassword', 'New password'),\n type: 'custom',\n required: true,\n component: (props) => <TrackedPasswordInput {...props} onValueChange={setNewPasswordValue} />,\n },\n {\n id: 'confirmPassword',\n label: t('security.profile.password.form.confirmPassword', 'Confirm new password'),\n type: 'custom',\n required: true,\n component: PasswordInputField,\n },\n {\n id: 'passwordRequirements',\n label: '',\n type: 'custom',\n component: () => (\n <PasswordStrengthStatus\n hasInput={hasNewPasswordInput}\n passwordPolicy={passwordPolicy}\n passwordRequirements={passwordRequirements}\n passwordValidation={passwordValidation}\n framed={false}\n />\n ),\n },\n ],\n [hasNewPasswordInput, passwordPolicy, passwordRequirements, passwordValidation, t],\n )\n\n const handleSubmit = React.useCallback(\n async (values: PasswordChangeValues) => {\n if (!values.currentPassword.trim() || !values.newPassword.trim()) {\n throw createCrudFormError(\n t('security.profile.password.form.errors.required', 'Both current and new password are required.'),\n )\n }\n\n await readApiResultOrThrow<ChangePasswordResponse>(\n '/api/security/profile/password',\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n currentPassword: values.currentPassword,\n newPassword: values.newPassword,\n }),\n },\n { errorMessage: t('security.profile.password.form.errors.save', 'Failed to update password.') },\n )\n\n setFormKey((previous) => previous + 1)\n setNewPasswordValue('')\n flash(t('security.profile.password.form.success', 'Password updated.'), 'success')\n },\n [t],\n )\n\n return (\n <CrudForm<PasswordChangeValues>\n title={t('security.profile.password.form.title', 'Change password')}\n key={formKey}\n formId={formId}\n schema={schema}\n fields={fields}\n initialValues={{\n currentPassword: '',\n newPassword: '',\n confirmPassword: '',\n }}\n hideFooterActions\n onSubmit={handleSubmit}\n embedded={false}\n />\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { CrudForm, type CrudField, type CrudCustomFieldRenderProps } from '@open-mercato/ui/backend/CrudForm'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { PasswordInput } from '@open-mercato/ui/primitives/password-input'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { getPasswordPolicy, getPasswordRequirements, buildPasswordSchema, validatePassword } from '@open-mercato/shared/lib/auth/passwordPolicy'\nimport { PasswordStrengthStatus } from './PasswordStrengthStatus'\n\ntype PasswordChangeValues = {\n currentPassword: string\n newPassword: string\n confirmPassword: string\n}\n\ntype ChangePasswordResponse = {\n ok: boolean\n}\n\nfunction PasswordInputField({\n value,\n setValue,\n disabled,\n autoFocus,\n}: CrudCustomFieldRenderProps) {\n return (\n <PasswordInput\n className=\"max-w-xl\"\n value={typeof value === 'string' ? value : ''}\n onChange={(event) => setValue(event.target.value)}\n disabled={disabled}\n autoFocus={autoFocus}\n />\n )\n}\n\ntype TrackedPasswordInputProps = CrudCustomFieldRenderProps & {\n onValueChange: (value: string) => void\n}\n\nfunction TrackedPasswordInput({\n value,\n setValue,\n disabled,\n autoFocus,\n onValueChange,\n}: TrackedPasswordInputProps) {\n return (\n <PasswordInput\n className=\"max-w-xl\"\n value={typeof value === 'string' ? value : ''}\n onChange={(event) => {\n const nextValue = event.target.value\n onValueChange(nextValue)\n setValue(nextValue)\n }}\n disabled={disabled}\n autoFocus={autoFocus}\n />\n )\n}\n\nexport default function PasswordChangeForm() {\n const t = useT()\n const [formKey, setFormKey] = React.useState(0)\n const [newPasswordValue, setNewPasswordValue] = React.useState('')\n const formId = 'security-password-change-form'\n\n const passwordPolicy = React.useMemo(() => getPasswordPolicy(), [])\n const passwordRequirements = React.useMemo(() => getPasswordRequirements(passwordPolicy), [passwordPolicy])\n\n const schema = React.useMemo(\n () =>\n z\n .object({\n currentPassword: z.string().min(1, t('security.profile.password.form.errors.currentRequired', 'Current password is required.')),\n newPassword: buildPasswordSchema({\n policy: passwordPolicy,\n message: t('security.profile.password.form.errors.passwordPolicy', 'Password does not meet the requirements.'),\n }),\n confirmPassword: z.string().min(1, t('security.profile.password.form.errors.confirmRequired', 'Please confirm the new password.')),\n })\n .superRefine((values, ctx) => {\n if (values.newPassword !== values.confirmPassword) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: t('security.profile.password.form.errors.passwordMismatch', 'Passwords do not match.'),\n path: ['confirmPassword'],\n })\n }\n }),\n [passwordPolicy, t],\n )\n\n const passwordValidation = React.useMemo(\n () => validatePassword(newPasswordValue, passwordPolicy),\n [newPasswordValue, passwordPolicy],\n )\n\n const hasNewPasswordInput = newPasswordValue.trim().length > 0\n const fields = React.useMemo<CrudField[]>(\n () => [\n {\n id: 'currentPassword',\n label: t('security.profile.password.form.currentPassword', 'Current password'),\n type: 'custom',\n required: true,\n component: PasswordInputField,\n },\n {\n id: 'newPassword',\n label: t('security.profile.password.form.newPassword', 'New password'),\n type: 'custom',\n required: true,\n component: (props) => <TrackedPasswordInput {...props} onValueChange={setNewPasswordValue} />,\n },\n {\n id: 'confirmPassword',\n label: t('security.profile.password.form.confirmPassword', 'Confirm new password'),\n type: 'custom',\n required: true,\n component: PasswordInputField,\n },\n {\n id: 'passwordRequirements',\n label: '',\n type: 'custom',\n component: () => (\n <PasswordStrengthStatus\n hasInput={hasNewPasswordInput}\n passwordPolicy={passwordPolicy}\n passwordRequirements={passwordRequirements}\n passwordValidation={passwordValidation}\n framed={false}\n />\n ),\n },\n ],\n [hasNewPasswordInput, passwordPolicy, passwordRequirements, passwordValidation, t],\n )\n\n const handleSubmit = React.useCallback(\n async (values: PasswordChangeValues) => {\n if (!values.currentPassword.trim() || !values.newPassword.trim()) {\n throw createCrudFormError(\n t('security.profile.password.form.errors.required', 'Both current and new password are required.'),\n )\n }\n\n await readApiResultOrThrow<ChangePasswordResponse>(\n '/api/security/profile/password',\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n currentPassword: values.currentPassword,\n newPassword: values.newPassword,\n }),\n },\n { errorMessage: t('security.profile.password.form.errors.save', 'Failed to update password.') },\n )\n\n setFormKey((previous) => previous + 1)\n setNewPasswordValue('')\n flash(t('security.profile.password.form.success', 'Password updated.'), 'success')\n },\n [t],\n )\n\n return (\n <CrudForm<PasswordChangeValues>\n title={t('security.profile.password.form.title', 'Change password')}\n key={formKey}\n formId={formId}\n schema={schema}\n fields={fields}\n initialValues={{\n currentPassword: '',\n newPassword: '',\n confirmPassword: '',\n }}\n hideFooterActions\n onSubmit={handleSubmit}\n embedded={false}\n />\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -6,7 +6,7 @@ import { useT } from "@open-mercato/shared/lib/i18n/context";
6
6
  import { readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
7
7
  import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@open-mercato/ui/primitives/dialog";
8
8
  import { Button } from "@open-mercato/ui/primitives/button";
9
- import { Input } from "@open-mercato/ui/primitives/input";
9
+ import { PasswordInput } from "@open-mercato/ui/primitives/password-input";
10
10
  import { useProviderChallengeComponent } from "./mfa-ui-registry.js";
11
11
  function readErrorMessage(error, fallback) {
12
12
  if (error && typeof error === "object" && "message" in error && typeof error.message === "string") {
@@ -180,14 +180,14 @@ function SudoChallengeModal({
180
180
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
181
181
  /* @__PURE__ */ jsx("label", { className: "text-sm font-medium", htmlFor: "sudo-password", children: t("security.admin.sudo.challenge.password.label", "Password") }),
182
182
  /* @__PURE__ */ jsx(
183
- Input,
183
+ PasswordInput,
184
184
  {
185
185
  id: "sudo-password",
186
- type: "password",
187
186
  value: password,
188
187
  autoFocus: true,
189
188
  onChange: (event) => setPassword(event.target.value),
190
- placeholder: t("security.admin.sudo.challenge.password.placeholder", "Enter your password")
189
+ placeholder: t("security.admin.sudo.challenge.password.placeholder", "Enter your password"),
190
+ autoComplete: "current-password"
191
191
  }
192
192
  )
193
193
  ] }),
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/security/components/SudoChallengeModal.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { AlertTriangle, ChevronDown, ChevronUp } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { useProviderChallengeComponent } from './mfa-ui-registry'\n\nexport type SudoChallengeMethod = {\n type: string\n label: string\n icon: string\n components?: {\n list?: string\n details?: string\n challenge?: string\n }\n}\n\nexport type PendingSudoChallenge = {\n sessionId: string\n targetIdentifier: string\n method: 'password' | 'mfa'\n availableMfaMethods: SudoChallengeMethod[]\n}\n\ntype SudoChallengeModalProps = {\n open: boolean\n challenge: PendingSudoChallenge | null\n onResolve: (result: { sudoToken: string; expiresAt: string } | null) => void\n}\n\ntype PrepareResponse = {\n clientData?: Record<string, unknown>\n}\n\ntype VerifyResponse = {\n sudoToken: string\n expiresAt: string\n}\n\nfunction readErrorMessage(error: unknown, fallback: string): string {\n if (error && typeof error === 'object' && 'message' in error && typeof error.message === 'string') {\n return error.message\n }\n return fallback\n}\n\nexport default function SudoChallengeModal({\n open,\n challenge,\n onResolve,\n}: SudoChallengeModalProps) {\n const t = useT()\n const [password, setPassword] = React.useState('')\n const [loading, setLoading] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n const [preparedMethods, setPreparedMethods] = React.useState<Record<string, true>>({})\n const [preparedClientData, setPreparedClientData] = React.useState<Record<string, Record<string, unknown> | undefined>>({})\n const [selectedMethod, setSelectedMethod] = React.useState<string>('')\n const [showMoreOptions, setShowMoreOptions] = React.useState(false)\n\n React.useEffect(() => {\n if (!open || !challenge) return\n setPassword('')\n setError(null)\n setPreparedMethods({})\n setPreparedClientData({})\n setSelectedMethod(challenge.availableMfaMethods[0]?.type ?? '')\n setShowMoreOptions(false)\n }, [challenge, open])\n\n const prepare = React.useCallback(async (methodType: string) => {\n if (!challenge) return undefined\n const result = await readApiResultOrThrow<PrepareResponse>('/api/security/sudo/prepare', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n sessionId: challenge.sessionId,\n methodType,\n }),\n })\n return result.clientData\n }, [challenge])\n\n React.useEffect(() => {\n if (!challenge || challenge.method !== 'mfa' || !selectedMethod) return\n if (preparedMethods[selectedMethod]) return\n\n let active = true\n setLoading(true)\n setError(null)\n\n prepare(selectedMethod)\n .then((clientData) => {\n if (!active) return\n setPreparedClientData((current) => ({ ...current, [selectedMethod]: clientData }))\n setPreparedMethods((current) => ({ ...current, [selectedMethod]: true }))\n })\n .catch((err) => {\n if (!active) return\n setError(\n readErrorMessage(\n err,\n t('security.admin.sudo.challenge.errors.prepare', 'Failed to prepare sudo challenge.'),\n ),\n )\n })\n .finally(() => {\n if (!active) return\n setLoading(false)\n })\n\n return () => {\n active = false\n }\n }, [challenge, prepare, preparedMethods, selectedMethod, t])\n\n const verify = React.useCallback(async (methodType: string, payload: Record<string, unknown>) => {\n if (!challenge) return\n const result = await readApiResultOrThrow<VerifyResponse>('/api/security/sudo/verify', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n sessionId: challenge.sessionId,\n targetIdentifier: challenge.targetIdentifier,\n methodType,\n payload,\n }),\n })\n onResolve(result)\n }, [challenge, onResolve])\n\n const handlePasswordVerify = React.useCallback(async () => {\n if (!challenge) return\n setLoading(true)\n setError(null)\n try {\n await verify('password', { password })\n } catch (err) {\n setError(\n readErrorMessage(\n err,\n t('security.admin.sudo.challenge.errors.verify', 'Failed to verify sudo challenge.'),\n ),\n )\n } finally {\n setLoading(false)\n }\n }, [challenge, password, t, verify])\n\n const handleMfaVerify = React.useCallback(async (payload: Record<string, unknown>) => {\n if (!selectedMethod) return\n setLoading(true)\n setError(null)\n try {\n await verify(selectedMethod, payload)\n } catch (err) {\n setError(\n readErrorMessage(\n err,\n t('security.admin.sudo.challenge.errors.verify', 'Failed to verify sudo challenge.'),\n ),\n )\n } finally {\n setLoading(false)\n }\n }, [selectedMethod, t, verify])\n\n const handlePrepare = React.useCallback(async () => {\n if (!selectedMethod) return\n const cached = preparedClientData[selectedMethod]\n if (preparedMethods[selectedMethod]) {\n return cached\n }\n\n setLoading(true)\n setError(null)\n try {\n const clientData = await prepare(selectedMethod)\n setPreparedClientData((current) => ({ ...current, [selectedMethod]: clientData }))\n setPreparedMethods((current) => ({ ...current, [selectedMethod]: true }))\n return clientData\n } catch (err) {\n setError(\n readErrorMessage(\n err,\n t('security.admin.sudo.challenge.errors.prepare', 'Failed to prepare sudo challenge.'),\n ),\n )\n } finally {\n setLoading(false)\n }\n return undefined\n }, [prepare, preparedClientData, preparedMethods, selectedMethod, t])\n\n const handleClose = React.useCallback(() => {\n onResolve(null)\n }, [onResolve])\n\n const selected = challenge?.availableMfaMethods.find((method) => method.type === selectedMethod) ?? null\n const alternativeMethods = React.useMemo(\n () => challenge?.availableMfaMethods.filter((method) => method.type !== selectedMethod) ?? [],\n [challenge, selectedMethod],\n )\n const ChallengeComponent = useProviderChallengeComponent(selected ?? { type: 'unknown' })\n\n return (\n <Dialog open={open} onOpenChange={(nextOpen) => { if (!nextOpen) handleClose() }}>\n <DialogContent\n className=\"sm:max-w-lg [&_[data-dialog-close]]:rounded-full [&_[data-dialog-close]]:border [&_[data-dialog-close]]:border-white/20 [&_[data-dialog-close]]:bg-white/5 [&_[data-dialog-close]]:opacity-100 [&_[data-dialog-close]]:transition-none [&_[data-dialog-close]]:hover:bg-white/10 [&_[data-dialog-close]]:hover:opacity-100 [&_[data-dialog-close]]:focus-visible:ring-0 [&_[data-dialog-close]]:focus-visible:ring-offset-0\"\n onKeyDown={(event) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter' && challenge?.method === 'password') {\n event.preventDefault()\n void handlePasswordVerify()\n }\n }}\n >\n <DialogHeader>\n <DialogTitle>{t('security.admin.sudo.challenge.title', 'Confirm sensitive action')}</DialogTitle>\n <DialogDescription>\n {t(\n 'security.admin.sudo.challenge.description',\n 'Re-authenticate to continue with this protected action.',\n )}\n </DialogDescription>\n </DialogHeader>\n\n {challenge?.method === 'password' ? (\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"sudo-password\">\n {t('security.admin.sudo.challenge.password.label', 'Password')}\n </label>\n <Input\n id=\"sudo-password\"\n type=\"password\"\n value={password}\n autoFocus\n onChange={(event) => setPassword(event.target.value)}\n placeholder={t('security.admin.sudo.challenge.password.placeholder', 'Enter your password')}\n />\n </div>\n\n {error ? <p className=\"text-sm text-red-500\" role=\"alert\">{error}</p> : null}\n\n <div className=\"space-y-3\">\n <Button\n type=\"button\"\n className=\"w-full\"\n onClick={() => void handlePasswordVerify()}\n disabled={loading || password.trim().length === 0}\n >\n {t('security.admin.sudo.challenge.actions.verify', 'Verify')}\n </Button>\n <div className=\"flex justify-center\">\n <Button type=\"button\" variant=\"outline\" className=\"min-w-28\" onClick={handleClose} disabled={loading}>\n {t('ui.actions.cancel', 'Cancel')}\n </Button>\n </div>\n </div>\n </div>\n ) : challenge?.method === 'mfa' && selected ? (\n <div className=\"space-y-4\">\n <ChallengeComponent\n method={selected}\n loading={loading}\n onVerify={handleMfaVerify}\n onPrepare={handlePrepare}\n onResend={handlePrepare}\n submitLabel={t('security.admin.sudo.challenge.actions.verify', 'Verify')}\n />\n\n {alternativeMethods.length > 0 ? (\n <div className=\"space-y-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n className=\"w-full justify-center\"\n onClick={() => setShowMoreOptions((current) => !current)}\n >\n {t('security.admin.sudo.challenge.actions.moreOptions', 'Use another method')}\n {showMoreOptions ? <ChevronUp className=\"ml-2 size-4\" /> : <ChevronDown className=\"ml-2 size-4\" />}\n </Button>\n {showMoreOptions ? (\n <div className=\"space-y-2\">\n {alternativeMethods.map((method) => (\n <Button\n key={`${challenge.sessionId}:${method.type}`}\n type=\"button\"\n variant=\"outline\"\n className=\"w-full justify-center\"\n onClick={() => {\n setSelectedMethod(method.type)\n setError(null)\n setShowMoreOptions(false)\n }}\n >\n {method.label}\n </Button>\n ))}\n </div>\n ) : null}\n </div>\n ) : null}\n\n {error ? <p className=\"text-sm text-red-500\" role=\"alert\">{error}</p> : null}\n\n </div>\n ) : (\n <div className=\"flex items-center gap-2 rounded-md border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-900\">\n <AlertTriangle className=\"size-4\" />\n <span>{t('security.admin.sudo.challenge.noMethods', 'No sudo authentication methods are available.')}</span>\n </div>\n )}\n </DialogContent>\n </Dialog>\n )\n}\n"],
5
- "mappings": ";AA6NQ,SACE,KADF;AA3NR,YAAY,WAAW;AACvB,SAAS,eAAe,aAAa,iBAAiB;AACtD,SAAS,YAAY;AACrB,SAAS,4BAA4B;AACrC,SAAS,QAAQ,eAAe,mBAAmB,cAAc,mBAAmB;AACpF,SAAS,cAAc;AACvB,SAAS,aAAa;AACtB,SAAS,qCAAqC;AAmC9C,SAAS,iBAAiB,OAAgB,UAA0B;AAClE,MAAI,SAAS,OAAO,UAAU,YAAY,aAAa,SAAS,OAAO,MAAM,YAAY,UAAU;AACjG,WAAO,MAAM;AAAA,EACf;AACA,SAAO;AACT;AAEe,SAAR,mBAAoC;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AACF,GAA4B;AAC1B,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAS,EAAE;AACjD,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAClD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,IAAI;AAC5D,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,MAAM,SAA+B,CAAC,CAAC;AACrF,QAAM,CAAC,oBAAoB,qBAAqB,IAAI,MAAM,SAA8D,CAAC,CAAC;AAC1H,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,MAAM,SAAiB,EAAE;AACrE,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,MAAM,SAAS,KAAK;AAElE,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,QAAQ,CAAC,UAAW;AACzB,gBAAY,EAAE;AACd,aAAS,IAAI;AACb,uBAAmB,CAAC,CAAC;AACrB,0BAAsB,CAAC,CAAC;AACxB,sBAAkB,UAAU,oBAAoB,CAAC,GAAG,QAAQ,EAAE;AAC9D,uBAAmB,KAAK;AAAA,EAC1B,GAAG,CAAC,WAAW,IAAI,CAAC;AAEpB,QAAM,UAAU,MAAM,YAAY,OAAO,eAAuB;AAC9D,QAAI,CAAC,UAAW,QAAO;AACvB,UAAM,SAAS,MAAM,qBAAsC,8BAA8B;AAAA,MACvF,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU;AAAA,QACnB,WAAW,UAAU;AAAA,QACrB;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AACD,WAAO,OAAO;AAAA,EAChB,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,aAAa,UAAU,WAAW,SAAS,CAAC,eAAgB;AACjE,QAAI,gBAAgB,cAAc,EAAG;AAErC,QAAI,SAAS;AACb,eAAW,IAAI;AACf,aAAS,IAAI;AAEb,YAAQ,cAAc,EACnB,KAAK,CAAC,eAAe;AACpB,UAAI,CAAC,OAAQ;AACb,4BAAsB,CAAC,aAAa,EAAE,GAAG,SAAS,CAAC,cAAc,GAAG,WAAW,EAAE;AACjF,yBAAmB,CAAC,aAAa,EAAE,GAAG,SAAS,CAAC,cAAc,GAAG,KAAK,EAAE;AAAA,IAC1E,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,UAAI,CAAC,OAAQ;AACb;AAAA,QACE;AAAA,UACE;AAAA,UACA,EAAE,gDAAgD,mCAAmC;AAAA,QACvF;AAAA,MACF;AAAA,IACF,CAAC,EACA,QAAQ,MAAM;AACb,UAAI,CAAC,OAAQ;AACb,iBAAW,KAAK;AAAA,IAClB,CAAC;AAEH,WAAO,MAAM;AACX,eAAS;AAAA,IACX;AAAA,EACF,GAAG,CAAC,WAAW,SAAS,iBAAiB,gBAAgB,CAAC,CAAC;AAE3D,QAAM,SAAS,MAAM,YAAY,OAAO,YAAoB,YAAqC;AAC/F,QAAI,CAAC,UAAW;AAChB,UAAM,SAAS,MAAM,qBAAqC,6BAA6B;AAAA,MACrF,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU;AAAA,QACnB,WAAW,UAAU;AAAA,QACrB,kBAAkB,UAAU;AAAA,QAC5B;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AACD,cAAU,MAAM;AAAA,EAClB,GAAG,CAAC,WAAW,SAAS,CAAC;AAEzB,QAAM,uBAAuB,MAAM,YAAY,YAAY;AACzD,QAAI,CAAC,UAAW;AAChB,eAAW,IAAI;AACf,aAAS,IAAI;AACb,QAAI;AACF,YAAM,OAAO,YAAY,EAAE,SAAS,CAAC;AAAA,IACvC,SAAS,KAAK;AACZ;AAAA,QACE;AAAA,UACE;AAAA,UACA,EAAE,+CAA+C,kCAAkC;AAAA,QACrF;AAAA,MACF;AAAA,IACF,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,WAAW,UAAU,GAAG,MAAM,CAAC;AAEnC,QAAM,kBAAkB,MAAM,YAAY,OAAO,YAAqC;AACpF,QAAI,CAAC,eAAgB;AACrB,eAAW,IAAI;AACf,aAAS,IAAI;AACb,QAAI;AACF,YAAM,OAAO,gBAAgB,OAAO;AAAA,IACtC,SAAS,KAAK;AACZ;AAAA,QACE;AAAA,UACE;AAAA,UACA,EAAE,+CAA+C,kCAAkC;AAAA,QACrF;AAAA,MACF;AAAA,IACF,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,gBAAgB,GAAG,MAAM,CAAC;AAE9B,QAAM,gBAAgB,MAAM,YAAY,YAAY;AAClD,QAAI,CAAC,eAAgB;AACrB,UAAM,SAAS,mBAAmB,cAAc;AAChD,QAAI,gBAAgB,cAAc,GAAG;AACnC,aAAO;AAAA,IACT;AAEA,eAAW,IAAI;AACf,aAAS,IAAI;AACb,QAAI;AACF,YAAM,aAAa,MAAM,QAAQ,cAAc;AAC/C,4BAAsB,CAAC,aAAa,EAAE,GAAG,SAAS,CAAC,cAAc,GAAG,WAAW,EAAE;AACjF,yBAAmB,CAAC,aAAa,EAAE,GAAG,SAAS,CAAC,cAAc,GAAG,KAAK,EAAE;AACxE,aAAO;AAAA,IACT,SAAS,KAAK;AACZ;AAAA,QACE;AAAA,UACE;AAAA,UACA,EAAE,gDAAgD,mCAAmC;AAAA,QACvF;AAAA,MACF;AAAA,IACF,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AACA,WAAO;AAAA,EACT,GAAG,CAAC,SAAS,oBAAoB,iBAAiB,gBAAgB,CAAC,CAAC;AAEpE,QAAM,cAAc,MAAM,YAAY,MAAM;AAC1C,cAAU,IAAI;AAAA,EAChB,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,WAAW,WAAW,oBAAoB,KAAK,CAAC,WAAW,OAAO,SAAS,cAAc,KAAK;AACpG,QAAM,qBAAqB,MAAM;AAAA,IAC/B,MAAM,WAAW,oBAAoB,OAAO,CAAC,WAAW,OAAO,SAAS,cAAc,KAAK,CAAC;AAAA,IAC5F,CAAC,WAAW,cAAc;AAAA,EAC5B;AACA,QAAM,qBAAqB,8BAA8B,YAAY,EAAE,MAAM,UAAU,CAAC;AAExF,SACE,oBAAC,UAAO,MAAY,cAAc,CAAC,aAAa;AAAE,QAAI,CAAC,SAAU,aAAY;AAAA,EAAE,GAC7E;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,WAAW,CAAC,UAAU;AACtB,aAAK,MAAM,WAAW,MAAM,YAAY,MAAM,QAAQ,WAAW,WAAW,WAAW,YAAY;AACjG,gBAAM,eAAe;AACrB,eAAK,qBAAqB;AAAA,QAC5B;AAAA,MACF;AAAA,MAEE;AAAA,6BAAC,gBACC;AAAA,8BAAC,eAAa,YAAE,uCAAuC,0BAA0B,GAAE;AAAA,UACnF,oBAAC,qBACE;AAAA,YACC;AAAA,YACA;AAAA,UACF,GACF;AAAA,WACF;AAAA,QAEC,WAAW,WAAW,aACrB,qBAAC,SAAI,WAAU,aACb;AAAA,+BAAC,SAAI,WAAU,aACb;AAAA,gCAAC,WAAM,WAAU,uBAAsB,SAAQ,iBAC5C,YAAE,gDAAgD,UAAU,GAC/D;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,IAAG;AAAA,gBACH,MAAK;AAAA,gBACL,OAAO;AAAA,gBACP,WAAS;AAAA,gBACT,UAAU,CAAC,UAAU,YAAY,MAAM,OAAO,KAAK;AAAA,gBACnD,aAAa,EAAE,sDAAsD,qBAAqB;AAAA;AAAA,YAC5F;AAAA,aACF;AAAA,UAEC,QAAQ,oBAAC,OAAE,WAAU,wBAAuB,MAAK,SAAS,iBAAM,IAAO;AAAA,UAExE,qBAAC,SAAI,WAAU,aACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,SAAS,MAAM,KAAK,qBAAqB;AAAA,gBACzC,UAAU,WAAW,SAAS,KAAK,EAAE,WAAW;AAAA,gBAE/C,YAAE,gDAAgD,QAAQ;AAAA;AAAA,YAC7D;AAAA,YACA,oBAAC,SAAI,WAAU,uBACb,8BAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,WAAU,YAAW,SAAS,aAAa,UAAU,SAC1F,YAAE,qBAAqB,QAAQ,GAClC,GACF;AAAA,aACF;AAAA,WACF,IACE,WAAW,WAAW,SAAS,WACjC,qBAAC,SAAI,WAAU,aACb;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,QAAQ;AAAA,cACR;AAAA,cACA,UAAU;AAAA,cACV,WAAW;AAAA,cACX,UAAU;AAAA,cACV,aAAa,EAAE,gDAAgD,QAAQ;AAAA;AAAA,UACzE;AAAA,UAEC,mBAAmB,SAAS,IAC3B,qBAAC,SAAI,WAAU,aACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAQ;AAAA,gBACR,WAAU;AAAA,gBACV,SAAS,MAAM,mBAAmB,CAAC,YAAY,CAAC,OAAO;AAAA,gBAEtD;AAAA,oBAAE,qDAAqD,oBAAoB;AAAA,kBAC3E,kBAAkB,oBAAC,aAAU,WAAU,eAAc,IAAK,oBAAC,eAAY,WAAU,eAAc;AAAA;AAAA;AAAA,YAClG;AAAA,YACC,kBACC,oBAAC,SAAI,WAAU,aACZ,6BAAmB,IAAI,CAAC,WACvB;AAAA,cAAC;AAAA;AAAA,gBAEC,MAAK;AAAA,gBACL,SAAQ;AAAA,gBACR,WAAU;AAAA,gBACV,SAAS,MAAM;AACb,oCAAkB,OAAO,IAAI;AAC7B,2BAAS,IAAI;AACb,qCAAmB,KAAK;AAAA,gBAC1B;AAAA,gBAEC,iBAAO;AAAA;AAAA,cAVH,GAAG,UAAU,SAAS,IAAI,OAAO,IAAI;AAAA,YAW5C,CACD,GACH,IACE;AAAA,aACN,IACE;AAAA,UAEH,QAAQ,oBAAC,OAAE,WAAU,wBAAuB,MAAK,SAAS,iBAAM,IAAO;AAAA,WAE1E,IAEA,qBAAC,SAAI,WAAU,2GACb;AAAA,8BAAC,iBAAc,WAAU,UAAS;AAAA,UAClC,oBAAC,UAAM,YAAE,2CAA2C,+CAA+C,GAAE;AAAA,WACvG;AAAA;AAAA;AAAA,EAEJ,GACF;AAEJ;",
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { AlertTriangle, ChevronDown, ChevronUp } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { PasswordInput } from '@open-mercato/ui/primitives/password-input'\nimport { useProviderChallengeComponent } from './mfa-ui-registry'\n\nexport type SudoChallengeMethod = {\n type: string\n label: string\n icon: string\n components?: {\n list?: string\n details?: string\n challenge?: string\n }\n}\n\nexport type PendingSudoChallenge = {\n sessionId: string\n targetIdentifier: string\n method: 'password' | 'mfa'\n availableMfaMethods: SudoChallengeMethod[]\n}\n\ntype SudoChallengeModalProps = {\n open: boolean\n challenge: PendingSudoChallenge | null\n onResolve: (result: { sudoToken: string; expiresAt: string } | null) => void\n}\n\ntype PrepareResponse = {\n clientData?: Record<string, unknown>\n}\n\ntype VerifyResponse = {\n sudoToken: string\n expiresAt: string\n}\n\nfunction readErrorMessage(error: unknown, fallback: string): string {\n if (error && typeof error === 'object' && 'message' in error && typeof error.message === 'string') {\n return error.message\n }\n return fallback\n}\n\nexport default function SudoChallengeModal({\n open,\n challenge,\n onResolve,\n}: SudoChallengeModalProps) {\n const t = useT()\n const [password, setPassword] = React.useState('')\n const [loading, setLoading] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n const [preparedMethods, setPreparedMethods] = React.useState<Record<string, true>>({})\n const [preparedClientData, setPreparedClientData] = React.useState<Record<string, Record<string, unknown> | undefined>>({})\n const [selectedMethod, setSelectedMethod] = React.useState<string>('')\n const [showMoreOptions, setShowMoreOptions] = React.useState(false)\n\n React.useEffect(() => {\n if (!open || !challenge) return\n setPassword('')\n setError(null)\n setPreparedMethods({})\n setPreparedClientData({})\n setSelectedMethod(challenge.availableMfaMethods[0]?.type ?? '')\n setShowMoreOptions(false)\n }, [challenge, open])\n\n const prepare = React.useCallback(async (methodType: string) => {\n if (!challenge) return undefined\n const result = await readApiResultOrThrow<PrepareResponse>('/api/security/sudo/prepare', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n sessionId: challenge.sessionId,\n methodType,\n }),\n })\n return result.clientData\n }, [challenge])\n\n React.useEffect(() => {\n if (!challenge || challenge.method !== 'mfa' || !selectedMethod) return\n if (preparedMethods[selectedMethod]) return\n\n let active = true\n setLoading(true)\n setError(null)\n\n prepare(selectedMethod)\n .then((clientData) => {\n if (!active) return\n setPreparedClientData((current) => ({ ...current, [selectedMethod]: clientData }))\n setPreparedMethods((current) => ({ ...current, [selectedMethod]: true }))\n })\n .catch((err) => {\n if (!active) return\n setError(\n readErrorMessage(\n err,\n t('security.admin.sudo.challenge.errors.prepare', 'Failed to prepare sudo challenge.'),\n ),\n )\n })\n .finally(() => {\n if (!active) return\n setLoading(false)\n })\n\n return () => {\n active = false\n }\n }, [challenge, prepare, preparedMethods, selectedMethod, t])\n\n const verify = React.useCallback(async (methodType: string, payload: Record<string, unknown>) => {\n if (!challenge) return\n const result = await readApiResultOrThrow<VerifyResponse>('/api/security/sudo/verify', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n sessionId: challenge.sessionId,\n targetIdentifier: challenge.targetIdentifier,\n methodType,\n payload,\n }),\n })\n onResolve(result)\n }, [challenge, onResolve])\n\n const handlePasswordVerify = React.useCallback(async () => {\n if (!challenge) return\n setLoading(true)\n setError(null)\n try {\n await verify('password', { password })\n } catch (err) {\n setError(\n readErrorMessage(\n err,\n t('security.admin.sudo.challenge.errors.verify', 'Failed to verify sudo challenge.'),\n ),\n )\n } finally {\n setLoading(false)\n }\n }, [challenge, password, t, verify])\n\n const handleMfaVerify = React.useCallback(async (payload: Record<string, unknown>) => {\n if (!selectedMethod) return\n setLoading(true)\n setError(null)\n try {\n await verify(selectedMethod, payload)\n } catch (err) {\n setError(\n readErrorMessage(\n err,\n t('security.admin.sudo.challenge.errors.verify', 'Failed to verify sudo challenge.'),\n ),\n )\n } finally {\n setLoading(false)\n }\n }, [selectedMethod, t, verify])\n\n const handlePrepare = React.useCallback(async () => {\n if (!selectedMethod) return\n const cached = preparedClientData[selectedMethod]\n if (preparedMethods[selectedMethod]) {\n return cached\n }\n\n setLoading(true)\n setError(null)\n try {\n const clientData = await prepare(selectedMethod)\n setPreparedClientData((current) => ({ ...current, [selectedMethod]: clientData }))\n setPreparedMethods((current) => ({ ...current, [selectedMethod]: true }))\n return clientData\n } catch (err) {\n setError(\n readErrorMessage(\n err,\n t('security.admin.sudo.challenge.errors.prepare', 'Failed to prepare sudo challenge.'),\n ),\n )\n } finally {\n setLoading(false)\n }\n return undefined\n }, [prepare, preparedClientData, preparedMethods, selectedMethod, t])\n\n const handleClose = React.useCallback(() => {\n onResolve(null)\n }, [onResolve])\n\n const selected = challenge?.availableMfaMethods.find((method) => method.type === selectedMethod) ?? null\n const alternativeMethods = React.useMemo(\n () => challenge?.availableMfaMethods.filter((method) => method.type !== selectedMethod) ?? [],\n [challenge, selectedMethod],\n )\n const ChallengeComponent = useProviderChallengeComponent(selected ?? { type: 'unknown' })\n\n return (\n <Dialog open={open} onOpenChange={(nextOpen) => { if (!nextOpen) handleClose() }}>\n <DialogContent\n className=\"sm:max-w-lg [&_[data-dialog-close]]:rounded-full [&_[data-dialog-close]]:border [&_[data-dialog-close]]:border-white/20 [&_[data-dialog-close]]:bg-white/5 [&_[data-dialog-close]]:opacity-100 [&_[data-dialog-close]]:transition-none [&_[data-dialog-close]]:hover:bg-white/10 [&_[data-dialog-close]]:hover:opacity-100 [&_[data-dialog-close]]:focus-visible:ring-0 [&_[data-dialog-close]]:focus-visible:ring-offset-0\"\n onKeyDown={(event) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter' && challenge?.method === 'password') {\n event.preventDefault()\n void handlePasswordVerify()\n }\n }}\n >\n <DialogHeader>\n <DialogTitle>{t('security.admin.sudo.challenge.title', 'Confirm sensitive action')}</DialogTitle>\n <DialogDescription>\n {t(\n 'security.admin.sudo.challenge.description',\n 'Re-authenticate to continue with this protected action.',\n )}\n </DialogDescription>\n </DialogHeader>\n\n {challenge?.method === 'password' ? (\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"sudo-password\">\n {t('security.admin.sudo.challenge.password.label', 'Password')}\n </label>\n <PasswordInput\n id=\"sudo-password\"\n value={password}\n autoFocus\n onChange={(event) => setPassword(event.target.value)}\n placeholder={t('security.admin.sudo.challenge.password.placeholder', 'Enter your password')}\n autoComplete=\"current-password\"\n />\n </div>\n\n {error ? <p className=\"text-sm text-red-500\" role=\"alert\">{error}</p> : null}\n\n <div className=\"space-y-3\">\n <Button\n type=\"button\"\n className=\"w-full\"\n onClick={() => void handlePasswordVerify()}\n disabled={loading || password.trim().length === 0}\n >\n {t('security.admin.sudo.challenge.actions.verify', 'Verify')}\n </Button>\n <div className=\"flex justify-center\">\n <Button type=\"button\" variant=\"outline\" className=\"min-w-28\" onClick={handleClose} disabled={loading}>\n {t('ui.actions.cancel', 'Cancel')}\n </Button>\n </div>\n </div>\n </div>\n ) : challenge?.method === 'mfa' && selected ? (\n <div className=\"space-y-4\">\n <ChallengeComponent\n method={selected}\n loading={loading}\n onVerify={handleMfaVerify}\n onPrepare={handlePrepare}\n onResend={handlePrepare}\n submitLabel={t('security.admin.sudo.challenge.actions.verify', 'Verify')}\n />\n\n {alternativeMethods.length > 0 ? (\n <div className=\"space-y-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n className=\"w-full justify-center\"\n onClick={() => setShowMoreOptions((current) => !current)}\n >\n {t('security.admin.sudo.challenge.actions.moreOptions', 'Use another method')}\n {showMoreOptions ? <ChevronUp className=\"ml-2 size-4\" /> : <ChevronDown className=\"ml-2 size-4\" />}\n </Button>\n {showMoreOptions ? (\n <div className=\"space-y-2\">\n {alternativeMethods.map((method) => (\n <Button\n key={`${challenge.sessionId}:${method.type}`}\n type=\"button\"\n variant=\"outline\"\n className=\"w-full justify-center\"\n onClick={() => {\n setSelectedMethod(method.type)\n setError(null)\n setShowMoreOptions(false)\n }}\n >\n {method.label}\n </Button>\n ))}\n </div>\n ) : null}\n </div>\n ) : null}\n\n {error ? <p className=\"text-sm text-red-500\" role=\"alert\">{error}</p> : null}\n\n </div>\n ) : (\n <div className=\"flex items-center gap-2 rounded-md border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-900\">\n <AlertTriangle className=\"size-4\" />\n <span>{t('security.admin.sudo.challenge.noMethods', 'No sudo authentication methods are available.')}</span>\n </div>\n )}\n </DialogContent>\n </Dialog>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -4,6 +4,7 @@ import React from "react";
4
4
  import { useParams, useRouter, useSearchParams } from "next/navigation";
5
5
  import { Page, PageBody } from "@open-mercato/ui/backend/Page";
6
6
  import { Button } from "@open-mercato/ui/primitives/button";
7
+ import { PasswordInput } from "@open-mercato/ui/primitives/password-input";
7
8
  import { FormHeader } from "@open-mercato/ui/backend/forms";
8
9
  import { LoadingMessage, ErrorMessage } from "@open-mercato/ui/backend/detail";
9
10
  import { apiCall, apiCallOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
@@ -315,10 +316,8 @@ function SsoConfigDetailPage() {
315
316
  /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: t("sso.admin.field.secretSet", "Client secret is configured") }),
316
317
  /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setShowSecretField(true), children: t("sso.admin.field.changeSecret", "Change") })
317
318
  ] }) : /* @__PURE__ */ jsx(
318
- "input",
319
+ PasswordInput,
319
320
  {
320
- type: "password",
321
- className: "w-full rounded-md border px-3 py-2 text-sm",
322
321
  value: newClientSecret,
323
322
  onChange: (e) => setNewClientSecret(e.target.value),
324
323
  placeholder: config.hasClientSecret ? t("sso.admin.field.secretPlaceholder", "Enter new secret to replace existing") : t("sso.admin.field.secretRequired", "Enter client secret")
@@ -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 { 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 <input\n type=\"password\"\n className=\"w-full rounded-md border px-3 py-2 text-sm\"\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\">&rarr;</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-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\">&rarr;</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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",
6
6
  "names": []
7
7
  }
@@ -4,6 +4,7 @@ import React from "react";
4
4
  import { useRouter } from "next/navigation";
5
5
  import { Page, PageBody } from "@open-mercato/ui/backend/Page";
6
6
  import { Button } from "@open-mercato/ui/primitives/button";
7
+ import { PasswordInput } from "@open-mercato/ui/primitives/password-input";
7
8
  import { apiCall, apiCallOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
8
9
  import { flash } from "@open-mercato/ui/backend/FlashMessages";
9
10
  import { useT } from "@open-mercato/shared/lib/i18n/context";
@@ -226,10 +227,8 @@ function SsoConfigCreateWizard() {
226
227
  /* @__PURE__ */ jsxs("div", { children: [
227
228
  /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-1", children: t("sso.admin.field.clientSecret", "Client Secret") }),
228
229
  /* @__PURE__ */ jsx(
229
- "input",
230
+ PasswordInput,
230
231
  {
231
- type: "password",
232
- className: "w-full rounded-md border px-3 py-2 text-sm",
233
232
  value: state.clientSecret,
234
233
  onChange: (e) => setState((prev) => ({ ...prev, clientSecret: e.target.value }))
235
234
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/sso/backend/sso/config/new/page.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\n\ntype WizardStep = 'protocol' | 'credentials' | 'domains' | 'options' | 'review'\n\nconst STEPS: WizardStep[] = ['protocol', 'credentials', 'domains', 'options', 'review']\n\ninterface WizardState {\n name: string\n protocol: 'oidc'\n issuer: string\n clientId: string\n clientSecret: string\n domains: string[]\n jitEnabled: boolean\n autoLinkByEmail: boolean\n}\n\nconst initialState: WizardState = {\n name: '',\n protocol: 'oidc',\n issuer: '',\n clientId: '',\n clientSecret: '',\n domains: [],\n jitEnabled: true,\n autoLinkByEmail: true,\n}\n\nexport default function SsoConfigCreateWizard() {\n const router = useRouter()\n const t = useT()\n const [step, setStep] = React.useState<WizardStep>('protocol')\n const [state, setState] = React.useState<WizardState>(initialState)\n const [domainInput, setDomainInput] = React.useState('')\n const [domainError, setDomainError] = React.useState('')\n const [isSubmitting, setIsSubmitting] = React.useState(false)\n const [testResult, setTestResult] = React.useState<{ ok: boolean; error?: string } | null>(null)\n const [isTesting, setIsTesting] = React.useState(false)\n\n const { runMutation, retryLastMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: 'sso-config-create',\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: { retryLastMutation },\n })\n },\n [retryLastMutation, runMutation],\n )\n\n React.useEffect(() => {\n const checkExisting = async () => {\n const call = await apiCall<{ items: { id: string }[] }>('/api/sso/config?pageSize=1')\n if (call.ok && call.result && call.result.items.length > 0) {\n flash(t('sso.admin.error.alreadyExists', 'An SSO configuration already exists for this organization'), 'error')\n router.replace(`/backend/sso/config/${call.result.items[0].id}`)\n }\n }\n checkExisting()\n }, [router, t])\n\n const currentStepIndex = STEPS.indexOf(step)\n\n const callbackUrl = typeof window !== 'undefined'\n ? `${window.location.origin}/api/sso/callback/oidc`\n : '/api/sso/callback/oidc'\n\n const goNext = () => {\n const nextIndex = currentStepIndex + 1\n if (nextIndex < STEPS.length) setStep(STEPS[nextIndex])\n }\n\n const goBack = () => {\n const prevIndex = currentStepIndex - 1\n if (prevIndex >= 0) setStep(STEPS[prevIndex])\n }\n\n const handleAddDomain = () => {\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 if (state.domains.includes(normalized)) {\n setDomainError(t('sso.admin.wizard.domain.duplicate', 'Domain already added'))\n return\n }\n\n if (state.domains.length >= 20) {\n setDomainError(t('sso.admin.wizard.domain.limit', 'Maximum 20 domains per configuration'))\n return\n }\n\n setState((prev) => ({ ...prev, domains: [...prev.domains, normalized] }))\n setDomainInput('')\n setDomainError('')\n }\n\n const handleRemoveDomain = (domain: string) => {\n setState((prev) => ({ ...prev, domains: prev.domains.filter((d) => d !== domain) }))\n }\n\n const handleSubmit = async () => {\n setIsSubmitting(true)\n try {\n const payload = {\n name: state.name,\n protocol: state.protocol,\n issuer: state.issuer,\n clientId: state.clientId,\n clientSecret: state.clientSecret,\n allowedDomains: state.domains,\n jitEnabled: state.jitEnabled,\n autoLinkByEmail: state.autoLinkByEmail,\n }\n const call = await runMutationWithContext(\n () => apiCallOrThrow<{ id: string }>(\n '/api/sso/config',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n },\n { errorMessage: t('sso.admin.error.createFailed', 'Failed to create SSO configuration') },\n ),\n payload,\n )\n flash(t('sso.admin.created', 'SSO configuration created'), 'success')\n router.push(`/backend/sso/config/${call.result?.id}?created=1`)\n } catch {\n // apiCallOrThrow handles the error\n } finally {\n setIsSubmitting(false)\n }\n }\n\n const handleTestConnection = async () => {\n setIsTesting(true)\n setTestResult(null)\n try {\n // Raw fetch is intentional: this is a pre-save OIDC discovery probe against an\n // external IdP URL, not an internal API call, so apiCall is not applicable here.\n const response = await fetch(state.issuer + '/.well-known/openid-configuration')\n if (response.ok) {\n setTestResult({ ok: true })\n flash(t('sso.admin.test.success', 'Discovery successful \u2014 issuer is reachable'), 'success')\n } else {\n setTestResult({ ok: false, error: `HTTP ${response.status}` })\n flash(t('sso.admin.test.failed', 'Discovery failed'), 'error')\n }\n } catch (err) {\n setTestResult({ ok: false, error: String(err) })\n flash(t('sso.admin.test.failed', 'Discovery failed \u2014 issuer is not reachable'), 'error')\n } finally {\n setIsTesting(false)\n }\n }\n\n const canProceed = (): boolean => {\n switch (step) {\n case 'protocol': return true\n case 'credentials': return !!(state.name && state.issuer && state.clientId && state.clientSecret)\n case 'domains': return true\n case 'options': return true\n case 'review': return !isSubmitting\n default: return false\n }\n }\n\n return (\n <Page>\n <PageBody>\n <div className=\"max-w-2xl mx-auto\">\n {/* Step indicator */}\n <div className=\"flex items-center gap-2 mb-8\">\n {STEPS.map((s, i) => (\n <React.Fragment key={s}>\n <div\n className={`flex items-center justify-center w-8 h-8 rounded-full text-sm font-medium ${\n i <= currentStepIndex\n ? 'bg-primary text-primary-foreground'\n : 'bg-muted text-muted-foreground'\n }`}\n >\n {i + 1}\n </div>\n {i < STEPS.length - 1 && (\n <div className={`flex-1 h-0.5 ${i < currentStepIndex ? 'bg-primary' : 'bg-muted'}`} />\n )}\n </React.Fragment>\n ))}\n </div>\n\n {/* Step content */}\n {step === 'protocol' && (\n <div>\n <h2 className=\"text-lg font-semibold mb-4\">{t('sso.admin.wizard.protocol.title', 'Select Protocol')}</h2>\n <div className=\"space-y-3\">\n <label className=\"flex items-center gap-3 p-4 border rounded-lg cursor-pointer bg-accent/50 border-primary\">\n <input type=\"radio\" name=\"protocol\" value=\"oidc\" checked readOnly className=\"accent-primary\" />\n <div>\n <div className=\"font-medium\">OpenID Connect (OIDC)</div>\n <div className=\"text-sm text-muted-foreground\">\n {t('sso.admin.wizard.protocol.oidcDesc', 'Works with Zitadel, Microsoft Entra ID, Google Workspace, Okta, and more')}\n </div>\n </div>\n </label>\n <div className=\"flex items-center gap-3 p-4 border rounded-lg opacity-50 cursor-not-allowed bg-muted/30\">\n <input type=\"radio\" name=\"protocol\" value=\"saml\" disabled className=\"accent-primary\" />\n <div>\n <div className=\"font-medium\">SAML 2.0</div>\n <div className=\"text-sm text-muted-foreground\">\n {t('sso.admin.wizard.protocol.samlDesc', 'Coming soon')}\n </div>\n </div>\n </div>\n </div>\n </div>\n )}\n\n {step === 'credentials' && (\n <div>\n <h2 className=\"text-lg font-semibold mb-4\">{t('sso.admin.wizard.credentials.title', 'OIDC Credentials')}</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 placeholder={t('sso.admin.wizard.credentials.namePlaceholder', 'e.g., Zitadel Production')}\n value={state.name}\n onChange={(e) => setState((prev) => ({ ...prev, name: e.target.value }))}\n />\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 placeholder=\"https://your-idp.example.com\"\n value={state.issuer}\n onChange={(e) => setState((prev) => ({ ...prev, issuer: 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={state.clientId}\n onChange={(e) => setState((prev) => ({ ...prev, clientId: 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 <input\n type=\"password\"\n className=\"w-full rounded-md border px-3 py-2 text-sm\"\n value={state.clientSecret}\n onChange={(e) => setState((prev) => ({ ...prev, clientSecret: e.target.value }))}\n />\n </div>\n <div className=\"rounded-md bg-muted/50 p-3\">\n <label className=\"block text-sm font-medium mb-1\">{t('sso.admin.wizard.credentials.callbackUrl', 'Redirect URI (copy to your IdP)')}</label>\n <div className=\"flex items-center gap-2\">\n <code className=\"flex-1 text-sm bg-background p-2 rounded border font-mono break-all\">{callbackUrl}</code>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n navigator.clipboard.writeText(callbackUrl)\n flash(t('common.copied', 'Copied to clipboard'), 'success')\n }}\n >\n {t('common.copy', 'Copy')}\n </Button>\n </div>\n </div>\n </div>\n </div>\n )}\n\n {step === 'domains' && (\n <div>\n <h2 className=\"text-lg font-semibold mb-4\">{t('sso.admin.wizard.domains.title', 'Allowed Email Domains')}</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 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 {state.domains.length > 0 && (\n <div className=\"space-y-2\">\n {state.domains.map((domain) => (\n <div key={domain} className=\"flex items-center justify-between p-2 border rounded-md\">\n <code className=\"text-sm font-mono\">{domain}</code>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => handleRemoveDomain(domain)}>\n {t('common.remove', 'Remove')}\n </Button>\n </div>\n ))}\n </div>\n )}\n </div>\n )}\n\n {step === 'options' && (\n <div>\n <h2 className=\"text-lg font-semibold mb-4\">{t('sso.admin.wizard.options.title', 'Options')}</h2>\n <div className=\"space-y-4\">\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={state.jitEnabled}\n onChange={(e) => setState((prev) => ({ ...prev, jitEnabled: e.target.checked }))}\n className=\"accent-primary\"\n />\n <div>\n <div className=\"font-medium text-sm\">{t('sso.admin.field.jitEnabled', 'Just-in-Time Provisioning')}</div>\n <div className=\"text-xs text-muted-foreground\">\n {t('sso.admin.field.jitEnabledDesc', 'Automatically create user accounts on first SSO login')}\n </div>\n </div>\n </label>\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={state.autoLinkByEmail}\n onChange={(e) => setState((prev) => ({ ...prev, autoLinkByEmail: e.target.checked }))}\n className=\"accent-primary\"\n />\n <div>\n <div className=\"font-medium text-sm\">{t('sso.admin.field.autoLinkByEmail', 'Auto-link by Email')}</div>\n <div className=\"text-xs text-muted-foreground\">\n {t('sso.admin.field.autoLinkByEmailDesc', 'Automatically link existing users by matching email address')}\n </div>\n </div>\n </label>\n </div>\n </div>\n )}\n\n {step === 'review' && (\n <div>\n <h2 className=\"text-lg font-semibold mb-4\">{t('sso.admin.wizard.review.title', 'Review & Save')}</h2>\n <div className=\"space-y-4\">\n <div className=\"border rounded-lg divide-y\">\n <div className=\"flex justify-between p-3\">\n <span className=\"text-sm text-muted-foreground\">{t('sso.admin.field.name', 'Name')}</span>\n <span className=\"text-sm font-medium\">{state.name}</span>\n </div>\n <div className=\"flex justify-between p-3\">\n <span className=\"text-sm text-muted-foreground\">{t('sso.admin.field.protocol', 'Protocol')}</span>\n <span className=\"text-sm font-medium\">{state.protocol.toUpperCase()}</span>\n </div>\n <div className=\"flex justify-between p-3\">\n <span className=\"text-sm text-muted-foreground\">{t('sso.admin.field.issuer', 'Issuer')}</span>\n <span className=\"text-sm font-medium break-all\">{state.issuer}</span>\n </div>\n <div className=\"flex justify-between p-3\">\n <span className=\"text-sm text-muted-foreground\">{t('sso.admin.field.clientId', 'Client ID')}</span>\n <span className=\"text-sm font-medium\">{state.clientId}</span>\n </div>\n <div className=\"flex justify-between p-3\">\n <span className=\"text-sm text-muted-foreground\">{t('sso.admin.wizard.domains.title', 'Domains')}</span>\n <span className=\"text-sm font-medium\">{state.domains.join(', ') || '\u2014'}</span>\n </div>\n <div className=\"flex justify-between p-3\">\n <span className=\"text-sm text-muted-foreground\">{t('sso.admin.field.jitEnabled', 'JIT Provisioning')}</span>\n <span className=\"text-sm font-medium\">{state.jitEnabled ? t('common.enabled', 'Enabled') : t('common.disabled', 'Disabled')}</span>\n </div>\n <div className=\"flex justify-between p-3\">\n <span className=\"text-sm text-muted-foreground\">{t('sso.admin.field.autoLinkByEmail', 'Auto-link')}</span>\n <span className=\"text-sm font-medium\">{state.autoLinkByEmail ? t('common.enabled', 'Enabled') : t('common.disabled', 'Disabled')}</span>\n </div>\n </div>\n\n {/* Test connection before saving */}\n <div className=\"flex items-center gap-3 p-4 border rounded-lg bg-muted/30\">\n <Button\n variant=\"outline\"\n onClick={handleTestConnection}\n disabled={isTesting}\n >\n {isTesting\n ? t('sso.admin.wizard.review.testing', 'Testing...')\n : t('sso.admin.action.test', 'Verify Discovery')}\n </Button>\n {testResult && (\n <span className={`text-sm ${testResult.ok ? 'text-green-600' : 'text-destructive'}`}>\n {testResult.ok\n ? t('sso.admin.test.success', 'Discovery successful')\n : testResult.error || t('sso.admin.test.failed', 'Discovery failed')}\n </span>\n )}\n </div>\n\n <p className=\"text-sm text-muted-foreground\">\n {t('sso.admin.wizard.review.note', 'The configuration will be created as inactive. You can activate it from the detail page after verifying everything is correct.')}\n </p>\n </div>\n </div>\n )}\n\n {/* Navigation */}\n <div className=\"flex justify-between mt-8 pt-4 border-t\">\n <div>\n {currentStepIndex > 0 ? (\n <Button variant=\"outline\" onClick={goBack}>\n {t('common.back', 'Back')}\n </Button>\n ) : (\n <Button variant=\"outline\" onClick={() => router.push('/backend/sso')}>\n {t('common.cancel', 'Cancel')}\n </Button>\n )}\n </div>\n <div>\n {step === 'review' ? (\n <Button onClick={handleSubmit} disabled={!canProceed()}>\n {isSubmitting\n ? t('common.saving', 'Saving...')\n : t('sso.admin.wizard.review.save', 'Create Configuration')}\n </Button>\n ) : (\n <Button onClick={goNext} disabled={!canProceed()}>\n {t('common.next', 'Next')}\n </Button>\n )}\n </div>\n </div>\n </div>\n </PageBody>\n </Page>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["'use client'\n\nimport React from 'react'\nimport { useRouter } 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 { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\n\ntype WizardStep = 'protocol' | 'credentials' | 'domains' | 'options' | 'review'\n\nconst STEPS: WizardStep[] = ['protocol', 'credentials', 'domains', 'options', 'review']\n\ninterface WizardState {\n name: string\n protocol: 'oidc'\n issuer: string\n clientId: string\n clientSecret: string\n domains: string[]\n jitEnabled: boolean\n autoLinkByEmail: boolean\n}\n\nconst initialState: WizardState = {\n name: '',\n protocol: 'oidc',\n issuer: '',\n clientId: '',\n clientSecret: '',\n domains: [],\n jitEnabled: true,\n autoLinkByEmail: true,\n}\n\nexport default function SsoConfigCreateWizard() {\n const router = useRouter()\n const t = useT()\n const [step, setStep] = React.useState<WizardStep>('protocol')\n const [state, setState] = React.useState<WizardState>(initialState)\n const [domainInput, setDomainInput] = React.useState('')\n const [domainError, setDomainError] = React.useState('')\n const [isSubmitting, setIsSubmitting] = React.useState(false)\n const [testResult, setTestResult] = React.useState<{ ok: boolean; error?: string } | null>(null)\n const [isTesting, setIsTesting] = React.useState(false)\n\n const { runMutation, retryLastMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: 'sso-config-create',\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: { retryLastMutation },\n })\n },\n [retryLastMutation, runMutation],\n )\n\n React.useEffect(() => {\n const checkExisting = async () => {\n const call = await apiCall<{ items: { id: string }[] }>('/api/sso/config?pageSize=1')\n if (call.ok && call.result && call.result.items.length > 0) {\n flash(t('sso.admin.error.alreadyExists', 'An SSO configuration already exists for this organization'), 'error')\n router.replace(`/backend/sso/config/${call.result.items[0].id}`)\n }\n }\n checkExisting()\n }, [router, t])\n\n const currentStepIndex = STEPS.indexOf(step)\n\n const callbackUrl = typeof window !== 'undefined'\n ? `${window.location.origin}/api/sso/callback/oidc`\n : '/api/sso/callback/oidc'\n\n const goNext = () => {\n const nextIndex = currentStepIndex + 1\n if (nextIndex < STEPS.length) setStep(STEPS[nextIndex])\n }\n\n const goBack = () => {\n const prevIndex = currentStepIndex - 1\n if (prevIndex >= 0) setStep(STEPS[prevIndex])\n }\n\n const handleAddDomain = () => {\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 if (state.domains.includes(normalized)) {\n setDomainError(t('sso.admin.wizard.domain.duplicate', 'Domain already added'))\n return\n }\n\n if (state.domains.length >= 20) {\n setDomainError(t('sso.admin.wizard.domain.limit', 'Maximum 20 domains per configuration'))\n return\n }\n\n setState((prev) => ({ ...prev, domains: [...prev.domains, normalized] }))\n setDomainInput('')\n setDomainError('')\n }\n\n const handleRemoveDomain = (domain: string) => {\n setState((prev) => ({ ...prev, domains: prev.domains.filter((d) => d !== domain) }))\n }\n\n const handleSubmit = async () => {\n setIsSubmitting(true)\n try {\n const payload = {\n name: state.name,\n protocol: state.protocol,\n issuer: state.issuer,\n clientId: state.clientId,\n clientSecret: state.clientSecret,\n allowedDomains: state.domains,\n jitEnabled: state.jitEnabled,\n autoLinkByEmail: state.autoLinkByEmail,\n }\n const call = await runMutationWithContext(\n () => apiCallOrThrow<{ id: string }>(\n '/api/sso/config',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n },\n { errorMessage: t('sso.admin.error.createFailed', 'Failed to create SSO configuration') },\n ),\n payload,\n )\n flash(t('sso.admin.created', 'SSO configuration created'), 'success')\n router.push(`/backend/sso/config/${call.result?.id}?created=1`)\n } catch {\n // apiCallOrThrow handles the error\n } finally {\n setIsSubmitting(false)\n }\n }\n\n const handleTestConnection = async () => {\n setIsTesting(true)\n setTestResult(null)\n try {\n // Raw fetch is intentional: this is a pre-save OIDC discovery probe against an\n // external IdP URL, not an internal API call, so apiCall is not applicable here.\n const response = await fetch(state.issuer + '/.well-known/openid-configuration')\n if (response.ok) {\n setTestResult({ ok: true })\n flash(t('sso.admin.test.success', 'Discovery successful \u2014 issuer is reachable'), 'success')\n } else {\n setTestResult({ ok: false, error: `HTTP ${response.status}` })\n flash(t('sso.admin.test.failed', 'Discovery failed'), 'error')\n }\n } catch (err) {\n setTestResult({ ok: false, error: String(err) })\n flash(t('sso.admin.test.failed', 'Discovery failed \u2014 issuer is not reachable'), 'error')\n } finally {\n setIsTesting(false)\n }\n }\n\n const canProceed = (): boolean => {\n switch (step) {\n case 'protocol': return true\n case 'credentials': return !!(state.name && state.issuer && state.clientId && state.clientSecret)\n case 'domains': return true\n case 'options': return true\n case 'review': return !isSubmitting\n default: return false\n }\n }\n\n return (\n <Page>\n <PageBody>\n <div className=\"max-w-2xl mx-auto\">\n {/* Step indicator */}\n <div className=\"flex items-center gap-2 mb-8\">\n {STEPS.map((s, i) => (\n <React.Fragment key={s}>\n <div\n className={`flex items-center justify-center w-8 h-8 rounded-full text-sm font-medium ${\n i <= currentStepIndex\n ? 'bg-primary text-primary-foreground'\n : 'bg-muted text-muted-foreground'\n }`}\n >\n {i + 1}\n </div>\n {i < STEPS.length - 1 && (\n <div className={`flex-1 h-0.5 ${i < currentStepIndex ? 'bg-primary' : 'bg-muted'}`} />\n )}\n </React.Fragment>\n ))}\n </div>\n\n {/* Step content */}\n {step === 'protocol' && (\n <div>\n <h2 className=\"text-lg font-semibold mb-4\">{t('sso.admin.wizard.protocol.title', 'Select Protocol')}</h2>\n <div className=\"space-y-3\">\n <label className=\"flex items-center gap-3 p-4 border rounded-lg cursor-pointer bg-accent/50 border-primary\">\n <input type=\"radio\" name=\"protocol\" value=\"oidc\" checked readOnly className=\"accent-primary\" />\n <div>\n <div className=\"font-medium\">OpenID Connect (OIDC)</div>\n <div className=\"text-sm text-muted-foreground\">\n {t('sso.admin.wizard.protocol.oidcDesc', 'Works with Zitadel, Microsoft Entra ID, Google Workspace, Okta, and more')}\n </div>\n </div>\n </label>\n <div className=\"flex items-center gap-3 p-4 border rounded-lg opacity-50 cursor-not-allowed bg-muted/30\">\n <input type=\"radio\" name=\"protocol\" value=\"saml\" disabled className=\"accent-primary\" />\n <div>\n <div className=\"font-medium\">SAML 2.0</div>\n <div className=\"text-sm text-muted-foreground\">\n {t('sso.admin.wizard.protocol.samlDesc', 'Coming soon')}\n </div>\n </div>\n </div>\n </div>\n </div>\n )}\n\n {step === 'credentials' && (\n <div>\n <h2 className=\"text-lg font-semibold mb-4\">{t('sso.admin.wizard.credentials.title', 'OIDC Credentials')}</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 placeholder={t('sso.admin.wizard.credentials.namePlaceholder', 'e.g., Zitadel Production')}\n value={state.name}\n onChange={(e) => setState((prev) => ({ ...prev, name: e.target.value }))}\n />\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 placeholder=\"https://your-idp.example.com\"\n value={state.issuer}\n onChange={(e) => setState((prev) => ({ ...prev, issuer: 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={state.clientId}\n onChange={(e) => setState((prev) => ({ ...prev, clientId: 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 <PasswordInput\n value={state.clientSecret}\n onChange={(e) => setState((prev) => ({ ...prev, clientSecret: e.target.value }))}\n />\n </div>\n <div className=\"rounded-md bg-muted/50 p-3\">\n <label className=\"block text-sm font-medium mb-1\">{t('sso.admin.wizard.credentials.callbackUrl', 'Redirect URI (copy to your IdP)')}</label>\n <div className=\"flex items-center gap-2\">\n <code className=\"flex-1 text-sm bg-background p-2 rounded border font-mono break-all\">{callbackUrl}</code>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n navigator.clipboard.writeText(callbackUrl)\n flash(t('common.copied', 'Copied to clipboard'), 'success')\n }}\n >\n {t('common.copy', 'Copy')}\n </Button>\n </div>\n </div>\n </div>\n </div>\n )}\n\n {step === 'domains' && (\n <div>\n <h2 className=\"text-lg font-semibold mb-4\">{t('sso.admin.wizard.domains.title', 'Allowed Email Domains')}</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 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 {state.domains.length > 0 && (\n <div className=\"space-y-2\">\n {state.domains.map((domain) => (\n <div key={domain} className=\"flex items-center justify-between p-2 border rounded-md\">\n <code className=\"text-sm font-mono\">{domain}</code>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => handleRemoveDomain(domain)}>\n {t('common.remove', 'Remove')}\n </Button>\n </div>\n ))}\n </div>\n )}\n </div>\n )}\n\n {step === 'options' && (\n <div>\n <h2 className=\"text-lg font-semibold mb-4\">{t('sso.admin.wizard.options.title', 'Options')}</h2>\n <div className=\"space-y-4\">\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={state.jitEnabled}\n onChange={(e) => setState((prev) => ({ ...prev, jitEnabled: e.target.checked }))}\n className=\"accent-primary\"\n />\n <div>\n <div className=\"font-medium text-sm\">{t('sso.admin.field.jitEnabled', 'Just-in-Time Provisioning')}</div>\n <div className=\"text-xs text-muted-foreground\">\n {t('sso.admin.field.jitEnabledDesc', 'Automatically create user accounts on first SSO login')}\n </div>\n </div>\n </label>\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={state.autoLinkByEmail}\n onChange={(e) => setState((prev) => ({ ...prev, autoLinkByEmail: e.target.checked }))}\n className=\"accent-primary\"\n />\n <div>\n <div className=\"font-medium text-sm\">{t('sso.admin.field.autoLinkByEmail', 'Auto-link by Email')}</div>\n <div className=\"text-xs text-muted-foreground\">\n {t('sso.admin.field.autoLinkByEmailDesc', 'Automatically link existing users by matching email address')}\n </div>\n </div>\n </label>\n </div>\n </div>\n )}\n\n {step === 'review' && (\n <div>\n <h2 className=\"text-lg font-semibold mb-4\">{t('sso.admin.wizard.review.title', 'Review & Save')}</h2>\n <div className=\"space-y-4\">\n <div className=\"border rounded-lg divide-y\">\n <div className=\"flex justify-between p-3\">\n <span className=\"text-sm text-muted-foreground\">{t('sso.admin.field.name', 'Name')}</span>\n <span className=\"text-sm font-medium\">{state.name}</span>\n </div>\n <div className=\"flex justify-between p-3\">\n <span className=\"text-sm text-muted-foreground\">{t('sso.admin.field.protocol', 'Protocol')}</span>\n <span className=\"text-sm font-medium\">{state.protocol.toUpperCase()}</span>\n </div>\n <div className=\"flex justify-between p-3\">\n <span className=\"text-sm text-muted-foreground\">{t('sso.admin.field.issuer', 'Issuer')}</span>\n <span className=\"text-sm font-medium break-all\">{state.issuer}</span>\n </div>\n <div className=\"flex justify-between p-3\">\n <span className=\"text-sm text-muted-foreground\">{t('sso.admin.field.clientId', 'Client ID')}</span>\n <span className=\"text-sm font-medium\">{state.clientId}</span>\n </div>\n <div className=\"flex justify-between p-3\">\n <span className=\"text-sm text-muted-foreground\">{t('sso.admin.wizard.domains.title', 'Domains')}</span>\n <span className=\"text-sm font-medium\">{state.domains.join(', ') || '\u2014'}</span>\n </div>\n <div className=\"flex justify-between p-3\">\n <span className=\"text-sm text-muted-foreground\">{t('sso.admin.field.jitEnabled', 'JIT Provisioning')}</span>\n <span className=\"text-sm font-medium\">{state.jitEnabled ? t('common.enabled', 'Enabled') : t('common.disabled', 'Disabled')}</span>\n </div>\n <div className=\"flex justify-between p-3\">\n <span className=\"text-sm text-muted-foreground\">{t('sso.admin.field.autoLinkByEmail', 'Auto-link')}</span>\n <span className=\"text-sm font-medium\">{state.autoLinkByEmail ? t('common.enabled', 'Enabled') : t('common.disabled', 'Disabled')}</span>\n </div>\n </div>\n\n {/* Test connection before saving */}\n <div className=\"flex items-center gap-3 p-4 border rounded-lg bg-muted/30\">\n <Button\n variant=\"outline\"\n onClick={handleTestConnection}\n disabled={isTesting}\n >\n {isTesting\n ? t('sso.admin.wizard.review.testing', 'Testing...')\n : t('sso.admin.action.test', 'Verify Discovery')}\n </Button>\n {testResult && (\n <span className={`text-sm ${testResult.ok ? 'text-green-600' : 'text-destructive'}`}>\n {testResult.ok\n ? t('sso.admin.test.success', 'Discovery successful')\n : testResult.error || t('sso.admin.test.failed', 'Discovery failed')}\n </span>\n )}\n </div>\n\n <p className=\"text-sm text-muted-foreground\">\n {t('sso.admin.wizard.review.note', 'The configuration will be created as inactive. You can activate it from the detail page after verifying everything is correct.')}\n </p>\n </div>\n </div>\n )}\n\n {/* Navigation */}\n <div className=\"flex justify-between mt-8 pt-4 border-t\">\n <div>\n {currentStepIndex > 0 ? (\n <Button variant=\"outline\" onClick={goBack}>\n {t('common.back', 'Back')}\n </Button>\n ) : (\n <Button variant=\"outline\" onClick={() => router.push('/backend/sso')}>\n {t('common.cancel', 'Cancel')}\n </Button>\n )}\n </div>\n <div>\n {step === 'review' ? (\n <Button onClick={handleSubmit} disabled={!canProceed()}>\n {isSubmitting\n ? t('common.saving', 'Saving...')\n : t('sso.admin.wizard.review.save', 'Create Configuration')}\n </Button>\n ) : (\n <Button onClick={goNext} disabled={!canProceed()}>\n {t('common.next', 'Next')}\n </Button>\n )}\n </div>\n </div>\n </div>\n </PageBody>\n </Page>\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.1-develop.3278.1.a4d26475e4",
3
+ "version": "0.6.1-develop.3287.1.450f4ffb56",
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.1-develop.3278.1.a4d26475e4",
68
- "@open-mercato/ui": "0.6.1-develop.3278.1.a4d26475e4",
67
+ "@open-mercato/core": "0.6.1-develop.3287.1.450f4ffb56",
68
+ "@open-mercato/ui": "0.6.1-develop.3287.1.450f4ffb56",
69
69
  "@simplewebauthn/browser": "^13.3.0",
70
70
  "@simplewebauthn/server": "^13.3.0",
71
71
  "@simplewebauthn/types": "^12.0.0",
@@ -75,10 +75,10 @@
75
75
  "qrcode": "^1.5.4"
76
76
  },
77
77
  "peerDependencies": {
78
- "@open-mercato/shared": "0.6.1-develop.3278.1.a4d26475e4"
78
+ "@open-mercato/shared": "0.6.1-develop.3287.1.450f4ffb56"
79
79
  },
80
80
  "devDependencies": {
81
- "@open-mercato/shared": "0.6.1-develop.3278.1.a4d26475e4",
81
+ "@open-mercato/shared": "0.6.1-develop.3287.1.450f4ffb56",
82
82
  "@types/jest": "^30.0.0",
83
83
  "jest": "^30.4.2",
84
84
  "ts-jest": "^29.4.9"
@@ -5,7 +5,7 @@ import { z } from 'zod'
5
5
  import { CrudForm, type CrudField, type CrudCustomFieldRenderProps } from '@open-mercato/ui/backend/CrudForm'
6
6
  import { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
7
7
  import { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'
8
- import { Input } from '@open-mercato/ui/primitives/input'
8
+ import { PasswordInput } from '@open-mercato/ui/primitives/password-input'
9
9
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
10
10
  import { useT } from '@open-mercato/shared/lib/i18n/context'
11
11
  import { getPasswordPolicy, getPasswordRequirements, buildPasswordSchema, validatePassword } from '@open-mercato/shared/lib/auth/passwordPolicy'
@@ -28,9 +28,8 @@ function PasswordInputField({
28
28
  autoFocus,
29
29
  }: CrudCustomFieldRenderProps) {
30
30
  return (
31
- <Input
31
+ <PasswordInput
32
32
  className="max-w-xl"
33
- type="password"
34
33
  value={typeof value === 'string' ? value : ''}
35
34
  onChange={(event) => setValue(event.target.value)}
36
35
  disabled={disabled}
@@ -51,9 +50,8 @@ function TrackedPasswordInput({
51
50
  onValueChange,
52
51
  }: TrackedPasswordInputProps) {
53
52
  return (
54
- <Input
53
+ <PasswordInput
55
54
  className="max-w-xl"
56
- type="password"
57
55
  value={typeof value === 'string' ? value : ''}
58
56
  onChange={(event) => {
59
57
  const nextValue = event.target.value
@@ -6,7 +6,7 @@ import { useT } from '@open-mercato/shared/lib/i18n/context'
6
6
  import { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
7
7
  import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'
8
8
  import { Button } from '@open-mercato/ui/primitives/button'
9
- import { Input } from '@open-mercato/ui/primitives/input'
9
+ import { PasswordInput } from '@open-mercato/ui/primitives/password-input'
10
10
  import { useProviderChallengeComponent } from './mfa-ui-registry'
11
11
 
12
12
  export type SudoChallengeMethod = {
@@ -235,13 +235,13 @@ export default function SudoChallengeModal({
235
235
  <label className="text-sm font-medium" htmlFor="sudo-password">
236
236
  {t('security.admin.sudo.challenge.password.label', 'Password')}
237
237
  </label>
238
- <Input
238
+ <PasswordInput
239
239
  id="sudo-password"
240
- type="password"
241
240
  value={password}
242
241
  autoFocus
243
242
  onChange={(event) => setPassword(event.target.value)}
244
243
  placeholder={t('security.admin.sudo.challenge.password.placeholder', 'Enter your password')}
244
+ autoComplete="current-password"
245
245
  />
246
246
  </div>
247
247
 
@@ -4,6 +4,7 @@ import React from 'react'
4
4
  import { useParams, useRouter, useSearchParams } from 'next/navigation'
5
5
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
6
6
  import { Button } from '@open-mercato/ui/primitives/button'
7
+ import { PasswordInput } from '@open-mercato/ui/primitives/password-input'
7
8
  import { FormHeader } from '@open-mercato/ui/backend/forms'
8
9
  import { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'
9
10
  import { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
@@ -407,9 +408,7 @@ export default function SsoConfigDetailPage() {
407
408
  </Button>
408
409
  </div>
409
410
  ) : (
410
- <input
411
- type="password"
412
- className="w-full rounded-md border px-3 py-2 text-sm"
411
+ <PasswordInput
413
412
  value={newClientSecret}
414
413
  onChange={(e) => setNewClientSecret(e.target.value)}
415
414
  placeholder={config.hasClientSecret
@@ -4,6 +4,7 @@ import React from 'react'
4
4
  import { useRouter } from 'next/navigation'
5
5
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
6
6
  import { Button } from '@open-mercato/ui/primitives/button'
7
+ import { PasswordInput } from '@open-mercato/ui/primitives/password-input'
7
8
  import { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
8
9
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
9
10
  import { useT } from '@open-mercato/shared/lib/i18n/context'
@@ -269,9 +270,7 @@ export default function SsoConfigCreateWizard() {
269
270
  </div>
270
271
  <div>
271
272
  <label className="block text-sm font-medium mb-1">{t('sso.admin.field.clientSecret', 'Client Secret')}</label>
272
- <input
273
- type="password"
274
- className="w-full rounded-md border px-3 py-2 text-sm"
273
+ <PasswordInput
275
274
  value={state.clientSecret}
276
275
  onChange={(e) => setState((prev) => ({ ...prev, clientSecret: e.target.value }))}
277
276
  />