@plumile/backoffice-react 0.1.188 → 0.1.189

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.
Files changed (104) hide show
  1. package/lib/esm/auth/login/LoginFlow.js.map +1 -1
  2. package/lib/esm/auth/login/MethodChooser.js.map +1 -1
  3. package/lib/esm/auth/login/MfaChallengeForm.js.map +1 -1
  4. package/lib/esm/auth/login/PasskeyLoginForm.js.map +1 -1
  5. package/lib/esm/auth/login/loginPage.css.js +2 -0
  6. package/lib/esm/auth/login/synchronizeAuthStatusQuery.js.map +1 -1
  7. package/lib/esm/auth/pages/AcceptInvitationScreen.js.map +1 -1
  8. package/lib/esm/auth/pages/PasswordResetCompleteScreen.js.map +1 -1
  9. package/lib/esm/auth/pages/PasswordResetRequestScreen.js.map +1 -1
  10. package/lib/esm/auth/pages/VerifyEmailScreen.js.map +1 -1
  11. package/lib/esm/components/backoffice/actions/LazyBackofficeEntityActionFormDialog.js.map +1 -1
  12. package/lib/esm/components/backoffice/billing/BackofficeBillingUsageChart.js.map +1 -1
  13. package/lib/esm/components/backoffice/columns/buildDataTableColumns.js.map +1 -1
  14. package/lib/esm/components/backoffice/detail/BackofficeLifecycleTimelineSection.js.map +1 -1
  15. package/lib/esm/components/backoffice/detail/BackofficeTokenUsageBreakdown.js.map +1 -1
  16. package/lib/esm/components/backoffice/detail/backofficeEntitySummaryHeader.css.js +0 -1
  17. package/lib/esm/components/backoffice/detail/createBackofficeEntityLinkProps.js.map +1 -1
  18. package/lib/esm/components/backoffice/detail/detailPayloadUtils.js.map +1 -1
  19. package/lib/esm/components/backoffice/filters/backofficeFilterAction.css.js +0 -1
  20. package/lib/esm/components/backoffice/filters/deferredFilterSearchInput.css.js +0 -1
  21. package/lib/esm/components/backoffice/layout/breadcrumb/assertValidBreadcrumb.js.map +1 -1
  22. package/lib/esm/components/backoffice/layout/buildSidebarSections.js.map +1 -1
  23. package/lib/esm/components/backoffice/layout/mapViewerToSidebarProfileView.js.map +1 -1
  24. package/lib/esm/components/backoffice/layout/sidebarUtils.js.map +1 -1
  25. package/lib/esm/components/backoffice/links/resolveBackofficeLink.js.map +1 -1
  26. package/lib/esm/components/backoffice/links/resolveBackofficeTargetIcon.js.map +1 -1
  27. package/lib/esm/components/backoffice/list/RowFlagsCell.css.js +1 -0
  28. package/lib/esm/components/backoffice/list/RowFlagsCell.js.map +1 -1
  29. package/lib/esm/components/backoffice/pickers/EntityIdPickerDialog.js.map +1 -1
  30. package/lib/esm/components/backoffice/refs/BackofficeLazyEntityCount.js.map +1 -1
  31. package/lib/esm/components/backoffice/refs/BackofficeRelatedCountLink.js.map +1 -1
  32. package/lib/esm/components/backoffice/routing/BackofficeContentBoundary.js.map +1 -1
  33. package/lib/esm/components/backoffice/scaffolds/BackofficeEntityListScaffold.js +285 -269
  34. package/lib/esm/components/backoffice/scaffolds/BackofficeEntityListScaffold.js.map +1 -1
  35. package/lib/esm/components/backoffice/shared/backofficeFilterableCell.css.js +1 -1
  36. package/lib/esm/components/backoffice/technical/TechnicalIdentifierValue.js.map +1 -1
  37. package/lib/esm/filters/filterHelpers.js +1 -1
  38. package/lib/esm/filters/filterHelpers.js.map +1 -1
  39. package/lib/esm/hooks/useAuth.js.map +1 -1
  40. package/lib/esm/hooks/useBackofficeAuth.js.map +1 -1
  41. package/lib/esm/hooks/useBackofficeInfiniteScrollSentinel.js.map +1 -1
  42. package/lib/esm/hooks/useBackofficeListUrlState.js.map +1 -1
  43. package/lib/esm/hooks/useBackofficeSessionAuth.js.map +1 -1
  44. package/lib/esm/hooks/useConditionalSubscription.js.map +1 -1
  45. package/lib/esm/hooks/useSidebarGroupCollapse.js.map +1 -1
  46. package/lib/esm/i18n/createI18nInstance.js.map +1 -1
  47. package/lib/esm/i18n/locales/en/backofficeReact.js +406 -405
  48. package/lib/esm/i18n/locales/fr/backofficeReact.js +408 -407
  49. package/lib/esm/i18n/mergeResourceLanguages.js.map +1 -1
  50. package/lib/esm/i18n/resources.js +1 -1
  51. package/lib/esm/i18n/resources.js.map +1 -1
  52. package/lib/esm/i18n/useBackofficeFormats.js.map +1 -1
  53. package/lib/esm/modules/base64.js.map +1 -1
  54. package/lib/esm/modules/formatFileSize.js.map +1 -1
  55. package/lib/esm/modules/uploads.js +17 -17
  56. package/lib/esm/modules/uploads.js.map +1 -1
  57. package/lib/esm/modules/webauthn.js.map +1 -1
  58. package/lib/esm/node_modules/@babel/runtime/helpers/objectSpread2.js.map +1 -1
  59. package/lib/esm/node_modules/@babel/runtime/helpers/toPrimitive.js.map +1 -1
  60. package/lib/esm/node_modules/@babel/runtime/helpers/toPropertyKey.js.map +1 -1
  61. package/lib/esm/node_modules/@babel/runtime/helpers/unsupportedIterableToArray.js.map +1 -1
  62. package/lib/esm/node_modules/@vanilla-extract/recipes/dist/createRuntimeFn-62c9670f.esm.js +1 -1
  63. package/lib/esm/node_modules/@vanilla-extract/recipes/dist/createRuntimeFn-62c9670f.esm.js.map +1 -1
  64. package/lib/esm/node_modules/fbjs/lib/areEqual.js.map +1 -1
  65. package/lib/esm/node_modules/relay-test-utils/lib/RelayMockPayloadGenerator.js.map +1 -1
  66. package/lib/esm/node_modules/relay-test-utils/lib/RelayModernMockEnvironment.js.map +1 -1
  67. package/lib/esm/node_modules/relay-test-utils/lib/RelayResolverTestUtils.js.map +1 -1
  68. package/lib/esm/pages/BackofficeDashboardPage.js.map +1 -1
  69. package/lib/esm/pages/BackofficeDashboardWidgetContent.js.map +1 -1
  70. package/lib/esm/pages/BackofficeEntityDetailLayoutPage.js.map +1 -1
  71. package/lib/esm/pages/BackofficeEntityDetailPage.js.map +1 -1
  72. package/lib/esm/pages/BackofficeEntityDetailPage.view-helpers.js.map +1 -1
  73. package/lib/esm/pages/BackofficeEntityListPage.helpers.js +5 -5
  74. package/lib/esm/pages/BackofficeEntityListPage.helpers.js.map +1 -1
  75. package/lib/esm/pages/BackofficeEntityListPage.js.map +1 -1
  76. package/lib/esm/pages/BackofficeHubPage.js.map +1 -1
  77. package/lib/esm/pages/BackofficeLayoutPage.js.map +1 -1
  78. package/lib/esm/pages/BackofficeLoginPage.js.map +1 -1
  79. package/lib/esm/pages/BackofficePasswordResetRequestPage.js.map +1 -1
  80. package/lib/esm/pages/BackofficeVerifyEmailPage.js.map +1 -1
  81. package/lib/esm/pages/detail/BackofficeEntityDetailManifestFallback.js.map +1 -1
  82. package/lib/esm/pages/detail/pageResolution.js.map +1 -1
  83. package/lib/esm/provider/BackofficeProvider.js.map +1 -1
  84. package/lib/esm/provider/entityRegistry.js.map +1 -1
  85. package/lib/esm/provider/lazyValue.js.map +1 -1
  86. package/lib/esm/provider/useBackofficeEntityLoader.js.map +1 -1
  87. package/lib/esm/relay/connectionUtils.js.map +1 -1
  88. package/lib/esm/relay/envHelpers.js.map +1 -1
  89. package/lib/esm/router/createBackofficeRoutes.js.map +1 -1
  90. package/lib/esm/storybook/relay/RelayStory.js.map +1 -1
  91. package/lib/esm/storybook/relay/mockResolvers.js.map +1 -1
  92. package/lib/types/components/backoffice/detail/BackofficeLifecycleTimelineSection.d.ts.map +1 -1
  93. package/lib/types/components/backoffice/pickers/EntityIdPickerDialog.d.ts.map +1 -1
  94. package/lib/types/components/backoffice/scaffolds/BackofficeEntityListScaffold.d.ts.map +1 -1
  95. package/lib/types/components/backoffice/technical/TechnicalIdentifierValue.d.ts.map +1 -1
  96. package/lib/types/filters/filterHelpers.d.ts.map +1 -1
  97. package/lib/types/hooks/useAuth.d.ts.map +1 -1
  98. package/lib/types/modules/uploads.d.ts.map +1 -1
  99. package/lib/types/modules/webauthn.d.ts.map +1 -1
  100. package/lib/types/pages/BackofficePasswordResetRequestPage.d.ts.map +1 -1
  101. package/lib/types/pages/BackofficeVerifyEmailPage.d.ts.map +1 -1
  102. package/lib/types/pages/detail/pageResolution.d.ts.map +1 -1
  103. package/lib/types/provider/types.d.ts.map +1 -1
  104. package/package.json +16 -16
