@burdenoff/microfe-auth 2026.1031.1 → 2026.1101.2

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 (135) hide show
  1. package/dist/auth/AuthRoot.js +14 -11
  2. package/dist/auth/AuthRoot.js.map +1 -1
  3. package/dist/auth/AuthRoutes.js +1 -2
  4. package/dist/auth/AuthRoutes.js.map +1 -1
  5. package/dist/auth/components/AuthIcons.js +1 -2
  6. package/dist/auth/components/AuthIcons.js.map +1 -1
  7. package/dist/auth/components/AuthLayout.js +1 -2
  8. package/dist/auth/components/AuthLayout.js.map +1 -1
  9. package/dist/auth/components/CaptchaV2.js +9 -10
  10. package/dist/auth/components/CaptchaV2.js.map +1 -1
  11. package/dist/auth/components/ConfirmActionDialog.js +1 -2
  12. package/dist/auth/components/ConfirmActionDialog.js.map +1 -1
  13. package/dist/auth/components/profile/UserProfileMenu.js +7 -6
  14. package/dist/auth/components/profile/UserProfileMenu.js.map +1 -1
  15. package/dist/auth/components/workspace/WorkspaceSelector.js +1 -2
  16. package/dist/auth/components/workspace/WorkspaceSelector.js.map +1 -1
  17. package/dist/auth/constants/permissions.js +1 -2
  18. package/dist/auth/constants/permissions.js.map +1 -1
  19. package/dist/auth/constants/sessionKeys.js +1 -2
  20. package/dist/auth/constants/sessionKeys.js.map +1 -1
  21. package/dist/auth/hooks/contextDeepLink.js +1 -2
  22. package/dist/auth/hooks/contextDeepLink.js.map +1 -1
  23. package/dist/auth/hooks/index.js +12 -11
  24. package/dist/auth/hooks/useAccountOperations.js +1 -2
  25. package/dist/auth/hooks/useAccountOperations.js.map +1 -1
  26. package/dist/auth/hooks/useActiveContextBootstrapMirror.js +1 -2
  27. package/dist/auth/hooks/useActiveContextBootstrapMirror.js.map +1 -1
  28. package/dist/auth/hooks/useAuthOperations.js +37 -37
  29. package/dist/auth/hooks/useAuthOperations.js.map +1 -1
  30. package/dist/auth/hooks/useAuthPermissions.js +1 -2
  31. package/dist/auth/hooks/useAuthPermissions.js.map +1 -1
  32. package/dist/auth/hooks/useCaptchaV3.js +1 -2
  33. package/dist/auth/hooks/useCaptchaV3.js.map +1 -1
  34. package/dist/auth/hooks/useContextSwitcherData.js +86 -79
  35. package/dist/auth/hooks/useContextSwitcherData.js.map +1 -1
  36. package/dist/auth/hooks/useMfaOperations.js +4 -5
  37. package/dist/auth/hooks/useMfaOperations.js.map +1 -1
  38. package/dist/auth/hooks/useMyWorkspaces.js +4 -5
  39. package/dist/auth/hooks/useMyWorkspaces.js.map +1 -1
  40. package/dist/auth/hooks/useOAuthOperations.js +11 -12
  41. package/dist/auth/hooks/useOAuthOperations.js.map +1 -1
  42. package/dist/auth/hooks/usePasswordOperations.js +1 -2
  43. package/dist/auth/hooks/usePasswordOperations.js.map +1 -1
  44. package/dist/auth/hooks/useSessionOperations.js +3 -4
  45. package/dist/auth/hooks/useSessionOperations.js.map +1 -1
  46. package/dist/auth/hooks/useSignupCaptcha.js +1 -2
  47. package/dist/auth/hooks/useSignupCaptcha.js.map +1 -1
  48. package/dist/auth/hooks/useSsoOperations.js +80 -81
  49. package/dist/auth/hooks/useSsoOperations.js.map +1 -1
  50. package/dist/auth/hooks/useTokenRefresh.js +8 -12
  51. package/dist/auth/hooks/useTokenRefresh.js.map +1 -1
  52. package/dist/auth/hooks/useUserOperations.js +1 -2
  53. package/dist/auth/hooks/useUserOperations.js.map +1 -1
  54. package/dist/auth/hooks/useWorkspaceOperations.js +1 -2
  55. package/dist/auth/hooks/useWorkspaceOperations.js.map +1 -1
  56. package/dist/auth/hooks/useWorkspaceToken.js +1 -2
  57. package/dist/auth/hooks/useWorkspaceToken.js.map +1 -1
  58. package/dist/auth/index.js +19 -18
  59. package/dist/auth/pages/AuditLogPage.js +21 -22
  60. package/dist/auth/pages/AuditLogPage.js.map +1 -1
  61. package/dist/auth/pages/EmailVerifyPage.js +15 -17
  62. package/dist/auth/pages/EmailVerifyPage.js.map +1 -1
  63. package/dist/auth/pages/LoginPage.js +89 -105
  64. package/dist/auth/pages/LoginPage.js.map +1 -1
  65. package/dist/auth/pages/MagicLinkPage.js +30 -31
  66. package/dist/auth/pages/MagicLinkPage.js.map +1 -1
  67. package/dist/auth/pages/MfaSetupPage.js +18 -20
  68. package/dist/auth/pages/MfaSetupPage.js.map +1 -1
  69. package/dist/auth/pages/MfaVerifyPage.js +31 -35
  70. package/dist/auth/pages/MfaVerifyPage.js.map +1 -1
  71. package/dist/auth/pages/OAuthCallbackPage.js +4 -11
  72. package/dist/auth/pages/OAuthCallbackPage.js.map +1 -1
  73. package/dist/auth/pages/PasswordResetPage.js +20 -23
  74. package/dist/auth/pages/PasswordResetPage.js.map +1 -1
  75. package/dist/auth/pages/ReactivatePage.js +14 -18
  76. package/dist/auth/pages/ReactivatePage.js.map +1 -1
  77. package/dist/auth/pages/RegisterPage.js +180 -181
  78. package/dist/auth/pages/RegisterPage.js.map +1 -1
  79. package/dist/auth/pages/SSOClearPage.js +9 -10
  80. package/dist/auth/pages/SSOClearPage.js.map +1 -1
  81. package/dist/auth/pages/SSODepositPage.js +9 -13
  82. package/dist/auth/pages/SSODepositPage.js.map +1 -1
  83. package/dist/auth/pages/SSORelayCallbackPage.js +3 -7
  84. package/dist/auth/pages/SSORelayCallbackPage.js.map +1 -1
  85. package/dist/auth/pages/SSORelayPage.js +11 -10
  86. package/dist/auth/pages/SSORelayPage.js.map +1 -1
  87. package/dist/auth/pages/SecurityPage.js +19 -21
  88. package/dist/auth/pages/SecurityPage.js.map +1 -1
  89. package/dist/auth/pages/SessionBridgePage.js +9 -16
  90. package/dist/auth/pages/SessionBridgePage.js.map +1 -1
  91. package/dist/auth/pages/SessionsPage.js +17 -18
  92. package/dist/auth/pages/SessionsPage.js.map +1 -1
  93. package/dist/auth/pages/SsoCallbackPage.js +17 -22
  94. package/dist/auth/pages/SsoCallbackPage.js.map +1 -1
  95. package/dist/auth/pages/SsoConfigPage.js +69 -69
  96. package/dist/auth/pages/SsoConfigPage.js.map +1 -1
  97. package/dist/auth/pages/SsoLoginPage.js +9 -10
  98. package/dist/auth/pages/SsoLoginPage.js.map +1 -1
  99. package/dist/auth/pages/UserDetailsPage.js +16 -23
  100. package/dist/auth/pages/UserDetailsPage.js.map +1 -1
  101. package/dist/auth/pages/UserProfilePage.js +140 -141
  102. package/dist/auth/pages/UserProfilePage.js.map +1 -1
  103. package/dist/auth/pages/UsersPage.js +446 -453
  104. package/dist/auth/pages/UsersPage.js.map +1 -1
  105. package/dist/auth/providers/AuthProvider.js +1 -2
  106. package/dist/auth/providers/AuthProvider.js.map +1 -1
  107. package/dist/auth/providers/CaptchaProvider.js +1 -2
  108. package/dist/auth/providers/CaptchaProvider.js.map +1 -1
  109. package/dist/auth/store/authStore.js +5 -12
  110. package/dist/auth/store/authStore.js.map +1 -1
  111. package/dist/auth/utils/captchaConfig.js +5 -8
  112. package/dist/auth/utils/captchaConfig.js.map +1 -1
  113. package/dist/auth/utils/formatters.js +1 -2
  114. package/dist/auth/utils/formatters.js.map +1 -1
  115. package/dist/auth/utils/gatewayBaseUrls.js +1 -2
  116. package/dist/auth/utils/gatewayBaseUrls.js.map +1 -1
  117. package/dist/auth/utils/legalUrls.js +2 -3
  118. package/dist/auth/utils/legalUrls.js.map +1 -1
  119. package/dist/auth/utils/redirect.js +1 -2
  120. package/dist/auth/utils/redirect.js.map +1 -1
  121. package/dist/auth/utils/ssoOrigins.js +2 -3
  122. package/dist/auth/utils/ssoOrigins.js.map +1 -1
  123. package/dist/auth/utils/validation.js +34 -36
  124. package/dist/auth/utils/validation.js.map +1 -1
  125. package/dist/generated/global-operations.js +2 -3
  126. package/dist/generated/global-operations.js.map +1 -1
  127. package/dist/generated/wspace-operations.js +2 -3
  128. package/dist/generated/wspace-operations.js.map +1 -1
  129. package/dist/microfe-auth.css +1 -1
  130. package/dist/shared/hooks/useTr.js +1 -2
  131. package/dist/shared/hooks/useTr.js.map +1 -1
  132. package/dist/translations/en.js +1 -2
  133. package/dist/translations/en.js.map +1 -1
  134. package/dist/utils/nativeBridge.js +1 -2
  135. package/package.json +7 -3
@@ -1 +1 @@
1
- {"version":3,"file":"SsoLoginPage.js","names":[],"sources":["../../../src/auth/pages/SsoLoginPage.tsx"],"sourcesContent":["import { useState, useEffect } from \"react\";\nimport { useTr } from \"../../shared/hooks/useTr\";\nimport { useNavigate, useSearchParams } from \"react-router\";\nimport { useSsoOperations } from \"../hooks/useSsoOperations\";\nimport { AuthLayout } from \"../components/AuthLayout\";\nimport { Button } from \"@burdenoff/fe-libs/button\";\nimport { PagePurpose } from \"@burdenoff/fe-libs/ui\";\nimport { EmailIcon, SpinnerIcon, AlertIcon } from \"../components/AuthIcons\";\n\ntype SsoMethod = \"saml\" | \"oidc\";\n\n/**\n * SSO Login Page\n * Allows users to enter their email/company domain to initiate SSO login\n * Supports both SAML 2.0 and OIDC authentication methods\n */\nexport function SsoLoginPage() {\n const tr = useTr();\n\n const navigate = useNavigate();\n const [searchParams] = useSearchParams();\n const tenantFromUrl = searchParams.get(\"tenant\");\n const methodFromUrl = searchParams.get(\"method\") as SsoMethod | null;\n const {\n initiateSsoLogin,\n initiateExternalOidcLogin,\n isGeneratingRequest,\n isGeneratingOidcUrl,\n } = useSsoOperations(tenantFromUrl ?? undefined);\n\n const [email, setEmail] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n const [ssoMethod, setSsoMethod] = useState<SsoMethod>(\"oidc\");\n const [isInitiating, setIsInitiating] = useState(false);\n const [isSsoNotConfigured, setIsSsoNotConfigured] = useState(false);\n\n // Set method from URL if provided\n useEffect(() => {\n if (methodFromUrl === \"saml\" || methodFromUrl === \"oidc\") {\n setSsoMethod(methodFromUrl);\n }\n }, [methodFromUrl]);\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n setIsSsoNotConfigured(false);\n setIsInitiating(true);\n\n try {\n // Extract domain from email\n const domain = email.split(\"@\")[1];\n if (!domain) {\n setError(\n tr(\n \"auth.ssoLogin.invalidEmailDomain\",\n \"Please enter a valid email address with your company domain\",\n ),\n );\n setIsInitiating(false);\n return;\n }\n\n // In a real app, you would look up the tenant by domain\n // For now, we'll use the tenant from URL or the domain itself\n const tenantId = tenantFromUrl || domain;\n\n // Store email for post-login\n sessionStorage.setItem(\"sso_email\", email);\n\n if (ssoMethod === \"oidc\") {\n // External OIDC flow (Azure AD, Google, etc.)\n await initiateExternalOidcLogin(tenantId, \"/dashboard\");\n } else {\n await initiateSsoLogin(tenantId, \"/dashboard\");\n }\n } catch (err) {\n console.error(\"SSO login failed:\", err);\n const errorMessage =\n err instanceof Error\n ? err.message\n : \"Failed to initiate SSO login. Please check your configuration.\";\n\n // Check if error is about SSO not being configured\n if (\n errorMessage.toLowerCase().includes(\"sso\") &&\n (errorMessage.toLowerCase().includes(\"not configured\") ||\n errorMessage.toLowerCase().includes(\"not set up\"))\n ) {\n setIsSsoNotConfigured(true);\n setError(\n tr(\n \"auth.ssoLogin.ssoNotConfigured\",\n \"SSO is not configured for your organization. Please use regular email/password login or contact your administrator to set up SSO.\",\n ),\n );\n } else {\n setError(errorMessage);\n }\n\n setIsInitiating(false);\n }\n };\n\n const isSubmitting =\n isGeneratingRequest || isGeneratingOidcUrl || isInitiating;\n\n return (\n <AuthLayout\n title={tr(\"auth.ssoLogin.title\", \"Enterprise SSO\")}\n subtitle={tr(\n \"auth.ssoLogin.subtitle\",\n \"Sign in with your company's identity provider\",\n )}\n >\n <PagePurpose className=\"mb-5\">\n {tr(\n \"auth.ssoLogin.purpose\",\n \"Sign in through your company's identity provider (Okta, Azure AD, Google, and more). Enter your work email and we'll route you to your organization's OIDC or SAML login.\",\n )}\n </PagePurpose>\n <form onSubmit={handleSubmit} className=\"space-y-5\">\n {/* Error Alert */}\n {error && (\n <div\n className={`flex items-start gap-3 p-4 rounded-xl border ${\n isSsoNotConfigured\n ? \"bg-status-warning-bg-subtle border-status-warning-border\"\n : \"bg-status-error-bg-subtle border-status-error-border\"\n }`}\n >\n <AlertIcon\n className={`size-5 flex-shrink-0 mt-0.5 ${\n isSsoNotConfigured\n ? \"text-status-warning-text\"\n : \"text-status-error-text\"\n }`}\n />\n <div className=\"flex-1\">\n <p\n className={`text-sm ${\n isSsoNotConfigured\n ? \"text-status-warning-text\"\n : \"text-status-error-text\"\n }`}\n >\n {error}\n </p>\n {isSsoNotConfigured && (\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/login\", { state: { email } })}\n className=\"mt-3 inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-status-warning-bg-subtle hover:bg-status-warning-bg-subtle text-status-warning-text text-sm font-semibold transition-all\"\n >\n <svg\n className=\"size-4\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1\"\n />\n </svg>\n {tr(\n \"auth.ssoLogin.useEmailLogin\",\n \"Use Email/Password Login Instead\",\n )}\n </button>\n )}\n </div>\n </div>\n )}\n\n {/* SSO Method Toggle */}\n <div className=\"space-y-2\">\n <label className=\"block text-sm font-medium text-[var(--color-text-primary)]\">\n {tr(\"auth.ssoLogin.authMethod\", \"Authentication Method\")}\n </label>\n <div className=\"grid grid-cols-2 gap-3\">\n <button\n type=\"button\"\n onClick={() => setSsoMethod(\"oidc\")}\n className={`p-4 rounded-xl border-2 transition-all ${\n ssoMethod === \"oidc\"\n ? \"border-action-primary-bg bg-status-info-bg-subtle\"\n : \"border-border-subtle hover:border-border-default\"\n }`}\n >\n <div className=\"flex items-center gap-3\">\n <div\n className={`size-10 rounded-lg flex items-center justify-center ${\n ssoMethod === \"oidc\"\n ? \"bg-status-info-bg-subtle0\"\n : \"bg-bg-sunken\"\n }`}\n >\n <svg\n className={`size-5 ${ssoMethod === \"oidc\" ? \"text-white\" : \"text-text-secondary\"}`}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9\"\n />\n </svg>\n </div>\n <div className=\"text-left\">\n <div\n className={`font-medium ${ssoMethod === \"oidc\" ? \"text-text-linkHover\" : \"text-text-primary\"}`}\n >\n OpenID Connect\n </div>\n <div className=\"text-xs text-text-secondary\">\n {tr(\n \"auth.ssoLogin.modernRecommended\",\n \"Modern & Recommended\",\n )}\n </div>\n </div>\n </div>\n </button>\n\n <button\n type=\"button\"\n onClick={() => setSsoMethod(\"saml\")}\n className={`p-4 rounded-xl border-2 transition-all ${\n ssoMethod === \"saml\"\n ? \"border-accent-purple bg-accent-purple-subtle\"\n : \"border-border-subtle hover:border-border-default\"\n }`}\n >\n <div className=\"flex items-center gap-3\">\n <div\n className={`size-10 rounded-lg flex items-center justify-center ${\n ssoMethod === \"saml\"\n ? \"bg-accent-purple-subtle0\"\n : \"bg-bg-sunken\"\n }`}\n >\n <svg\n className={`size-5 ${ssoMethod === \"saml\" ? \"text-white\" : \"text-text-secondary\"}`}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z\"\n />\n </svg>\n </div>\n <div className=\"text-left\">\n <div\n className={`font-medium ${ssoMethod === \"saml\" ? \"text-accent-purple\" : \"text-text-primary\"}`}\n >\n SAML 2.0\n </div>\n <div className=\"text-xs text-text-secondary\">\n {tr(\"auth.ssoLogin.enterpriseLegacy\", \"Enterprise Legacy\")}\n </div>\n </div>\n </div>\n </button>\n </div>\n </div>\n\n {/* Info Banner */}\n <div\n className={`p-4 rounded-xl border ${\n ssoMethod === \"oidc\"\n ? \"bg-status-info-bg-subtle border-status-info-border\"\n : \"bg-accent-purple-subtle border-accent-purple-subtle\"\n }`}\n >\n <p\n className={`text-sm ${\n ssoMethod === \"oidc\"\n ? \"text-status-info-text\"\n : \"text-accent-purple\"\n }`}\n >\n {ssoMethod === \"oidc\"\n ? tr(\n \"auth.sso.oidcInfoBannerPkce\",\n \"Sign in using OpenID Connect with your identity provider. Supports modern SSO with PKCE security.\",\n )\n : tr(\n \"auth.sso.samlInfoBanner\",\n \"Sign in using SAML 2.0 with your enterprise identity provider (Okta, Azure AD, etc.).\",\n )}\n </p>\n </div>\n\n {/* Email Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"email\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.ssoLogin.workEmail\", \"Work Email Address\")}\n </label>\n <div className=\"relative\">\n <div className=\"absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none\">\n <EmailIcon className=\"size-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"email\"\n type=\"email\"\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.ssoLogin.workEmail\", \"Work Email Address\")}\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n autoComplete=\"email\"\n className=\"block w-full pl-[3.5rem] pr-4 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all\"\n placeholder={tr(\n \"auth.ssoLogin.emailPlaceholder\",\n \"you@company.com\",\n )}\n />\n </div>\n <p className=\"text-xs text-[var(--color-text-tertiary)]\">\n {tr(\n \"auth.ssoLogin.useCompanyEmail\",\n \"Use your company email (e.g., @yourcompany.com)\",\n )}\n </p>\n </div>\n\n {/* Submit Button */}\n <Button\n type=\"submit\"\n disabled={isSubmitting || !email}\n variant=\"default\"\n size=\"lg\"\n className={`w-full font-semibold ${\n ssoMethod === \"saml\"\n ? \"bg-accent-purple text-white hover:bg-accent-purple hover:opacity-90\"\n : \"\"\n }`}\n >\n {isSubmitting ? (\n <>\n <SpinnerIcon className=\"size-5 animate-spin\" />\n <span>{tr(\"auth.sso.redirecting\", \"Redirecting...\")}</span>\n </>\n ) : (\n <>\n <svg\n className=\"size-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1\"\n />\n </svg>\n <span>\n {ssoMethod === \"oidc\"\n ? tr(\"auth.sso.continueWithOidc\", \"Continue with OIDC\")\n : tr(\"auth.sso.continueWithSaml\", \"Continue with SAML\")}\n </span>\n </>\n )}\n </Button>\n </form>\n\n {/* Back to Regular Login */}\n <div className=\"mt-6 text-center\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/login\")}\n className=\"text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] transition-colors\"\n >\n <span className=\"font-semibold text-text-link hover:text-text-linkHover\">\n {tr(\"auth.ssoLogin.backToLogin\", \"Back to regular login\")}\n </span>\n </button>\n </div>\n\n {/* Reactivate Account Link */}\n <div className=\"mt-4 text-center\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/reactivate\")}\n className=\"text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] transition-colors\"\n >\n {tr(\n \"auth.ssoLogin.deactivatedAccount\",\n \"Have a deactivated account?\",\n )}{\" \"}\n <span className=\"font-semibold text-status-success-text hover:text-status-success-text\">\n {tr(\"auth.ssoLogin.reactivateIt\", \"Reactivate it\")}\n </span>\n </button>\n </div>\n\n {/* Help Text */}\n <div className=\"mt-6 p-4 rounded-xl bg-bg-canvas/50 border border-border-subtle\">\n <h4 className=\"text-sm font-medium text-[var(--color-text-primary)] mb-2\">\n {tr(\"auth.ssoLogin.noSso\", \"Don't see your company's SSO?\")}\n </h4>\n <p className=\"text-xs text-[var(--color-text-secondary)]\">\n {tr(\n \"auth.ssoLogin.contactIT\",\n \"Contact your IT administrator to set up SSO for your organization. They'll need to configure your identity provider with our service.\",\n )}\n </p>\n </div>\n </AuthLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SsoLoginPage.js","names":["_Fragment","SsoLoginPage","tr","useTr","navigate","useNavigate","searchParams","useSearchParams","tenantFromUrl","methodFromUrl","useSsoOperations","email","setEmail","useState","error","setError","ssoMethod","setSsoMethod","isInitiating","setIsInitiating","isSsoNotConfigured","setIsSsoNotConfigured","handleSubmit","domain","tenantId","initiateExternalOidcLogin","initiateSsoLogin","err","errorMessage","isSubmitting","isGeneratingRequest","isGeneratingOidcUrl","AuthLayout","PagePurpose","AlertIcon","EmailIcon","Button","Fragment","SpinnerIcon"],"sources":["../../../src/auth/pages/SsoLoginPage.tsx"],"sourcesContent":["import { useState, useEffect } from \"react\";\nimport { useTr } from \"../../shared/hooks/useTr\";\nimport { useNavigate, useSearchParams } from \"react-router\";\nimport { useSsoOperations } from \"../hooks/useSsoOperations\";\nimport { AuthLayout } from \"../components/AuthLayout\";\nimport { Button } from \"@burdenoff/fe-libs/button\";\nimport { PagePurpose } from \"@burdenoff/fe-libs/ui\";\nimport { EmailIcon, SpinnerIcon, AlertIcon } from \"../components/AuthIcons\";\n\ntype SsoMethod = \"saml\" | \"oidc\";\n\n/**\n * SSO Login Page\n * Allows users to enter their email/company domain to initiate SSO login\n * Supports both SAML 2.0 and OIDC authentication methods\n */\nexport function SsoLoginPage() {\n const tr = useTr();\n\n const navigate = useNavigate();\n const [searchParams] = useSearchParams();\n const tenantFromUrl = searchParams.get(\"tenant\");\n const methodFromUrl = searchParams.get(\"method\") as SsoMethod | null;\n const {\n initiateSsoLogin,\n initiateExternalOidcLogin,\n isGeneratingRequest,\n isGeneratingOidcUrl,\n } = useSsoOperations(tenantFromUrl ?? undefined);\n\n const [email, setEmail] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n const [ssoMethod, setSsoMethod] = useState<SsoMethod>(\"oidc\");\n const [isInitiating, setIsInitiating] = useState(false);\n const [isSsoNotConfigured, setIsSsoNotConfigured] = useState(false);\n\n // Set method from URL if provided\n useEffect(() => {\n if (methodFromUrl === \"saml\" || methodFromUrl === \"oidc\") {\n setSsoMethod(methodFromUrl);\n }\n }, [methodFromUrl]);\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n setIsSsoNotConfigured(false);\n setIsInitiating(true);\n\n try {\n // Extract domain from email\n const domain = email.split(\"@\")[1];\n if (!domain) {\n setError(\n tr(\n \"auth.ssoLogin.invalidEmailDomain\",\n \"Please enter a valid email address with your company domain\",\n ),\n );\n setIsInitiating(false);\n return;\n }\n\n // In a real app, you would look up the tenant by domain\n // For now, we'll use the tenant from URL or the domain itself\n const tenantId = tenantFromUrl || domain;\n\n // Store email for post-login\n sessionStorage.setItem(\"sso_email\", email);\n\n if (ssoMethod === \"oidc\") {\n // External OIDC flow (Azure AD, Google, etc.)\n await initiateExternalOidcLogin(tenantId, \"/dashboard\");\n } else {\n await initiateSsoLogin(tenantId, \"/dashboard\");\n }\n } catch (err) {\n console.error(\"SSO login failed:\", err);\n const errorMessage =\n err instanceof Error\n ? err.message\n : \"Failed to initiate SSO login. Please check your configuration.\";\n\n // Check if error is about SSO not being configured\n if (\n errorMessage.toLowerCase().includes(\"sso\") &&\n (errorMessage.toLowerCase().includes(\"not configured\") ||\n errorMessage.toLowerCase().includes(\"not set up\"))\n ) {\n setIsSsoNotConfigured(true);\n setError(\n tr(\n \"auth.ssoLogin.ssoNotConfigured\",\n \"SSO is not configured for your organization. Please use regular email/password login or contact your administrator to set up SSO.\",\n ),\n );\n } else {\n setError(errorMessage);\n }\n\n setIsInitiating(false);\n }\n };\n\n const isSubmitting =\n isGeneratingRequest || isGeneratingOidcUrl || isInitiating;\n\n return (\n <AuthLayout\n title={tr(\"auth.ssoLogin.title\", \"Enterprise SSO\")}\n subtitle={tr(\n \"auth.ssoLogin.subtitle\",\n \"Sign in with your company's identity provider\",\n )}\n >\n <PagePurpose className=\"mb-5\">\n {tr(\n \"auth.ssoLogin.purpose\",\n \"Sign in through your company's identity provider (Okta, Azure AD, Google, and more). Enter your work email and we'll route you to your organization's OIDC or SAML login.\",\n )}\n </PagePurpose>\n <form onSubmit={handleSubmit} className=\"space-y-5\">\n {/* Error Alert */}\n {error && (\n <div\n className={`flex items-start gap-3 p-4 rounded-xl border ${\n isSsoNotConfigured\n ? \"bg-status-warning-bg-subtle border-status-warning-border\"\n : \"bg-status-error-bg-subtle border-status-error-border\"\n }`}\n >\n <AlertIcon\n className={`size-5 flex-shrink-0 mt-0.5 ${\n isSsoNotConfigured\n ? \"text-status-warning-text\"\n : \"text-status-error-text\"\n }`}\n />\n <div className=\"flex-1\">\n <p\n className={`text-sm ${\n isSsoNotConfigured\n ? \"text-status-warning-text\"\n : \"text-status-error-text\"\n }`}\n >\n {error}\n </p>\n {isSsoNotConfigured && (\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/login\", { state: { email } })}\n className=\"mt-3 inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-status-warning-bg-subtle hover:bg-status-warning-bg-subtle text-status-warning-text text-sm font-semibold transition-all\"\n >\n <svg\n className=\"size-4\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1\"\n />\n </svg>\n {tr(\n \"auth.ssoLogin.useEmailLogin\",\n \"Use Email/Password Login Instead\",\n )}\n </button>\n )}\n </div>\n </div>\n )}\n\n {/* SSO Method Toggle */}\n <div className=\"space-y-2\">\n <label className=\"block text-sm font-medium text-[var(--color-text-primary)]\">\n {tr(\"auth.ssoLogin.authMethod\", \"Authentication Method\")}\n </label>\n <div className=\"grid grid-cols-2 gap-3\">\n <button\n type=\"button\"\n onClick={() => setSsoMethod(\"oidc\")}\n className={`p-4 rounded-xl border-2 transition-all ${\n ssoMethod === \"oidc\"\n ? \"border-action-primary-bg bg-status-info-bg-subtle\"\n : \"border-border-subtle hover:border-border-default\"\n }`}\n >\n <div className=\"flex items-center gap-3\">\n <div\n className={`size-10 rounded-lg flex items-center justify-center ${\n ssoMethod === \"oidc\"\n ? \"bg-status-info-bg-subtle0\"\n : \"bg-bg-sunken\"\n }`}\n >\n <svg\n className={`size-5 ${ssoMethod === \"oidc\" ? \"text-white\" : \"text-text-secondary\"}`}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9\"\n />\n </svg>\n </div>\n <div className=\"text-left\">\n <div\n className={`font-medium ${ssoMethod === \"oidc\" ? \"text-text-linkHover\" : \"text-text-primary\"}`}\n >\n OpenID Connect\n </div>\n <div className=\"text-xs text-text-secondary\">\n {tr(\n \"auth.ssoLogin.modernRecommended\",\n \"Modern & Recommended\",\n )}\n </div>\n </div>\n </div>\n </button>\n\n <button\n type=\"button\"\n onClick={() => setSsoMethod(\"saml\")}\n className={`p-4 rounded-xl border-2 transition-all ${\n ssoMethod === \"saml\"\n ? \"border-accent-purple bg-accent-purple-subtle\"\n : \"border-border-subtle hover:border-border-default\"\n }`}\n >\n <div className=\"flex items-center gap-3\">\n <div\n className={`size-10 rounded-lg flex items-center justify-center ${\n ssoMethod === \"saml\"\n ? \"bg-accent-purple-subtle0\"\n : \"bg-bg-sunken\"\n }`}\n >\n <svg\n className={`size-5 ${ssoMethod === \"saml\" ? \"text-white\" : \"text-text-secondary\"}`}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z\"\n />\n </svg>\n </div>\n <div className=\"text-left\">\n <div\n className={`font-medium ${ssoMethod === \"saml\" ? \"text-accent-purple\" : \"text-text-primary\"}`}\n >\n SAML 2.0\n </div>\n <div className=\"text-xs text-text-secondary\">\n {tr(\"auth.ssoLogin.enterpriseLegacy\", \"Enterprise Legacy\")}\n </div>\n </div>\n </div>\n </button>\n </div>\n </div>\n\n {/* Info Banner */}\n <div\n className={`p-4 rounded-xl border ${\n ssoMethod === \"oidc\"\n ? \"bg-status-info-bg-subtle border-status-info-border\"\n : \"bg-accent-purple-subtle border-accent-purple-subtle\"\n }`}\n >\n <p\n className={`text-sm ${\n ssoMethod === \"oidc\"\n ? \"text-status-info-text\"\n : \"text-accent-purple\"\n }`}\n >\n {ssoMethod === \"oidc\"\n ? tr(\n \"auth.sso.oidcInfoBannerPkce\",\n \"Sign in using OpenID Connect with your identity provider. Supports modern SSO with PKCE security.\",\n )\n : tr(\n \"auth.sso.samlInfoBanner\",\n \"Sign in using SAML 2.0 with your enterprise identity provider (Okta, Azure AD, etc.).\",\n )}\n </p>\n </div>\n\n {/* Email Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"email\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.ssoLogin.workEmail\", \"Work Email Address\")}\n </label>\n <div className=\"relative\">\n <div className=\"absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none\">\n <EmailIcon className=\"size-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"email\"\n type=\"email\"\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.ssoLogin.workEmail\", \"Work Email Address\")}\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n autoComplete=\"email\"\n className=\"block w-full pl-[3.5rem] pr-4 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all\"\n placeholder={tr(\n \"auth.ssoLogin.emailPlaceholder\",\n \"you@company.com\",\n )}\n />\n </div>\n <p className=\"text-xs text-[var(--color-text-tertiary)]\">\n {tr(\n \"auth.ssoLogin.useCompanyEmail\",\n \"Use your company email (e.g., @yourcompany.com)\",\n )}\n </p>\n </div>\n\n {/* Submit Button */}\n <Button\n type=\"submit\"\n disabled={isSubmitting || !email}\n variant=\"default\"\n size=\"lg\"\n className={`w-full font-semibold ${\n ssoMethod === \"saml\"\n ? \"bg-accent-purple text-white hover:bg-accent-purple hover:opacity-90\"\n : \"\"\n }`}\n >\n {isSubmitting ? (\n <>\n <SpinnerIcon className=\"size-5 animate-spin\" />\n <span>{tr(\"auth.sso.redirecting\", \"Redirecting...\")}</span>\n </>\n ) : (\n <>\n <svg\n className=\"size-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1\"\n />\n </svg>\n <span>\n {ssoMethod === \"oidc\"\n ? tr(\"auth.sso.continueWithOidc\", \"Continue with OIDC\")\n : tr(\"auth.sso.continueWithSaml\", \"Continue with SAML\")}\n </span>\n </>\n )}\n </Button>\n </form>\n\n {/* Back to Regular Login */}\n <div className=\"mt-6 text-center\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/login\")}\n className=\"text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] transition-colors\"\n >\n <span className=\"font-semibold text-text-link hover:text-text-linkHover\">\n {tr(\"auth.ssoLogin.backToLogin\", \"Back to regular login\")}\n </span>\n </button>\n </div>\n\n {/* Reactivate Account Link */}\n <div className=\"mt-4 text-center\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/reactivate\")}\n className=\"text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] transition-colors\"\n >\n {tr(\n \"auth.ssoLogin.deactivatedAccount\",\n \"Have a deactivated account?\",\n )}{\" \"}\n <span className=\"font-semibold text-status-success-text hover:text-status-success-text\">\n {tr(\"auth.ssoLogin.reactivateIt\", \"Reactivate it\")}\n </span>\n </button>\n </div>\n\n {/* Help Text */}\n <div className=\"mt-6 p-4 rounded-xl bg-bg-canvas/50 border border-border-subtle\">\n <h4 className=\"text-sm font-medium text-[var(--color-text-primary)] mb-2\">\n {tr(\"auth.ssoLogin.noSso\", \"Don't see your company's SSO?\")}\n </h4>\n <p className=\"text-xs text-[var(--color-text-secondary)]\">\n {tr(\n \"auth.ssoLogin.contactIT\",\n \"Contact your IT administrator to set up SSO for your organization. They'll need to configure your identity provider with our service.\",\n )}\n </p>\n </div>\n </AuthLayout>\n );\n}\n"],"mappings":";;;;;;;;;;AAgBA,SAAgBC,IAAe;CAC7B,IAAMC,IAAKC,EAAM,GAEXC,IAAWC,EAAY,GACvB,CAACC,KAAgBC,EAAgB,GACjCC,IAAgBF,EAAa,IAAI,QAAQ,GACzCG,IAAgBH,EAAa,IAAI,QAAQ,GACzC,EACJ,qBACA,8BACA,wBACA,2BACEI,EAAiBF,KAAiB,KAAA,CAAS,GAEzC,CAACG,GAAOC,KAAYC,EAAS,EAAE,GAC/B,CAACC,GAAOC,KAAYF,EAAwB,IAAI,GAChD,CAACG,GAAWC,KAAgBJ,EAAoB,MAAM,GACtD,CAACK,GAAcC,KAAmBN,EAAS,EAAK,GAChD,CAACO,GAAoBC,KAAyBR,EAAS,EAAK;CAGlE,QAAgB;EACd,CAAIJ,MAAkB,UAAUA,MAAkB,WAChDQ,EAAaR,CAAa;CAE9B,GAAG,CAACA,CAAa,CAAC;CAElB,IAAMa,IAAe,OAAO,MAAuB;EAIjD,AAHA,EAAE,eAAe,GACjBP,EAAS,IAAI,GACbM,EAAsB,EAAK,GAC3BF,EAAgB,EAAI;EAEpB,IAAI;GAEF,IAAMI,IAASZ,EAAM,MAAM,GAAG,CAAC,CAAC;GAChC,IAAI,CAACY,GAAQ;IAOX,AANAR,EACEb,EACE,oCACA,6DACF,CACF,GACAiB,EAAgB,EAAK;IACrB;GACF;GAIA,IAAMK,IAAWhB,KAAiBe;GAKlC,AAFA,eAAe,QAAQ,aAAaZ,CAAK,GAErCK,MAAc,SAEhB,MAAMS,EAA0BD,GAAU,YAAY,IAEtD,MAAME,EAAiBF,GAAU,YAAY;EAEjD,SAASG,GAAK;GACZ,QAAQ,MAAM,qBAAqBA,CAAG;GACtC,IAAMC,IACJD,aAAe,QACXA,EAAI,UACJ;GAmBN,AAfEC,EAAa,YAAY,CAAC,CAAC,SAAS,KAAK,MACxCA,EAAa,YAAY,CAAC,CAAC,SAAS,gBAAgB,KACnDA,EAAa,YAAY,CAAC,CAAC,SAAS,YAAY,MAElDP,EAAsB,EAAI,GAC1BN,EACEb,EACE,kCACA,mIACF,CACF,KAEAa,EAASa,CAAY,GAGvBT,EAAgB,EAAK;EACvB;CACF,GAEMU,IACJC,KAAuBC,KAAuBb;CAEhD,OACE,kBAACc,GAAD;EACE,OAAO9B,EAAG,uBAAuB,gBAAgB;EACjD,UAAUA,EACR,0BACA,+CACF;EALF,UAAA;GAOE,kBAAC+B,GAAD;IAAa,WAAU;IACpB,UAAA/B,EACC,yBACA,2KACF;GACW,CAAA;GACb,kBAAC,QAAD;IAAM,UAAUoB;IAAc,WAAU;IAAxC,UAAA;KAEGR,KACC,kBAAC,OAAD;MACE,WAAW,gDACTM,IACI,6DACA;MAJR,UAAA,CAOE,kBAACc,GAAD,EACE,WAAW,+BACTd,IACI,6BACA,2BAEP,CAAA,GACD,kBAAC,OAAD;OAAK,WAAU;OAAf,UAAA,CACE,kBAAC,KAAD;QACE,WAAW,WACTA,IACI,6BACA;QAGL,UAAAN;OACA,CAAA,GACFM,KACC,kBAAC,UAAD;QACE,MAAK;QACL,eAAehB,EAAS,eAAe,EAAE,OAAO,EAAE,SAAM,EAAE,CAAC;QAC3D,WAAU;QAHZ,UAAA,CAKE,kBAAC,OAAD;SACE,WAAU;SACV,MAAK;SACL,SAAQ;SACR,QAAO;SAEP,UAAA,kBAAC,QAAD;UACE,eAAc;UACd,gBAAe;UACf,aAAa;UACb,GAAE;SACH,CAAA;QACE,CAAA,GACJF,EACC,+BACA,kCACF,CACM;OAEP,CAAA,CAAA;MACF,CAAA,CAAA;;KAIP,kBAAC,OAAD;MAAK,WAAU;MAAf,UAAA,CACE,kBAAC,SAAD;OAAO,WAAU;OACd,UAAAA,EAAG,4BAA4B,uBAAuB;MAClD,CAAA,GACP,kBAAC,OAAD;OAAK,WAAU;OAAf,UAAA,CACE,kBAAC,UAAD;QACE,MAAK;QACL,eAAee,EAAa,MAAM;QAClC,WAAW,0CACTD,MAAc,SACV,sDACA;QAGN,UAAA,kBAAC,OAAD;SAAK,WAAU;SAAf,UAAA,CACE,kBAAC,OAAD;UACE,WAAW,uDACTA,MAAc,SACV,8BACA;UAGN,UAAA,kBAAC,OAAD;WACE,WAAW,UAAUA,MAAc,SAAS,eAAe;WAC3D,MAAK;WACL,SAAQ;WACR,QAAO;WAEP,UAAA,kBAAC,QAAD;YACE,eAAc;YACd,gBAAe;YACf,aAAa;YACb,GAAE;WACH,CAAA;UACE,CAAA;SACF,CAAA,GACL,kBAAC,OAAD;UAAK,WAAU;UAAf,UAAA,CACE,kBAAC,OAAD;WACE,WAAW,eAAeA,MAAc,SAAS,wBAAwB;WAC1E,UAAA;UAEI,CAAA,GACL,kBAAC,OAAD;WAAK,WAAU;WACZ,UAAAd,EACC,mCACA,sBACF;UACG,CAAA,CACF;SACF,CAAA,CAAA;;OACC,CAAA,GAER,kBAAC,UAAD;QACE,MAAK;QACL,eAAee,EAAa,MAAM;QAClC,WAAW,0CACTD,MAAc,SACV,iDACA;QAGN,UAAA,kBAAC,OAAD;SAAK,WAAU;SAAf,UAAA,CACE,kBAAC,OAAD;UACE,WAAW,uDACTA,MAAc,SACV,6BACA;UAGN,UAAA,kBAAC,OAAD;WACE,WAAW,UAAUA,MAAc,SAAS,eAAe;WAC3D,MAAK;WACL,SAAQ;WACR,QAAO;WAEP,UAAA,kBAAC,QAAD;YACE,eAAc;YACd,gBAAe;YACf,aAAa;YACb,GAAE;WACH,CAAA;UACE,CAAA;SACF,CAAA,GACL,kBAAC,OAAD;UAAK,WAAU;UAAf,UAAA,CACE,kBAAC,OAAD;WACE,WAAW,eAAeA,MAAc,SAAS,uBAAuB;WACzE,UAAA;UAEI,CAAA,GACL,kBAAC,OAAD;WAAK,WAAU;WACZ,UAAAd,EAAG,kCAAkC,mBAAmB;UACtD,CAAA,CACF;SACF,CAAA,CAAA;;OACC,CAAA,CACL;MACF,CAAA,CAAA;;KAGL,kBAAC,OAAD;MACE,WAAW,yBACTc,MAAc,SACV,uDACA;MAGN,UAAA,kBAAC,KAAD;OACE,WAAW,WACTA,MAAc,SACV,0BACA;OAGL,UAAAA,MAAc,SACXd,EACE,+BACA,mGACF,IACAA,EACE,2BACA,uFACF;MACH,CAAA;KACA,CAAA;KAGL,kBAAC,OAAD;MAAK,WAAU;MAAf,UAAA;OACE,kBAAC,SAAD;QACE,SAAQ;QACR,WAAU;QAET,UAAAA,EAAG,2BAA2B,oBAAoB;OAC9C,CAAA;OACP,kBAAC,OAAD;QAAK,WAAU;QAAf,UAAA,CACE,kBAAC,OAAD;SAAK,WAAU;SACb,UAAA,kBAACiC,GAAD,EAAW,WAAU,2CAA4C,CAAA;QAC9D,CAAA,GACL,kBAAC,SAAD;SACE,IAAG;SACH,MAAK;SACL,UAAA;SACA,iBAAc;SACd,cAAYjC,EAAG,2BAA2B,oBAAoB;SAC9D,OAAOS;SACP,WAAW,MAAMC,EAAS,EAAE,OAAO,KAAK;SACxC,cAAa;SACb,WAAU;SACV,aAAaV,EACX,kCACA,iBACF;QACD,CAAA,CACE;;OACL,kBAAC,KAAD;QAAG,WAAU;QACV,UAAAA,EACC,iCACA,iDACF;OACC,CAAA;MACA;;KAGL,kBAACkC,GAAD;MACE,MAAK;MACL,UAAUP,KAAgB,CAAClB;MAC3B,SAAQ;MACR,MAAK;MACL,WAAW,wBACTK,MAAc,SACV,wEACA;MAGL,UAAAa,IACC,kBAAA7B,GAAA,EAAA,UAAA,CACE,kBAACsC,GAAD,EAAa,WAAU,sBAAuB,CAAA,GAC9C,kBAAC,QAAD,EAAA,UAAOpC,EAAG,wBAAwB,gBAAgB,EAAQ,CAAA,CAC1D,EAAA,CAAA,IAEF,kBAAAF,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;OACE,WAAU;OACV,MAAK;OACL,SAAQ;OACR,QAAO;OACP,eAAY;OAEZ,UAAA,kBAAC,QAAD;QACE,eAAc;QACd,gBAAe;QACf,aAAa;QACb,GAAE;OACH,CAAA;MACE,CAAA,GACL,kBAAC,QAAD,EAAA,UACGgB,MAAc,SACXd,EAAG,6BAA6B,oBAAoB,IACpDA,EAAG,6BAA6B,oBAAoB,EACpD,CAAA,CACN,EAAA,CAAA;KAEE,CAAA;IACJ;;GAGN,kBAAC,OAAD;IAAK,WAAU;IACb,UAAA,kBAAC,UAAD;KACE,MAAK;KACL,eAAeE,EAAS,aAAa;KACrC,WAAU;KAEV,UAAA,kBAAC,QAAD;MAAM,WAAU;MACb,UAAAF,EAAG,6BAA6B,uBAAuB;KACpD,CAAA;IACA,CAAA;GACL,CAAA;GAGL,kBAAC,OAAD;IAAK,WAAU;IACb,UAAA,kBAAC,UAAD;KACE,MAAK;KACL,eAAeE,EAAS,kBAAkB;KAC1C,WAAU;KAHZ,UAAA;MAKGF,EACC,oCACA,6BACF;MAAG;MACH,kBAAC,QAAD;OAAM,WAAU;OACb,UAAAA,EAAG,8BAA8B,eAAe;MAC7C,CAAA;KACA;;GACL,CAAA;GAGL,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;KACX,UAAAA,EAAG,uBAAuB,+BAA+B;IACxD,CAAA,GACJ,kBAAC,KAAD;KAAG,WAAU;KACV,UAAAA,EACC,2BACA,uIACF;IACC,CAAA,CACA;;EACK;;AAEhB"}
@@ -1,16 +1,16 @@
1
- import { useAuthStore as e } from "../store/authStore.js";
2
- import { useTr as t } from "../../shared/hooks/useTr.js";
3
- import { AlertIcon as n, SpinnerIcon as r } from "../components/AuthIcons.js";
4
- import { useUserOperations as i } from "../hooks/useUserOperations.js";
5
- import { useAuthPermissions as a } from "../hooks/useAuthPermissions.js";
6
- import { ConfirmActionDialog as o } from "../components/ConfirmActionDialog.js";
1
+ import { useTr as e } from "../../shared/hooks/useTr.js";
2
+ import { useAuthPermissions as t } from "../hooks/useAuthPermissions.js";
3
+ import { useUserOperations as n } from "../hooks/useUserOperations.js";
4
+ import { useAuthStore as r } from "../store/authStore.js";
5
+ import { ConfirmActionDialog as i } from "../components/ConfirmActionDialog.js";
6
+ import { AlertIcon as a, SpinnerIcon as o } from "../components/AuthIcons.js";
7
7
  import { useCreateAuthExportJobMutation as s } from "../../generated/wspace-operations.js";
8
8
  import { useEffect as c, useState as l } from "react";
9
9
  import { useNavigate as u, useParams as d } from "react-router";
10
10
  import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
11
11
  import { isQuotaExhaustedError as h } from "@burdenoff/fe-libs/shared/utils";
12
12
  import { EmphasisPanel as g, IllustratedEmptyState as _, NextSteps as v, PagePurpose as y } from "@burdenoff/fe-libs/ui";
13
- //#region src/auth/pages/UserDetailsPage.tsx
13
+ // src/auth/pages/UserDetailsPage.tsx
14
14
  var b = (e) => {
15
15
  switch (e) {
16
16
  case "SUPER_ADMIN": return "bg-accent-purple-subtle text-accent-purple";
@@ -31,18 +31,12 @@ var b = (e) => {
31
31
  }
32
32
  };
33
33
  function S() {
34
- let S = t(), { id: C } = d(), w = u(), T = e((e) => e.isAuthenticated), E = e((e) => e.user), { canViewUsers: D, canDeleteUsers: O, isLoading: k } = a(), { getUser: A, deleteUser: j, hardDeleteUser: M } = i(), [N, P] = l(null), [F, I] = l(!0), [L, R] = l(null), [z, B] = l(null), [V, H] = l(null), [U, { loading: W }] = s(), [G, K] = l(null), [q, J] = l(!1);
34
+ let S = e(), { id: C } = d(), w = u(), T = r((e) => e.isAuthenticated), E = r((e) => e.user), { canViewUsers: D, canDeleteUsers: O, isLoading: k } = t(), { getUser: A, deleteUser: j, hardDeleteUser: M } = n(), [N, P] = l(null), [F, I] = l(!0), [L, R] = l(null), [z, B] = l(null), [V, H] = l(null), [U, { loading: W }] = s(), [G, K] = l(null), [q, J] = l(!1);
35
35
  c(() => {
36
36
  let e = !1;
37
- if (!C) {
38
- R("Missing user id"), I(!1);
39
- return;
40
- }
41
- if (!T) {
42
- I(!1);
43
- return;
44
- }
45
- return I(!0), R(null), A(C).then((t) => {
37
+ if (!C) R("Missing user id"), I(!1);
38
+ else if (!T) I(!1);
39
+ else return I(!0), R(null), A(C).then((t) => {
46
40
  e || (t ? P(t) : R(S("auth.userDetails.userNotFound", "User not found")));
47
41
  }).catch((t) => {
48
42
  e || R(t instanceof Error ? t.message : "Failed to fetch user details");
@@ -104,7 +98,7 @@ function S() {
104
98
  className: "text-center py-16",
105
99
  "aria-live": "polite",
106
100
  "aria-busy": "true",
107
- children: [/* @__PURE__ */ p(r, {
101
+ children: [/* @__PURE__ */ p(o, {
108
102
  className: "size-8 mx-auto text-text-secondary animate-spin",
109
103
  "aria-hidden": "true"
110
104
  }), /* @__PURE__ */ p("p", {
@@ -142,7 +136,7 @@ function S() {
142
136
  }) : /* @__PURE__ */ m("div", {
143
137
  className: "max-w-4xl mx-auto p-6",
144
138
  children: [
145
- /* @__PURE__ */ p(o, {
139
+ /* @__PURE__ */ p(i, {
146
140
  open: V !== null,
147
141
  onOpenChange: (e) => {
148
142
  e || H(null);
@@ -180,7 +174,7 @@ function S() {
180
174
  disabled: W,
181
175
  "aria-label": S("auth.userDetails.exportAriaLabel", "Export user data"),
182
176
  className: "flex items-center gap-2 px-4 py-2.5 rounded-xl border border-border-default text-text-primary bg-bg-surface hover:bg-bg-sunken disabled:opacity-50 disabled:cursor-not-allowed transition-colors font-medium",
183
- children: W ? /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p(r, {
177
+ children: W ? /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p(o, {
184
178
  className: "size-4 animate-spin",
185
179
  "aria-hidden": "true"
186
180
  }), S("auth.userDetails.exporting", "Exporting...")] }) : /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("svg", {
@@ -205,7 +199,7 @@ function S() {
205
199
  G && /* @__PURE__ */ m("div", {
206
200
  role: "alert",
207
201
  className: "mb-6 flex items-start gap-3 p-4 rounded-xl bg-status-error-bg-subtle border border-status-error-border",
208
- children: [/* @__PURE__ */ p(n, {
202
+ children: [/* @__PURE__ */ p(a, {
209
203
  className: "size-5 text-status-error-text flex-shrink-0 mt-0.5",
210
204
  "aria-hidden": "true"
211
205
  }), /* @__PURE__ */ p("p", {
@@ -385,7 +379,7 @@ function S() {
385
379
  onClick: () => void Z()
386
380
  }]
387
381
  }),
388
- /* @__PURE__ */ p(o, {
382
+ /* @__PURE__ */ p(i, {
389
383
  open: q,
390
384
  onOpenChange: J,
391
385
  title: S("auth.userDetails.exportQuotaTitle", "Export Limit Reached"),
@@ -414,7 +408,6 @@ function S() {
414
408
  })
415
409
  });
416
410
  }
417
- //#endregion
418
411
  export { S as UserDetailsPage };
419
412
 
420
413
  //# sourceMappingURL=UserDetailsPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"UserDetailsPage.js","names":[],"sources":["../../../src/auth/pages/UserDetailsPage.tsx"],"sourcesContent":["import { useState, useEffect } from \"react\";\nimport { useNavigate, useParams } from \"react-router\";\nimport { useTr } from \"../../shared/hooks/useTr\";\nimport { isQuotaExhaustedError } from \"@burdenoff/fe-libs/shared/utils\";\nimport {\n EmphasisPanel,\n IllustratedEmptyState,\n PagePurpose,\n NextSteps,\n} from \"@burdenoff/fe-libs/ui\";\nimport { useAuthPermissions } from \"../hooks/useAuthPermissions\";\nimport { useUserOperations } from \"../hooks/useUserOperations\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { ConfirmActionDialog } from \"../components/ConfirmActionDialog\";\nimport { SpinnerIcon, AlertIcon } from \"../components/AuthIcons\";\nimport { useCreateAuthExportJobMutation } from \"../../generated/wspace-operations\";\nimport type { User, UserRole, UserStatus } from \"../types\";\n\nconst getRoleColor = (role: UserRole) => {\n switch (role) {\n case \"SUPER_ADMIN\":\n return \"bg-accent-purple-subtle text-accent-purple\";\n case \"TENANT_ADMIN\":\n return \"bg-status-info-bg-subtle text-status-info-text\";\n case \"ORG_ADMIN\":\n return \"bg-accent-indigo-subtle text-accent-indigo\";\n case \"WORKSPACE_ADMIN\":\n return \"bg-accent-cyan-subtle text-accent-cyan\";\n case \"MEMBER\":\n return \"bg-status-success-bg-subtle text-status-success-text\";\n case \"GUEST\":\n return \"bg-bg-sunken text-text-primary\";\n default:\n return \"bg-bg-sunken text-text-primary\";\n }\n};\n\nconst getStatusColor = (status: UserStatus) => {\n switch (status) {\n case \"ACTIVE\":\n return \"bg-status-success-bg-subtle text-status-success-text\";\n case \"INACTIVE\":\n return \"bg-status-warning-bg-subtle text-status-warning-text\";\n case \"SUSPENDED\":\n return \"bg-status-warning-bg-subtle text-status-warning-text\";\n case \"DELETED\":\n return \"bg-status-error-bg-subtle text-status-error-text\";\n default:\n return \"bg-bg-sunken text-text-primary\";\n }\n};\n\n/**\n * User details page (admin only).\n *\n * Route: /auth/users/:id\n *\n * Renders the full profile of a single user, with admin actions\n * (edit, deactivate, hard-delete, export). Reuses the same getUser query\n * that UsersPage relies on.\n */\nexport function UserDetailsPage() {\n const tr = useTr();\n const { id } = useParams<{ id: string }>();\n const navigate = useNavigate();\n\n const isAuthenticated = useAuthStore((state) => state.isAuthenticated);\n const currentUser = useAuthStore((state) => state.user);\n const {\n canViewUsers,\n canDeleteUsers,\n isLoading: permissionsLoading,\n } = useAuthPermissions();\n const { getUser, deleteUser, hardDeleteUser } = useUserOperations();\n\n const [user, setUser] = useState<User | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n const [pendingAction, setPendingAction] = useState<\n \"deactivate\" | \"hard-delete\" | null\n >(null);\n\n const [createExportJob, { loading: isExporting }] =\n useCreateAuthExportJobMutation();\n const [exportError, setExportError] = useState<string | null>(null);\n const [showExportQuotaDialog, setShowExportQuotaDialog] = useState(false);\n\n useEffect(() => {\n let cancelled = false;\n if (!id) {\n setError(\"Missing user id\");\n setLoading(false);\n return;\n }\n if (!isAuthenticated) {\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n getUser(id)\n .then((fetched) => {\n if (cancelled) return;\n if (!fetched) {\n setError(tr(\"auth.userDetails.userNotFound\", \"User not found\"));\n } else {\n setUser(fetched);\n }\n })\n .catch((err: unknown) => {\n if (cancelled) return;\n setError(\n err instanceof Error ? err.message : \"Failed to fetch user details\",\n );\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n\n return () => {\n cancelled = true;\n };\n // tr is stable within the render, id/getUser/isAuthenticated drive the effect\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [id, getUser, isAuthenticated]);\n\n const handleDeactivate = async () => {\n if (!user) return;\n try {\n const result = await deleteUser(user.id);\n if (result.success) {\n setSuccessMessage(\n tr(\n \"auth.userDetails.userDeactivated\",\n 'User \"{email}\" has been deactivated.',\n ).replace(\"{email}\", user.email),\n );\n setPendingAction(null);\n // Refresh\n const fresh = await getUser(user.id);\n if (fresh) setUser(fresh);\n } else {\n setError(result.message);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to delete user\");\n }\n };\n\n const handleHardDelete = async () => {\n if (!user) return;\n try {\n const result = await hardDeleteUser(user.id);\n if (result.success) {\n setPendingAction(null);\n navigate(\"/auth/users\");\n } else {\n setError(result.message);\n }\n } catch (err) {\n setError(\n err instanceof Error ? err.message : \"Failed to hard delete user\",\n );\n }\n };\n\n const handleExport = async () => {\n if (!user) return;\n setExportError(null);\n try {\n await createExportJob({\n variables: {\n input: {\n name: `User Data Export - ${user.email}`,\n entityTypes: [\n \"user_profile\",\n \"user_sessions\",\n \"permission_audit_logs\",\n ],\n format: \"CSV\",\n exportScope: \"INDIVIDUAL\",\n includeMetadata: true,\n },\n },\n });\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowExportQuotaDialog(true);\n setExportError(\n tr(\n \"auth.userDetails.exportQuotaReached\",\n \"You have reached your export limit for this month.\",\n ),\n );\n return;\n }\n setExportError(\n err instanceof Error ? err.message : \"Failed to start export\",\n );\n }\n };\n\n // Auth + role guards\n if (!isAuthenticated) {\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title={tr(\"auth.userDetails.authRequired\", \"Authentication Required\")}\n description={tr(\n \"auth.userDetails.signInToView\",\n \"Please sign in to view user details\",\n )}\n />\n </div>\n );\n }\n\n if (permissionsLoading || loading) {\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n <div className=\"text-center py-16\" aria-live=\"polite\" aria-busy=\"true\">\n <SpinnerIcon\n className=\"size-8 mx-auto text-text-secondary animate-spin\"\n aria-hidden=\"true\"\n />\n <p className=\"mt-4 text-text-secondary\">\n {tr(\"auth.userDetails.loading\", \"Loading user...\")}\n </p>\n </div>\n </div>\n );\n }\n\n if (!canViewUsers) {\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title={tr(\"auth.userDetails.adminRequired\", \"Admin Access Required\")}\n description={tr(\n \"auth.userDetails.adminRequiredDesc\",\n \"You need administrator privileges to view user details.\",\n )}\n />\n </div>\n );\n }\n\n if (error || !user) {\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/users\")}\n className=\"mb-6 flex items-center gap-2 text-text-link hover:underline font-medium\"\n >\n <svg\n className=\"size-4\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M10 19l-7-7m0 0l7-7m-7 7h18\"\n />\n </svg>\n {tr(\"auth.userDetails.backToUsers\", \"Back to Users\")}\n </button>\n <div role=\"alert\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title={tr(\"auth.userDetails.userNotFound\", \"User not found\")}\n description={\n error ??\n tr(\n \"auth.userDetails.userNotFoundDesc\",\n \"We couldn't find this user. They may have been removed, or the link is out of date.\",\n )\n }\n />\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n <ConfirmActionDialog\n open={pendingAction !== null}\n onOpenChange={(open) => {\n if (!open) setPendingAction(null);\n }}\n title={\n pendingAction === \"hard-delete\"\n ? tr(\"auth.userDetails.hardDeleteTitle\", \"Permanently delete user?\")\n : tr(\"auth.userDetails.deactivateTitle\", \"Deactivate user?\")\n }\n description={\n pendingAction === \"hard-delete\"\n ? tr(\n \"auth.userDetails.hardDeleteDesc\",\n 'This permanently deletes \"{email}\" and cannot be undone.',\n ).replace(\"{email}\", user.email)\n : tr(\n \"auth.userDetails.deactivateDesc\",\n '\"{email}\" will be prevented from signing in until reactivated.',\n ).replace(\"{email}\", user.email)\n }\n confirmText={\n pendingAction === \"hard-delete\"\n ? tr(\"auth.userDetails.hardDeleteConfirm\", \"Delete permanently\")\n : tr(\"auth.userDetails.deactivateConfirm\", \"Deactivate user\")\n }\n variant={pendingAction === \"hard-delete\" ? \"danger\" : \"warning\"}\n onConfirm={async () => {\n if (pendingAction === \"hard-delete\") await handleHardDelete();\n else if (pendingAction === \"deactivate\") await handleDeactivate();\n }}\n />\n\n {/* Header row */}\n <div className=\"flex items-center justify-between mb-6\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/users\")}\n className=\"flex items-center gap-2 text-text-link hover:underline font-medium\"\n >\n <svg\n className=\"size-4\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M10 19l-7-7m0 0l7-7m-7 7h18\"\n />\n </svg>\n {tr(\"auth.userDetails.backToUsers\", \"Back to Users\")}\n </button>\n\n {/* Export button */}\n <button\n type=\"button\"\n onClick={() => void handleExport()}\n disabled={isExporting}\n aria-label={tr(\n \"auth.userDetails.exportAriaLabel\",\n \"Export user data\",\n )}\n className=\"flex items-center gap-2 px-4 py-2.5 rounded-xl border border-border-default text-text-primary bg-bg-surface hover:bg-bg-sunken disabled:opacity-50 disabled:cursor-not-allowed transition-colors font-medium\"\n >\n {isExporting ? (\n <>\n <SpinnerIcon className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n {tr(\"auth.userDetails.exporting\", \"Exporting...\")}\n </>\n ) : (\n <>\n <svg\n className=\"size-4\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4\"\n />\n </svg>\n {tr(\"auth.userDetails.exportButton\", \"Export User Data\")}\n </>\n )}\n </button>\n </div>\n\n <PagePurpose className=\"mb-6\">\n {tr(\n \"auth.userDetails.purpose\",\n \"The full record for this person — their role, status, verification, and activity. From here you can export their data, deactivate them, or (for super admins) permanently delete a removed account.\",\n )}\n </PagePurpose>\n\n {/* Export error */}\n {exportError && (\n <div\n role=\"alert\"\n className=\"mb-6 flex items-start gap-3 p-4 rounded-xl bg-status-error-bg-subtle border border-status-error-border\"\n >\n <AlertIcon\n className=\"size-5 text-status-error-text flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n <p className=\"text-sm text-status-error-text\">{exportError}</p>\n </div>\n )}\n\n {successMessage && (\n <div className=\"mb-6 p-4 rounded-xl bg-status-success-bg-subtle border border-status-success-border\">\n <p className=\"text-status-success-text\">{successMessage}</p>\n <button\n type=\"button\"\n onClick={() => setSuccessMessage(null)}\n className=\"mt-2 text-sm text-status-success-text hover:underline\"\n >\n {tr(\"auth.userDetails.dismiss\", \"Dismiss\")}\n </button>\n </div>\n )}\n\n <div className=\"p-6 rounded-xl border border-border-subtle bg-bg-surface shadow-sm\">\n {/* Identity header — the one emphasis zone on this page */}\n <EmphasisPanel className=\"mb-8\">\n <div className=\"flex items-center gap-4\">\n <div\n className=\"auth-brand-badge flex size-20 items-center justify-center rounded-full text-3xl font-semibold\"\n aria-hidden=\"true\"\n >\n {(user.name || user.username || user.email)\n .charAt(0)\n .toUpperCase()}\n </div>\n <div>\n <h1 className=\"text-2xl font-semibold text-text-primary\">\n {user.name || user.username}\n </h1>\n <p className=\"text-text-secondary\" data-testid=\"user-email\">\n {user.email}\n </p>\n <div className=\"flex items-center gap-2 mt-2\">\n <span\n className={`px-2.5 py-1 rounded-full text-xs font-medium ${getRoleColor(user.role)}`}\n >\n {user.role.replace(\"_\", \" \")}\n </span>\n <span\n className={`px-2.5 py-1 rounded-full text-xs font-medium ${getStatusColor(user.status)}`}\n >\n {user.status}\n </span>\n </div>\n </div>\n </div>\n </EmphasisPanel>\n\n <dl className=\"grid grid-cols-2 gap-6\">\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.username\", \"Username\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">{user.username}</dd>\n </div>\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.email\", \"Email\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">{user.email}</dd>\n </div>\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.emailVerified\", \"Email Verified\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {user.emailVerified ? (\n <span className=\"text-status-success-text\">\n {tr(\"auth.userDetails.yes\", \"Yes\")}\n </span>\n ) : (\n <span className=\"text-status-warning-text\">\n {tr(\"auth.userDetails.no\", \"No\")}\n </span>\n )}\n </dd>\n </div>\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.mfaEnabled\", \"MFA Enabled\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {user.mfaEnabled ? (\n <span className=\"text-status-success-text\">\n {tr(\"auth.userDetails.yes\", \"Yes\")}\n </span>\n ) : (\n <span className=\"text-text-secondary\">\n {tr(\"auth.userDetails.no\", \"No\")}\n </span>\n )}\n </dd>\n </div>\n {user.phoneNumber && (\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.phoneNumber\", \"Phone Number\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {user.phoneNumber}\n </dd>\n </div>\n )}\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.lastLogin\", \"Last Login\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {user.lastLoginAt\n ? new Date(user.lastLoginAt).toLocaleString()\n : tr(\"auth.userDetails.never\", \"Never\")}\n </dd>\n </div>\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.created\", \"Created\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {new Date(user.createdAt).toLocaleString()}\n </dd>\n </div>\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.lastUpdated\", \"Last Updated\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {new Date(user.updatedAt).toLocaleString()}\n </dd>\n </div>\n </dl>\n\n <div className=\"mt-8 pt-6 border-t border-border-subtle flex gap-3\">\n {user.status !== \"DELETED\" && canDeleteUsers && (\n <button\n type=\"button\"\n onClick={() => setPendingAction(\"deactivate\")}\n className=\"px-4 py-2.5 rounded-xl border border-status-error-border text-status-error-text font-medium hover:bg-status-error-bg-subtle transition-colors\"\n >\n {tr(\"auth.userDetails.deactivateUser\", \"Deactivate User\")}\n </button>\n )}\n {canDeleteUsers &&\n user.status === \"DELETED\" &&\n currentUser?.role === \"SUPER_ADMIN\" && (\n <button\n type=\"button\"\n onClick={() => setPendingAction(\"hard-delete\")}\n className=\"px-4 py-2.5 rounded-xl bg-status-error-bg text-white font-medium hover:bg-status-error-bg-hover transition-colors\"\n >\n {tr(\"auth.userDetails.permanentlyDelete\", \"Permanently Delete\")}\n </button>\n )}\n </div>\n\n <NextSteps\n className=\"mt-8\"\n storageKey=\"auth-user-details\"\n steps={[\n {\n id: \"back-to-users\",\n label: tr(\"auth.userDetails.nextBack\", \"Return to the user list\"),\n description: tr(\n \"auth.userDetails.nextBackDesc\",\n \"Browse or search everyone in your organization.\",\n ),\n onClick: () => navigate(\"/auth/users\"),\n },\n {\n id: \"export-user\",\n label: tr(\n \"auth.userDetails.nextExport\",\n \"Export this user's data\",\n ),\n description: tr(\n \"auth.userDetails.nextExportDesc\",\n \"Download their profile, sessions, and audit history as CSV.\",\n ),\n onClick: () => void handleExport(),\n },\n ]}\n />\n\n <ConfirmActionDialog\n open={showExportQuotaDialog}\n onOpenChange={setShowExportQuotaDialog}\n title={tr(\n \"auth.userDetails.exportQuotaTitle\",\n \"Export Limit Reached\",\n )}\n description={tr(\n \"auth.userDetails.exportQuotaDescription\",\n \"You have reached your export limit for this month.\",\n )}\n confirmText={tr(\n \"auth.userDetails.exportQuotaViewBilling\",\n \"View Billing\",\n )}\n cancelText={tr(\"auth.userDetails.exportQuotaDismiss\", \"Dismiss\")}\n variant=\"warning\"\n onConfirm={() => window.location.assign(\"/billing\")}\n />\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"UserDetailsPage.js","names":["_Fragment","getRoleColor","role","getStatusColor","status","UserDetailsPage","tr","useTr","useParams","navigate","useNavigate","isAuthenticated","useAuthStore","state","currentUser","permissionsLoading","useAuthPermissions","useUserOperations","user","setUser","useState","loading","setLoading","error","setError","successMessage","setSuccessMessage","pendingAction","setPendingAction","createExportJob","isExporting","useCreateAuthExportJobMutation","exportError","setExportError","showExportQuotaDialog","setShowExportQuotaDialog","cancelled","id","getUser","fetched","err","handleDeactivate","result","deleteUser","fresh","handleHardDelete","hardDeleteUser","handleExport","isQuotaExhaustedError","SpinnerIcon","canViewUsers","IllustratedEmptyState","ConfirmActionDialog","open","Fragment","PagePurpose","AlertIcon","EmphasisPanel","canDeleteUsers","NextSteps"],"sources":["../../../src/auth/pages/UserDetailsPage.tsx"],"sourcesContent":["import { useState, useEffect } from \"react\";\nimport { useNavigate, useParams } from \"react-router\";\nimport { useTr } from \"../../shared/hooks/useTr\";\nimport { isQuotaExhaustedError } from \"@burdenoff/fe-libs/shared/utils\";\nimport {\n EmphasisPanel,\n IllustratedEmptyState,\n PagePurpose,\n NextSteps,\n} from \"@burdenoff/fe-libs/ui\";\nimport { useAuthPermissions } from \"../hooks/useAuthPermissions\";\nimport { useUserOperations } from \"../hooks/useUserOperations\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { ConfirmActionDialog } from \"../components/ConfirmActionDialog\";\nimport { SpinnerIcon, AlertIcon } from \"../components/AuthIcons\";\nimport { useCreateAuthExportJobMutation } from \"../../generated/wspace-operations\";\nimport type { User, UserRole, UserStatus } from \"../types\";\n\nconst getRoleColor = (role: UserRole) => {\n switch (role) {\n case \"SUPER_ADMIN\":\n return \"bg-accent-purple-subtle text-accent-purple\";\n case \"TENANT_ADMIN\":\n return \"bg-status-info-bg-subtle text-status-info-text\";\n case \"ORG_ADMIN\":\n return \"bg-accent-indigo-subtle text-accent-indigo\";\n case \"WORKSPACE_ADMIN\":\n return \"bg-accent-cyan-subtle text-accent-cyan\";\n case \"MEMBER\":\n return \"bg-status-success-bg-subtle text-status-success-text\";\n case \"GUEST\":\n return \"bg-bg-sunken text-text-primary\";\n default:\n return \"bg-bg-sunken text-text-primary\";\n }\n};\n\nconst getStatusColor = (status: UserStatus) => {\n switch (status) {\n case \"ACTIVE\":\n return \"bg-status-success-bg-subtle text-status-success-text\";\n case \"INACTIVE\":\n return \"bg-status-warning-bg-subtle text-status-warning-text\";\n case \"SUSPENDED\":\n return \"bg-status-warning-bg-subtle text-status-warning-text\";\n case \"DELETED\":\n return \"bg-status-error-bg-subtle text-status-error-text\";\n default:\n return \"bg-bg-sunken text-text-primary\";\n }\n};\n\n/**\n * User details page (admin only).\n *\n * Route: /auth/users/:id\n *\n * Renders the full profile of a single user, with admin actions\n * (edit, deactivate, hard-delete, export). Reuses the same getUser query\n * that UsersPage relies on.\n */\nexport function UserDetailsPage() {\n const tr = useTr();\n const { id } = useParams<{ id: string }>();\n const navigate = useNavigate();\n\n const isAuthenticated = useAuthStore((state) => state.isAuthenticated);\n const currentUser = useAuthStore((state) => state.user);\n const {\n canViewUsers,\n canDeleteUsers,\n isLoading: permissionsLoading,\n } = useAuthPermissions();\n const { getUser, deleteUser, hardDeleteUser } = useUserOperations();\n\n const [user, setUser] = useState<User | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n const [pendingAction, setPendingAction] = useState<\n \"deactivate\" | \"hard-delete\" | null\n >(null);\n\n const [createExportJob, { loading: isExporting }] =\n useCreateAuthExportJobMutation();\n const [exportError, setExportError] = useState<string | null>(null);\n const [showExportQuotaDialog, setShowExportQuotaDialog] = useState(false);\n\n useEffect(() => {\n let cancelled = false;\n if (!id) {\n setError(\"Missing user id\");\n setLoading(false);\n return;\n }\n if (!isAuthenticated) {\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n getUser(id)\n .then((fetched) => {\n if (cancelled) return;\n if (!fetched) {\n setError(tr(\"auth.userDetails.userNotFound\", \"User not found\"));\n } else {\n setUser(fetched);\n }\n })\n .catch((err: unknown) => {\n if (cancelled) return;\n setError(\n err instanceof Error ? err.message : \"Failed to fetch user details\",\n );\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n\n return () => {\n cancelled = true;\n };\n // tr is stable within the render, id/getUser/isAuthenticated drive the effect\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [id, getUser, isAuthenticated]);\n\n const handleDeactivate = async () => {\n if (!user) return;\n try {\n const result = await deleteUser(user.id);\n if (result.success) {\n setSuccessMessage(\n tr(\n \"auth.userDetails.userDeactivated\",\n 'User \"{email}\" has been deactivated.',\n ).replace(\"{email}\", user.email),\n );\n setPendingAction(null);\n // Refresh\n const fresh = await getUser(user.id);\n if (fresh) setUser(fresh);\n } else {\n setError(result.message);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to delete user\");\n }\n };\n\n const handleHardDelete = async () => {\n if (!user) return;\n try {\n const result = await hardDeleteUser(user.id);\n if (result.success) {\n setPendingAction(null);\n navigate(\"/auth/users\");\n } else {\n setError(result.message);\n }\n } catch (err) {\n setError(\n err instanceof Error ? err.message : \"Failed to hard delete user\",\n );\n }\n };\n\n const handleExport = async () => {\n if (!user) return;\n setExportError(null);\n try {\n await createExportJob({\n variables: {\n input: {\n name: `User Data Export - ${user.email}`,\n entityTypes: [\n \"user_profile\",\n \"user_sessions\",\n \"permission_audit_logs\",\n ],\n format: \"CSV\",\n exportScope: \"INDIVIDUAL\",\n includeMetadata: true,\n },\n },\n });\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowExportQuotaDialog(true);\n setExportError(\n tr(\n \"auth.userDetails.exportQuotaReached\",\n \"You have reached your export limit for this month.\",\n ),\n );\n return;\n }\n setExportError(\n err instanceof Error ? err.message : \"Failed to start export\",\n );\n }\n };\n\n // Auth + role guards\n if (!isAuthenticated) {\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title={tr(\"auth.userDetails.authRequired\", \"Authentication Required\")}\n description={tr(\n \"auth.userDetails.signInToView\",\n \"Please sign in to view user details\",\n )}\n />\n </div>\n );\n }\n\n if (permissionsLoading || loading) {\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n <div className=\"text-center py-16\" aria-live=\"polite\" aria-busy=\"true\">\n <SpinnerIcon\n className=\"size-8 mx-auto text-text-secondary animate-spin\"\n aria-hidden=\"true\"\n />\n <p className=\"mt-4 text-text-secondary\">\n {tr(\"auth.userDetails.loading\", \"Loading user...\")}\n </p>\n </div>\n </div>\n );\n }\n\n if (!canViewUsers) {\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title={tr(\"auth.userDetails.adminRequired\", \"Admin Access Required\")}\n description={tr(\n \"auth.userDetails.adminRequiredDesc\",\n \"You need administrator privileges to view user details.\",\n )}\n />\n </div>\n );\n }\n\n if (error || !user) {\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/users\")}\n className=\"mb-6 flex items-center gap-2 text-text-link hover:underline font-medium\"\n >\n <svg\n className=\"size-4\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M10 19l-7-7m0 0l7-7m-7 7h18\"\n />\n </svg>\n {tr(\"auth.userDetails.backToUsers\", \"Back to Users\")}\n </button>\n <div role=\"alert\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title={tr(\"auth.userDetails.userNotFound\", \"User not found\")}\n description={\n error ??\n tr(\n \"auth.userDetails.userNotFoundDesc\",\n \"We couldn't find this user. They may have been removed, or the link is out of date.\",\n )\n }\n />\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n <ConfirmActionDialog\n open={pendingAction !== null}\n onOpenChange={(open) => {\n if (!open) setPendingAction(null);\n }}\n title={\n pendingAction === \"hard-delete\"\n ? tr(\"auth.userDetails.hardDeleteTitle\", \"Permanently delete user?\")\n : tr(\"auth.userDetails.deactivateTitle\", \"Deactivate user?\")\n }\n description={\n pendingAction === \"hard-delete\"\n ? tr(\n \"auth.userDetails.hardDeleteDesc\",\n 'This permanently deletes \"{email}\" and cannot be undone.',\n ).replace(\"{email}\", user.email)\n : tr(\n \"auth.userDetails.deactivateDesc\",\n '\"{email}\" will be prevented from signing in until reactivated.',\n ).replace(\"{email}\", user.email)\n }\n confirmText={\n pendingAction === \"hard-delete\"\n ? tr(\"auth.userDetails.hardDeleteConfirm\", \"Delete permanently\")\n : tr(\"auth.userDetails.deactivateConfirm\", \"Deactivate user\")\n }\n variant={pendingAction === \"hard-delete\" ? \"danger\" : \"warning\"}\n onConfirm={async () => {\n if (pendingAction === \"hard-delete\") await handleHardDelete();\n else if (pendingAction === \"deactivate\") await handleDeactivate();\n }}\n />\n\n {/* Header row */}\n <div className=\"flex items-center justify-between mb-6\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/users\")}\n className=\"flex items-center gap-2 text-text-link hover:underline font-medium\"\n >\n <svg\n className=\"size-4\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M10 19l-7-7m0 0l7-7m-7 7h18\"\n />\n </svg>\n {tr(\"auth.userDetails.backToUsers\", \"Back to Users\")}\n </button>\n\n {/* Export button */}\n <button\n type=\"button\"\n onClick={() => void handleExport()}\n disabled={isExporting}\n aria-label={tr(\n \"auth.userDetails.exportAriaLabel\",\n \"Export user data\",\n )}\n className=\"flex items-center gap-2 px-4 py-2.5 rounded-xl border border-border-default text-text-primary bg-bg-surface hover:bg-bg-sunken disabled:opacity-50 disabled:cursor-not-allowed transition-colors font-medium\"\n >\n {isExporting ? (\n <>\n <SpinnerIcon className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n {tr(\"auth.userDetails.exporting\", \"Exporting...\")}\n </>\n ) : (\n <>\n <svg\n className=\"size-4\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4\"\n />\n </svg>\n {tr(\"auth.userDetails.exportButton\", \"Export User Data\")}\n </>\n )}\n </button>\n </div>\n\n <PagePurpose className=\"mb-6\">\n {tr(\n \"auth.userDetails.purpose\",\n \"The full record for this person — their role, status, verification, and activity. From here you can export their data, deactivate them, or (for super admins) permanently delete a removed account.\",\n )}\n </PagePurpose>\n\n {/* Export error */}\n {exportError && (\n <div\n role=\"alert\"\n className=\"mb-6 flex items-start gap-3 p-4 rounded-xl bg-status-error-bg-subtle border border-status-error-border\"\n >\n <AlertIcon\n className=\"size-5 text-status-error-text flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n <p className=\"text-sm text-status-error-text\">{exportError}</p>\n </div>\n )}\n\n {successMessage && (\n <div className=\"mb-6 p-4 rounded-xl bg-status-success-bg-subtle border border-status-success-border\">\n <p className=\"text-status-success-text\">{successMessage}</p>\n <button\n type=\"button\"\n onClick={() => setSuccessMessage(null)}\n className=\"mt-2 text-sm text-status-success-text hover:underline\"\n >\n {tr(\"auth.userDetails.dismiss\", \"Dismiss\")}\n </button>\n </div>\n )}\n\n <div className=\"p-6 rounded-xl border border-border-subtle bg-bg-surface shadow-sm\">\n {/* Identity header — the one emphasis zone on this page */}\n <EmphasisPanel className=\"mb-8\">\n <div className=\"flex items-center gap-4\">\n <div\n className=\"auth-brand-badge flex size-20 items-center justify-center rounded-full text-3xl font-semibold\"\n aria-hidden=\"true\"\n >\n {(user.name || user.username || user.email)\n .charAt(0)\n .toUpperCase()}\n </div>\n <div>\n <h1 className=\"text-2xl font-semibold text-text-primary\">\n {user.name || user.username}\n </h1>\n <p className=\"text-text-secondary\" data-testid=\"user-email\">\n {user.email}\n </p>\n <div className=\"flex items-center gap-2 mt-2\">\n <span\n className={`px-2.5 py-1 rounded-full text-xs font-medium ${getRoleColor(user.role)}`}\n >\n {user.role.replace(\"_\", \" \")}\n </span>\n <span\n className={`px-2.5 py-1 rounded-full text-xs font-medium ${getStatusColor(user.status)}`}\n >\n {user.status}\n </span>\n </div>\n </div>\n </div>\n </EmphasisPanel>\n\n <dl className=\"grid grid-cols-2 gap-6\">\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.username\", \"Username\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">{user.username}</dd>\n </div>\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.email\", \"Email\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">{user.email}</dd>\n </div>\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.emailVerified\", \"Email Verified\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {user.emailVerified ? (\n <span className=\"text-status-success-text\">\n {tr(\"auth.userDetails.yes\", \"Yes\")}\n </span>\n ) : (\n <span className=\"text-status-warning-text\">\n {tr(\"auth.userDetails.no\", \"No\")}\n </span>\n )}\n </dd>\n </div>\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.mfaEnabled\", \"MFA Enabled\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {user.mfaEnabled ? (\n <span className=\"text-status-success-text\">\n {tr(\"auth.userDetails.yes\", \"Yes\")}\n </span>\n ) : (\n <span className=\"text-text-secondary\">\n {tr(\"auth.userDetails.no\", \"No\")}\n </span>\n )}\n </dd>\n </div>\n {user.phoneNumber && (\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.phoneNumber\", \"Phone Number\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {user.phoneNumber}\n </dd>\n </div>\n )}\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.lastLogin\", \"Last Login\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {user.lastLoginAt\n ? new Date(user.lastLoginAt).toLocaleString()\n : tr(\"auth.userDetails.never\", \"Never\")}\n </dd>\n </div>\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.created\", \"Created\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {new Date(user.createdAt).toLocaleString()}\n </dd>\n </div>\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.lastUpdated\", \"Last Updated\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {new Date(user.updatedAt).toLocaleString()}\n </dd>\n </div>\n </dl>\n\n <div className=\"mt-8 pt-6 border-t border-border-subtle flex gap-3\">\n {user.status !== \"DELETED\" && canDeleteUsers && (\n <button\n type=\"button\"\n onClick={() => setPendingAction(\"deactivate\")}\n className=\"px-4 py-2.5 rounded-xl border border-status-error-border text-status-error-text font-medium hover:bg-status-error-bg-subtle transition-colors\"\n >\n {tr(\"auth.userDetails.deactivateUser\", \"Deactivate User\")}\n </button>\n )}\n {canDeleteUsers &&\n user.status === \"DELETED\" &&\n currentUser?.role === \"SUPER_ADMIN\" && (\n <button\n type=\"button\"\n onClick={() => setPendingAction(\"hard-delete\")}\n className=\"px-4 py-2.5 rounded-xl bg-status-error-bg text-white font-medium hover:bg-status-error-bg-hover transition-colors\"\n >\n {tr(\"auth.userDetails.permanentlyDelete\", \"Permanently Delete\")}\n </button>\n )}\n </div>\n\n <NextSteps\n className=\"mt-8\"\n storageKey=\"auth-user-details\"\n steps={[\n {\n id: \"back-to-users\",\n label: tr(\"auth.userDetails.nextBack\", \"Return to the user list\"),\n description: tr(\n \"auth.userDetails.nextBackDesc\",\n \"Browse or search everyone in your organization.\",\n ),\n onClick: () => navigate(\"/auth/users\"),\n },\n {\n id: \"export-user\",\n label: tr(\n \"auth.userDetails.nextExport\",\n \"Export this user's data\",\n ),\n description: tr(\n \"auth.userDetails.nextExportDesc\",\n \"Download their profile, sessions, and audit history as CSV.\",\n ),\n onClick: () => void handleExport(),\n },\n ]}\n />\n\n <ConfirmActionDialog\n open={showExportQuotaDialog}\n onOpenChange={setShowExportQuotaDialog}\n title={tr(\n \"auth.userDetails.exportQuotaTitle\",\n \"Export Limit Reached\",\n )}\n description={tr(\n \"auth.userDetails.exportQuotaDescription\",\n \"You have reached your export limit for this month.\",\n )}\n confirmText={tr(\n \"auth.userDetails.exportQuotaViewBilling\",\n \"View Billing\",\n )}\n cancelText={tr(\"auth.userDetails.exportQuotaDismiss\", \"Dismiss\")}\n variant=\"warning\"\n onConfirm={() => window.location.assign(\"/billing\")}\n />\n </div>\n </div>\n );\n}\n"],"mappings":"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"}