@@ -1 +1 @@
1
- {"version":3,"file":"LoginFlow.js","names":[],"sources":["../../../../src/auth/login/LoginFlow.tsx"],"sourcesContent":["/* eslint-disable no-ternary */\nimport {\n type JSX,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useBackofficeReactTranslation } from '../../i18n/useBackofficeReactTranslation.js';\n\nimport { AuthLayout } from '@plumile/ui/atomic/templates/auth_layout/AuthLayout.js';\n\nimport type {\n AuthMethod,\n OidcProviderKind,\n} from '../../modules/sharedSchemaTypes.js';\nimport type { UseAuthReturn } from '../../hooks/useAuth.js';\n\nimport { MfaChallengeForm } from './MfaChallengeForm.js';\nimport { PasskeyLoginForm } from './PasskeyLoginForm.js';\nimport { MethodChooser } from './MethodChooser.js';\nimport AuthPanel from './AuthPanel.js';\nimport { EmailCapturePanel } from './EmailCapturePanel.js';\nimport { PasswordLoginPanel } from './PasswordLoginPanel.js';\n\nexport type LoginFlowProps = {\n auth: UseAuthReturn;\n oidcProviders: readonly OidcProviderKind[];\n onLoginSuccess: () => void;\n onForgotPassword: () => void;\n};\n\ntype View = 'email' | 'passkey' | 'methods' | 'password' | 'mfa';\n\nexport const LoginFlow = ({\n auth,\n oidcProviders,\n onLoginSuccess,\n onForgotPassword,\n}: LoginFlowProps): JSX.Element => {\n const { t } = useBackofficeReactTranslation();\n const [email, setEmail] = useState('');\n const [methods, setMethods] = useState<readonly AuthMethod[]>([]);\n const [lockedUntil, setLockedUntil] = useState<string | null>(null);\n const [view, setView] = useState<View>('email');\n const [hasTriedPasskey, setHasTriedPasskey] = useState(false);\n const [localError, setLocalError] = useState<string | null>(null);\n const passkeyAttemptActive = useRef(true);\n\n const loginAdapter = useMemo(() => {\n return {\n ...auth,\n beginAuthentication: async (inputEmail: string) => {\n const result = await auth.beginAuthentication(inputEmail);\n setEmail(inputEmail);\n setMethods(result.methods);\n setLockedUntil(result.lockedUntil);\n return result;\n },\n };\n }, [auth]);\n\n const handleLoginSuccess = useCallback(\n ({ force } = { force: false }) => {\n if (!force && auth.nextStep === 'TOTP') {\n return;\n }\n onLoginSuccess();\n },\n [auth.nextStep, onLoginSuccess],\n );\n\n const isMfaStep = auth.nextStep === 'TOTP' || view === 'mfa';\n const title = isMfaStep\n ? t('auth.loginFlow.title.mfa')\n : t('auth.loginFlow.title.default');\n const subtitle = isMfaStep\n ? t('auth.loginFlow.subtitle.mfa')\n : t('auth.loginFlow.subtitle.default');\n\n const startFlowForEmail = useCallback(\n async (inputEmail: string) => {\n setLocalError(null);\n try {\n const trimmed = inputEmail.trim();\n const result = await auth.beginAuthentication(trimmed);\n setEmail(trimmed);\n setMethods(result.methods);\n setLockedUntil(result.lockedUntil);\n const hasPasskey = result.methods.some((method) => {\n return method === 'PASSKEY';\n });\n\n if (hasPasskey) {\n setView('passkey');\n } else if (result.methods.includes('PASSWORD')) {\n setView('password');\n } else {\n setView('methods');\n }\n } catch {\n setLocalError(t('auth.loginFlow.errors.tryAgain'));\n }\n },\n [auth, t],\n );\n\n useEffect(() => {\n passkeyAttemptActive.current = true;\n const trimmedEmail = email.trim();\n const shouldAttempt =\n view === 'passkey' && !hasTriedPasskey && trimmedEmail !== '';\n\n if (shouldAttempt) {\n setHasTriedPasskey(true);\n const runPasskeyLogin = async () => {\n try {\n const status = await loginAdapter.loginWithPasskey({\n email: trimmedEmail,\n });\n\n if (!passkeyAttemptActive.current) {\n return;\n }\n\n if (status === 'success') {\n handleLoginSuccess();\n return;\n }\n\n if (status === 'mfa-required') {\n setView('mfa');\n return;\n }\n\n setView('methods');\n setLocalError(t('auth.loginFlow.errors.passkeyUnavailable'));\n } catch {\n if (!passkeyAttemptActive.current) {\n return;\n }\n\n setLocalError(t('auth.loginFlow.errors.passkeyUnavailable'));\n setView('methods');\n }\n };\n runPasskeyLogin().catch(() => {\n // Errors are handled inside runPasskeyLogin; ignore any unexpected rethrow.\n });\n }\n\n return () => {\n passkeyAttemptActive.current = false;\n };\n }, [email, handleLoginSuccess, hasTriedPasskey, loginAdapter, t, view]);\n\n let content: JSX.Element;\n\n if (isMfaStep) {\n content = (\n <AuthPanel>\n <MfaChallengeForm\n auth={auth}\n onSuccess={() => {\n handleLoginSuccess({ force: true });\n }}\n onBack={() => {\n auth.reset();\n setView('email');\n }}\n />\n </AuthPanel>\n );\n } else if (view === 'passkey') {\n content = (\n <AuthPanel\n title={t('auth.loginFlow.passkey.title')}\n description={t('auth.loginFlow.passkey.description', { email })}\n >\n <PasskeyLoginForm\n auth={loginAdapter}\n onSuccess={handleLoginSuccess}\n onShowMethods={() => {\n setView('methods');\n }}\n defaultEmail={email}\n isAttemptingPasskey={auth.isLoading}\n />\n </AuthPanel>\n );\n } else if (view === 'methods') {\n content = (\n <AuthPanel title={t('auth.loginFlow.methods.title')}>\n <MethodChooser\n email={email}\n methods={methods}\n lockedUntil={lockedUntil}\n onSelect={(method) => {\n if (method === 'PASSKEY') {\n setView('passkey');\n return;\n }\n if (method === 'PASSWORD') {\n setView('password');\n }\n }}\n onBack={() => {\n setView('email');\n }}\n />\n </AuthPanel>\n );\n } else if (view === 'password') {\n content = (\n <PasswordLoginPanel\n auth={loginAdapter}\n defaultEmail={email}\n onForgotPassword={onForgotPassword}\n onSuccess={handleLoginSuccess}\n providers={oidcProviders}\n />\n );\n } else {\n // email capture only\n content = (\n <EmailCapturePanel\n email={email}\n errorMessage={localError}\n isLoading={auth.isLoading}\n onEmailChange={(value) => {\n setEmail(value);\n setLocalError(null);\n }}\n onContinue={() => {\n const trimmed = email.trim();\n if (trimmed === '') {\n setLocalError(t('auth.loginFlow.errors.emailRequired'));\n return;\n }\n startFlowForEmail(trimmed).catch(() => {\n // startFlowForEmail handles its own errors.\n });\n }}\n onForgotPassword={onForgotPassword}\n />\n );\n }\n\n return (\n <AuthLayout title={title} subtitle={subtitle}>\n {content}\n </AuthLayout>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"LoginFlow.js","names":[],"sources":["../../../../src/auth/login/LoginFlow.tsx"],"sourcesContent":["/* eslint-disable no-ternary */\nimport {\n type JSX,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useBackofficeReactTranslation } from '../../i18n/useBackofficeReactTranslation.js';\n\nimport { AuthLayout } from '@plumile/ui/atomic/templates/auth_layout/AuthLayout.js';\n\nimport type {\n AuthMethod,\n OidcProviderKind,\n} from '../../modules/sharedSchemaTypes.js';\nimport type { UseAuthReturn } from '../../hooks/useAuth.js';\n\nimport { MfaChallengeForm } from './MfaChallengeForm.js';\nimport { PasskeyLoginForm } from './PasskeyLoginForm.js';\nimport { MethodChooser } from './MethodChooser.js';\nimport AuthPanel from './AuthPanel.js';\nimport { EmailCapturePanel } from './EmailCapturePanel.js';\nimport { PasswordLoginPanel } from './PasswordLoginPanel.js';\n\nexport type LoginFlowProps = {\n auth: UseAuthReturn;\n oidcProviders: readonly OidcProviderKind[];\n onLoginSuccess: () => void;\n onForgotPassword: () => void;\n};\n\ntype View = 'email' | 'passkey' | 'methods' | 'password' | 'mfa';\n\nexport const LoginFlow = ({\n auth,\n oidcProviders,\n onLoginSuccess,\n onForgotPassword,\n}: LoginFlowProps): JSX.Element => {\n const { t } = useBackofficeReactTranslation();\n const [email, setEmail] = useState('');\n const [methods, setMethods] = useState<readonly AuthMethod[]>([]);\n const [lockedUntil, setLockedUntil] = useState<string | null>(null);\n const [view, setView] = useState<View>('email');\n const [hasTriedPasskey, setHasTriedPasskey] = useState(false);\n const [localError, setLocalError] = useState<string | null>(null);\n const passkeyAttemptActive = useRef(true);\n\n const loginAdapter = useMemo(() => {\n return {\n ...auth,\n beginAuthentication: async (inputEmail: string) => {\n const result = await auth.beginAuthentication(inputEmail);\n setEmail(inputEmail);\n setMethods(result.methods);\n setLockedUntil(result.lockedUntil);\n return result;\n },\n };\n }, [auth]);\n\n const handleLoginSuccess = useCallback(\n ({ force } = { force: false }) => {\n if (!force && auth.nextStep === 'TOTP') {\n return;\n }\n onLoginSuccess();\n },\n [auth.nextStep, onLoginSuccess],\n );\n\n const isMfaStep = auth.nextStep === 'TOTP' || view === 'mfa';\n const title = isMfaStep\n ? t('auth.loginFlow.title.mfa')\n : t('auth.loginFlow.title.default');\n const subtitle = isMfaStep\n ? t('auth.loginFlow.subtitle.mfa')\n : t('auth.loginFlow.subtitle.default');\n\n const startFlowForEmail = useCallback(\n async (inputEmail: string) => {\n setLocalError(null);\n try {\n const trimmed = inputEmail.trim();\n const result = await auth.beginAuthentication(trimmed);\n setEmail(trimmed);\n setMethods(result.methods);\n setLockedUntil(result.lockedUntil);\n const hasPasskey = result.methods.some((method) => {\n return method === 'PASSKEY';\n });\n\n if (hasPasskey) {\n setView('passkey');\n } else if (result.methods.includes('PASSWORD')) {\n setView('password');\n } else {\n setView('methods');\n }\n } catch {\n setLocalError(t('auth.loginFlow.errors.tryAgain'));\n }\n },\n [auth, t],\n );\n\n useEffect(() => {\n passkeyAttemptActive.current = true;\n const trimmedEmail = email.trim();\n const shouldAttempt =\n view === 'passkey' && !hasTriedPasskey && trimmedEmail !== '';\n\n if (shouldAttempt) {\n setHasTriedPasskey(true);\n const runPasskeyLogin = async () => {\n try {\n const status = await loginAdapter.loginWithPasskey({\n email: trimmedEmail,\n });\n\n if (!passkeyAttemptActive.current) {\n return;\n }\n\n if (status === 'success') {\n handleLoginSuccess();\n return;\n }\n\n if (status === 'mfa-required') {\n setView('mfa');\n return;\n }\n\n setView('methods');\n setLocalError(t('auth.loginFlow.errors.passkeyUnavailable'));\n } catch {\n if (!passkeyAttemptActive.current) {\n return;\n }\n\n setLocalError(t('auth.loginFlow.errors.passkeyUnavailable'));\n setView('methods');\n }\n };\n runPasskeyLogin().catch(() => {\n // Errors are handled inside runPasskeyLogin; ignore any unexpected rethrow.\n });\n }\n\n return () => {\n passkeyAttemptActive.current = false;\n };\n }, [email, handleLoginSuccess, hasTriedPasskey, loginAdapter, t, view]);\n\n let content: JSX.Element;\n\n if (isMfaStep) {\n content = (\n <AuthPanel>\n <MfaChallengeForm\n auth={auth}\n onSuccess={() => {\n handleLoginSuccess({ force: true });\n }}\n onBack={() => {\n auth.reset();\n setView('email');\n }}\n />\n </AuthPanel>\n );\n } else if (view === 'passkey') {\n content = (\n <AuthPanel\n title={t('auth.loginFlow.passkey.title')}\n description={t('auth.loginFlow.passkey.description', { email })}\n >\n <PasskeyLoginForm\n auth={loginAdapter}\n onSuccess={handleLoginSuccess}\n onShowMethods={() => {\n setView('methods');\n }}\n defaultEmail={email}\n isAttemptingPasskey={auth.isLoading}\n />\n </AuthPanel>\n );\n } else if (view === 'methods') {\n content = (\n <AuthPanel title={t('auth.loginFlow.methods.title')}>\n <MethodChooser\n email={email}\n methods={methods}\n lockedUntil={lockedUntil}\n onSelect={(method) => {\n if (method === 'PASSKEY') {\n setView('passkey');\n return;\n }\n if (method === 'PASSWORD') {\n setView('password');\n }\n }}\n onBack={() => {\n setView('email');\n }}\n />\n </AuthPanel>\n );\n } else if (view === 'password') {\n content = (\n <PasswordLoginPanel\n auth={loginAdapter}\n defaultEmail={email}\n onForgotPassword={onForgotPassword}\n onSuccess={handleLoginSuccess}\n providers={oidcProviders}\n />\n );\n } else {\n // email capture only\n content = (\n <EmailCapturePanel\n email={email}\n errorMessage={localError}\n isLoading={auth.isLoading}\n onEmailChange={(value) => {\n setEmail(value);\n setLocalError(null);\n }}\n onContinue={() => {\n const trimmed = email.trim();\n if (trimmed === '') {\n setLocalError(t('auth.loginFlow.errors.emailRequired'));\n return;\n }\n startFlowForEmail(trimmed).catch(() => {\n // startFlowForEmail handles its own errors.\n });\n }}\n onForgotPassword={onForgotPassword}\n />\n );\n }\n\n return (\n <AuthLayout title={title} subtitle={subtitle}>\n {content}\n </AuthLayout>\n );\n};\n"],"mappings":";;;;;;;;;;;AAmCA,IAAa,KAAa,EACxB,SACA,kBACA,mBACA,0BACiC;CACjC,IAAM,EAAE,SAAM,EAA8B,GACtC,CAAC,GAAO,KAAY,EAAS,EAAE,GAC/B,CAAC,GAAS,KAAc,EAAgC,CAAC,CAAC,GAC1D,CAAC,GAAa,KAAkB,EAAwB,IAAI,GAC5D,CAAC,GAAM,KAAW,EAAe,OAAO,GACxC,CAAC,GAAiB,KAAsB,EAAS,EAAK,GACtD,CAAC,GAAY,KAAiB,EAAwB,IAAI,GAC1D,IAAuB,EAAO,EAAI,GAElC,IAAe,SACZ;EACL,GAAG;EACH,qBAAqB,OAAO,MAAuB;GACjD,IAAM,IAAS,MAAM,EAAK,oBAAoB,CAAU;GAIxD,OAHA,EAAS,CAAU,GACnB,EAAW,EAAO,OAAO,GACzB,EAAe,EAAO,WAAW,GAC1B;EACT;CACF,IACC,CAAC,CAAI,CAAC,GAEH,IAAqB,GACxB,EAAE,aAAU,EAAE,OAAO,GAAM,MAAM;EAC5B,CAAC,KAAS,EAAK,aAAa,UAGhC,EAAe;CACjB,GACA,CAAC,EAAK,UAAU,CAAc,CAChC,GAEM,IAAY,EAAK,aAAa,UAAU,MAAS,OACjD,IACF,EADU,IACR,6BACA,8BAA8B,GAC9B,IACF,EADa,IACX,gCACA,iCAAiC,GAEjC,IAAoB,EACxB,OAAO,MAAuB;EAC5B,EAAc,IAAI;EAClB,IAAI;GACF,IAAM,IAAU,EAAW,KAAK,GAC1B,IAAS,MAAM,EAAK,oBAAoB,CAAO;GAQrD,AAPA,EAAS,CAAO,GAChB,EAAW,EAAO,OAAO,GACzB,EAAe,EAAO,WAAW,GACd,EAAO,QAAQ,MAAM,MAC/B,MAAW,SAGhB,IACF,EAAQ,SAAS,IACR,EAAO,QAAQ,SAAS,UAAU,IAC3C,EAAQ,UAAU,IAElB,EAAQ,SAAS;EAErB,QAAQ;GACN,EAAc,EAAE,gCAAgC,CAAC;EACnD;CACF,GACA,CAAC,GAAM,CAAC,CACV;CAEA,QAAgB;EACd,EAAqB,UAAU;EAC/B,IAAM,IAAe,EAAM,KAAK;EA0ChC,OAxCE,MAAS,aAAa,CAAC,KAAmB,MAAiB,OAG3D,EAAmB,EAAI,IAgCvB,YA/BoC;GAClC,IAAI;IACF,IAAM,IAAS,MAAM,EAAa,iBAAiB,EACjD,OAAO,EACT,CAAC;IAED,IAAI,CAAC,EAAqB,SACxB;IAGF,IAAI,MAAW,WAAW;KACxB,EAAmB;KACnB;IACF;IAEA,IAAI,MAAW,gBAAgB;KAC7B,EAAQ,KAAK;KACb;IACF;IAGA,AADA,EAAQ,SAAS,GACjB,EAAc,EAAE,0CAA0C,CAAC;GAC7D,QAAQ;IACN,IAAI,CAAC,EAAqB,SACxB;IAIF,AADA,EAAc,EAAE,0CAA0C,CAAC,GAC3D,EAAQ,SAAS;GACnB;EACF,EACA,CAAgB,CAAC,CAAC,YAAY,CAE9B,CAAC,UAGU;GACX,EAAqB,UAAU;EACjC;CACF,GAAG;EAAC;EAAO;EAAoB;EAAiB;EAAc;EAAG;CAAI,CAAC;CAEtE,IAAI;CA4FJ,OA1FA,AAkEE,IAlEE,IAEA,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;EACQ;EACN,iBAAiB;GACf,EAAmB,EAAE,OAAO,GAAK,CAAC;EACpC;EACA,cAAc;GAEZ,AADA,EAAK,MAAM,GACX,EAAQ,OAAO;EACjB;CACD,CAAA,EACQ,CAAA,IAEJ,MAAS,YAEhB,kBAAC,GAAD;EACE,OAAO,EAAE,8BAA8B;EACvC,aAAa,EAAE,sCAAsC,EAAE,SAAM,CAAC;YAE9D,kBAAC,GAAD;GACE,MAAM;GACN,WAAW;GACX,qBAAqB;IACnB,EAAQ,SAAS;GACnB;GACA,cAAc;GACd,qBAAqB,EAAK;EAC3B,CAAA;CACQ,CAAA,IAEJ,MAAS,YAEhB,kBAAC,GAAD;EAAW,OAAO,EAAE,8BAA8B;YAChD,kBAAC,GAAD;GACS;GACE;GACI;GACb,WAAW,MAAW;IACpB,IAAI,MAAW,WAAW;KACxB,EAAQ,SAAS;KACjB;IACF;IACA,AAAI,MAAW,cACb,EAAQ,UAAU;GAEtB;GACA,cAAc;IACZ,EAAQ,OAAO;GACjB;EACD,CAAA;CACQ,CAAA,IAEJ,MAAS,aAEhB,kBAAC,GAAD;EACE,MAAM;EACN,cAAc;EACI;EAClB,WAAW;EACX,WAAW;CACZ,CAAA,IAKD,kBAAC,GAAD;EACS;EACP,cAAc;EACd,WAAW,EAAK;EAChB,gBAAgB,MAAU;GAExB,AADA,EAAS,CAAK,GACd,EAAc,IAAI;EACpB;EACA,kBAAkB;GAChB,IAAM,IAAU,EAAM,KAAK;GAC3B,IAAI,MAAY,IAAI;IAClB,EAAc,EAAE,qCAAqC,CAAC;IACtD;GACF;GACA,EAAkB,CAAO,CAAC,CAAC,YAAY,CAEvC,CAAC;EACH;EACkB;CACnB,CAAA,GAKH,kBAAC,GAAD;EAAmB;EAAiB;YACjC;CACS,CAAA;AAEhB"}
@@ -1 +1 @@
1
- {"version":3,"file":"MethodChooser.js","names":[],"sources":["../../../../src/auth/login/MethodChooser.tsx"],"sourcesContent":["/* eslint-disable no-ternary */\nimport { type JSX } from 'react';\nimport { Trans } from 'react-i18next';\nimport { Button } from '@plumile/ui/atomic/atoms/button/Button.js';\nimport { FormError } from '@plumile/ui/atomic/molecules/form_error/FormError.js';\nimport { cx } from '@plumile/ui/theme/tools.js';\n\nimport type { AuthMethod } from '../../modules/sharedSchemaTypes.js';\nimport { useBackofficeReactTranslation } from '../../i18n/useBackofficeReactTranslation.js';\n\nimport * as styles from './loginPage.css.js';\n\ntype Props = {\n email: string;\n methods: readonly AuthMethod[];\n lockedUntil: string | null;\n onSelect: (method: AuthMethod) => void;\n onBack: () => void;\n};\n\nexport const MethodChooser = ({\n email,\n methods,\n lockedUntil,\n onSelect,\n onBack,\n}: Props): JSX.Element => {\n const { t } = useBackofficeReactTranslation();\n const isLocked = methods.length === 0;\n const lockedMessage =\n lockedUntil != null\n ? t('auth.methodChooser.lockedWithTime', {\n time: new Date(lockedUntil).toLocaleString(),\n })\n : t('auth.methodChooser.locked');\n\n const methodButtons = methods.map((method) => {\n const label =\n method === 'PASSKEY'\n ? t('auth.methodChooser.methods.passkey')\n : method === 'PASSWORD'\n ? t('auth.methodChooser.methods.password')\n : t('auth.methodChooser.methods.other', {\n method: method.toLowerCase(),\n });\n return (\n <Button\n key={method}\n type=\"button\"\n variant=\"secondary\"\n onClick={() => {\n onSelect(method);\n }}\n className={cx(styles.fullWidth, styles.brandGhostButton)}\n >\n {label}\n </Button>\n );\n });\n\n return (\n <div className={styles.formSurface}>\n <div className={styles.stack}>\n <p className={styles.helper}>\n <Trans\n i18nKey=\"auth.methodChooser.prompt\"\n values={{ email }}\n components={{ strong: <strong /> }}\n />\n </p>\n {isLocked ? <FormError>{lockedMessage}</FormError> : null}\n {!isLocked ? methodButtons : null}\n <Button\n type=\"button\"\n variant=\"text\"\n onClick={onBack}\n className={styles.inlineLink}\n >\n {t('auth.methodChooser.actions.back')}\n </Button>\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;AAoBA,IAAa,KAAiB,EAC5B,UACA,YACA,gBACA,aACA,gBACwB;CACxB,IAAM,EAAE,SAAM,EAA8B,GACtC,IAAW,EAAQ,WAAW,GAC9B,IACJ,KAAe,OAIX,EAAE,2BAA2B,IAH7B,EAAE,qCAAqC,EACrC,MAAM,IAAI,KAAK,CAAW,EAAE,eAAe,EAC7C,CAAC,GAGD,IAAgB,EAAQ,KAAK,MAAW;EAC5C,IAAM,IACJ,MAAW,YACP,EAAE,oCAAoC,IACtC,MAAW,aACT,EAAE,qCAAqC,IACvC,EAAE,oCAAoC,EACpC,QAAQ,EAAO,YAAY,EAC7B,CAAC;EACT,OACE,kBAAC,GAAD;GAEE,MAAK;GACL,SAAQ;GACR,eAAe;IACb,EAAS,CAAM;GACjB;GACA,WAAW,EAAG,GAAkB,CAAuB;aAEtD;EACK,GATD,CASC;CAEZ,CAAC;CAED,OACE,kBAAC,OAAD;EAAK,WAAW;YACd,kBAAC,OAAD;GAAK,WAAW;aAAhB;IACE,kBAAC,KAAD;KAAG,WAAW;eACZ,kBAAC,GAAD;MACE,SAAQ;MACR,QAAQ,EAAE,SAAM;MAChB,YAAY,EAAE,QAAQ,kBAAC,UAAD,CAAS,CAAA,EAAE;KAClC,CAAA;IACA,CAAA;IACF,IAAW,kBAAC,GAAD,EAAA,UAAY,EAAyB,CAAA,IAAI;IACnD,IAA2B,OAAhB;IACb,kBAAC,GAAD;KACE,MAAK;KACL,SAAQ;KACR,SAAS;KACT,WAAW;eAEV,EAAE,iCAAiC;IAC9B,CAAA;GACL;;CACF,CAAA;AAET"}
1
+ {"version":3,"file":"MethodChooser.js","names":[],"sources":["../../../../src/auth/login/MethodChooser.tsx"],"sourcesContent":["/* eslint-disable no-ternary */\nimport { type JSX } from 'react';\nimport { Trans } from 'react-i18next';\nimport { Button } from '@plumile/ui/atomic/atoms/button/Button.js';\nimport { FormError } from '@plumile/ui/atomic/molecules/form_error/FormError.js';\nimport { cx } from '@plumile/ui/theme/tools.js';\n\nimport type { AuthMethod } from '../../modules/sharedSchemaTypes.js';\nimport { useBackofficeReactTranslation } from '../../i18n/useBackofficeReactTranslation.js';\n\nimport * as styles from './loginPage.css.js';\n\ntype Props = {\n email: string;\n methods: readonly AuthMethod[];\n lockedUntil: string | null;\n onSelect: (method: AuthMethod) => void;\n onBack: () => void;\n};\n\nexport const MethodChooser = ({\n email,\n methods,\n lockedUntil,\n onSelect,\n onBack,\n}: Props): JSX.Element => {\n const { t } = useBackofficeReactTranslation();\n const isLocked = methods.length === 0;\n const lockedMessage =\n lockedUntil != null\n ? t('auth.methodChooser.lockedWithTime', {\n time: new Date(lockedUntil).toLocaleString(),\n })\n : t('auth.methodChooser.locked');\n\n const methodButtons = methods.map((method) => {\n const label =\n method === 'PASSKEY'\n ? t('auth.methodChooser.methods.passkey')\n : method === 'PASSWORD'\n ? t('auth.methodChooser.methods.password')\n : t('auth.methodChooser.methods.other', {\n method: method.toLowerCase(),\n });\n return (\n <Button\n key={method}\n type=\"button\"\n variant=\"secondary\"\n onClick={() => {\n onSelect(method);\n }}\n className={cx(styles.fullWidth, styles.brandGhostButton)}\n >\n {label}\n </Button>\n );\n });\n\n return (\n <div className={styles.formSurface}>\n <div className={styles.stack}>\n <p className={styles.helper}>\n <Trans\n i18nKey=\"auth.methodChooser.prompt\"\n values={{ email }}\n components={{ strong: <strong /> }}\n />\n </p>\n {isLocked ? <FormError>{lockedMessage}</FormError> : null}\n {!isLocked ? methodButtons : null}\n <Button\n type=\"button\"\n variant=\"text\"\n onClick={onBack}\n className={styles.inlineLink}\n >\n {t('auth.methodChooser.actions.back')}\n </Button>\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;AAoBA,IAAa,KAAiB,EAC5B,UACA,YACA,gBACA,aACA,gBACwB;CACxB,IAAM,EAAE,SAAM,EAA8B,GACtC,IAAW,EAAQ,WAAW,GAC9B,IACJ,KAAe,OAIX,EAAE,2BAA2B,IAH7B,EAAE,qCAAqC,EACrC,MAAM,IAAI,KAAK,CAAW,CAAC,CAAC,eAAe,EAC7C,CAAC,GAGD,IAAgB,EAAQ,KAAK,MAAW;EAC5C,IAAM,IACJ,MAAW,YACP,EAAE,oCAAoC,IACtC,MAAW,aACT,EAAE,qCAAqC,IACvC,EAAE,oCAAoC,EACpC,QAAQ,EAAO,YAAY,EAC7B,CAAC;EACT,OACE,kBAAC,GAAD;GAEE,MAAK;GACL,SAAQ;GACR,eAAe;IACb,EAAS,CAAM;GACjB;GACA,WAAW,EAAG,GAAkB,CAAuB;aAEtD;EACK,GATD,CASC;CAEZ,CAAC;CAED,OACE,kBAAC,OAAD;EAAK,WAAW;YACd,kBAAC,OAAD;GAAK,WAAW;aAAhB;IACE,kBAAC,KAAD;KAAG,WAAW;eACZ,kBAAC,GAAD;MACE,SAAQ;MACR,QAAQ,EAAE,SAAM;MAChB,YAAY,EAAE,QAAQ,kBAAC,UAAD,CAAS,CAAA,EAAE;KAClC,CAAA;IACA,CAAA;IACF,IAAW,kBAAC,GAAD,EAAA,UAAY,EAAyB,CAAA,IAAI;IACnD,IAA2B,OAAhB;IACb,kBAAC,GAAD;KACE,MAAK;KACL,SAAQ;KACR,SAAS;KACT,WAAW;eAEV,EAAE,iCAAiC;IAC9B,CAAA;GACL;;CACF,CAAA;AAET"}
@@ -1 +1 @@
1
- {"version":3,"file":"MfaChallengeForm.js","names":[],"sources":["../../../../src/auth/login/MfaChallengeForm.tsx"],"sourcesContent":["/* eslint-disable no-ternary */\nimport {\n useCallback,\n useEffect,\n useState,\n type FormEvent,\n type JSX,\n} from 'react';\nimport { useBackofficeReactTranslation } from '../../i18n/useBackofficeReactTranslation.js';\n\nimport { Button } from '@plumile/ui/atomic/atoms/button/Button.js';\nimport { FormError } from '@plumile/ui/atomic/molecules/form_error/FormError.js';\nimport { FormField } from '@plumile/ui/atomic/molecules/form_field/FormField.js';\nimport { Form } from '@plumile/ui/atomic/molecules/form/Form.js';\n\nimport type { TotpCredentials, UseAuthReturn } from '../../hooks/useAuth.js';\n\nimport * as styles from './loginPage.css.js';\n\ntype Props = {\n auth: Pick<\n UseAuthReturn,\n 'completeMfa' | 'isLoading' | 'error' | 'emailHint' | 'reset' | 'clearError'\n >;\n onSuccess: () => void;\n onBack: () => void;\n};\n\nexport const MfaChallengeForm = ({\n auth,\n onSuccess,\n onBack,\n}: Props): JSX.Element => {\n const { t } = useBackofficeReactTranslation();\n const [code, setCode] = useState('');\n const [localError, setLocalError] = useState<string | null>(null);\n\n // Clear any stale errors when the MFA form is shown\n useEffect(() => {\n auth.clearError();\n setLocalError(null);\n }, [auth]);\n\n const handleSubmit = useCallback(\n async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setLocalError(null);\n\n const trimmed = code.trim();\n if (trimmed.length < 4) {\n setLocalError(t('auth.mfa.errors.shortCode'));\n return;\n }\n\n try {\n const credentials: TotpCredentials = {\n code: trimmed,\n };\n await auth.completeMfa(credentials);\n onSuccess();\n } catch {\n setLocalError(t('auth.mfa.errors.verificationFailed'));\n }\n },\n [auth, code, onSuccess, t],\n );\n\n const helper =\n auth.emailHint != null\n ? t('auth.mfa.helper.withEmail', { email: auth.emailHint })\n : t('auth.mfa.helper.default');\n\n const formError = localError;\n\n const handleSubmitEvent = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n handleSubmit(event).catch(() => {\n return undefined;\n });\n },\n [handleSubmit],\n );\n\n return (\n <Form\n className={styles.formSurface}\n onSubmit={handleSubmitEvent}\n noValidate\n initialFocus=\"first-form-control\"\n >\n <div className={styles.stack}>\n <p className={styles.helper}>{helper}</p>\n {formError != null ? <FormError>{formError}</FormError> : null}\n <FormField\n label={t('auth.mfa.form.label')}\n name=\"code\"\n type=\"text\"\n value={code}\n onChange={(event) => {\n setCode(event.target.value);\n setLocalError(null);\n }}\n placeholder={t('auth.mfa.form.placeholder')}\n autoComplete=\"one-time-code\"\n required\n />\n </div>\n <div className={styles.actionsRow}>\n <Button\n type=\"button\"\n variant=\"text\"\n size=\"small\"\n className={styles.inlineLink}\n onClick={() => {\n auth.reset();\n onBack();\n }}\n >\n {t('auth.mfa.actions.back')}\n </Button>\n <Button type=\"submit\" size=\"large\" isLoading={auth.isLoading}>\n {t('auth.mfa.actions.submit')}\n </Button>\n </div>\n </Form>\n );\n};\n"],"mappings":";;;;;;;;;AA4BA,IAAa,KAAoB,EAC/B,SACA,cACA,gBACwB;CACxB,IAAM,EAAE,SAAM,EAA8B,GACtC,CAAC,GAAM,KAAW,EAAS,EAAE,GAC7B,CAAC,GAAY,KAAiB,EAAwB,IAAI;CAGhE,QAAgB;EAEd,AADA,EAAK,WAAW,GAChB,EAAc,IAAI;CACpB,GAAG,CAAC,CAAI,CAAC;CAET,IAAM,IAAe,EACnB,OAAO,MAAsC;EAE3C,AADA,EAAM,eAAe,GACrB,EAAc,IAAI;EAElB,IAAM,IAAU,EAAK,KAAK;EAC1B,IAAI,EAAQ,SAAS,GAAG;GACtB,EAAc,EAAE,2BAA2B,CAAC;GAC5C;EACF;EAEA,IAAI;GACF,IAAM,IAA+B,EACnC,MAAM,EACR;GAEA,AADA,MAAM,EAAK,YAAY,CAAW,GAClC,EAAU;EACZ,QAAQ;GACN,EAAc,EAAE,oCAAoC,CAAC;EACvD;CACF,GACA;EAAC;EAAM;EAAM;EAAW;CAAC,CAC3B,GAEM,IACJ,EAAK,aAAa,OAEd,EAAE,yBAAyB,IAD3B,EAAE,6BAA6B,EAAE,OAAO,EAAK,UAAU,CAAC,GAGxD,IAAY;CAWlB,OACE,kBAAC,GAAD;EACE,WAAW;EACX,UAZsB,GACvB,MAAsC;GACrC,EAAa,CAAK,EAAE,YAAY,CAEhC,CAAC;EACH,GACA,CAAC,CAAY,CAMD;EACV,YAAA;EACA,cAAa;YAJf,CAME,kBAAC,OAAD;GAAK,WAAW;aAAhB;IACE,kBAAC,KAAD;KAAG,WAAW;eAAgB;IAAU,CAAA;IACvC,KAAa,OAA4C,OAArC,kBAAC,GAAD,EAAA,UAAY,EAAqB,CAAA;IACtD,kBAAC,GAAD;KACE,OAAO,EAAE,qBAAqB;KAC9B,MAAK;KACL,MAAK;KACL,OAAO;KACP,WAAW,MAAU;MAEnB,AADA,EAAQ,EAAM,OAAO,KAAK,GAC1B,EAAc,IAAI;KACpB;KACA,aAAa,EAAE,2BAA2B;KAC1C,cAAa;KACb,UAAA;IACD,CAAA;GACE;MACL,kBAAC,OAAD;GAAK,WAAW;aAAhB,CACE,kBAAC,GAAD;IACE,MAAK;IACL,SAAQ;IACR,MAAK;IACL,WAAW;IACX,eAAe;KAEb,AADA,EAAK,MAAM,GACX,EAAO;IACT;cAEC,EAAE,uBAAuB;GACpB,CAAA,GACR,kBAAC,GAAD;IAAQ,MAAK;IAAS,MAAK;IAAQ,WAAW,EAAK;cAChD,EAAE,yBAAyB;GACtB,CAAA,CACL;IACD;;AAEV"}
1
+ {"version":3,"file":"MfaChallengeForm.js","names":[],"sources":["../../../../src/auth/login/MfaChallengeForm.tsx"],"sourcesContent":["/* eslint-disable no-ternary */\nimport {\n useCallback,\n useEffect,\n useState,\n type FormEvent,\n type JSX,\n} from 'react';\nimport { useBackofficeReactTranslation } from '../../i18n/useBackofficeReactTranslation.js';\n\nimport { Button } from '@plumile/ui/atomic/atoms/button/Button.js';\nimport { FormError } from '@plumile/ui/atomic/molecules/form_error/FormError.js';\nimport { FormField } from '@plumile/ui/atomic/molecules/form_field/FormField.js';\nimport { Form } from '@plumile/ui/atomic/molecules/form/Form.js';\n\nimport type { TotpCredentials, UseAuthReturn } from '../../hooks/useAuth.js';\n\nimport * as styles from './loginPage.css.js';\n\ntype Props = {\n auth: Pick<\n UseAuthReturn,\n 'completeMfa' | 'isLoading' | 'error' | 'emailHint' | 'reset' | 'clearError'\n >;\n onSuccess: () => void;\n onBack: () => void;\n};\n\nexport const MfaChallengeForm = ({\n auth,\n onSuccess,\n onBack,\n}: Props): JSX.Element => {\n const { t } = useBackofficeReactTranslation();\n const [code, setCode] = useState('');\n const [localError, setLocalError] = useState<string | null>(null);\n\n // Clear any stale errors when the MFA form is shown\n useEffect(() => {\n auth.clearError();\n setLocalError(null);\n }, [auth]);\n\n const handleSubmit = useCallback(\n async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setLocalError(null);\n\n const trimmed = code.trim();\n if (trimmed.length < 4) {\n setLocalError(t('auth.mfa.errors.shortCode'));\n return;\n }\n\n try {\n const credentials: TotpCredentials = {\n code: trimmed,\n };\n await auth.completeMfa(credentials);\n onSuccess();\n } catch {\n setLocalError(t('auth.mfa.errors.verificationFailed'));\n }\n },\n [auth, code, onSuccess, t],\n );\n\n const helper =\n auth.emailHint != null\n ? t('auth.mfa.helper.withEmail', { email: auth.emailHint })\n : t('auth.mfa.helper.default');\n\n const formError = localError;\n\n const handleSubmitEvent = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n handleSubmit(event).catch(() => {\n return undefined;\n });\n },\n [handleSubmit],\n );\n\n return (\n <Form\n className={styles.formSurface}\n onSubmit={handleSubmitEvent}\n noValidate\n initialFocus=\"first-form-control\"\n >\n <div className={styles.stack}>\n <p className={styles.helper}>{helper}</p>\n {formError != null ? <FormError>{formError}</FormError> : null}\n <FormField\n label={t('auth.mfa.form.label')}\n name=\"code\"\n type=\"text\"\n value={code}\n onChange={(event) => {\n setCode(event.target.value);\n setLocalError(null);\n }}\n placeholder={t('auth.mfa.form.placeholder')}\n autoComplete=\"one-time-code\"\n required\n />\n </div>\n <div className={styles.actionsRow}>\n <Button\n type=\"button\"\n variant=\"text\"\n size=\"small\"\n className={styles.inlineLink}\n onClick={() => {\n auth.reset();\n onBack();\n }}\n >\n {t('auth.mfa.actions.back')}\n </Button>\n <Button type=\"submit\" size=\"large\" isLoading={auth.isLoading}>\n {t('auth.mfa.actions.submit')}\n </Button>\n </div>\n </Form>\n );\n};\n"],"mappings":";;;;;;;;;AA4BA,IAAa,KAAoB,EAC/B,SACA,cACA,gBACwB;CACxB,IAAM,EAAE,SAAM,EAA8B,GACtC,CAAC,GAAM,KAAW,EAAS,EAAE,GAC7B,CAAC,GAAY,KAAiB,EAAwB,IAAI;CAGhE,QAAgB;EAEd,AADA,EAAK,WAAW,GAChB,EAAc,IAAI;CACpB,GAAG,CAAC,CAAI,CAAC;CAET,IAAM,IAAe,EACnB,OAAO,MAAsC;EAE3C,AADA,EAAM,eAAe,GACrB,EAAc,IAAI;EAElB,IAAM,IAAU,EAAK,KAAK;EAC1B,IAAI,EAAQ,SAAS,GAAG;GACtB,EAAc,EAAE,2BAA2B,CAAC;GAC5C;EACF;EAEA,IAAI;GACF,IAAM,IAA+B,EACnC,MAAM,EACR;GAEA,AADA,MAAM,EAAK,YAAY,CAAW,GAClC,EAAU;EACZ,QAAQ;GACN,EAAc,EAAE,oCAAoC,CAAC;EACvD;CACF,GACA;EAAC;EAAM;EAAM;EAAW;CAAC,CAC3B,GAEM,IACJ,EAAK,aAAa,OAEd,EAAE,yBAAyB,IAD3B,EAAE,6BAA6B,EAAE,OAAO,EAAK,UAAU,CAAC,GAGxD,IAAY;CAWlB,OACE,kBAAC,GAAD;EACE,WAAW;EACX,UAZsB,GACvB,MAAsC;GACrC,EAAa,CAAK,CAAC,CAAC,YAAY,CAEhC,CAAC;EACH,GACA,CAAC,CAAY,CAMD;EACV,YAAA;EACA,cAAa;YAJf,CAME,kBAAC,OAAD;GAAK,WAAW;aAAhB;IACE,kBAAC,KAAD;KAAG,WAAW;eAAgB;IAAU,CAAA;IACvC,KAAa,OAA4C,OAArC,kBAAC,GAAD,EAAA,UAAY,EAAqB,CAAA;IACtD,kBAAC,GAAD;KACE,OAAO,EAAE,qBAAqB;KAC9B,MAAK;KACL,MAAK;KACL,OAAO;KACP,WAAW,MAAU;MAEnB,AADA,EAAQ,EAAM,OAAO,KAAK,GAC1B,EAAc,IAAI;KACpB;KACA,aAAa,EAAE,2BAA2B;KAC1C,cAAa;KACb,UAAA;IACD,CAAA;GACE;MACL,kBAAC,OAAD;GAAK,WAAW;aAAhB,CACE,kBAAC,GAAD;IACE,MAAK;IACL,SAAQ;IACR,MAAK;IACL,WAAW;IACX,eAAe;KAEb,AADA,EAAK,MAAM,GACX,EAAO;IACT;cAEC,EAAE,uBAAuB;GACpB,CAAA,GACR,kBAAC,GAAD;IAAQ,MAAK;IAAS,MAAK;IAAQ,WAAW,EAAK;cAChD,EAAE,yBAAyB;GACtB,CAAA,CACL;IACD;;AAEV"}
@@ -1 +1 @@
1
- {"version":3,"file":"PasskeyLoginForm.js","names":[],"sources":["../../../../src/auth/login/PasskeyLoginForm.tsx"],"sourcesContent":["/* eslint-disable no-ternary */\nimport {\n useCallback,\n useEffect,\n useState,\n type FormEvent,\n type JSX,\n} from 'react';\nimport { useBackofficeReactTranslation } from '../../i18n/useBackofficeReactTranslation.js';\n\nimport { Button } from '@plumile/ui/atomic/atoms/button/Button.js';\nimport { FormError } from '@plumile/ui/atomic/molecules/form_error/FormError.js';\nimport { FormField } from '@plumile/ui/atomic/molecules/form_field/FormField.js';\nimport { Form } from '@plumile/ui/atomic/molecules/form/Form.js';\n\nimport type { UseAuthReturn } from '../../hooks/useAuth.js';\n\nimport * as styles from './loginPage.css.js';\n\ntype Props = {\n auth: Pick<\n UseAuthReturn,\n | 'loginWithPasskey'\n | 'isLoading'\n | 'error'\n | 'emailHint'\n | 'nextStep'\n | 'beginAuthentication'\n | 'lockedUntil'\n | 'availableMethods'\n >;\n onSuccess: () => void;\n defaultEmail?: string;\n isAttemptingPasskey?: boolean;\n onShowMethods?: () => void;\n};\n\nexport const PasskeyLoginForm = ({\n auth,\n onSuccess,\n defaultEmail,\n isAttemptingPasskey = false,\n onShowMethods,\n}: Props): JSX.Element => {\n const { t } = useBackofficeReactTranslation();\n const [email, setEmail] = useState(auth.emailHint ?? '');\n const [localError, setLocalError] = useState<string | null>(null);\n\n useEffect(() => {\n if (auth.emailHint != null && auth.emailHint !== '') {\n setEmail(auth.emailHint);\n } else if (defaultEmail != null && defaultEmail !== '') {\n setEmail(defaultEmail);\n }\n }, [auth.emailHint, defaultEmail]);\n\n const handleSubmit = useCallback(\n async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setLocalError(null);\n\n if (email.trim() === '') {\n setLocalError(t('auth.passkey.errors.emailRequired'));\n return;\n }\n\n try {\n const result = await auth.beginAuthentication(email.trim());\n if (Array.isArray(result.methods) && result.methods.length === 0) {\n const lockedUntilMessage =\n result.lockedUntil != null\n ? t('auth.passkey.errors.lockedWithTime', {\n time: new Date(result.lockedUntil).toLocaleString(),\n })\n : t('auth.passkey.errors.locked');\n setLocalError(lockedUntilMessage);\n return;\n }\n if (\n Array.isArray(result.methods) &&\n !result.methods.includes('PASSKEY')\n ) {\n setLocalError(t('auth.passkey.errors.notAvailable'));\n return;\n }\n\n const status = await auth.loginWithPasskey({ email: email.trim() });\n if (status === 'success') {\n onSuccess();\n }\n } catch {\n setLocalError(t('auth.passkey.errors.failed'));\n }\n },\n [auth, email, onSuccess, t],\n );\n\n const formError = localError;\n\n const handleSubmitEvent = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n handleSubmit(event).catch(() => {\n return undefined;\n });\n },\n [handleSubmit],\n );\n\n return (\n <Form\n className={styles.formSurface}\n onSubmit={handleSubmitEvent}\n noValidate\n initialFocus=\"first-form-control\"\n >\n <div className={styles.stack}>\n <p className={styles.helper}>{t('auth.passkey.helper')}</p>\n {formError != null ? <FormError>{formError}</FormError> : null}\n <FormField\n label={t('auth.passkey.form.emailLabel')}\n name=\"passkey-email\"\n type=\"email\"\n value={email}\n onChange={(event) => {\n setEmail(event.target.value);\n setLocalError(null);\n }}\n placeholder={t('auth.passkey.form.emailPlaceholder')}\n autoComplete=\"email\"\n required\n />\n </div>\n <div className={styles.actionsRow}>\n <Button type=\"submit\" variant=\"primary\" isLoading={auth.isLoading}>\n {isAttemptingPasskey\n ? t('auth.passkey.actions.submitting')\n : t('auth.passkey.actions.submit')}\n </Button>\n {onShowMethods != null ? (\n <button\n type=\"button\"\n className={styles.inlineLink}\n onClick={onShowMethods}\n >\n {t('auth.passkey.actions.showMethods')}\n </button>\n ) : null}\n </div>\n </Form>\n );\n};\n"],"mappings":";;;;;;;;;AAqCA,IAAa,KAAoB,EAC/B,SACA,cACA,iBACA,yBAAsB,IACtB,uBACwB;CACxB,IAAM,EAAE,SAAM,EAA8B,GACtC,CAAC,GAAO,KAAY,EAAS,EAAK,aAAa,EAAE,GACjD,CAAC,GAAY,KAAiB,EAAwB,IAAI;CAEhE,QAAgB;EACd,AAAI,EAAK,aAAa,QAAQ,EAAK,cAAc,KAC/C,EAAS,EAAK,SAAS,IACd,KAAgB,QAAQ,MAAiB,MAClD,EAAS,CAAY;CAEzB,GAAG,CAAC,EAAK,WAAW,CAAY,CAAC;CAEjC,IAAM,IAAe,EACnB,OAAO,MAAsC;EAI3C,IAHA,EAAM,eAAe,GACrB,EAAc,IAAI,GAEd,EAAM,KAAK,MAAM,IAAI;GACvB,EAAc,EAAE,mCAAmC,CAAC;GACpD;EACF;EAEA,IAAI;GACF,IAAM,IAAS,MAAM,EAAK,oBAAoB,EAAM,KAAK,CAAC;GAC1D,IAAI,MAAM,QAAQ,EAAO,OAAO,KAAK,EAAO,QAAQ,WAAW,GAAG;IAOhE,EALE,EAAO,eAAe,OAIlB,EAAE,4BAA4B,IAH9B,EAAE,sCAAsC,EACtC,MAAM,IAAI,KAAK,EAAO,WAAW,EAAE,eAAe,EACpD,CAAC,CAEyB;IAChC;GACF;GACA,IACE,MAAM,QAAQ,EAAO,OAAO,KAC5B,CAAC,EAAO,QAAQ,SAAS,SAAS,GAClC;IACA,EAAc,EAAE,kCAAkC,CAAC;IACnD;GACF;GAGA,AAAI,MADiB,EAAK,iBAAiB,EAAE,OAAO,EAAM,KAAK,EAAE,CAAC,MACnD,aACb,EAAU;EAEd,QAAQ;GACN,EAAc,EAAE,4BAA4B,CAAC;EAC/C;CACF,GACA;EAAC;EAAM;EAAO;EAAW;CAAC,CAC5B,GAEM,IAAY;CAWlB,OACE,kBAAC,GAAD;EACE,WAAW;EACX,UAZsB,GACvB,MAAsC;GACrC,EAAa,CAAK,EAAE,YAAY,CAEhC,CAAC;EACH,GACA,CAAC,CAAY,CAMD;EACV,YAAA;EACA,cAAa;YAJf,CAME,kBAAC,OAAD;GAAK,WAAW;aAAhB;IACE,kBAAC,KAAD;KAAG,WAAW;eAAgB,EAAE,qBAAqB;IAAK,CAAA;IACzD,KAAa,OAA4C,OAArC,kBAAC,GAAD,EAAA,UAAY,EAAqB,CAAA;IACtD,kBAAC,GAAD;KACE,OAAO,EAAE,8BAA8B;KACvC,MAAK;KACL,MAAK;KACL,OAAO;KACP,WAAW,MAAU;MAEnB,AADA,EAAS,EAAM,OAAO,KAAK,GAC3B,EAAc,IAAI;KACpB;KACA,aAAa,EAAE,oCAAoC;KACnD,cAAa;KACb,UAAA;IACD,CAAA;GACE;MACL,kBAAC,OAAD;GAAK,WAAW;aAAhB,CACE,kBAAC,GAAD;IAAQ,MAAK;IAAS,SAAQ;IAAU,WAAW,EAAK;cAElD,EADH,IACK,oCACA,6BAA6B;GAC7B,CAAA,GACP,KAAiB,OAQd,OAPF,kBAAC,UAAD;IACE,MAAK;IACL,WAAW;IACX,SAAS;cAER,EAAE,kCAAkC;GAC/B,CAAA,CAEP;IACD;;AAEV"}
1
+ {"version":3,"file":"PasskeyLoginForm.js","names":[],"sources":["../../../../src/auth/login/PasskeyLoginForm.tsx"],"sourcesContent":["/* eslint-disable no-ternary */\nimport {\n useCallback,\n useEffect,\n useState,\n type FormEvent,\n type JSX,\n} from 'react';\nimport { useBackofficeReactTranslation } from '../../i18n/useBackofficeReactTranslation.js';\n\nimport { Button } from '@plumile/ui/atomic/atoms/button/Button.js';\nimport { FormError } from '@plumile/ui/atomic/molecules/form_error/FormError.js';\nimport { FormField } from '@plumile/ui/atomic/molecules/form_field/FormField.js';\nimport { Form } from '@plumile/ui/atomic/molecules/form/Form.js';\n\nimport type { UseAuthReturn } from '../../hooks/useAuth.js';\n\nimport * as styles from './loginPage.css.js';\n\ntype Props = {\n auth: Pick<\n UseAuthReturn,\n | 'loginWithPasskey'\n | 'isLoading'\n | 'error'\n | 'emailHint'\n | 'nextStep'\n | 'beginAuthentication'\n | 'lockedUntil'\n | 'availableMethods'\n >;\n onSuccess: () => void;\n defaultEmail?: string;\n isAttemptingPasskey?: boolean;\n onShowMethods?: () => void;\n};\n\nexport const PasskeyLoginForm = ({\n auth,\n onSuccess,\n defaultEmail,\n isAttemptingPasskey = false,\n onShowMethods,\n}: Props): JSX.Element => {\n const { t } = useBackofficeReactTranslation();\n const [email, setEmail] = useState(auth.emailHint ?? '');\n const [localError, setLocalError] = useState<string | null>(null);\n\n useEffect(() => {\n if (auth.emailHint != null && auth.emailHint !== '') {\n setEmail(auth.emailHint);\n } else if (defaultEmail != null && defaultEmail !== '') {\n setEmail(defaultEmail);\n }\n }, [auth.emailHint, defaultEmail]);\n\n const handleSubmit = useCallback(\n async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setLocalError(null);\n\n if (email.trim() === '') {\n setLocalError(t('auth.passkey.errors.emailRequired'));\n return;\n }\n\n try {\n const result = await auth.beginAuthentication(email.trim());\n if (Array.isArray(result.methods) && result.methods.length === 0) {\n const lockedUntilMessage =\n result.lockedUntil != null\n ? t('auth.passkey.errors.lockedWithTime', {\n time: new Date(result.lockedUntil).toLocaleString(),\n })\n : t('auth.passkey.errors.locked');\n setLocalError(lockedUntilMessage);\n return;\n }\n if (\n Array.isArray(result.methods) &&\n !result.methods.includes('PASSKEY')\n ) {\n setLocalError(t('auth.passkey.errors.notAvailable'));\n return;\n }\n\n const status = await auth.loginWithPasskey({ email: email.trim() });\n if (status === 'success') {\n onSuccess();\n }\n } catch {\n setLocalError(t('auth.passkey.errors.failed'));\n }\n },\n [auth, email, onSuccess, t],\n );\n\n const formError = localError;\n\n const handleSubmitEvent = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n handleSubmit(event).catch(() => {\n return undefined;\n });\n },\n [handleSubmit],\n );\n\n return (\n <Form\n className={styles.formSurface}\n onSubmit={handleSubmitEvent}\n noValidate\n initialFocus=\"first-form-control\"\n >\n <div className={styles.stack}>\n <p className={styles.helper}>{t('auth.passkey.helper')}</p>\n {formError != null ? <FormError>{formError}</FormError> : null}\n <FormField\n label={t('auth.passkey.form.emailLabel')}\n name=\"passkey-email\"\n type=\"email\"\n value={email}\n onChange={(event) => {\n setEmail(event.target.value);\n setLocalError(null);\n }}\n placeholder={t('auth.passkey.form.emailPlaceholder')}\n autoComplete=\"email\"\n required\n />\n </div>\n <div className={styles.actionsRow}>\n <Button type=\"submit\" variant=\"primary\" isLoading={auth.isLoading}>\n {isAttemptingPasskey\n ? t('auth.passkey.actions.submitting')\n : t('auth.passkey.actions.submit')}\n </Button>\n {onShowMethods != null ? (\n <button\n type=\"button\"\n className={styles.inlineLink}\n onClick={onShowMethods}\n >\n {t('auth.passkey.actions.showMethods')}\n </button>\n ) : null}\n </div>\n </Form>\n );\n};\n"],"mappings":";;;;;;;;;AAqCA,IAAa,KAAoB,EAC/B,SACA,cACA,iBACA,yBAAsB,IACtB,uBACwB;CACxB,IAAM,EAAE,SAAM,EAA8B,GACtC,CAAC,GAAO,KAAY,EAAS,EAAK,aAAa,EAAE,GACjD,CAAC,GAAY,KAAiB,EAAwB,IAAI;CAEhE,QAAgB;EACd,AAAI,EAAK,aAAa,QAAQ,EAAK,cAAc,KAC/C,EAAS,EAAK,SAAS,IACd,KAAgB,QAAQ,MAAiB,MAClD,EAAS,CAAY;CAEzB,GAAG,CAAC,EAAK,WAAW,CAAY,CAAC;CAEjC,IAAM,IAAe,EACnB,OAAO,MAAsC;EAI3C,IAHA,EAAM,eAAe,GACrB,EAAc,IAAI,GAEd,EAAM,KAAK,MAAM,IAAI;GACvB,EAAc,EAAE,mCAAmC,CAAC;GACpD;EACF;EAEA,IAAI;GACF,IAAM,IAAS,MAAM,EAAK,oBAAoB,EAAM,KAAK,CAAC;GAC1D,IAAI,MAAM,QAAQ,EAAO,OAAO,KAAK,EAAO,QAAQ,WAAW,GAAG;IAOhE,EALE,EAAO,eAAe,OAIlB,EAAE,4BAA4B,IAH9B,EAAE,sCAAsC,EACtC,MAAM,IAAI,KAAK,EAAO,WAAW,CAAC,CAAC,eAAe,EACpD,CAAC,CAEyB;IAChC;GACF;GACA,IACE,MAAM,QAAQ,EAAO,OAAO,KAC5B,CAAC,EAAO,QAAQ,SAAS,SAAS,GAClC;IACA,EAAc,EAAE,kCAAkC,CAAC;IACnD;GACF;GAGA,AAAI,MADiB,EAAK,iBAAiB,EAAE,OAAO,EAAM,KAAK,EAAE,CAAC,MACnD,aACb,EAAU;EAEd,QAAQ;GACN,EAAc,EAAE,4BAA4B,CAAC;EAC/C;CACF,GACA;EAAC;EAAM;EAAO;EAAW;CAAC,CAC5B,GAEM,IAAY;CAWlB,OACE,kBAAC,GAAD;EACE,WAAW;EACX,UAZsB,GACvB,MAAsC;GACrC,EAAa,CAAK,CAAC,CAAC,YAAY,CAEhC,CAAC;EACH,GACA,CAAC,CAAY,CAMD;EACV,YAAA;EACA,cAAa;YAJf,CAME,kBAAC,OAAD;GAAK,WAAW;aAAhB;IACE,kBAAC,KAAD;KAAG,WAAW;eAAgB,EAAE,qBAAqB;IAAK,CAAA;IACzD,KAAa,OAA4C,OAArC,kBAAC,GAAD,EAAA,UAAY,EAAqB,CAAA;IACtD,kBAAC,GAAD;KACE,OAAO,EAAE,8BAA8B;KACvC,MAAK;KACL,MAAK;KACL,OAAO;KACP,WAAW,MAAU;MAEnB,AADA,EAAS,EAAM,OAAO,KAAK,GAC3B,EAAc,IAAI;KACpB;KACA,aAAa,EAAE,oCAAoC;KACnD,cAAa;KACb,UAAA;IACD,CAAA;GACE;MACL,kBAAC,OAAD;GAAK,WAAW;aAAhB,CACE,kBAAC,GAAD;IAAQ,MAAK;IAAS,SAAQ;IAAU,WAAW,EAAK;cAElD,EADH,IACK,oCACA,6BAA6B;GAC7B,CAAA,GACP,KAAiB,OAQd,OAPF,kBAAC,UAAD;IACE,MAAK;IACL,WAAW;IACX,SAAS;cAER,EAAE,kCAAkC;GAC/B,CAAA,CAEP;IACD;;AAEV"}
@@ -1,3 +1,5 @@
1
+ /* empty css */
2
+ /* empty css */
1
3
  /* empty css */
2
4
  //#region src/auth/login/loginPage.css.ts
3
5
  var e = "txvbqbfpn txvbqbh4x txvbqbheb", t = "txvbqbfpn txvbqbh4x txvbqb19us", n = "txvbqb1bgt txvbqb9j txvbqbo7v txvbqbny4", r = "txvbqbey txvbqb1bgt txvbqbfpn txvbqbhdl txvbqbs00 txvbqbp6j", i = "txvbqb1bah txvbqbk07 txvbqbfvs txvbqb2wi txvbqb1bkt", a = "txvbqbey txvbqbfpn txvbqbh7g txvbqbhdy txvbqblt7 txvbqbrza", o = "w8yhmy0 txvbqb1b5h txvbqb1tv txvbqb2rt txvbqb1bi1 txvbqbv txvbqbhbf txvbqbwvv txvbqb3f txvbqb7h txvbqb76 txvbqb7t txvbqb1euf txvbqb1gdj", s = "txvbqb10od txvbqb11ly txvbqb12jj txvbqbxvm txvbqbc7z txvbqbc27 txvbqbci3 txvbqbce4", c = "txvbqbfpn txvbqbh4x txvbqbheb", l = "txvbqb19us", u = "txvbqbo7v txvbqbam txvbqbhbs txvbqb1bi1 txvbqbmu1", d = "txvbqbrzn", f = "w8yhmy1 txvbqb1f2 txvbqb29r txvbqb2sj txvbqb1bi1 txvbqb1bad txvbqb7h txvbqb75 txvbqb7t";
@@ -1 +1 @@
1
- {"version":3,"file":"synchronizeAuthStatusQuery.js","names":[],"sources":["../../../../src/auth/login/synchronizeAuthStatusQuery.ts"],"sourcesContent":["import RelayRuntime, {\n type GraphQLTaggedNode,\n type IEnvironment,\n type OperationType,\n} from 'relay-runtime';\n\nconst { fetchQuery } = RelayRuntime;\n\ntype AuthStatusQueryResponse = {\n readonly isLoggedIn?: boolean | null;\n};\n\n/**\n * Forces a fresh auth-status read from network to avoid stale cache guards\n * right after login mutations complete.\n */\nexport async function synchronizeAuthStatusQuery<TQuery extends OperationType>(\n environment: IEnvironment,\n query: GraphQLTaggedNode,\n): Promise<boolean> {\n const response = await fetchQuery<TQuery>(\n environment,\n query,\n {},\n { fetchPolicy: 'network-only' },\n ).toPromise();\n\n const isLoggedIn = (response as AuthStatusQueryResponse | null | undefined)\n ?.isLoggedIn;\n return isLoggedIn === true;\n}\n"],"mappings":";;AAMA,IAAM,EAAE,kBAAe;AAUvB,eAAsB,EACpB,GACA,GACkB;CAUlB,QAFoB,MAPG,EACrB,GACA,GACA,CAAC,GACD,EAAE,aAAa,eAAe,CAChC,EAAE,UAAU,IAGR,eACkB;AACxB"}
1
+ {"version":3,"file":"synchronizeAuthStatusQuery.js","names":[],"sources":["../../../../src/auth/login/synchronizeAuthStatusQuery.ts"],"sourcesContent":["import RelayRuntime, {\n type GraphQLTaggedNode,\n type IEnvironment,\n type OperationType,\n} from 'relay-runtime';\n\nconst { fetchQuery } = RelayRuntime;\n\ntype AuthStatusQueryResponse = {\n readonly isLoggedIn?: boolean | null;\n};\n\n/**\n * Forces a fresh auth-status read from network to avoid stale cache guards\n * right after login mutations complete.\n */\nexport async function synchronizeAuthStatusQuery<TQuery extends OperationType>(\n environment: IEnvironment,\n query: GraphQLTaggedNode,\n): Promise<boolean> {\n const response = await fetchQuery<TQuery>(\n environment,\n query,\n {},\n { fetchPolicy: 'network-only' },\n ).toPromise();\n\n const isLoggedIn = (response as AuthStatusQueryResponse | null | undefined)\n ?.isLoggedIn;\n return isLoggedIn === true;\n}\n"],"mappings":";;AAMA,IAAM,EAAE,kBAAe;AAUvB,eAAsB,EACpB,GACA,GACkB;CAUlB,QAFoB,MAPG,EACrB,GACA,GACA,CAAC,GACD,EAAE,aAAa,eAAe,CAChC,CAAC,CAAC,UAAU,EAAA,EAGR,eACkB;AACxB"}
@@ -1 +1 @@
1
- {"version":3,"file":"AcceptInvitationScreen.js","names":[],"sources":["../../../../src/auth/pages/AcceptInvitationScreen.tsx"],"sourcesContent":["/* eslint-disable no-ternary */\nimport {\n useCallback,\n useMemo,\n useState,\n type FormEvent,\n type JSX,\n} from 'react';\nimport { useBackofficeReactTranslation } from '../../i18n/useBackofficeReactTranslation.js';\n\nimport { Button } from '@plumile/ui/atomic/atoms/button/Button.js';\nimport { FormError } from '@plumile/ui/atomic/molecules/form_error/FormError.js';\nimport { FormField } from '@plumile/ui/atomic/molecules/form_field/FormField.js';\nimport { Form } from '@plumile/ui/atomic/molecules/form/Form.js';\nimport { AuthLayout } from '@plumile/ui/atomic/templates/auth_layout/AuthLayout.js';\n\nimport type { UseAuthReturn } from '../../hooks/useAuth.js';\nimport { MfaChallengeForm } from '../login/MfaChallengeForm.js';\nimport AuthPanel from '../login/AuthPanel.js';\nimport * as styles from '../login/loginPage.css.js';\n\ntype AcceptanceState = 'pending' | 'mfa' | 'completed' | 'error' | 'idle';\n\ntype Props = {\n auth: UseAuthReturn;\n onSuccessRedirect: () => void;\n onBackToLogin: () => void;\n token?: string;\n};\n\nexport const AcceptInvitationScreen = ({\n auth,\n onSuccessRedirect,\n onBackToLogin,\n token,\n}: Props): JSX.Element => {\n const { t } = useBackofficeReactTranslation();\n const { acceptInvitation } = auth;\n const [status, setStatus] = useState<AcceptanceState>('idle');\n const [password, setPassword] = useState('');\n const [passwordConfirmation, setPasswordConfirmation] = useState('');\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n\n const resolvedToken = useMemo(() => {\n if (token != null) {\n return token;\n }\n if (typeof window === 'undefined') {\n return '';\n }\n const params = new URLSearchParams(window.location.search);\n return params.get('token') ?? '';\n }, [token]);\n\n const handleSubmit = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n if (resolvedToken === '') {\n setErrorMessage(t('auth.acceptInvitation.errors.missingToken'));\n setStatus('error');\n return;\n }\n\n if (password !== passwordConfirmation) {\n setErrorMessage(t('auth.acceptInvitation.errors.passwordMismatch'));\n setStatus('error');\n return;\n }\n\n setStatus('pending');\n setErrorMessage(null);\n setIsSubmitting(true);\n\n // eslint-disable-next-line no-void\n void acceptInvitation({\n token: resolvedToken,\n password,\n passwordConfirmation,\n })\n .then((result) => {\n if (result === 'mfa-required') {\n setStatus('mfa');\n return;\n }\n setStatus('completed');\n onSuccessRedirect();\n })\n .catch(() => {\n setStatus('error');\n setErrorMessage(t('auth.acceptInvitation.errors.default'));\n })\n .finally(() => {\n setIsSubmitting(false);\n });\n },\n [\n acceptInvitation,\n onSuccessRedirect,\n password,\n passwordConfirmation,\n resolvedToken,\n t,\n ],\n );\n\n const handleMfaSuccess = useCallback(() => {\n setStatus('completed');\n onSuccessRedirect();\n }, [onSuccessRedirect]);\n\n const handleRetry = useCallback(() => {\n setStatus('idle');\n setErrorMessage(null);\n setIsSubmitting(false);\n }, []);\n\n if (status === 'mfa') {\n return (\n <AuthLayout\n title={t('auth.acceptInvitation.mfaTitle')}\n subtitle={t('auth.acceptInvitation.mfaSubtitle')}\n >\n <MfaChallengeForm\n auth={auth}\n onSuccess={handleMfaSuccess}\n onBack={() => {\n auth.reset();\n handleRetry();\n }}\n />\n </AuthLayout>\n );\n }\n\n let content: JSX.Element;\n if (status === 'completed') {\n content = (\n <div className={styles.stack}>\n <p className={styles.helper}>\n {t('auth.acceptInvitation.status.success')}\n </p>\n </div>\n );\n } else {\n content = (\n <AuthPanel description={t('auth.acceptInvitation.subtitle')}>\n <Form\n className={styles.formSurface}\n onSubmit={handleSubmit}\n noValidate\n initialFocus=\"first-form-control\"\n >\n {errorMessage != null ? <FormError>{errorMessage}</FormError> : null}\n <FormField\n label={t('auth.acceptInvitation.form.passwordLabel')}\n name=\"password\"\n type=\"password\"\n value={password}\n onChange={(event) => {\n setPassword(event.target.value);\n setErrorMessage(null);\n if (status === 'error') {\n setStatus('idle');\n }\n }}\n placeholder={t('auth.acceptInvitation.form.passwordPlaceholder')}\n autoComplete=\"new-password\"\n required\n />\n <FormField\n label={t('auth.acceptInvitation.form.confirmLabel')}\n name=\"password-confirmation\"\n type=\"password\"\n value={passwordConfirmation}\n onChange={(event) => {\n setPasswordConfirmation(event.target.value);\n setErrorMessage(null);\n if (status === 'error') {\n setStatus('idle');\n }\n }}\n placeholder={t('auth.acceptInvitation.form.confirmPlaceholder')}\n autoComplete=\"new-password\"\n required\n />\n <Button type=\"submit\" isLoading={isSubmitting}>\n {status === 'pending'\n ? t('auth.acceptInvitation.status.workingButton')\n : t('auth.acceptInvitation.form.submit')}\n </Button>\n <Button type=\"button\" variant=\"secondary\" onClick={onBackToLogin}>\n {t('auth.acceptInvitation.actions.backToLogin')}\n </Button>\n </Form>\n </AuthPanel>\n );\n }\n\n return (\n <AuthLayout title={t('auth.acceptInvitation.title')}>{content}</AuthLayout>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"AcceptInvitationScreen.js","names":[],"sources":["../../../../src/auth/pages/AcceptInvitationScreen.tsx"],"sourcesContent":["/* eslint-disable no-ternary */\nimport {\n useCallback,\n useMemo,\n useState,\n type FormEvent,\n type JSX,\n} from 'react';\nimport { useBackofficeReactTranslation } from '../../i18n/useBackofficeReactTranslation.js';\n\nimport { Button } from '@plumile/ui/atomic/atoms/button/Button.js';\nimport { FormError } from '@plumile/ui/atomic/molecules/form_error/FormError.js';\nimport { FormField } from '@plumile/ui/atomic/molecules/form_field/FormField.js';\nimport { Form } from '@plumile/ui/atomic/molecules/form/Form.js';\nimport { AuthLayout } from '@plumile/ui/atomic/templates/auth_layout/AuthLayout.js';\n\nimport type { UseAuthReturn } from '../../hooks/useAuth.js';\nimport { MfaChallengeForm } from '../login/MfaChallengeForm.js';\nimport AuthPanel from '../login/AuthPanel.js';\nimport * as styles from '../login/loginPage.css.js';\n\ntype AcceptanceState = 'pending' | 'mfa' | 'completed' | 'error' | 'idle';\n\ntype Props = {\n auth: UseAuthReturn;\n onSuccessRedirect: () => void;\n onBackToLogin: () => void;\n token?: string;\n};\n\nexport const AcceptInvitationScreen = ({\n auth,\n onSuccessRedirect,\n onBackToLogin,\n token,\n}: Props): JSX.Element => {\n const { t } = useBackofficeReactTranslation();\n const { acceptInvitation } = auth;\n const [status, setStatus] = useState<AcceptanceState>('idle');\n const [password, setPassword] = useState('');\n const [passwordConfirmation, setPasswordConfirmation] = useState('');\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n\n const resolvedToken = useMemo(() => {\n if (token != null) {\n return token;\n }\n if (typeof window === 'undefined') {\n return '';\n }\n const params = new URLSearchParams(window.location.search);\n return params.get('token') ?? '';\n }, [token]);\n\n const handleSubmit = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n if (resolvedToken === '') {\n setErrorMessage(t('auth.acceptInvitation.errors.missingToken'));\n setStatus('error');\n return;\n }\n\n if (password !== passwordConfirmation) {\n setErrorMessage(t('auth.acceptInvitation.errors.passwordMismatch'));\n setStatus('error');\n return;\n }\n\n setStatus('pending');\n setErrorMessage(null);\n setIsSubmitting(true);\n\n // eslint-disable-next-line no-void\n void acceptInvitation({\n token: resolvedToken,\n password,\n passwordConfirmation,\n })\n .then((result) => {\n if (result === 'mfa-required') {\n setStatus('mfa');\n return;\n }\n setStatus('completed');\n onSuccessRedirect();\n })\n .catch(() => {\n setStatus('error');\n setErrorMessage(t('auth.acceptInvitation.errors.default'));\n })\n .finally(() => {\n setIsSubmitting(false);\n });\n },\n [\n acceptInvitation,\n onSuccessRedirect,\n password,\n passwordConfirmation,\n resolvedToken,\n t,\n ],\n );\n\n const handleMfaSuccess = useCallback(() => {\n setStatus('completed');\n onSuccessRedirect();\n }, [onSuccessRedirect]);\n\n const handleRetry = useCallback(() => {\n setStatus('idle');\n setErrorMessage(null);\n setIsSubmitting(false);\n }, []);\n\n if (status === 'mfa') {\n return (\n <AuthLayout\n title={t('auth.acceptInvitation.mfaTitle')}\n subtitle={t('auth.acceptInvitation.mfaSubtitle')}\n >\n <MfaChallengeForm\n auth={auth}\n onSuccess={handleMfaSuccess}\n onBack={() => {\n auth.reset();\n handleRetry();\n }}\n />\n </AuthLayout>\n );\n }\n\n let content: JSX.Element;\n if (status === 'completed') {\n content = (\n <div className={styles.stack}>\n <p className={styles.helper}>\n {t('auth.acceptInvitation.status.success')}\n </p>\n </div>\n );\n } else {\n content = (\n <AuthPanel description={t('auth.acceptInvitation.subtitle')}>\n <Form\n className={styles.formSurface}\n onSubmit={handleSubmit}\n noValidate\n initialFocus=\"first-form-control\"\n >\n {errorMessage != null ? <FormError>{errorMessage}</FormError> : null}\n <FormField\n label={t('auth.acceptInvitation.form.passwordLabel')}\n name=\"password\"\n type=\"password\"\n value={password}\n onChange={(event) => {\n setPassword(event.target.value);\n setErrorMessage(null);\n if (status === 'error') {\n setStatus('idle');\n }\n }}\n placeholder={t('auth.acceptInvitation.form.passwordPlaceholder')}\n autoComplete=\"new-password\"\n required\n />\n <FormField\n label={t('auth.acceptInvitation.form.confirmLabel')}\n name=\"password-confirmation\"\n type=\"password\"\n value={passwordConfirmation}\n onChange={(event) => {\n setPasswordConfirmation(event.target.value);\n setErrorMessage(null);\n if (status === 'error') {\n setStatus('idle');\n }\n }}\n placeholder={t('auth.acceptInvitation.form.confirmPlaceholder')}\n autoComplete=\"new-password\"\n required\n />\n <Button type=\"submit\" isLoading={isSubmitting}>\n {status === 'pending'\n ? t('auth.acceptInvitation.status.workingButton')\n : t('auth.acceptInvitation.form.submit')}\n </Button>\n <Button type=\"button\" variant=\"secondary\" onClick={onBackToLogin}>\n {t('auth.acceptInvitation.actions.backToLogin')}\n </Button>\n </Form>\n </AuthPanel>\n );\n }\n\n return (\n <AuthLayout title={t('auth.acceptInvitation.title')}>{content}</AuthLayout>\n );\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"PasswordResetCompleteScreen.js","names":[],"sources":["../../../../src/auth/pages/PasswordResetCompleteScreen.tsx"],"sourcesContent":["/* eslint-disable no-ternary */\nimport {\n useCallback,\n useMemo,\n useState,\n type FormEvent,\n type JSX,\n} from 'react';\nimport { useBackofficeReactTranslation } from '../../i18n/useBackofficeReactTranslation.js';\n\nimport { Button } from '@plumile/ui/atomic/atoms/button/Button.js';\nimport { FormError } from '@plumile/ui/atomic/molecules/form_error/FormError.js';\nimport { FormField } from '@plumile/ui/atomic/molecules/form_field/FormField.js';\nimport { Form } from '@plumile/ui/atomic/molecules/form/Form.js';\nimport { AuthLayout } from '@plumile/ui/atomic/templates/auth_layout/AuthLayout.js';\n\nimport * as styles from '../login/loginPage.css.js';\nimport AuthPanel from '../login/AuthPanel.js';\n\nconst MIN_PASSWORD_LENGTH = 8;\n\ntype Props = {\n onBackToLogin: () => void;\n onCompletePasswordReset: (input: {\n newPassword: string;\n token: string;\n }) => Promise<boolean>;\n token?: string;\n};\n\nexport const PasswordResetCompleteScreen = ({\n onBackToLogin,\n onCompletePasswordReset,\n token,\n}: Props): JSX.Element => {\n const { t } = useBackofficeReactTranslation();\n const [password, setPassword] = useState('');\n const [passwordConfirm, setPasswordConfirm] = useState('');\n const [status, setStatus] = useState<'idle' | 'completed'>('idle');\n const [error, setError] = useState<string | null>(null);\n const [isSubmitting, setIsSubmitting] = useState(false);\n\n const resolvedToken = useMemo(() => {\n if (token != null) {\n return token;\n }\n if (typeof window === 'undefined') {\n return '';\n }\n const params = new URLSearchParams(window.location.search);\n return params.get('token') ?? '';\n }, [token]);\n\n const handleSubmit = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setError(null);\n\n if (resolvedToken === '') {\n setError(t('auth.passwordResetComplete.errors.missingToken'));\n return;\n }\n\n if (password.length < MIN_PASSWORD_LENGTH) {\n setError(\n t('auth.passwordResetComplete.errors.minLength', {\n minLength: MIN_PASSWORD_LENGTH,\n }),\n );\n return;\n }\n\n if (password !== passwordConfirm) {\n setError(t('auth.passwordResetComplete.errors.mismatch'));\n return;\n }\n\n setIsSubmitting(true);\n\n const completePasswordReset = async (): Promise<void> => {\n try {\n const success = await onCompletePasswordReset({\n newPassword: password,\n token: resolvedToken,\n });\n if (success) {\n setStatus('completed');\n } else {\n setError(t('auth.passwordResetComplete.errors.invalid'));\n }\n } catch (mutationError) {\n setError(\n mutationError instanceof Error\n ? mutationError.message\n : t('auth.passwordResetComplete.errors.invalid'),\n );\n } finally {\n setIsSubmitting(false);\n }\n };\n\n completePasswordReset().catch(() => {\n return undefined;\n });\n },\n [onCompletePasswordReset, password, passwordConfirm, resolvedToken, t],\n );\n\n let content: JSX.Element;\n if (status === 'completed') {\n content = (\n <AuthPanel\n title={t('auth.passwordResetComplete.success.title')}\n description={t('auth.passwordResetComplete.success.description')}\n footer={\n <Button\n type=\"button\"\n variant=\"secondary\"\n className={styles.brandGhostButton}\n onClick={onBackToLogin}\n >\n {t('auth.passwordResetComplete.success.action')}\n </Button>\n }\n >\n <p className={styles.helper}>\n {t('auth.passwordResetComplete.success.helper')}\n </p>\n </AuthPanel>\n );\n } else {\n content = (\n <AuthPanel\n title={t('auth.passwordResetComplete.form.title')}\n description={t('auth.passwordResetComplete.form.description')}\n >\n <Form\n className={styles.formSurface}\n onSubmit={handleSubmit}\n noValidate\n initialFocus=\"first-form-control\"\n >\n {error != null ? <FormError>{error}</FormError> : null}\n <FormField\n label={t('auth.passwordResetComplete.form.passwordLabel')}\n name=\"new-password\"\n type=\"password\"\n value={password}\n onChange={(event) => {\n setPassword(event.target.value);\n setError(null);\n }}\n placeholder={t(\n 'auth.passwordResetComplete.form.passwordPlaceholder',\n )}\n autoComplete=\"new-password\"\n required\n />\n <FormField\n label={t('auth.passwordResetComplete.form.confirmLabel')}\n name=\"confirm-password\"\n type=\"password\"\n value={passwordConfirm}\n onChange={(event) => {\n setPasswordConfirm(event.target.value);\n setError(null);\n }}\n placeholder={t(\n 'auth.passwordResetComplete.form.confirmPlaceholder',\n )}\n autoComplete=\"new-password\"\n required\n />\n <Button type=\"submit\" isLoading={isSubmitting}>\n {t('auth.passwordResetComplete.form.submit')}\n </Button>\n </Form>\n </AuthPanel>\n );\n }\n\n return (\n <AuthLayout\n title={t('auth.passwordResetComplete.title')}\n subtitle={t('auth.passwordResetComplete.subtitle')}\n >\n {content}\n </AuthLayout>\n );\n};\n"],"mappings":";;;;;;;;;;;AAmBA,IAAM,IAAsB,GAWf,KAA+B,EAC1C,kBACA,4BACA,eACwB;CACxB,IAAM,EAAE,SAAM,EAA8B,GACtC,CAAC,GAAU,KAAe,EAAS,EAAE,GACrC,CAAC,GAAiB,KAAsB,EAAS,EAAE,GACnD,CAAC,GAAQ,KAAa,EAA+B,MAAM,GAC3D,CAAC,GAAO,KAAY,EAAwB,IAAI,GAChD,CAAC,GAAc,KAAmB,EAAS,EAAK,GAEhD,IAAgB,QAChB,MAGA,OAAO,SAAW,MACb,KAGF,IADY,gBAAgB,OAAO,SAAS,MAC5C,EAAO,IAAI,OAAO,KAAK,KAC7B,CAAC,CAAK,CAAC,GAEJ,IAAe,GAClB,MAAsC;EAIrC,IAHA,EAAM,eAAe,GACrB,EAAS,IAAI,GAET,MAAkB,IAAI;GACxB,EAAS,EAAE,gDAAgD,CAAC;GAC5D;EACF;EAEA,IAAI,EAAS,SAAS,GAAqB;GACzC,EACE,EAAE,+CAA+C,EAC/C,WAAW,EACb,CAAC,CACH;GACA;EACF;EAEA,IAAI,MAAa,GAAiB;GAChC,EAAS,EAAE,4CAA4C,CAAC;GACxD;EACF;EA0BA,AAxBA,EAAgB,EAAI,IAwBpB,YAtByD;GACvD,IAAI;IAKF,AAAI,MAJkB,EAAwB;KAC5C,aAAa;KACb,OAAO;IACT,CAAC,IAEC,EAAU,WAAW,IAErB,EAAS,EAAE,2CAA2C,CAAC;GAE3D,SAAS,GAAe;IACtB,EACE,aAAyB,QACrB,EAAc,UACd,EAAE,2CAA2C,CACnD;GACF,UAAU;IACR,EAAgB,EAAK;GACvB;EACF,GAEsB,EAAE,YAAY,CAEpC,CAAC;CACH,GACA;EAAC;EAAyB;EAAU;EAAiB;EAAe;CAAC,CACvE,GAEI;CAyEJ,OAxEA,AAsBE,IAtBE,MAAW,cAEX,kBAAC,GAAD;EACE,OAAO,EAAE,0CAA0C;EACnD,aAAa,EAAE,gDAAgD;EAC/D,QACE,kBAAC,GAAD;GACE,MAAK;GACL,SAAQ;GACR,WAAW;GACX,SAAS;aAER,EAAE,2CAA2C;EACxC,CAAA;YAGV,kBAAC,KAAD;GAAG,WAAW;aACX,EAAE,2CAA2C;EAC7C,CAAA;CACM,CAAA,IAIX,kBAAC,GAAD;EACE,OAAO,EAAE,uCAAuC;EAChD,aAAa,EAAE,6CAA6C;YAE5D,kBAAC,GAAD;GACE,WAAW;GACX,UAAU;GACV,YAAA;GACA,cAAa;aAJf;IAMG,KAAS,OAAwC,OAAjC,kBAAC,GAAD,EAAA,UAAY,EAAiB,CAAA;IAC9C,kBAAC,GAAD;KACE,OAAO,EAAE,+CAA+C;KACxD,MAAK;KACL,MAAK;KACL,OAAO;KACP,WAAW,MAAU;MAEnB,AADA,EAAY,EAAM,OAAO,KAAK,GAC9B,EAAS,IAAI;KACf;KACA,aAAa,EACX,qDACF;KACA,cAAa;KACb,UAAA;IACD,CAAA;IACD,kBAAC,GAAD;KACE,OAAO,EAAE,8CAA8C;KACvD,MAAK;KACL,MAAK;KACL,OAAO;KACP,WAAW,MAAU;MAEnB,AADA,EAAmB,EAAM,OAAO,KAAK,GACrC,EAAS,IAAI;KACf;KACA,aAAa,EACX,oDACF;KACA,cAAa;KACb,UAAA;IACD,CAAA;IACD,kBAAC,GAAD;KAAQ,MAAK;KAAS,WAAW;eAC9B,EAAE,wCAAwC;IACrC,CAAA;GACJ;;CACG,CAAA,GAKb,kBAAC,GAAD;EACE,OAAO,EAAE,kCAAkC;EAC3C,UAAU,EAAE,qCAAqC;YAEhD;CACS,CAAA;AAEhB"}
1
+ {"version":3,"file":"PasswordResetCompleteScreen.js","names":[],"sources":["../../../../src/auth/pages/PasswordResetCompleteScreen.tsx"],"sourcesContent":["/* eslint-disable no-ternary */\nimport {\n useCallback,\n useMemo,\n useState,\n type FormEvent,\n type JSX,\n} from 'react';\nimport { useBackofficeReactTranslation } from '../../i18n/useBackofficeReactTranslation.js';\n\nimport { Button } from '@plumile/ui/atomic/atoms/button/Button.js';\nimport { FormError } from '@plumile/ui/atomic/molecules/form_error/FormError.js';\nimport { FormField } from '@plumile/ui/atomic/molecules/form_field/FormField.js';\nimport { Form } from '@plumile/ui/atomic/molecules/form/Form.js';\nimport { AuthLayout } from '@plumile/ui/atomic/templates/auth_layout/AuthLayout.js';\n\nimport * as styles from '../login/loginPage.css.js';\nimport AuthPanel from '../login/AuthPanel.js';\n\nconst MIN_PASSWORD_LENGTH = 8;\n\ntype Props = {\n onBackToLogin: () => void;\n onCompletePasswordReset: (input: {\n newPassword: string;\n token: string;\n }) => Promise<boolean>;\n token?: string;\n};\n\nexport const PasswordResetCompleteScreen = ({\n onBackToLogin,\n onCompletePasswordReset,\n token,\n}: Props): JSX.Element => {\n const { t } = useBackofficeReactTranslation();\n const [password, setPassword] = useState('');\n const [passwordConfirm, setPasswordConfirm] = useState('');\n const [status, setStatus] = useState<'idle' | 'completed'>('idle');\n const [error, setError] = useState<string | null>(null);\n const [isSubmitting, setIsSubmitting] = useState(false);\n\n const resolvedToken = useMemo(() => {\n if (token != null) {\n return token;\n }\n if (typeof window === 'undefined') {\n return '';\n }\n const params = new URLSearchParams(window.location.search);\n return params.get('token') ?? '';\n }, [token]);\n\n const handleSubmit = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setError(null);\n\n if (resolvedToken === '') {\n setError(t('auth.passwordResetComplete.errors.missingToken'));\n return;\n }\n\n if (password.length < MIN_PASSWORD_LENGTH) {\n setError(\n t('auth.passwordResetComplete.errors.minLength', {\n minLength: MIN_PASSWORD_LENGTH,\n }),\n );\n return;\n }\n\n if (password !== passwordConfirm) {\n setError(t('auth.passwordResetComplete.errors.mismatch'));\n return;\n }\n\n setIsSubmitting(true);\n\n const completePasswordReset = async (): Promise<void> => {\n try {\n const success = await onCompletePasswordReset({\n newPassword: password,\n token: resolvedToken,\n });\n if (success) {\n setStatus('completed');\n } else {\n setError(t('auth.passwordResetComplete.errors.invalid'));\n }\n } catch (mutationError) {\n setError(\n mutationError instanceof Error\n ? mutationError.message\n : t('auth.passwordResetComplete.errors.invalid'),\n );\n } finally {\n setIsSubmitting(false);\n }\n };\n\n completePasswordReset().catch(() => {\n return undefined;\n });\n },\n [onCompletePasswordReset, password, passwordConfirm, resolvedToken, t],\n );\n\n let content: JSX.Element;\n if (status === 'completed') {\n content = (\n <AuthPanel\n title={t('auth.passwordResetComplete.success.title')}\n description={t('auth.passwordResetComplete.success.description')}\n footer={\n <Button\n type=\"button\"\n variant=\"secondary\"\n className={styles.brandGhostButton}\n onClick={onBackToLogin}\n >\n {t('auth.passwordResetComplete.success.action')}\n </Button>\n }\n >\n <p className={styles.helper}>\n {t('auth.passwordResetComplete.success.helper')}\n </p>\n </AuthPanel>\n );\n } else {\n content = (\n <AuthPanel\n title={t('auth.passwordResetComplete.form.title')}\n description={t('auth.passwordResetComplete.form.description')}\n >\n <Form\n className={styles.formSurface}\n onSubmit={handleSubmit}\n noValidate\n initialFocus=\"first-form-control\"\n >\n {error != null ? <FormError>{error}</FormError> : null}\n <FormField\n label={t('auth.passwordResetComplete.form.passwordLabel')}\n name=\"new-password\"\n type=\"password\"\n value={password}\n onChange={(event) => {\n setPassword(event.target.value);\n setError(null);\n }}\n placeholder={t(\n 'auth.passwordResetComplete.form.passwordPlaceholder',\n )}\n autoComplete=\"new-password\"\n required\n />\n <FormField\n label={t('auth.passwordResetComplete.form.confirmLabel')}\n name=\"confirm-password\"\n type=\"password\"\n value={passwordConfirm}\n onChange={(event) => {\n setPasswordConfirm(event.target.value);\n setError(null);\n }}\n placeholder={t(\n 'auth.passwordResetComplete.form.confirmPlaceholder',\n )}\n autoComplete=\"new-password\"\n required\n />\n <Button type=\"submit\" isLoading={isSubmitting}>\n {t('auth.passwordResetComplete.form.submit')}\n </Button>\n </Form>\n </AuthPanel>\n );\n }\n\n return (\n <AuthLayout\n title={t('auth.passwordResetComplete.title')}\n subtitle={t('auth.passwordResetComplete.subtitle')}\n >\n {content}\n </AuthLayout>\n );\n};\n"],"mappings":";;;;;;;;;;;AAmBA,IAAM,IAAsB,GAWf,KAA+B,EAC1C,kBACA,4BACA,eACwB;CACxB,IAAM,EAAE,SAAM,EAA8B,GACtC,CAAC,GAAU,KAAe,EAAS,EAAE,GACrC,CAAC,GAAiB,KAAsB,EAAS,EAAE,GACnD,CAAC,GAAQ,KAAa,EAA+B,MAAM,GAC3D,CAAC,GAAO,KAAY,EAAwB,IAAI,GAChD,CAAC,GAAc,KAAmB,EAAS,EAAK,GAEhD,IAAgB,QAChB,MAGA,OAAO,SAAW,MACb,KAGF,IADY,gBAAgB,OAAO,SAAS,MAC5C,CAAA,CAAO,IAAI,OAAO,KAAK,KAC7B,CAAC,CAAK,CAAC,GAEJ,IAAe,GAClB,MAAsC;EAIrC,IAHA,EAAM,eAAe,GACrB,EAAS,IAAI,GAET,MAAkB,IAAI;GACxB,EAAS,EAAE,gDAAgD,CAAC;GAC5D;EACF;EAEA,IAAI,EAAS,SAAS,GAAqB;GACzC,EACE,EAAE,+CAA+C,EAC/C,WAAW,EACb,CAAC,CACH;GACA;EACF;EAEA,IAAI,MAAa,GAAiB;GAChC,EAAS,EAAE,4CAA4C,CAAC;GACxD;EACF;EA0BA,AAxBA,EAAgB,EAAI,IAwBpB,YAtByD;GACvD,IAAI;IAKF,AAAI,MAJkB,EAAwB;KAC5C,aAAa;KACb,OAAO;IACT,CAAC,IAEC,EAAU,WAAW,IAErB,EAAS,EAAE,2CAA2C,CAAC;GAE3D,SAAS,GAAe;IACtB,EACE,aAAyB,QACrB,EAAc,UACd,EAAE,2CAA2C,CACnD;GACF,UAAU;IACR,EAAgB,EAAK;GACvB;EACF,EAEA,CAAsB,CAAC,CAAC,YAAY,CAEpC,CAAC;CACH,GACA;EAAC;EAAyB;EAAU;EAAiB;EAAe;CAAC,CACvE,GAEI;CAyEJ,OAxEA,AAsBE,IAtBE,MAAW,cAEX,kBAAC,GAAD;EACE,OAAO,EAAE,0CAA0C;EACnD,aAAa,EAAE,gDAAgD;EAC/D,QACE,kBAAC,GAAD;GACE,MAAK;GACL,SAAQ;GACR,WAAW;GACX,SAAS;aAER,EAAE,2CAA2C;EACxC,CAAA;YAGV,kBAAC,KAAD;GAAG,WAAW;aACX,EAAE,2CAA2C;EAC7C,CAAA;CACM,CAAA,IAIX,kBAAC,GAAD;EACE,OAAO,EAAE,uCAAuC;EAChD,aAAa,EAAE,6CAA6C;YAE5D,kBAAC,GAAD;GACE,WAAW;GACX,UAAU;GACV,YAAA;GACA,cAAa;aAJf;IAMG,KAAS,OAAwC,OAAjC,kBAAC,GAAD,EAAA,UAAY,EAAiB,CAAA;IAC9C,kBAAC,GAAD;KACE,OAAO,EAAE,+CAA+C;KACxD,MAAK;KACL,MAAK;KACL,OAAO;KACP,WAAW,MAAU;MAEnB,AADA,EAAY,EAAM,OAAO,KAAK,GAC9B,EAAS,IAAI;KACf;KACA,aAAa,EACX,qDACF;KACA,cAAa;KACb,UAAA;IACD,CAAA;IACD,kBAAC,GAAD;KACE,OAAO,EAAE,8CAA8C;KACvD,MAAK;KACL,MAAK;KACL,OAAO;KACP,WAAW,MAAU;MAEnB,AADA,EAAmB,EAAM,OAAO,KAAK,GACrC,EAAS,IAAI;KACf;KACA,aAAa,EACX,oDACF;KACA,cAAa;KACb,UAAA;IACD,CAAA;IACD,kBAAC,GAAD;KAAQ,MAAK;KAAS,WAAW;eAC9B,EAAE,wCAAwC;IACrC,CAAA;GACJ;;CACG,CAAA,GAKb,kBAAC,GAAD;EACE,OAAO,EAAE,kCAAkC;EAC3C,UAAU,EAAE,qCAAqC;YAEhD;CACS,CAAA;AAEhB"}
@@ -1 +1 @@
1
- {"version":3,"file":"PasswordResetRequestScreen.js","names":[],"sources":["../../../../src/auth/pages/PasswordResetRequestScreen.tsx"],"sourcesContent":["/* eslint-disable no-ternary */\nimport {\n useCallback,\n useMemo,\n useState,\n type FormEvent,\n type JSX,\n} from 'react';\nimport { useBackofficeReactTranslation } from '../../i18n/useBackofficeReactTranslation.js';\n\nimport { Button } from '@plumile/ui/atomic/atoms/button/Button.js';\nimport { FormError } from '@plumile/ui/atomic/molecules/form_error/FormError.js';\nimport { FormField } from '@plumile/ui/atomic/molecules/form_field/FormField.js';\nimport { Form } from '@plumile/ui/atomic/molecules/form/Form.js';\nimport { AuthLayout } from '@plumile/ui/atomic/templates/auth_layout/AuthLayout.js';\n\nimport * as styles from '../login/loginPage.css.js';\nimport AuthPanel from '../login/AuthPanel.js';\n\ntype Props = {\n onStartPasswordReset: (input: {\n email: string;\n locale?: string;\n }) => Promise<boolean>;\n};\n\nexport const PasswordResetRequestScreen = ({\n onStartPasswordReset,\n}: Props): JSX.Element => {\n const { i18n, t } = useBackofficeReactTranslation();\n const [email, setEmail] = useState('');\n const [status, setStatus] = useState<'idle' | 'sent'>('idle');\n const [error, setError] = useState<string | null>(null);\n const [isSubmitting, setIsSubmitting] = useState(false);\n\n const locale = useMemo(() => {\n const language = i18n.language.trim();\n if (language !== '') {\n return language;\n }\n if (typeof navigator === 'undefined') {\n return undefined;\n }\n return navigator.language;\n }, [i18n.language]);\n\n const handleSubmit = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setError(null);\n\n if (email.trim() === '') {\n setError(t('auth.passwordResetRequest.errors.emailRequired'));\n return;\n }\n\n setIsSubmitting(true);\n onStartPasswordReset({\n email: email.trim(),\n locale,\n })\n .then((success) => {\n if (success) {\n setStatus('sent');\n } else {\n setError(t('auth.passwordResetRequest.errors.startFailed'));\n }\n })\n .catch((mutationError) => {\n setError(\n mutationError instanceof Error\n ? mutationError.message\n : t('auth.passwordResetRequest.errors.startFailed'),\n );\n })\n .finally(() => {\n setIsSubmitting(false);\n });\n },\n [email, locale, onStartPasswordReset, t],\n );\n\n let content: JSX.Element;\n if (status === 'sent') {\n content = (\n <AuthPanel\n title={t('auth.passwordResetRequest.sent.title')}\n description={t('auth.passwordResetRequest.sent.description', { email })}\n footer={\n <Button\n type=\"button\"\n variant=\"secondary\"\n onClick={() => {\n setStatus('idle');\n }}\n >\n {t('auth.passwordResetRequest.sent.action')}\n </Button>\n }\n >\n <p className={styles.helper}>\n {t('auth.passwordResetRequest.sent.helper')}\n </p>\n </AuthPanel>\n );\n } else {\n content = (\n <AuthPanel description={t('auth.passwordResetRequest.form.description')}>\n <Form\n className={styles.formSurface}\n onSubmit={handleSubmit}\n noValidate\n initialFocus=\"first-form-control\"\n >\n {error != null ? <FormError>{error}</FormError> : null}\n <FormField\n label={t('auth.passwordResetRequest.form.emailLabel')}\n name=\"email\"\n type=\"email\"\n value={email}\n onChange={(event) => {\n setEmail(event.target.value);\n setError(null);\n }}\n placeholder={t('auth.passwordResetRequest.form.emailPlaceholder')}\n autoComplete=\"email\"\n required\n />\n <Button type=\"submit\" isLoading={isSubmitting}>\n {t('auth.passwordResetRequest.form.submit')}\n </Button>\n </Form>\n </AuthPanel>\n );\n }\n\n return (\n <AuthLayout title={t('auth.passwordResetRequest.title')}>\n {content}\n </AuthLayout>\n );\n};\n"],"mappings":";;;;;;;;;;;AA0BA,IAAa,KAA8B,EACzC,8BACwB;CACxB,IAAM,EAAE,SAAM,SAAM,EAA8B,GAC5C,CAAC,GAAO,KAAY,EAAS,EAAE,GAC/B,CAAC,GAAQ,KAAa,EAA0B,MAAM,GACtD,CAAC,GAAO,KAAY,EAAwB,IAAI,GAChD,CAAC,GAAc,KAAmB,EAAS,EAAK,GAEhD,IAAS,QAAc;EAC3B,IAAM,IAAW,EAAK,SAAS,KAAK;EACpC,IAAI,MAAa,IACf,OAAO;EAEL,aAAO,YAAc,MAGzB,OAAO,UAAU;CACnB,GAAG,CAAC,EAAK,QAAQ,CAAC,GAEZ,IAAe,GAClB,MAAsC;EAIrC,IAHA,EAAM,eAAe,GACrB,EAAS,IAAI,GAET,EAAM,KAAK,MAAM,IAAI;GACvB,EAAS,EAAE,gDAAgD,CAAC;GAC5D;EACF;EAGA,AADA,EAAgB,EAAI,GACpB,EAAqB;GACnB,OAAO,EAAM,KAAK;GAClB;EACF,CAAC,EACE,MAAM,MAAY;GACjB,AAAI,IACF,EAAU,MAAM,IAEhB,EAAS,EAAE,8CAA8C,CAAC;EAE9D,CAAC,EACA,OAAO,MAAkB;GACxB,EACE,aAAyB,QACrB,EAAc,UACd,EAAE,8CAA8C,CACtD;EACF,CAAC,EACA,cAAc;GACb,EAAgB,EAAK;EACvB,CAAC;CACL,GACA;EAAC;EAAO;EAAQ;EAAsB;CAAC,CACzC,GAEI;CAsDJ,OArDA,AAuBE,IAvBE,MAAW,SAEX,kBAAC,GAAD;EACE,OAAO,EAAE,sCAAsC;EAC/C,aAAa,EAAE,8CAA8C,EAAE,SAAM,CAAC;EACtE,QACE,kBAAC,GAAD;GACE,MAAK;GACL,SAAQ;GACR,eAAe;IACb,EAAU,MAAM;GAClB;aAEC,EAAE,uCAAuC;EACpC,CAAA;YAGV,kBAAC,KAAD;GAAG,WAAW;aACX,EAAE,uCAAuC;EACzC,CAAA;CACM,CAAA,IAIX,kBAAC,GAAD;EAAW,aAAa,EAAE,4CAA4C;YACpE,kBAAC,GAAD;GACE,WAAW;GACX,UAAU;GACV,YAAA;GACA,cAAa;aAJf;IAMG,KAAS,OAAwC,OAAjC,kBAAC,GAAD,EAAA,UAAY,EAAiB,CAAA;IAC9C,kBAAC,GAAD;KACE,OAAO,EAAE,2CAA2C;KACpD,MAAK;KACL,MAAK;KACL,OAAO;KACP,WAAW,MAAU;MAEnB,AADA,EAAS,EAAM,OAAO,KAAK,GAC3B,EAAS,IAAI;KACf;KACA,aAAa,EAAE,iDAAiD;KAChE,cAAa;KACb,UAAA;IACD,CAAA;IACD,kBAAC,GAAD;KAAQ,MAAK;KAAS,WAAW;eAC9B,EAAE,uCAAuC;IACpC,CAAA;GACJ;;CACG,CAAA,GAKb,kBAAC,GAAD;EAAY,OAAO,EAAE,iCAAiC;YACnD;CACS,CAAA;AAEhB"}
1
+ {"version":3,"file":"PasswordResetRequestScreen.js","names":[],"sources":["../../../../src/auth/pages/PasswordResetRequestScreen.tsx"],"sourcesContent":["/* eslint-disable no-ternary */\nimport {\n useCallback,\n useMemo,\n useState,\n type FormEvent,\n type JSX,\n} from 'react';\nimport { useBackofficeReactTranslation } from '../../i18n/useBackofficeReactTranslation.js';\n\nimport { Button } from '@plumile/ui/atomic/atoms/button/Button.js';\nimport { FormError } from '@plumile/ui/atomic/molecules/form_error/FormError.js';\nimport { FormField } from '@plumile/ui/atomic/molecules/form_field/FormField.js';\nimport { Form } from '@plumile/ui/atomic/molecules/form/Form.js';\nimport { AuthLayout } from '@plumile/ui/atomic/templates/auth_layout/AuthLayout.js';\n\nimport * as styles from '../login/loginPage.css.js';\nimport AuthPanel from '../login/AuthPanel.js';\n\ntype Props = {\n onStartPasswordReset: (input: {\n email: string;\n locale?: string;\n }) => Promise<boolean>;\n};\n\nexport const PasswordResetRequestScreen = ({\n onStartPasswordReset,\n}: Props): JSX.Element => {\n const { i18n, t } = useBackofficeReactTranslation();\n const [email, setEmail] = useState('');\n const [status, setStatus] = useState<'idle' | 'sent'>('idle');\n const [error, setError] = useState<string | null>(null);\n const [isSubmitting, setIsSubmitting] = useState(false);\n\n const locale = useMemo(() => {\n const language = i18n.language.trim();\n if (language !== '') {\n return language;\n }\n if (typeof navigator === 'undefined') {\n return undefined;\n }\n return navigator.language;\n }, [i18n.language]);\n\n const handleSubmit = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setError(null);\n\n if (email.trim() === '') {\n setError(t('auth.passwordResetRequest.errors.emailRequired'));\n return;\n }\n\n setIsSubmitting(true);\n onStartPasswordReset({\n email: email.trim(),\n locale,\n })\n .then((success) => {\n if (success) {\n setStatus('sent');\n } else {\n setError(t('auth.passwordResetRequest.errors.startFailed'));\n }\n })\n .catch((mutationError) => {\n setError(\n mutationError instanceof Error\n ? mutationError.message\n : t('auth.passwordResetRequest.errors.startFailed'),\n );\n })\n .finally(() => {\n setIsSubmitting(false);\n });\n },\n [email, locale, onStartPasswordReset, t],\n );\n\n let content: JSX.Element;\n if (status === 'sent') {\n content = (\n <AuthPanel\n title={t('auth.passwordResetRequest.sent.title')}\n description={t('auth.passwordResetRequest.sent.description', { email })}\n footer={\n <Button\n type=\"button\"\n variant=\"secondary\"\n onClick={() => {\n setStatus('idle');\n }}\n >\n {t('auth.passwordResetRequest.sent.action')}\n </Button>\n }\n >\n <p className={styles.helper}>\n {t('auth.passwordResetRequest.sent.helper')}\n </p>\n </AuthPanel>\n );\n } else {\n content = (\n <AuthPanel description={t('auth.passwordResetRequest.form.description')}>\n <Form\n className={styles.formSurface}\n onSubmit={handleSubmit}\n noValidate\n initialFocus=\"first-form-control\"\n >\n {error != null ? <FormError>{error}</FormError> : null}\n <FormField\n label={t('auth.passwordResetRequest.form.emailLabel')}\n name=\"email\"\n type=\"email\"\n value={email}\n onChange={(event) => {\n setEmail(event.target.value);\n setError(null);\n }}\n placeholder={t('auth.passwordResetRequest.form.emailPlaceholder')}\n autoComplete=\"email\"\n required\n />\n <Button type=\"submit\" isLoading={isSubmitting}>\n {t('auth.passwordResetRequest.form.submit')}\n </Button>\n </Form>\n </AuthPanel>\n );\n }\n\n return (\n <AuthLayout title={t('auth.passwordResetRequest.title')}>\n {content}\n </AuthLayout>\n );\n};\n"],"mappings":";;;;;;;;;;;AA0BA,IAAa,KAA8B,EACzC,8BACwB;CACxB,IAAM,EAAE,SAAM,SAAM,EAA8B,GAC5C,CAAC,GAAO,KAAY,EAAS,EAAE,GAC/B,CAAC,GAAQ,KAAa,EAA0B,MAAM,GACtD,CAAC,GAAO,KAAY,EAAwB,IAAI,GAChD,CAAC,GAAc,KAAmB,EAAS,EAAK,GAEhD,IAAS,QAAc;EAC3B,IAAM,IAAW,EAAK,SAAS,KAAK;EACpC,IAAI,MAAa,IACf,OAAO;EAEL,aAAO,YAAc,MAGzB,OAAO,UAAU;CACnB,GAAG,CAAC,EAAK,QAAQ,CAAC,GAEZ,IAAe,GAClB,MAAsC;EAIrC,IAHA,EAAM,eAAe,GACrB,EAAS,IAAI,GAET,EAAM,KAAK,MAAM,IAAI;GACvB,EAAS,EAAE,gDAAgD,CAAC;GAC5D;EACF;EAGA,AADA,EAAgB,EAAI,GACpB,EAAqB;GACnB,OAAO,EAAM,KAAK;GAClB;EACF,CAAC,CAAC,CACC,MAAM,MAAY;GACjB,AAAI,IACF,EAAU,MAAM,IAEhB,EAAS,EAAE,8CAA8C,CAAC;EAE9D,CAAC,CAAC,CACD,OAAO,MAAkB;GACxB,EACE,aAAyB,QACrB,EAAc,UACd,EAAE,8CAA8C,CACtD;EACF,CAAC,CAAC,CACD,cAAc;GACb,EAAgB,EAAK;EACvB,CAAC;CACL,GACA;EAAC;EAAO;EAAQ;EAAsB;CAAC,CACzC,GAEI;CAsDJ,OArDA,AAuBE,IAvBE,MAAW,SAEX,kBAAC,GAAD;EACE,OAAO,EAAE,sCAAsC;EAC/C,aAAa,EAAE,8CAA8C,EAAE,SAAM,CAAC;EACtE,QACE,kBAAC,GAAD;GACE,MAAK;GACL,SAAQ;GACR,eAAe;IACb,EAAU,MAAM;GAClB;aAEC,EAAE,uCAAuC;EACpC,CAAA;YAGV,kBAAC,KAAD;GAAG,WAAW;aACX,EAAE,uCAAuC;EACzC,CAAA;CACM,CAAA,IAIX,kBAAC,GAAD;EAAW,aAAa,EAAE,4CAA4C;YACpE,kBAAC,GAAD;GACE,WAAW;GACX,UAAU;GACV,YAAA;GACA,cAAa;aAJf;IAMG,KAAS,OAAwC,OAAjC,kBAAC,GAAD,EAAA,UAAY,EAAiB,CAAA;IAC9C,kBAAC,GAAD;KACE,OAAO,EAAE,2CAA2C;KACpD,MAAK;KACL,MAAK;KACL,OAAO;KACP,WAAW,MAAU;MAEnB,AADA,EAAS,EAAM,OAAO,KAAK,GAC3B,EAAS,IAAI;KACf;KACA,aAAa,EAAE,iDAAiD;KAChE,cAAa;KACb,UAAA;IACD,CAAA;IACD,kBAAC,GAAD;KAAQ,MAAK;KAAS,WAAW;eAC9B,EAAE,uCAAuC;IACpC,CAAA;GACJ;;CACG,CAAA,GAKb,kBAAC,GAAD;EAAY,OAAO,EAAE,iCAAiC;YACnD;CACS,CAAA;AAEhB"}
@@ -1 +1 @@
1
- {"version":3,"file":"VerifyEmailScreen.js","names":[],"sources":["../../../../src/auth/pages/VerifyEmailScreen.tsx"],"sourcesContent":["/* eslint-disable no-ternary */\nimport { useEffect, useMemo, useState, type JSX } from 'react';\nimport { useBackofficeReactTranslation } from '../../i18n/useBackofficeReactTranslation.js';\n\nimport { Button } from '@plumile/ui/atomic/atoms/button/Button.js';\nimport { FormError } from '@plumile/ui/atomic/molecules/form_error/FormError.js';\nimport { AuthLayout } from '@plumile/ui/atomic/templates/auth_layout/AuthLayout.js';\n\nimport * as styles from '../login/loginPage.css.js';\n\ntype VerificationState = 'pending' | 'success' | 'error';\n\ntype Props = {\n onBackToLogin: () => void;\n onVerifyEmail: (input: { token: string }) => Promise<boolean>;\n token?: string;\n};\n\nexport const VerifyEmailScreen = ({\n onBackToLogin,\n onVerifyEmail,\n token,\n}: Props): JSX.Element => {\n const { t } = useBackofficeReactTranslation();\n const [state, setState] = useState<VerificationState>('pending');\n const [error, setError] = useState<string | null>(null);\n\n const resolvedToken = useMemo(() => {\n if (token != null) {\n return token;\n }\n if (typeof window === 'undefined') {\n return '';\n }\n const params = new URLSearchParams(window.location.search);\n return params.get('token') ?? '';\n }, [token]);\n\n useEffect(() => {\n if (resolvedToken === '') {\n setState('error');\n setError(t('auth.verifyEmail.errors.missingToken'));\n return;\n }\n\n const verifyEmail = async (): Promise<void> => {\n try {\n const success = await onVerifyEmail({ token: resolvedToken });\n if (success) {\n setState('success');\n } else {\n setState('error');\n setError(t('auth.verifyEmail.errors.invalid'));\n }\n } catch (mutationError) {\n setState('error');\n setError(\n mutationError instanceof Error\n ? mutationError.message\n : t('auth.verifyEmail.errors.invalid'),\n );\n }\n };\n\n verifyEmail().catch(() => {\n return undefined;\n });\n }, [onVerifyEmail, resolvedToken, t]);\n\n let content: JSX.Element;\n if (state === 'success') {\n content = (\n <div className={styles.stack}>\n <p className={styles.helper}>{t('auth.verifyEmail.status.success')}</p>\n <Button type=\"button\" variant=\"secondary\" onClick={onBackToLogin}>\n {t('auth.verifyEmail.actions.continue')}\n </Button>\n </div>\n );\n } else if (state === 'error') {\n content = (\n <div className={styles.stack}>\n {error != null ? <FormError>{error}</FormError> : null}\n <Button type=\"button\" variant=\"secondary\" onClick={onBackToLogin}>\n {t('auth.verifyEmail.actions.return')}\n </Button>\n </div>\n );\n } else {\n content = (\n <div className={styles.stack}>\n <p className={styles.helper}>\n {t('auth.verifyEmail.status.verifying')}\n </p>\n <Button type=\"button\" isLoading>\n {t('auth.verifyEmail.status.verifyingButton')}\n </Button>\n </div>\n );\n }\n\n return (\n <AuthLayout\n title={t('auth.verifyEmail.title')}\n subtitle={t('auth.verifyEmail.subtitle')}\n >\n {content}\n </AuthLayout>\n );\n};\n"],"mappings":";;;;;;;;AAkBA,IAAa,KAAqB,EAChC,kBACA,kBACA,eACwB;CACxB,IAAM,EAAE,SAAM,EAA8B,GACtC,CAAC,GAAO,KAAY,EAA4B,SAAS,GACzD,CAAC,GAAO,KAAY,EAAwB,IAAI,GAEhD,IAAgB,QAChB,MAGA,OAAO,SAAW,MACb,KAGF,IADY,gBAAgB,OAAO,SAAS,MAC5C,EAAO,IAAI,OAAO,KAAK,KAC7B,CAAC,CAAK,CAAC;CAEV,QAAgB;EACd,IAAI,MAAkB,IAAI;GAExB,AADA,EAAS,OAAO,GAChB,EAAS,EAAE,sCAAsC,CAAC;GAClD;EACF;EAqBA,aAnB+C;GAC7C,IAAI;IAEF,AAAI,MADkB,EAAc,EAAE,OAAO,EAAc,CAAC,IAE1D,EAAS,SAAS,KAElB,EAAS,OAAO,GAChB,EAAS,EAAE,iCAAiC,CAAC;GAEjD,SAAS,GAAe;IAEtB,AADA,EAAS,OAAO,GAChB,EACE,aAAyB,QACrB,EAAc,UACd,EAAE,iCAAiC,CACzC;GACF;EACF,GAEY,EAAE,YAAY,CAE1B,CAAC;CACH,GAAG;EAAC;EAAe;EAAe;CAAC,CAAC;CAEpC,IAAI;CAgCJ,OA/BA,AAmBE,IAnBE,MAAU,YAEV,kBAAC,OAAD;EAAK,WAAW;YAAhB,CACE,kBAAC,KAAD;GAAG,WAAW;aAAgB,EAAE,iCAAiC;EAAK,CAAA,GACtE,kBAAC,GAAD;GAAQ,MAAK;GAAS,SAAQ;GAAY,SAAS;aAChD,EAAE,mCAAmC;EAChC,CAAA,CACL;MAEE,MAAU,UAEjB,kBAAC,OAAD;EAAK,WAAW;YAAhB,CACG,KAAS,OAAwC,OAAjC,kBAAC,GAAD,EAAA,UAAY,EAAiB,CAAA,GAC9C,kBAAC,GAAD;GAAQ,MAAK;GAAS,SAAQ;GAAY,SAAS;aAChD,EAAE,iCAAiC;EAC9B,CAAA,CACL;MAIL,kBAAC,OAAD;EAAK,WAAW;YAAhB,CACE,kBAAC,KAAD;GAAG,WAAW;aACX,EAAE,mCAAmC;EACrC,CAAA,GACH,kBAAC,GAAD;GAAQ,MAAK;GAAS,WAAA;aACnB,EAAE,yCAAyC;EACtC,CAAA,CACL;KAKP,kBAAC,GAAD;EACE,OAAO,EAAE,wBAAwB;EACjC,UAAU,EAAE,2BAA2B;YAEtC;CACS,CAAA;AAEhB"}
1
+ {"version":3,"file":"VerifyEmailScreen.js","names":[],"sources":["../../../../src/auth/pages/VerifyEmailScreen.tsx"],"sourcesContent":["/* eslint-disable no-ternary */\nimport { useEffect, useMemo, useState, type JSX } from 'react';\nimport { useBackofficeReactTranslation } from '../../i18n/useBackofficeReactTranslation.js';\n\nimport { Button } from '@plumile/ui/atomic/atoms/button/Button.js';\nimport { FormError } from '@plumile/ui/atomic/molecules/form_error/FormError.js';\nimport { AuthLayout } from '@plumile/ui/atomic/templates/auth_layout/AuthLayout.js';\n\nimport * as styles from '../login/loginPage.css.js';\n\ntype VerificationState = 'pending' | 'success' | 'error';\n\ntype Props = {\n onBackToLogin: () => void;\n onVerifyEmail: (input: { token: string }) => Promise<boolean>;\n token?: string;\n};\n\nexport const VerifyEmailScreen = ({\n onBackToLogin,\n onVerifyEmail,\n token,\n}: Props): JSX.Element => {\n const { t } = useBackofficeReactTranslation();\n const [state, setState] = useState<VerificationState>('pending');\n const [error, setError] = useState<string | null>(null);\n\n const resolvedToken = useMemo(() => {\n if (token != null) {\n return token;\n }\n if (typeof window === 'undefined') {\n return '';\n }\n const params = new URLSearchParams(window.location.search);\n return params.get('token') ?? '';\n }, [token]);\n\n useEffect(() => {\n if (resolvedToken === '') {\n setState('error');\n setError(t('auth.verifyEmail.errors.missingToken'));\n return;\n }\n\n const verifyEmail = async (): Promise<void> => {\n try {\n const success = await onVerifyEmail({ token: resolvedToken });\n if (success) {\n setState('success');\n } else {\n setState('error');\n setError(t('auth.verifyEmail.errors.invalid'));\n }\n } catch (mutationError) {\n setState('error');\n setError(\n mutationError instanceof Error\n ? mutationError.message\n : t('auth.verifyEmail.errors.invalid'),\n );\n }\n };\n\n verifyEmail().catch(() => {\n return undefined;\n });\n }, [onVerifyEmail, resolvedToken, t]);\n\n let content: JSX.Element;\n if (state === 'success') {\n content = (\n <div className={styles.stack}>\n <p className={styles.helper}>{t('auth.verifyEmail.status.success')}</p>\n <Button type=\"button\" variant=\"secondary\" onClick={onBackToLogin}>\n {t('auth.verifyEmail.actions.continue')}\n </Button>\n </div>\n );\n } else if (state === 'error') {\n content = (\n <div className={styles.stack}>\n {error != null ? <FormError>{error}</FormError> : null}\n <Button type=\"button\" variant=\"secondary\" onClick={onBackToLogin}>\n {t('auth.verifyEmail.actions.return')}\n </Button>\n </div>\n );\n } else {\n content = (\n <div className={styles.stack}>\n <p className={styles.helper}>\n {t('auth.verifyEmail.status.verifying')}\n </p>\n <Button type=\"button\" isLoading>\n {t('auth.verifyEmail.status.verifyingButton')}\n </Button>\n </div>\n );\n }\n\n return (\n <AuthLayout\n title={t('auth.verifyEmail.title')}\n subtitle={t('auth.verifyEmail.subtitle')}\n >\n {content}\n </AuthLayout>\n );\n};\n"],"mappings":";;;;;;;;AAkBA,IAAa,KAAqB,EAChC,kBACA,kBACA,eACwB;CACxB,IAAM,EAAE,SAAM,EAA8B,GACtC,CAAC,GAAO,KAAY,EAA4B,SAAS,GACzD,CAAC,GAAO,KAAY,EAAwB,IAAI,GAEhD,IAAgB,QAChB,MAGA,OAAO,SAAW,MACb,KAGF,IADY,gBAAgB,OAAO,SAAS,MAC5C,CAAA,CAAO,IAAI,OAAO,KAAK,KAC7B,CAAC,CAAK,CAAC;CAEV,QAAgB;EACd,IAAI,MAAkB,IAAI;GAExB,AADA,EAAS,OAAO,GAChB,EAAS,EAAE,sCAAsC,CAAC;GAClD;EACF;EAqBA,aAnB+C;GAC7C,IAAI;IAEF,AAAI,MADkB,EAAc,EAAE,OAAO,EAAc,CAAC,IAE1D,EAAS,SAAS,KAElB,EAAS,OAAO,GAChB,EAAS,EAAE,iCAAiC,CAAC;GAEjD,SAAS,GAAe;IAEtB,AADA,EAAS,OAAO,GAChB,EACE,aAAyB,QACrB,EAAc,UACd,EAAE,iCAAiC,CACzC;GACF;EACF,EAEA,CAAY,CAAC,CAAC,YAAY,CAE1B,CAAC;CACH,GAAG;EAAC;EAAe;EAAe;CAAC,CAAC;CAEpC,IAAI;CAgCJ,OA/BA,AAmBE,IAnBE,MAAU,YAEV,kBAAC,OAAD;EAAK,WAAW;YAAhB,CACE,kBAAC,KAAD;GAAG,WAAW;aAAgB,EAAE,iCAAiC;EAAK,CAAA,GACtE,kBAAC,GAAD;GAAQ,MAAK;GAAS,SAAQ;GAAY,SAAS;aAChD,EAAE,mCAAmC;EAChC,CAAA,CACL;MAEE,MAAU,UAEjB,kBAAC,OAAD;EAAK,WAAW;YAAhB,CACG,KAAS,OAAwC,OAAjC,kBAAC,GAAD,EAAA,UAAY,EAAiB,CAAA,GAC9C,kBAAC,GAAD;GAAQ,MAAK;GAAS,SAAQ;GAAY,SAAS;aAChD,EAAE,iCAAiC;EAC9B,CAAA,CACL;MAIL,kBAAC,OAAD;EAAK,WAAW;YAAhB,CACE,kBAAC,KAAD;GAAG,WAAW;aACX,EAAE,mCAAmC;EACrC,CAAA,GACH,kBAAC,GAAD;GAAQ,MAAK;GAAS,WAAA;aACnB,EAAE,yCAAyC;EACtC,CAAA,CACL;KAKP,kBAAC,GAAD;EACE,OAAO,EAAE,wBAAwB;EACjC,UAAU,EAAE,2BAA2B;YAEtC;CACS,CAAA;AAEhB"}
@@ -1 +1 @@
1
- {"version":3,"file":"LazyBackofficeEntityActionFormDialog.js","names":[],"sources":["../../../../../src/components/backoffice/actions/LazyBackofficeEntityActionFormDialog.tsx"],"sourcesContent":["import { Suspense, lazy, type JSX } from 'react';\nimport { Spinner } from '@plumile/ui/backoffice/atoms/spinner/Spinner.js';\n\nimport type { BackofficeEntityActionFormDialogProps } from './BackofficeEntityActionFormDialog.js';\nimport { useBackofficeReactTranslation } from '../../../i18n/useBackofficeReactTranslation.js';\nimport * as styles from './backofficeEntityActionFormDialog.css.js';\n\nconst BackofficeEntityActionFormDialog = lazy(async () => {\n const module = await import('./BackofficeEntityActionFormDialog.js');\n return { default: module.BackofficeEntityActionFormDialog };\n});\n\nconst ActionDialogFallback = (): JSX.Element => {\n const { t } = useBackofficeReactTranslation();\n\n return (\n <div\n className={styles.lazyFallback}\n role=\"status\"\n aria-live=\"polite\"\n aria-busy=\"true\"\n >\n <Spinner ariaLabel={t('common.loading')} size={20} />\n <span>{t('common.loading')}</span>\n </div>\n );\n};\n\nexport const LazyBackofficeEntityActionFormDialog = <Node,>(\n props: BackofficeEntityActionFormDialogProps<Node>,\n): JSX.Element => {\n const Dialog = BackofficeEntityActionFormDialog as <TNode>(\n dialogProps: BackofficeEntityActionFormDialogProps<TNode>,\n ) => JSX.Element;\n\n return (\n <Suspense fallback={<ActionDialogFallback />}>\n <Dialog {...props} />\n </Suspense>\n );\n};\n\nexport default LazyBackofficeEntityActionFormDialog;\n"],"mappings":";;;;;;AAOA,IAAM,IAAmC,EAAK,aAErC,EAAE,UAAS,MADG,OAAO,0CACH,iCAAiC,EAC3D,GAEK,UAA0C;CAC9C,IAAM,EAAE,SAAM,EAA8B;CAE5C,OACE,kBAAC,OAAD;EACE,WAAW;EACX,MAAK;EACL,aAAU;EACV,aAAU;YAJZ,CAME,kBAAC,GAAD;GAAS,WAAW,EAAE,gBAAgB;GAAG,MAAM;EAAK,CAAA,GACpD,kBAAC,QAAD,EAAA,UAAO,EAAE,gBAAgB,EAAQ,CAAA,CAC9B;;AAET,GAEa,KACX,MAOE,kBAAC,GAAD;CAAU,UAAU,kBAAC,GAAD,CAAuB,CAAA;WACzC,kBAAC,GAAD,EAAQ,GAAI,EAAQ,CAAA;AACZ,CAAA"}
1
+ {"version":3,"file":"LazyBackofficeEntityActionFormDialog.js","names":[],"sources":["../../../../../src/components/backoffice/actions/LazyBackofficeEntityActionFormDialog.tsx"],"sourcesContent":["import { Suspense, lazy, type JSX } from 'react';\nimport { Spinner } from '@plumile/ui/backoffice/atoms/spinner/Spinner.js';\n\nimport type { BackofficeEntityActionFormDialogProps } from './BackofficeEntityActionFormDialog.js';\nimport { useBackofficeReactTranslation } from '../../../i18n/useBackofficeReactTranslation.js';\nimport * as styles from './backofficeEntityActionFormDialog.css.js';\n\nconst BackofficeEntityActionFormDialog = lazy(async () => {\n const module = await import('./BackofficeEntityActionFormDialog.js');\n return { default: module.BackofficeEntityActionFormDialog };\n});\n\nconst ActionDialogFallback = (): JSX.Element => {\n const { t } = useBackofficeReactTranslation();\n\n return (\n <div\n className={styles.lazyFallback}\n role=\"status\"\n aria-live=\"polite\"\n aria-busy=\"true\"\n >\n <Spinner ariaLabel={t('common.loading')} size={20} />\n <span>{t('common.loading')}</span>\n </div>\n );\n};\n\nexport const LazyBackofficeEntityActionFormDialog = <Node,>(\n props: BackofficeEntityActionFormDialogProps<Node>,\n): JSX.Element => {\n const Dialog = BackofficeEntityActionFormDialog as <TNode>(\n dialogProps: BackofficeEntityActionFormDialogProps<TNode>,\n ) => JSX.Element;\n\n return (\n <Suspense fallback={<ActionDialogFallback />}>\n <Dialog {...props} />\n </Suspense>\n );\n};\n\nexport default LazyBackofficeEntityActionFormDialog;\n"],"mappings":";;;;;;AAOA,IAAM,IAAmC,EAAK,aAErC,EAAE,UAAS,MADG,OAAO,yCAAA,CACH,iCAAiC,EAC3D,GAEK,UAA0C;CAC9C,IAAM,EAAE,SAAM,EAA8B;CAE5C,OACE,kBAAC,OAAD;EACE,WAAW;EACX,MAAK;EACL,aAAU;EACV,aAAU;YAJZ,CAME,kBAAC,GAAD;GAAS,WAAW,EAAE,gBAAgB;GAAG,MAAM;EAAK,CAAA,GACpD,kBAAC,QAAD,EAAA,UAAO,EAAE,gBAAgB,EAAQ,CAAA,CAC9B;;AAET,GAEa,KACX,MAOE,kBAAC,GAAD;CAAU,UAAU,kBAAC,GAAD,CAAuB,CAAA;WACzC,kBAAC,GAAD,EAAQ,GAAI,EAAQ,CAAA;AACZ,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"BackofficeBillingUsageChart.js","names":[],"sources":["../../../../../src/components/backoffice/billing/BackofficeBillingUsageChart.tsx"],"sourcesContent":["import { useMemo, type JSX } from 'react';\nimport { FormattedDate } from '@plumile/ui/atomic/atoms/formatted-date/FormattedDate.js';\nimport { TimeSeriesLineChart } from '@plumile/ui/components/charts/TimeSeriesLineChart.js';\nimport { formatCurrencyAmount } from '@plumile/ui/shared/currencyAmount.js';\nimport { toUtcDailyCategorySeries } from '@plumile/ui/shared/timeSeries.js';\n\nimport * as styles from './backofficeBillingUsageChart.css.js';\n\nexport type BackofficeBillingUsageChartBucket = {\n readonly day: string;\n readonly category: string;\n readonly value: number;\n};\n\nexport type BackofficeBillingUsageChartCategory = {\n readonly id: string;\n readonly label: string;\n readonly color: string;\n};\n\nexport type BackofficeBillingUsageChartProps = {\n readonly currency: string;\n readonly totalAmount: number;\n readonly from: string | null;\n readonly to: string | null;\n readonly buckets: readonly BackofficeBillingUsageChartBucket[];\n readonly categories: readonly BackofficeBillingUsageChartCategory[];\n readonly emptyLabel: string;\n readonly rangePrefix: string;\n readonly ariaLabel: string;\n};\n\nconst dateSeparator = '->';\n\nconst formatBillingAxisValue = (value: number, currency: string): string => {\n return new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency,\n minimumFractionDigits: 0,\n maximumFractionDigits: 0,\n }).format(value);\n};\n\nexport const BackofficeBillingUsageChart = ({\n ariaLabel,\n buckets,\n categories,\n currency,\n emptyLabel,\n from,\n rangePrefix,\n to,\n totalAmount,\n}: BackofficeBillingUsageChartProps): JSX.Element => {\n const categoryOrder = useMemo(() => {\n return categories.map((category) => {\n return category.id;\n });\n }, [categories]);\n\n const categoryColorById = useMemo(() => {\n return Object.fromEntries(\n categories.map((category) => {\n return [category.id, category.color];\n }),\n );\n }, [categories]);\n\n const categoryLabelById = useMemo(() => {\n return Object.fromEntries(\n categories.map((category) => {\n return [category.id, category.label];\n }),\n );\n }, [categories]);\n\n const series = useMemo(() => {\n if (from == null || to == null) {\n return [];\n }\n\n return toUtcDailyCategorySeries({\n fromIsoDateTime: from,\n toIsoDateTime: to,\n buckets,\n categories: categoryOrder,\n });\n }, [buckets, categoryOrder, from, to]);\n\n const formatChartValue = (value: number): string => {\n return formatCurrencyAmount({\n amount: value,\n currency,\n });\n };\n\n if (buckets.length === 0) {\n return <p className={styles.rangeText}>{emptyLabel}</p>;\n }\n\n return (\n <div className={styles.root}>\n <p className={styles.totalValue}>{formatChartValue(totalAmount)}</p>\n {from != null && to != null && (\n <p className={styles.rangeText}>\n {rangePrefix} <FormattedDate value={from} /> {dateSeparator}{' '}\n <FormattedDate value={to} />\n </p>\n )}\n <TimeSeriesLineChart\n ariaLabel={ariaLabel}\n categoryColorById={categoryColorById}\n categoryLabel={(category) => {\n return categoryLabelById[category] ?? category;\n }}\n categoryOrder={categoryOrder}\n formatValue={formatChartValue}\n series={series}\n yAxis={{\n min: 0,\n minTickStep: 1,\n maxTickCount: 5,\n formatValue: (value) => {\n return formatBillingAxisValue(value, currency);\n },\n }}\n />\n </div>\n );\n};\n\nexport default BackofficeBillingUsageChart;\n"],"mappings":";;;;;;;;AAgCA,IAAM,IAAgB,MAEhB,KAA0B,GAAe,MACtC,IAAI,KAAK,aAAa,KAAA,GAAW;CACtC,OAAO;CACP;CACA,uBAAuB;CACvB,uBAAuB;AACzB,CAAC,EAAE,OAAO,CAAK,GAGJ,KAA+B,EAC1C,cACA,YACA,eACA,aACA,eACA,SACA,gBACA,OACA,qBACmD;CACnD,IAAM,IAAgB,QACb,EAAW,KAAK,MACd,EAAS,EACjB,GACA,CAAC,CAAU,CAAC,GAET,IAAoB,QACjB,OAAO,YACZ,EAAW,KAAK,MACP,CAAC,EAAS,IAAI,EAAS,KAAK,CACpC,CACH,GACC,CAAC,CAAU,CAAC,GAET,IAAoB,QACjB,OAAO,YACZ,EAAW,KAAK,MACP,CAAC,EAAS,IAAI,EAAS,KAAK,CACpC,CACH,GACC,CAAC,CAAU,CAAC,GAET,IAAS,QACT,KAAQ,QAAQ,KAAM,OACjB,CAAC,IAGH,EAAyB;EAC9B,iBAAiB;EACjB,eAAe;EACf;EACA,YAAY;CACd,CAAC,GACA;EAAC;EAAS;EAAe;EAAM;CAAE,CAAC,GAE/B,KAAoB,MACjB,EAAqB;EAC1B,QAAQ;EACR;CACF,CAAC;CAOH,OAJI,EAAQ,WAAW,IACd,kBAAC,KAAD;EAAG,WAAW;YAAmB;CAAc,CAAA,IAItD,kBAAC,OAAD;EAAK,WAAW;YAAhB;GACE,kBAAC,KAAD;IAAG,WAAW;cAAoB,EAAiB,CAAW;GAAK,CAAA;GAClE,KAAQ,QAAQ,KAAM,QACrB,kBAAC,KAAD;IAAG,WAAW;cAAd;KACG;KAAY;KAAC,kBAAC,GAAD,EAAe,OAAO,EAAO,CAAA;KAAC;KAAE;KAAe;KAC7D,kBAAC,GAAD,EAAe,OAAO,EAAK,CAAA;IAC1B;;GAEL,kBAAC,GAAD;IACa;IACQ;IACnB,gBAAgB,MACP,EAAkB,MAAa;IAEzB;IACf,aAAa;IACL;IACR,OAAO;KACL,KAAK;KACL,aAAa;KACb,cAAc;KACd,cAAc,MACL,EAAuB,GAAO,CAAQ;IAEjD;GACD,CAAA;EACE;;AAET"}
1
+ {"version":3,"file":"BackofficeBillingUsageChart.js","names":[],"sources":["../../../../../src/components/backoffice/billing/BackofficeBillingUsageChart.tsx"],"sourcesContent":["import { useMemo, type JSX } from 'react';\nimport { FormattedDate } from '@plumile/ui/atomic/atoms/formatted-date/FormattedDate.js';\nimport { TimeSeriesLineChart } from '@plumile/ui/components/charts/TimeSeriesLineChart.js';\nimport { formatCurrencyAmount } from '@plumile/ui/shared/currencyAmount.js';\nimport { toUtcDailyCategorySeries } from '@plumile/ui/shared/timeSeries.js';\n\nimport * as styles from './backofficeBillingUsageChart.css.js';\n\nexport type BackofficeBillingUsageChartBucket = {\n readonly day: string;\n readonly category: string;\n readonly value: number;\n};\n\nexport type BackofficeBillingUsageChartCategory = {\n readonly id: string;\n readonly label: string;\n readonly color: string;\n};\n\nexport type BackofficeBillingUsageChartProps = {\n readonly currency: string;\n readonly totalAmount: number;\n readonly from: string | null;\n readonly to: string | null;\n readonly buckets: readonly BackofficeBillingUsageChartBucket[];\n readonly categories: readonly BackofficeBillingUsageChartCategory[];\n readonly emptyLabel: string;\n readonly rangePrefix: string;\n readonly ariaLabel: string;\n};\n\nconst dateSeparator = '->';\n\nconst formatBillingAxisValue = (value: number, currency: string): string => {\n return new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency,\n minimumFractionDigits: 0,\n maximumFractionDigits: 0,\n }).format(value);\n};\n\nexport const BackofficeBillingUsageChart = ({\n ariaLabel,\n buckets,\n categories,\n currency,\n emptyLabel,\n from,\n rangePrefix,\n to,\n totalAmount,\n}: BackofficeBillingUsageChartProps): JSX.Element => {\n const categoryOrder = useMemo(() => {\n return categories.map((category) => {\n return category.id;\n });\n }, [categories]);\n\n const categoryColorById = useMemo(() => {\n return Object.fromEntries(\n categories.map((category) => {\n return [category.id, category.color];\n }),\n );\n }, [categories]);\n\n const categoryLabelById = useMemo(() => {\n return Object.fromEntries(\n categories.map((category) => {\n return [category.id, category.label];\n }),\n );\n }, [categories]);\n\n const series = useMemo(() => {\n if (from == null || to == null) {\n return [];\n }\n\n return toUtcDailyCategorySeries({\n fromIsoDateTime: from,\n toIsoDateTime: to,\n buckets,\n categories: categoryOrder,\n });\n }, [buckets, categoryOrder, from, to]);\n\n const formatChartValue = (value: number): string => {\n return formatCurrencyAmount({\n amount: value,\n currency,\n });\n };\n\n if (buckets.length === 0) {\n return <p className={styles.rangeText}>{emptyLabel}</p>;\n }\n\n return (\n <div className={styles.root}>\n <p className={styles.totalValue}>{formatChartValue(totalAmount)}</p>\n {from != null && to != null && (\n <p className={styles.rangeText}>\n {rangePrefix} <FormattedDate value={from} /> {dateSeparator}{' '}\n <FormattedDate value={to} />\n </p>\n )}\n <TimeSeriesLineChart\n ariaLabel={ariaLabel}\n categoryColorById={categoryColorById}\n categoryLabel={(category) => {\n return categoryLabelById[category] ?? category;\n }}\n categoryOrder={categoryOrder}\n formatValue={formatChartValue}\n series={series}\n yAxis={{\n min: 0,\n minTickStep: 1,\n maxTickCount: 5,\n formatValue: (value) => {\n return formatBillingAxisValue(value, currency);\n },\n }}\n />\n </div>\n );\n};\n\nexport default BackofficeBillingUsageChart;\n"],"mappings":";;;;;;;;AAgCA,IAAM,IAAgB,MAEhB,KAA0B,GAAe,MACtC,IAAI,KAAK,aAAa,KAAA,GAAW;CACtC,OAAO;CACP;CACA,uBAAuB;CACvB,uBAAuB;AACzB,CAAC,CAAC,CAAC,OAAO,CAAK,GAGJ,KAA+B,EAC1C,cACA,YACA,eACA,aACA,eACA,SACA,gBACA,OACA,qBACmD;CACnD,IAAM,IAAgB,QACb,EAAW,KAAK,MACd,EAAS,EACjB,GACA,CAAC,CAAU,CAAC,GAET,IAAoB,QACjB,OAAO,YACZ,EAAW,KAAK,MACP,CAAC,EAAS,IAAI,EAAS,KAAK,CACpC,CACH,GACC,CAAC,CAAU,CAAC,GAET,IAAoB,QACjB,OAAO,YACZ,EAAW,KAAK,MACP,CAAC,EAAS,IAAI,EAAS,KAAK,CACpC,CACH,GACC,CAAC,CAAU,CAAC,GAET,IAAS,QACT,KAAQ,QAAQ,KAAM,OACjB,CAAC,IAGH,EAAyB;EAC9B,iBAAiB;EACjB,eAAe;EACf;EACA,YAAY;CACd,CAAC,GACA;EAAC;EAAS;EAAe;EAAM;CAAE,CAAC,GAE/B,KAAoB,MACjB,EAAqB;EAC1B,QAAQ;EACR;CACF,CAAC;CAOH,OAJI,EAAQ,WAAW,IACd,kBAAC,KAAD;EAAG,WAAW;YAAmB;CAAc,CAAA,IAItD,kBAAC,OAAD;EAAK,WAAW;YAAhB;GACE,kBAAC,KAAD;IAAG,WAAW;cAAoB,EAAiB,CAAW;GAAK,CAAA;GAClE,KAAQ,QAAQ,KAAM,QACrB,kBAAC,KAAD;IAAG,WAAW;cAAd;KACG;KAAY;KAAC,kBAAC,GAAD,EAAe,OAAO,EAAO,CAAA;KAAC;KAAE;KAAe;KAC7D,kBAAC,GAAD,EAAe,OAAO,EAAK,CAAA;IAC1B;;GAEL,kBAAC,GAAD;IACa;IACQ;IACnB,gBAAgB,MACP,EAAkB,MAAa;IAEzB;IACf,aAAa;IACL;IACR,OAAO;KACL,KAAK;KACL,aAAa;KACb,cAAc;KACd,cAAc,MACL,EAAuB,GAAO,CAAQ;IAEjD;GACD,CAAA;EACE;;AAET"}
@@ -1 +1 @@
1
- {"version":3,"file":"buildDataTableColumns.js","names":[],"sources":["../../../../../src/components/backoffice/columns/buildDataTableColumns.tsx"],"sourcesContent":["import { BACKOFFICE_DATE_TIME_OPTIONS } from '@plumile/backoffice-core/constants.js';\nimport type {\n BackofficeColumnSpec,\n BackofficeFieldSize,\n BackofficeListColumnVisibility,\n I18nLabel,\n} from '@plumile/backoffice-core/types.js';\nimport type { TFunction } from 'i18next';\nimport Link from '@plumile/router/routing/Link.js';\nimport { FormattedDate } from '@plumile/ui/atomic/atoms/formatted-date/FormattedDate.js';\nimport { Tag } from '@plumile/ui/backoffice/atoms/tag/Tag.js';\nimport { type DataTableColumn } from '@plumile/ui/components/data-table/DataTable.js';\nimport { type DataTableBreakpoint } from '@plumile/ui/components/data-table/tableBreakpoints.js';\nimport { BackofficeInlineLink } from '../links/BackofficeInlineLink.js';\nimport type { BackofficeLinkTarget } from '../links/types.js';\nimport { TechnicalIdentifierValue } from '../technical/TechnicalIdentifierValue.js';\n\nconst resolveLabel = (label: I18nLabel, tApp: TFunction): string => {\n return label(tApp);\n};\n\nconst resolveTextValue = (\n value: string | number | null,\n fallback: string,\n): string => {\n if (value == null) {\n return fallback;\n }\n if (typeof value === 'string' && value.trim() === '') {\n return fallback;\n }\n return String(value);\n};\n\nexport type BuildDataTableColumnsOptions = {\n tApp: TFunction;\n t: TFunction;\n};\n\nexport type BackofficeSizedDataTableColumn<Row> = DataTableColumn<Row> & {\n size: BackofficeFieldSize;\n};\n\nconst resolveBreakpoint = (\n value: BackofficeListColumnVisibility['minVisibleAt'] | undefined,\n): DataTableBreakpoint | undefined => {\n if (value === 'sm' || value === 'md' || value === 'lg' || value === 'xl') {\n return value;\n }\n return undefined;\n};\n\n/**\n *\n */\nexport function buildDataTableColumns<Row>(\n columns: readonly BackofficeColumnSpec<Row>[],\n options: BuildDataTableColumnsOptions,\n): readonly BackofficeSizedDataTableColumn<Row>[] {\n const { tApp, t } = options;\n const fallback = t('common.notAvailable');\n const filteredColumns = columns.filter((column) => {\n return column.key !== 'id';\n });\n\n return filteredColumns.map((column) => {\n return {\n id: column.key,\n header: resolveLabel(column.header, tApp),\n size: column.size,\n isPrimary: column.visibility?.priority === 'primary',\n priority: column.visibility?.priority,\n align: column.visibility?.align,\n mobileRole: column.visibility?.mobile,\n minVisibleAt: resolveBreakpoint(column.visibility?.minVisibleAt),\n cell: (row) => {\n const { cell } = column;\n switch (cell.type) {\n case 'text': {\n return resolveTextValue(cell.value(row), fallback);\n }\n case 'link': {\n const value = cell.value(row);\n if (\n value == null ||\n (typeof value === 'string' && value.trim() === '')\n ) {\n return fallback;\n }\n return (\n <Link to={cell.to(row)} preloadOnHover=\"code\">\n {value}\n </Link>\n );\n }\n case 'badge': {\n const value = cell.value(row);\n if (value == null || value.trim() === '') {\n return fallback;\n }\n let { tone } = cell;\n if (typeof tone === 'function') {\n tone = tone(row);\n }\n return <Tag tone={tone}>{value}</Tag>;\n }\n case 'dateTime': {\n const value = cell.value(row);\n return (\n <FormattedDate\n value={value}\n fallback={fallback}\n options={BACKOFFICE_DATE_TIME_OPTIONS}\n />\n );\n }\n case 'entityRef': {\n const id = cell.value(row);\n if (id.trim() === '') {\n return fallback;\n }\n const label = cell.label?.(row);\n if (label == null || label.trim() === '') {\n return t('filters.placeholders.unresolved');\n }\n const target = {\n kind: 'entity-detail',\n entityId: cell.entity,\n id,\n } as BackofficeLinkTarget;\n return (\n <BackofficeInlineLink target={target}>\n {label}\n </BackofficeInlineLink>\n );\n }\n case 'technicalIdentifier': {\n const value = cell.value(row);\n if (value == null || String(value).trim() === '') {\n return fallback;\n }\n return (\n <TechnicalIdentifierValue\n value={value}\n copyValue={cell.copyValue?.(row) ?? undefined}\n variant={cell.variant}\n copyLabel={t('common.actions.copy')}\n />\n );\n }\n case 'custom': {\n const rendered = cell.render(row);\n if (rendered == null || rendered === '') {\n return fallback;\n }\n return <>{rendered}</>;\n }\n default: {\n return fallback;\n }\n }\n },\n };\n });\n}\n"],"mappings":";;;;;;;;AAiBA,IAAM,KAAgB,GAAkB,MAC/B,EAAM,CAAI,GAGb,KACJ,GACA,MAEI,KAAS,QAGT,OAAO,KAAU,YAAY,EAAM,KAAK,MAAM,KACzC,IAEF,OAAO,CAAK,GAYf,KACJ,MACoC;CACpC,IAAI,MAAU,QAAQ,MAAU,QAAQ,MAAU,QAAQ,MAAU,MAClE,OAAO;AAGX;AAKA,SAAgB,EACd,GACA,GACgD;CAChD,IAAM,EAAE,SAAM,SAAM,GACd,IAAW,EAAE,qBAAqB;CAKxC,OAJwB,EAAQ,QAAQ,MAC/B,EAAO,QAAQ,IAGjB,EAAgB,KAAK,OACnB;EACL,IAAI,EAAO;EACX,QAAQ,EAAa,EAAO,QAAQ,CAAI;EACxC,MAAM,EAAO;EACb,WAAW,EAAO,YAAY,aAAa;EAC3C,UAAU,EAAO,YAAY;EAC7B,OAAO,EAAO,YAAY;EAC1B,YAAY,EAAO,YAAY;EAC/B,cAAc,EAAkB,EAAO,YAAY,YAAY;EAC/D,OAAO,MAAQ;GACb,IAAM,EAAE,YAAS;GACjB,QAAQ,EAAK,MAAb;IACE,KAAK,QACH,OAAO,EAAiB,EAAK,MAAM,CAAG,GAAG,CAAQ;IAEnD,KAAK,QAAQ;KACX,IAAM,IAAQ,EAAK,MAAM,CAAG;KAO5B,OALE,KAAS,QACR,OAAO,KAAU,YAAY,EAAM,KAAK,MAAM,KAExC,IAGP,kBAAC,GAAD;MAAM,IAAI,EAAK,GAAG,CAAG;MAAG,gBAAe;gBACpC;KACG,CAAA;IAEV;IACA,KAAK,SAAS;KACZ,IAAM,IAAQ,EAAK,MAAM,CAAG;KAC5B,IAAI,KAAS,QAAQ,EAAM,KAAK,MAAM,IACpC,OAAO;KAET,IAAI,EAAE,YAAS;KAIf,OAHI,OAAO,KAAS,eAClB,IAAO,EAAK,CAAG,IAEV,kBAAC,GAAD;MAAW;gBAAO;KAAW,CAAA;IACtC;IACA,KAAK,YAEH,OACE,kBAAC,GAAD;KACS,OAHG,EAAK,MAAM,CAGd;KACG;KACV,SAAS;IACV,CAAA;IAGL,KAAK,aAAa;KAChB,IAAM,IAAK,EAAK,MAAM,CAAG;KACzB,IAAI,EAAG,KAAK,MAAM,IAChB,OAAO;KAET,IAAM,IAAQ,EAAK,QAAQ,CAAG;KAS9B,OARI,KAAS,QAAQ,EAAM,KAAK,MAAM,KAC7B,EAAE,iCAAiC,IAQ1C,kBAAC,GAAD;MAA8B,QAAA;OAL9B,MAAM;OACN,UAAU,EAAK;OACf;MAG8B;gBAC3B;KACmB,CAAA;IAE1B;IACA,KAAK,uBAAuB;KAC1B,IAAM,IAAQ,EAAK,MAAM,CAAG;KAI5B,OAHI,KAAS,QAAQ,OAAO,CAAK,EAAE,KAAK,MAAM,KACrC,IAGP,kBAAC,GAAD;MACS;MACP,WAAW,EAAK,YAAY,CAAG,KAAK,KAAA;MACpC,SAAS,EAAK;MACd,WAAW,EAAE,qBAAqB;KACnC,CAAA;IAEL;IACA,KAAK,UAAU;KACb,IAAM,IAAW,EAAK,OAAO,CAAG;KAIhC,OAHI,KAAY,QAAQ,MAAa,KAC5B,IAEF,kBAAA,GAAA,EAAA,UAAG,EAAW,CAAA;IACvB;IACA,SACE,OAAO;GAEX;EACF;CACF,EACD;AACH"}
1
+ {"version":3,"file":"buildDataTableColumns.js","names":[],"sources":["../../../../../src/components/backoffice/columns/buildDataTableColumns.tsx"],"sourcesContent":["import { BACKOFFICE_DATE_TIME_OPTIONS } from '@plumile/backoffice-core/constants.js';\nimport type {\n BackofficeColumnSpec,\n BackofficeFieldSize,\n BackofficeListColumnVisibility,\n I18nLabel,\n} from '@plumile/backoffice-core/types.js';\nimport type { TFunction } from 'i18next';\nimport Link from '@plumile/router/routing/Link.js';\nimport { FormattedDate } from '@plumile/ui/atomic/atoms/formatted-date/FormattedDate.js';\nimport { Tag } from '@plumile/ui/backoffice/atoms/tag/Tag.js';\nimport { type DataTableColumn } from '@plumile/ui/components/data-table/DataTable.js';\nimport { type DataTableBreakpoint } from '@plumile/ui/components/data-table/tableBreakpoints.js';\nimport { BackofficeInlineLink } from '../links/BackofficeInlineLink.js';\nimport type { BackofficeLinkTarget } from '../links/types.js';\nimport { TechnicalIdentifierValue } from '../technical/TechnicalIdentifierValue.js';\n\nconst resolveLabel = (label: I18nLabel, tApp: TFunction): string => {\n return label(tApp);\n};\n\nconst resolveTextValue = (\n value: string | number | null,\n fallback: string,\n): string => {\n if (value == null) {\n return fallback;\n }\n if (typeof value === 'string' && value.trim() === '') {\n return fallback;\n }\n return String(value);\n};\n\nexport type BuildDataTableColumnsOptions = {\n tApp: TFunction;\n t: TFunction;\n};\n\nexport type BackofficeSizedDataTableColumn<Row> = DataTableColumn<Row> & {\n size: BackofficeFieldSize;\n};\n\nconst resolveBreakpoint = (\n value: BackofficeListColumnVisibility['minVisibleAt'] | undefined,\n): DataTableBreakpoint | undefined => {\n if (value === 'sm' || value === 'md' || value === 'lg' || value === 'xl') {\n return value;\n }\n return undefined;\n};\n\n/**\n *\n */\nexport function buildDataTableColumns<Row>(\n columns: readonly BackofficeColumnSpec<Row>[],\n options: BuildDataTableColumnsOptions,\n): readonly BackofficeSizedDataTableColumn<Row>[] {\n const { tApp, t } = options;\n const fallback = t('common.notAvailable');\n const filteredColumns = columns.filter((column) => {\n return column.key !== 'id';\n });\n\n return filteredColumns.map((column) => {\n return {\n id: column.key,\n header: resolveLabel(column.header, tApp),\n size: column.size,\n isPrimary: column.visibility?.priority === 'primary',\n priority: column.visibility?.priority,\n align: column.visibility?.align,\n mobileRole: column.visibility?.mobile,\n minVisibleAt: resolveBreakpoint(column.visibility?.minVisibleAt),\n cell: (row) => {\n const { cell } = column;\n switch (cell.type) {\n case 'text': {\n return resolveTextValue(cell.value(row), fallback);\n }\n case 'link': {\n const value = cell.value(row);\n if (\n value == null ||\n (typeof value === 'string' && value.trim() === '')\n ) {\n return fallback;\n }\n return (\n <Link to={cell.to(row)} preloadOnHover=\"code\">\n {value}\n </Link>\n );\n }\n case 'badge': {\n const value = cell.value(row);\n if (value == null || value.trim() === '') {\n return fallback;\n }\n let { tone } = cell;\n if (typeof tone === 'function') {\n tone = tone(row);\n }\n return <Tag tone={tone}>{value}</Tag>;\n }\n case 'dateTime': {\n const value = cell.value(row);\n return (\n <FormattedDate\n value={value}\n fallback={fallback}\n options={BACKOFFICE_DATE_TIME_OPTIONS}\n />\n );\n }\n case 'entityRef': {\n const id = cell.value(row);\n if (id.trim() === '') {\n return fallback;\n }\n const label = cell.label?.(row);\n if (label == null || label.trim() === '') {\n return t('filters.placeholders.unresolved');\n }\n const target = {\n kind: 'entity-detail',\n entityId: cell.entity,\n id,\n } as BackofficeLinkTarget;\n return (\n <BackofficeInlineLink target={target}>\n {label}\n </BackofficeInlineLink>\n );\n }\n case 'technicalIdentifier': {\n const value = cell.value(row);\n if (value == null || String(value).trim() === '') {\n return fallback;\n }\n return (\n <TechnicalIdentifierValue\n value={value}\n copyValue={cell.copyValue?.(row) ?? undefined}\n variant={cell.variant}\n copyLabel={t('common.actions.copy')}\n />\n );\n }\n case 'custom': {\n const rendered = cell.render(row);\n if (rendered == null || rendered === '') {\n return fallback;\n }\n return <>{rendered}</>;\n }\n default: {\n return fallback;\n }\n }\n },\n };\n });\n}\n"],"mappings":";;;;;;;;AAiBA,IAAM,KAAgB,GAAkB,MAC/B,EAAM,CAAI,GAGb,KACJ,GACA,MAEI,KAAS,QAGT,OAAO,KAAU,YAAY,EAAM,KAAK,MAAM,KACzC,IAEF,OAAO,CAAK,GAYf,KACJ,MACoC;CACpC,IAAI,MAAU,QAAQ,MAAU,QAAQ,MAAU,QAAQ,MAAU,MAClE,OAAO;AAGX;AAKA,SAAgB,EACd,GACA,GACgD;CAChD,IAAM,EAAE,SAAM,SAAM,GACd,IAAW,EAAE,qBAAqB;CAKxC,OAJwB,EAAQ,QAAQ,MAC/B,EAAO,QAAQ,IAGjB,CAAA,CAAgB,KAAK,OACnB;EACL,IAAI,EAAO;EACX,QAAQ,EAAa,EAAO,QAAQ,CAAI;EACxC,MAAM,EAAO;EACb,WAAW,EAAO,YAAY,aAAa;EAC3C,UAAU,EAAO,YAAY;EAC7B,OAAO,EAAO,YAAY;EAC1B,YAAY,EAAO,YAAY;EAC/B,cAAc,EAAkB,EAAO,YAAY,YAAY;EAC/D,OAAO,MAAQ;GACb,IAAM,EAAE,YAAS;GACjB,QAAQ,EAAK,MAAb;IACE,KAAK,QACH,OAAO,EAAiB,EAAK,MAAM,CAAG,GAAG,CAAQ;IAEnD,KAAK,QAAQ;KACX,IAAM,IAAQ,EAAK,MAAM,CAAG;KAO5B,OALE,KAAS,QACR,OAAO,KAAU,YAAY,EAAM,KAAK,MAAM,KAExC,IAGP,kBAAC,GAAD;MAAM,IAAI,EAAK,GAAG,CAAG;MAAG,gBAAe;gBACpC;KACG,CAAA;IAEV;IACA,KAAK,SAAS;KACZ,IAAM,IAAQ,EAAK,MAAM,CAAG;KAC5B,IAAI,KAAS,QAAQ,EAAM,KAAK,MAAM,IACpC,OAAO;KAET,IAAI,EAAE,YAAS;KAIf,OAHI,OAAO,KAAS,eAClB,IAAO,EAAK,CAAG,IAEV,kBAAC,GAAD;MAAW;gBAAO;KAAW,CAAA;IACtC;IACA,KAAK,YAEH,OACE,kBAAC,GAAD;KACS,OAHG,EAAK,MAAM,CAGd;KACG;KACV,SAAS;IACV,CAAA;IAGL,KAAK,aAAa;KAChB,IAAM,IAAK,EAAK,MAAM,CAAG;KACzB,IAAI,EAAG,KAAK,MAAM,IAChB,OAAO;KAET,IAAM,IAAQ,EAAK,QAAQ,CAAG;KAS9B,OARI,KAAS,QAAQ,EAAM,KAAK,MAAM,KAC7B,EAAE,iCAAiC,IAQ1C,kBAAC,GAAD;MAA8B,QAAA;OAL9B,MAAM;OACN,UAAU,EAAK;OACf;MAG8B;gBAC3B;KACmB,CAAA;IAE1B;IACA,KAAK,uBAAuB;KAC1B,IAAM,IAAQ,EAAK,MAAM,CAAG;KAI5B,OAHI,KAAS,QAAQ,OAAO,CAAK,CAAC,CAAC,KAAK,MAAM,KACrC,IAGP,kBAAC,GAAD;MACS;MACP,WAAW,EAAK,YAAY,CAAG,KAAK,KAAA;MACpC,SAAS,EAAK;MACd,WAAW,EAAE,qBAAqB;KACnC,CAAA;IAEL;IACA,KAAK,UAAU;KACb,IAAM,IAAW,EAAK,OAAO,CAAG;KAIhC,OAHI,KAAY,QAAQ,MAAa,KAC5B,IAEF,kBAAA,GAAA,EAAA,UAAG,EAAW,CAAA;IACvB;IACA,SACE,OAAO;GAEX;EACF;CACF,EACD;AACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"BackofficeLifecycleTimelineSection.js","names":[],"sources":["../../../../../src/components/backoffice/detail/BackofficeLifecycleTimelineSection.tsx"],"sourcesContent":["import { type JSX, type ReactNode } from 'react';\nimport { BackofficeDetailSection } from '@plumile/ui/backoffice/molecules/backoffice_detail_section/BackofficeDetailSection.js';\nimport { AuditTimeline } from '@plumile/ui/backoffice/organisms/audit_timeline/AuditTimeline.js';\n\nimport { useBackofficeReactTranslation } from '../../../i18n/useBackofficeReactTranslation.js';\n\nexport type BackofficeLifecycleEventTone =\n | 'neutral'\n | 'info'\n | 'success'\n | 'warning'\n | 'danger';\n\nexport type BackofficeLifecycleEvent = {\n readonly id: string;\n readonly label: string;\n readonly timestamp?: ReactNode;\n readonly description?: ReactNode;\n readonly actor?: ReactNode;\n readonly tone?: BackofficeLifecycleEventTone;\n readonly payload?: ReactNode;\n};\n\nexport type BackofficeLifecycleTimelineSectionProps = {\n readonly title: string;\n readonly description?: string;\n readonly events: readonly BackofficeLifecycleEvent[];\n readonly emptyLabel?: string;\n};\n\nexport const BackofficeLifecycleTimelineSection = ({\n title,\n description,\n events,\n emptyLabel,\n}: BackofficeLifecycleTimelineSectionProps): JSX.Element => {\n const { t } = useBackofficeReactTranslation();\n\n if (events.length === 0) {\n return (\n <BackofficeDetailSection title={title} description={description}>\n {emptyLabel ?? t('common.notAvailable')}\n </BackofficeDetailSection>\n );\n }\n\n return (\n <BackofficeDetailSection title={title} description={description}>\n <AuditTimeline\n events={events.map((event) => {\n return {\n id: event.id,\n title: event.label,\n time: event.timestamp,\n description: event.description,\n actor: event.actor,\n details: event.payload,\n tone: event.tone,\n };\n })}\n />\n </BackofficeDetailSection>\n );\n};\n\nexport default BackofficeLifecycleTimelineSection;\n"],"mappings":";;;;;AA8BA,IAAa,KAAsC,EACjD,UACA,gBACA,WACA,oBAC0D;CAC1D,IAAM,EAAE,SAAM,EAA8B;CAU5C,OARI,EAAO,WAAW,IAElB,kBAAC,GAAD;EAAgC;EAAoB;YACjD,KAAc,EAAE,qBAAqB;CACf,CAAA,IAK3B,kBAAC,GAAD;EAAgC;EAAoB;YAClD,kBAAC,GAAD,EACE,QAAQ,EAAO,KAAK,OACX;GACL,IAAI,EAAM;GACV,OAAO,EAAM;GACb,MAAM,EAAM;GACZ,aAAa,EAAM;GACnB,OAAO,EAAM;GACb,SAAS,EAAM;GACf,MAAM,EAAM;EACd,EACD,EACF,CAAA;CACsB,CAAA;AAE7B"}
1
+ {"version":3,"file":"BackofficeLifecycleTimelineSection.js","names":[],"sources":["../../../../../src/components/backoffice/detail/BackofficeLifecycleTimelineSection.tsx"],"sourcesContent":["import { type JSX, type ReactNode } from 'react';\nimport { BackofficeDetailSection } from '@plumile/ui/backoffice/molecules/backoffice_detail_section/BackofficeDetailSection.js';\nimport { AuditTimeline } from '@plumile/ui/backoffice/organisms/audit_timeline/AuditTimeline.js';\n\nimport { useBackofficeReactTranslation } from '../../../i18n/useBackofficeReactTranslation.js';\n\nexport type BackofficeLifecycleEventTone =\n 'neutral' | 'info' | 'success' | 'warning' | 'danger';\n\nexport type BackofficeLifecycleEvent = {\n readonly id: string;\n readonly label: string;\n readonly timestamp?: ReactNode;\n readonly description?: ReactNode;\n readonly actor?: ReactNode;\n readonly tone?: BackofficeLifecycleEventTone;\n readonly payload?: ReactNode;\n};\n\nexport type BackofficeLifecycleTimelineSectionProps = {\n readonly title: string;\n readonly description?: string;\n readonly events: readonly BackofficeLifecycleEvent[];\n readonly emptyLabel?: string;\n};\n\nexport const BackofficeLifecycleTimelineSection = ({\n title,\n description,\n events,\n emptyLabel,\n}: BackofficeLifecycleTimelineSectionProps): JSX.Element => {\n const { t } = useBackofficeReactTranslation();\n\n if (events.length === 0) {\n return (\n <BackofficeDetailSection title={title} description={description}>\n {emptyLabel ?? t('common.notAvailable')}\n </BackofficeDetailSection>\n );\n }\n\n return (\n <BackofficeDetailSection title={title} description={description}>\n <AuditTimeline\n events={events.map((event) => {\n return {\n id: event.id,\n title: event.label,\n time: event.timestamp,\n description: event.description,\n actor: event.actor,\n details: event.payload,\n tone: event.tone,\n };\n })}\n />\n </BackofficeDetailSection>\n );\n};\n\nexport default BackofficeLifecycleTimelineSection;\n"],"mappings":";;;;;AA0BA,IAAa,KAAsC,EACjD,UACA,gBACA,WACA,oBAC0D;CAC1D,IAAM,EAAE,SAAM,EAA8B;CAU5C,OARI,EAAO,WAAW,IAElB,kBAAC,GAAD;EAAgC;EAAoB;YACjD,KAAc,EAAE,qBAAqB;CACf,CAAA,IAK3B,kBAAC,GAAD;EAAgC;EAAoB;YAClD,kBAAC,GAAD,EACE,QAAQ,EAAO,KAAK,OACX;GACL,IAAI,EAAM;GACV,OAAO,EAAM;GACb,MAAM,EAAM;GACZ,aAAa,EAAM;GACnB,OAAO,EAAM;GACb,SAAS,EAAM;GACf,MAAM,EAAM;EACd,EACD,EACF,CAAA;CACsB,CAAA;AAE7B"}
@@ -1 +1 @@
1
- {"version":3,"file":"BackofficeTokenUsageBreakdown.js","names":[],"sources":["../../../../../src/components/backoffice/detail/BackofficeTokenUsageBreakdown.tsx"],"sourcesContent":["import { type JSX, type ReactNode } from 'react';\nimport { MetricCard } from '@plumile/ui/components/dashboard/metric_card/MetricCard.js';\nimport { MetricTileGroup } from '@plumile/ui/components/dashboard/metric_tile_group/MetricTileGroup.js';\n\nexport type BackofficeTokenUsageBreakdownProps = {\n readonly title: string;\n readonly total?: ReactNode;\n readonly input?: ReactNode;\n readonly cachedInput?: ReactNode;\n readonly output?: ReactNode;\n readonly reasoning?: ReactNode;\n readonly labels: {\n readonly input: string;\n readonly cachedInput: string;\n readonly output: string;\n readonly reasoning: string;\n };\n};\n\nexport const BackofficeTokenUsageBreakdown = ({\n title,\n total,\n input,\n cachedInput,\n output,\n reasoning,\n labels,\n}: BackofficeTokenUsageBreakdownProps): JSX.Element => {\n const items = [\n { id: 'total', label: title, value: total },\n { id: 'input', label: labels.input, value: input },\n { id: 'cached-input', label: labels.cachedInput, value: cachedInput },\n { id: 'output', label: labels.output, value: output },\n { id: 'reasoning', label: labels.reasoning, value: reasoning },\n ].filter((item) => {\n return item.value != null;\n });\n\n if (items.length === 0) {\n return <></>;\n }\n\n return (\n <MetricTileGroup density=\"compact\" minColumn=\"180\">\n {items.map((item) => {\n return (\n <MetricCard\n key={item.id}\n label={item.label}\n value={item.value}\n density=\"compact\"\n tone=\"neutral\"\n />\n );\n })}\n </MetricTileGroup>\n );\n};\n\nexport default BackofficeTokenUsageBreakdown;\n"],"mappings":";;;;AAmBA,IAAa,KAAiC,EAC5C,UACA,UACA,UACA,gBACA,WACA,cACA,gBACqD;CACrD,IAAM,IAAQ;EACZ;GAAE,IAAI;GAAS,OAAO;GAAO,OAAO;EAAM;EAC1C;GAAE,IAAI;GAAS,OAAO,EAAO;GAAO,OAAO;EAAM;EACjD;GAAE,IAAI;GAAgB,OAAO,EAAO;GAAa,OAAO;EAAY;EACpE;GAAE,IAAI;GAAU,OAAO,EAAO;GAAQ,OAAO;EAAO;EACpD;GAAE,IAAI;GAAa,OAAO,EAAO;GAAW,OAAO;EAAU;CAC/D,EAAE,QAAQ,MACD,EAAK,SAAS,IACtB;CAMD,OAJI,EAAM,WAAW,IACZ,kBAAA,GAAA,CAAI,CAAA,IAIX,kBAAC,GAAD;EAAiB,SAAQ;EAAU,WAAU;YAC1C,EAAM,KAAK,MAER,kBAAC,GAAD;GAEE,OAAO,EAAK;GACZ,OAAO,EAAK;GACZ,SAAQ;GACR,MAAK;EACN,GALM,EAAK,EAKX,CAEJ;CACc,CAAA;AAErB"}
1
+ {"version":3,"file":"BackofficeTokenUsageBreakdown.js","names":[],"sources":["../../../../../src/components/backoffice/detail/BackofficeTokenUsageBreakdown.tsx"],"sourcesContent":["import { type JSX, type ReactNode } from 'react';\nimport { MetricCard } from '@plumile/ui/components/dashboard/metric_card/MetricCard.js';\nimport { MetricTileGroup } from '@plumile/ui/components/dashboard/metric_tile_group/MetricTileGroup.js';\n\nexport type BackofficeTokenUsageBreakdownProps = {\n readonly title: string;\n readonly total?: ReactNode;\n readonly input?: ReactNode;\n readonly cachedInput?: ReactNode;\n readonly output?: ReactNode;\n readonly reasoning?: ReactNode;\n readonly labels: {\n readonly input: string;\n readonly cachedInput: string;\n readonly output: string;\n readonly reasoning: string;\n };\n};\n\nexport const BackofficeTokenUsageBreakdown = ({\n title,\n total,\n input,\n cachedInput,\n output,\n reasoning,\n labels,\n}: BackofficeTokenUsageBreakdownProps): JSX.Element => {\n const items = [\n { id: 'total', label: title, value: total },\n { id: 'input', label: labels.input, value: input },\n { id: 'cached-input', label: labels.cachedInput, value: cachedInput },\n { id: 'output', label: labels.output, value: output },\n { id: 'reasoning', label: labels.reasoning, value: reasoning },\n ].filter((item) => {\n return item.value != null;\n });\n\n if (items.length === 0) {\n return <></>;\n }\n\n return (\n <MetricTileGroup density=\"compact\" minColumn=\"180\">\n {items.map((item) => {\n return (\n <MetricCard\n key={item.id}\n label={item.label}\n value={item.value}\n density=\"compact\"\n tone=\"neutral\"\n />\n );\n })}\n </MetricTileGroup>\n );\n};\n\nexport default BackofficeTokenUsageBreakdown;\n"],"mappings":";;;;AAmBA,IAAa,KAAiC,EAC5C,UACA,UACA,UACA,gBACA,WACA,cACA,gBACqD;CACrD,IAAM,IAAQ;EACZ;GAAE,IAAI;GAAS,OAAO;GAAO,OAAO;EAAM;EAC1C;GAAE,IAAI;GAAS,OAAO,EAAO;GAAO,OAAO;EAAM;EACjD;GAAE,IAAI;GAAgB,OAAO,EAAO;GAAa,OAAO;EAAY;EACpE;GAAE,IAAI;GAAU,OAAO,EAAO;GAAQ,OAAO;EAAO;EACpD;GAAE,IAAI;GAAa,OAAO,EAAO;GAAW,OAAO;EAAU;CAC/D,CAAC,CAAC,QAAQ,MACD,EAAK,SAAS,IACtB;CAMD,OAJI,EAAM,WAAW,IACZ,kBAAA,GAAA,CAAI,CAAA,IAIX,kBAAC,GAAD;EAAiB,SAAQ;EAAU,WAAU;YAC1C,EAAM,KAAK,MAER,kBAAC,GAAD;GAEE,OAAO,EAAK;GACZ,OAAO,EAAK;GACZ,SAAQ;GACR,MAAK;EACN,GALM,EAAK,EAKX,CAEJ;CACc,CAAA;AAErB"}
@@ -1,4 +1,3 @@
1
- /* empty css */
2
1
  /* empty css */
3
2
  //#region src/components/backoffice/detail/backofficeEntitySummaryHeader.css.ts
4
3
  var e = "txvbqbfpn txvbqbh4x txvbqbheb txvbqbwxb txvbqb2uc txvbqb1b8p txvbqb28o txvbqb2sj txvbqb1dz", t = "txvbqbfpn txvbqblt7 txvbqbel txvbqbheb txvbqbh7g", n = "txvbqbfpn txvbqbh4x txvbqbhdl txvbqbv7x", r = "txvbqbfpn txvbqbey txvbqbhdl txvbqbh7g", i = "txvbqbaz txvbqbo4z txvbqbhbf txvbqb1bgr txvbqbws9", a = "txvbqb9j txvbqb1bgt txvbqbws9", o = "txvbqbfpn txvbqbey txvbqblsh txvbqbhdl txvbqbh7g", s = "txvbqbfq0 txvbqbhdy txvbqbjcq", c = "txvbqbfpn txvbqbh4x txvbqbhd8 txvbqbv7x", l = "txvbqb96 txvbqb6a txvbqbmw7 txvbqb1bgt", u = "txvbqb9j txvbqb1bgr txvbqbws9";
@@ -1 +1 @@
1
- {"version":3,"file":"createBackofficeEntityLinkProps.js","names":[],"sources":["../../../../../src/components/backoffice/detail/createBackofficeEntityLinkProps.ts"],"sourcesContent":["import type { ReactNode } from 'react';\nimport type { BackofficeRuntimeResolvedListFacetConfig } from '@plumile/backoffice-core/types.js';\n\nexport type BackofficeEntityLinkValue = {\n readonly id: string;\n readonly label?: ReactNode;\n readonly filterValue?: string | null;\n};\n\nexport type CreateBackofficeEntityLinkPropsOptions<TNode> = {\n readonly entity: string;\n readonly node: TNode | null | undefined;\n readonly getId: (node: TNode) => string | null | undefined;\n readonly getLabel: (node: TNode) => ReactNode;\n readonly filterWhereKey?: string;\n readonly getFilterValue?: (node: TNode) => string | null | undefined;\n readonly filterPath?: readonly string[];\n readonly filterLabel?: string;\n readonly listConfig?: BackofficeRuntimeResolvedListFacetConfig;\n};\n\nexport type BackofficeResolvedEntityLinkProps = {\n readonly entity: string;\n readonly id: string;\n readonly label?: string;\n readonly filterWhereKey?: string;\n readonly filterValue?: string;\n readonly filterPath?: readonly string[];\n readonly filterLabel?: string;\n readonly listConfig?: BackofficeRuntimeResolvedListFacetConfig;\n};\n\nexport const createBackofficeEntityLinkProps = <TNode>({\n entity,\n node,\n getId,\n getLabel,\n filterWhereKey,\n getFilterValue,\n filterPath,\n filterLabel,\n listConfig,\n}: CreateBackofficeEntityLinkPropsOptions<TNode>): BackofficeResolvedEntityLinkProps | null => {\n if (node == null) {\n return null;\n }\n\n const id = getId(node)?.trim();\n if (id == null || id === '') {\n return null;\n }\n\n const label = getLabel(node);\n const filterValue = getFilterValue?.(node) ?? undefined;\n let resolvedLabel: string | undefined;\n if (typeof label === 'string') {\n resolvedLabel = label;\n }\n\n return {\n entity,\n id,\n label: resolvedLabel,\n filterWhereKey,\n filterValue: filterValue ?? undefined,\n filterPath,\n filterLabel,\n listConfig,\n };\n};\n"],"mappings":";AAgCA,IAAa,KAA0C,EACrD,WACA,SACA,UACA,aACA,mBACA,mBACA,eACA,gBACA,oBAC6F;CAC7F,IAAI,KAAQ,MACV,OAAO;CAGT,IAAM,IAAK,EAAM,CAAI,GAAG,KAAK;CAC7B,IAAI,KAAM,QAAQ,MAAO,IACvB,OAAO;CAGT,IAAM,IAAQ,EAAS,CAAI,GACrB,IAAc,IAAiB,CAAI,KAAK,KAAA,GAC1C;CAKJ,OAJI,OAAO,KAAU,aACnB,IAAgB,IAGX;EACL;EACA;EACA,OAAO;EACP;EACA,aAAa,KAAe,KAAA;EAC5B;EACA;EACA;CACF;AACF"}
1
+ {"version":3,"file":"createBackofficeEntityLinkProps.js","names":[],"sources":["../../../../../src/components/backoffice/detail/createBackofficeEntityLinkProps.ts"],"sourcesContent":["import type { ReactNode } from 'react';\nimport type { BackofficeRuntimeResolvedListFacetConfig } from '@plumile/backoffice-core/types.js';\n\nexport type BackofficeEntityLinkValue = {\n readonly id: string;\n readonly label?: ReactNode;\n readonly filterValue?: string | null;\n};\n\nexport type CreateBackofficeEntityLinkPropsOptions<TNode> = {\n readonly entity: string;\n readonly node: TNode | null | undefined;\n readonly getId: (node: TNode) => string | null | undefined;\n readonly getLabel: (node: TNode) => ReactNode;\n readonly filterWhereKey?: string;\n readonly getFilterValue?: (node: TNode) => string | null | undefined;\n readonly filterPath?: readonly string[];\n readonly filterLabel?: string;\n readonly listConfig?: BackofficeRuntimeResolvedListFacetConfig;\n};\n\nexport type BackofficeResolvedEntityLinkProps = {\n readonly entity: string;\n readonly id: string;\n readonly label?: string;\n readonly filterWhereKey?: string;\n readonly filterValue?: string;\n readonly filterPath?: readonly string[];\n readonly filterLabel?: string;\n readonly listConfig?: BackofficeRuntimeResolvedListFacetConfig;\n};\n\nexport const createBackofficeEntityLinkProps = <TNode>({\n entity,\n node,\n getId,\n getLabel,\n filterWhereKey,\n getFilterValue,\n filterPath,\n filterLabel,\n listConfig,\n}: CreateBackofficeEntityLinkPropsOptions<TNode>): BackofficeResolvedEntityLinkProps | null => {\n if (node == null) {\n return null;\n }\n\n const id = getId(node)?.trim();\n if (id == null || id === '') {\n return null;\n }\n\n const label = getLabel(node);\n const filterValue = getFilterValue?.(node) ?? undefined;\n let resolvedLabel: string | undefined;\n if (typeof label === 'string') {\n resolvedLabel = label;\n }\n\n return {\n entity,\n id,\n label: resolvedLabel,\n filterWhereKey,\n filterValue: filterValue ?? undefined,\n filterPath,\n filterLabel,\n listConfig,\n };\n};\n"],"mappings":";AAgCA,IAAa,KAA0C,EACrD,WACA,SACA,UACA,aACA,mBACA,mBACA,eACA,gBACA,oBAC6F;CAC7F,IAAI,KAAQ,MACV,OAAO;CAGT,IAAM,IAAK,EAAM,CAAI,CAAC,EAAE,KAAK;CAC7B,IAAI,KAAM,QAAQ,MAAO,IACvB,OAAO;CAGT,IAAM,IAAQ,EAAS,CAAI,GACrB,IAAc,IAAiB,CAAI,KAAK,KAAA,GAC1C;CAKJ,OAJI,OAAO,KAAU,aACnB,IAAgB,IAGX;EACL;EACA;EACA,OAAO;EACP;EACA,aAAa,KAAe,KAAA;EAC5B;EACA;EACA;CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"detailPayloadUtils.js","names":[],"sources":["../../../../../src/components/backoffice/detail/detailPayloadUtils.ts"],"sourcesContent":["/**\n * Formats a list of strings as a Markdown bullet list.\n */\nexport function formatListAsMarkdown(items: readonly string[]): string {\n if (items.length === 0) {\n return '';\n }\n return items\n .map((item) => {\n return `- ${item}`;\n })\n .join('\\n');\n}\n"],"mappings":";AAGA,SAAgB,EAAqB,GAAkC;CAIrE,OAHI,EAAM,WAAW,IACZ,KAEF,EACJ,KAAK,MACG,KAAK,GACb,EACA,KAAK,IAAI;AACd"}
1
+ {"version":3,"file":"detailPayloadUtils.js","names":[],"sources":["../../../../../src/components/backoffice/detail/detailPayloadUtils.ts"],"sourcesContent":["/**\n * Formats a list of strings as a Markdown bullet list.\n */\nexport function formatListAsMarkdown(items: readonly string[]): string {\n if (items.length === 0) {\n return '';\n }\n return items\n .map((item) => {\n return `- ${item}`;\n })\n .join('\\n');\n}\n"],"mappings":";AAGA,SAAgB,EAAqB,GAAkC;CAIrE,OAHI,EAAM,WAAW,IACZ,KAEF,EACJ,KAAK,MACG,KAAK,GACb,CAAC,CACD,KAAK,IAAI;AACd"}
@@ -1,4 +1,3 @@
1
- /* empty css */
2
1
  /* empty css */
3
2
  //#region src/components/backoffice/filters/backofficeFilterAction.css.ts
4
3
  var e = "hwnq700 txvbqbfqq txvbqbey txvbqbls4 txvbqbh6d txvbqb19h2 txvbqbjm4 txvbqbwvv txvbqb2tz txvbqb28o txvbqb2sj txvbqb1d9 txvbqb1b8t txvbqb1bi1 txvbqbv txvbqb3f txvbqb7h txvbqb75 txvbqb7t txvbqb1epb txvbqb1d07 txvbqb1du7 qbwcue0 txvbqb1fgi txvbqb1gaq", t = "txvbqb19fz txvbqbjl1";
@@ -1,4 +1,3 @@
1
- /* empty css */
2
1
  /* empty css */
3
2
  //#region src/components/backoffice/filters/deferredFilterSearchInput.css.ts
4
3
  var e = "txvbqb19us", t = "ds2thc0 txvbqbfqq txvbqbey txvbqbi8a", n = "ds2thc1 txvbqb6 txvbqbfqq txvbqbey txvbqbls4 txvbqb19h2 txvbqbjm4 txvbqbwvv txvbqb2tz txvbqb1tv txvbqb1b5h txvbqb1bgv txvbqbv txvbqb7h txvbqb75 txvbqb7t", r = "txvbqbfqq txvbqb19h2 txvbqbjm4 txvbqbh6d";
@@ -1 +1 @@
1
- {"version":3,"file":"assertValidBreadcrumb.js","names":[],"sources":["../../../../../../src/components/backoffice/layout/breadcrumb/assertValidBreadcrumb.ts"],"sourcesContent":["import type {\n BackofficeTopbarBreadcrumbItem,\n BreadcrumbContractErrorCode,\n} from './types.js';\n\nconst formatContractError = (\n code: BreadcrumbContractErrorCode,\n details?: string,\n): Error => {\n if (details == null) {\n return new Error(`Invalid breadcrumb contract: ${code}`);\n }\n return new Error(`Invalid breadcrumb contract: ${code} (${details})`);\n};\n\nconst getBreadcrumbItemId = (item: BackofficeTopbarBreadcrumbItem): string => {\n const { target } = item as {\n readonly target?: BackofficeTopbarBreadcrumbItem['target'];\n };\n if (target == null) {\n return '';\n }\n if (target.kind === 'dashboard') {\n return 'dashboard';\n }\n if (target.kind === 'entity-list') {\n return `${target.entityId}-list`;\n }\n if (target.kind === 'entity-detail') {\n return `${target.entityId}-entity-${target.id}`;\n }\n if (target.kind === 'entity-detail-page') {\n return `${target.entityId}-page-${target.id}-${target.pageId}`;\n }\n if (target.kind === 'tool') {\n return `tool-${target.toolId}`;\n }\n if (target.kind === 'hub') {\n return `hub-${target.hubId}`;\n }\n return `href-${target.href}`;\n};\n\nexport const assertValidBreadcrumb = (\n items: readonly BackofficeTopbarBreadcrumbItem[] | null | undefined,\n): readonly BackofficeTopbarBreadcrumbItem[] => {\n if (items == null) {\n throw formatContractError('MISSING_BREADCRUMB');\n }\n if (items.length === 0) {\n throw formatContractError('EMPTY_BREADCRUMB');\n }\n\n const seen = new Set<string>();\n items.forEach((item, index) => {\n const id = getBreadcrumbItemId(item);\n if (id.trim() === '') {\n throw formatContractError('INVALID_SEGMENT_ID', `index=${index}`);\n }\n\n if (seen.has(id)) {\n throw formatContractError('INVALID_SEGMENT_ID', `duplicate id=\"${id}\"`);\n }\n seen.add(id);\n });\n\n const last = items[items.length - 1];\n if (last?.kind !== 'current') {\n throw formatContractError('MISSING_CURRENT_SEGMENT');\n }\n\n return items;\n};\n"],"mappings":";AAKA,IAAM,KACJ,GACA,MAGa,MADT,KAAW,OACI,gCAAgC,MAElC,gCAAgC,EAAK,IAAI,EAAQ,EAFT,GAKrD,KAAuB,MAAiD;CAC5E,IAAM,EAAE,cAAW;CAwBnB,OArBI,KAAU,OACL,KAEL,EAAO,SAAS,cACX,cAEL,EAAO,SAAS,gBACX,GAAG,EAAO,SAAS,SAExB,EAAO,SAAS,kBACX,GAAG,EAAO,SAAS,UAAU,EAAO,OAEzC,EAAO,SAAS,uBACX,GAAG,EAAO,SAAS,QAAQ,EAAO,GAAG,GAAG,EAAO,WAEpD,EAAO,SAAS,SACX,QAAQ,EAAO,WAEpB,EAAO,SAAS,QACX,OAAO,EAAO,UAEhB,QAAQ,EAAO;AACxB,GAEa,KACX,MAC8C;CAC9C,IAAI,KAAS,MACX,MAAM,EAAoB,oBAAoB;CAEhD,IAAI,EAAM,WAAW,GACnB,MAAM,EAAoB,kBAAkB;CAG9C,IAAM,oBAAO,IAAI,IAAY;CAc7B,IAbA,EAAM,SAAS,GAAM,MAAU;EAC7B,IAAM,IAAK,EAAoB,CAAI;EACnC,IAAI,EAAG,KAAK,MAAM,IAChB,MAAM,EAAoB,sBAAsB,SAAS,GAAO;EAGlE,IAAI,EAAK,IAAI,CAAE,GACb,MAAM,EAAoB,sBAAsB,iBAAiB,EAAG,EAAE;EAExE,EAAK,IAAI,CAAE;CACb,CAAC,GAEY,EAAM,EAAM,SAAS,IACxB,SAAS,WACjB,MAAM,EAAoB,yBAAyB;CAGrD,OAAO;AACT"}
1
+ {"version":3,"file":"assertValidBreadcrumb.js","names":[],"sources":["../../../../../../src/components/backoffice/layout/breadcrumb/assertValidBreadcrumb.ts"],"sourcesContent":["import type {\n BackofficeTopbarBreadcrumbItem,\n BreadcrumbContractErrorCode,\n} from './types.js';\n\nconst formatContractError = (\n code: BreadcrumbContractErrorCode,\n details?: string,\n): Error => {\n if (details == null) {\n return new Error(`Invalid breadcrumb contract: ${code}`);\n }\n return new Error(`Invalid breadcrumb contract: ${code} (${details})`);\n};\n\nconst getBreadcrumbItemId = (item: BackofficeTopbarBreadcrumbItem): string => {\n const { target } = item as {\n readonly target?: BackofficeTopbarBreadcrumbItem['target'];\n };\n if (target == null) {\n return '';\n }\n if (target.kind === 'dashboard') {\n return 'dashboard';\n }\n if (target.kind === 'entity-list') {\n return `${target.entityId}-list`;\n }\n if (target.kind === 'entity-detail') {\n return `${target.entityId}-entity-${target.id}`;\n }\n if (target.kind === 'entity-detail-page') {\n return `${target.entityId}-page-${target.id}-${target.pageId}`;\n }\n if (target.kind === 'tool') {\n return `tool-${target.toolId}`;\n }\n if (target.kind === 'hub') {\n return `hub-${target.hubId}`;\n }\n return `href-${target.href}`;\n};\n\nexport const assertValidBreadcrumb = (\n items: readonly BackofficeTopbarBreadcrumbItem[] | null | undefined,\n): readonly BackofficeTopbarBreadcrumbItem[] => {\n if (items == null) {\n throw formatContractError('MISSING_BREADCRUMB');\n }\n if (items.length === 0) {\n throw formatContractError('EMPTY_BREADCRUMB');\n }\n\n const seen = new Set<string>();\n items.forEach((item, index) => {\n const id = getBreadcrumbItemId(item);\n if (id.trim() === '') {\n throw formatContractError('INVALID_SEGMENT_ID', `index=${index}`);\n }\n\n if (seen.has(id)) {\n throw formatContractError('INVALID_SEGMENT_ID', `duplicate id=\"${id}\"`);\n }\n seen.add(id);\n });\n\n const last = items[items.length - 1];\n if (last?.kind !== 'current') {\n throw formatContractError('MISSING_CURRENT_SEGMENT');\n }\n\n return items;\n};\n"],"mappings":";AAKA,IAAM,KACJ,GACA,MAGa,MADT,KAAW,OACI,gCAAgC,MAElC,gCAAgC,EAAK,IAAI,EAAQ,EAFT,GAKrD,KAAuB,MAAiD;CAC5E,IAAM,EAAE,cAAW;CAwBnB,OArBI,KAAU,OACL,KAEL,EAAO,SAAS,cACX,cAEL,EAAO,SAAS,gBACX,GAAG,EAAO,SAAS,SAExB,EAAO,SAAS,kBACX,GAAG,EAAO,SAAS,UAAU,EAAO,OAEzC,EAAO,SAAS,uBACX,GAAG,EAAO,SAAS,QAAQ,EAAO,GAAG,GAAG,EAAO,WAEpD,EAAO,SAAS,SACX,QAAQ,EAAO,WAEpB,EAAO,SAAS,QACX,OAAO,EAAO,UAEhB,QAAQ,EAAO;AACxB,GAEa,KACX,MAC8C;CAC9C,IAAI,KAAS,MACX,MAAM,EAAoB,oBAAoB;CAEhD,IAAI,EAAM,WAAW,GACnB,MAAM,EAAoB,kBAAkB;CAG9C,IAAM,oBAAO,IAAI,IAAY;CAc7B,IAbA,EAAM,SAAS,GAAM,MAAU;EAC7B,IAAM,IAAK,EAAoB,CAAI;EACnC,IAAI,EAAG,KAAK,MAAM,IAChB,MAAM,EAAoB,sBAAsB,SAAS,GAAO;EAGlE,IAAI,EAAK,IAAI,CAAE,GACb,MAAM,EAAoB,sBAAsB,iBAAiB,EAAG,EAAE;EAExE,EAAK,IAAI,CAAE;CACb,CAAC,GAEY,EAAM,EAAM,SAAS,EAC9B,EAAM,SAAS,WACjB,MAAM,EAAoB,yBAAyB;CAGrD,OAAO;AACT"}