@burdenoff/microfe-auth 2026.519.2 → 2026.520.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.
- package/dist/auth/AuthRoutes.js +1 -1
- package/dist/auth/AuthRoutes.js.map +1 -1
- package/dist/auth/components/AuthLayout.js +1 -1
- package/dist/auth/components/AuthLayout.js.map +1 -1
- package/dist/auth/hooks/useContextSwitcherData.d.ts.map +1 -1
- package/dist/auth/hooks/useContextSwitcherData.js +25 -22
- package/dist/auth/hooks/useContextSwitcherData.js.map +1 -1
- package/dist/auth/hooks/useSessionOperations.d.ts.map +1 -1
- package/dist/auth/hooks/useSessionOperations.js +4 -1
- package/dist/auth/hooks/useSessionOperations.js.map +1 -1
- package/dist/auth/hooks/useUserOperations.d.ts.map +1 -1
- package/dist/auth/hooks/useUserOperations.js +26 -11
- package/dist/auth/hooks/useUserOperations.js.map +1 -1
- package/dist/auth/pages/LoginPage.d.ts.map +1 -1
- package/dist/auth/pages/LoginPage.js +3 -2
- package/dist/auth/pages/LoginPage.js.map +1 -1
- package/dist/auth/pages/PasswordResetPage.js +1 -1
- package/dist/auth/pages/PasswordResetPage.js.map +1 -1
- package/dist/auth/pages/ReactivatePage.js +1 -1
- package/dist/auth/pages/ReactivatePage.js.map +1 -1
- package/dist/auth/pages/RegisterPage.js +1 -1
- package/dist/auth/pages/RegisterPage.js.map +1 -1
- package/dist/auth/pages/SessionsPage.d.ts.map +1 -1
- package/dist/auth/pages/SessionsPage.js +185 -130
- package/dist/auth/pages/SessionsPage.js.map +1 -1
- package/dist/auth/pages/SsoCallbackPage.js +1 -1
- package/dist/auth/pages/SsoCallbackPage.js.map +1 -1
- package/dist/auth/pages/SsoConfigPage.d.ts.map +1 -1
- package/dist/auth/pages/SsoConfigPage.js +214 -190
- package/dist/auth/pages/SsoConfigPage.js.map +1 -1
- package/dist/auth/pages/SsoLoginPage.js +1 -1
- package/dist/auth/pages/SsoLoginPage.js.map +1 -1
- package/dist/auth/pages/UserProfilePage.d.ts.map +1 -1
- package/dist/auth/pages/UserProfilePage.js +246 -233
- package/dist/auth/pages/UserProfilePage.js.map +1 -1
- package/dist/auth/pages/UsersPage.d.ts.map +1 -1
- package/dist/auth/pages/UsersPage.js +0 -1
- package/dist/auth/pages/UsersPage.js.map +1 -1
- package/dist/auth/store/authStore.d.ts +20 -0
- package/dist/auth/store/authStore.d.ts.map +1 -1
- package/dist/auth/store/authStore.js.map +1 -1
- package/dist/microfe-auth.css +1 -1
- package/dist/translations/en.d.ts +12 -0
- package/dist/translations/en.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/auth/pages/AppsPage.d.ts +0 -5
- package/dist/auth/pages/AppsPage.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LoginPage.js","names":[],"sources":["../../../src/auth/pages/LoginPage.tsx"],"sourcesContent":["import { useState, useEffect, useCallback, useRef } from \"react\";\nimport { useNavigate, useSearchParams, useLocation } from \"react-router-dom\";\nimport {\n useI18n,\n useActiveContext,\n} from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport {\n getSSORedirectUrl,\n SSORedirectSpinner,\n} from \"@burdenoff/fe-libs/shared/components\";\nimport { resolveAuthBridgeConfig } from \"@burdenoff/fe-libs/shared/config\";\n\nimport {\n getProfileTokens,\n getProfileUser,\n getProfiles,\n getProfileWorkspaceToken,\n resolveActiveProfileId,\n} from \"@burdenoff/fe-libs/shared/providers/shell/ProfileStorageManager\";\nimport { useAuthOperations, useMyWorkspaces } from \"../hooks\";\nimport { useOAuthOperations } from \"../hooks/useOAuthOperations\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { AuthLayout } from \"../components/AuthLayout\";\nimport {\n EmailIcon,\n LockIcon,\n EyeIcon,\n EyeOffIcon,\n AlertIcon,\n SpinnerIcon,\n GoogleIcon,\n // GitHubIcon,\n MicrosoftIcon,\n} from \"../components/AuthIcons\";\nimport { useCaptchaV3 } from \"../hooks/useCaptchaV3\";\nimport type { SignInInput } from \"../types\";\n\nfunction hasValidAccessToken(token: string): boolean {\n try {\n const raw = token.split(\".\")[1].replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = raw.padEnd(Math.ceil(raw.length / 4) * 4, \"=\");\n const payload = JSON.parse(atob(padded)) as { exp?: unknown };\n return typeof payload.exp === \"number\" && payload.exp > Date.now() / 1000;\n } catch {\n return false;\n }\n}\n\nfunction decodeSameOriginReturnTo(returnTo: string | null): string | null {\n if (!returnTo) return null;\n try {\n const decoded = decodeURIComponent(returnTo);\n const url = new URL(decoded, window.location.origin);\n if (url.origin !== window.location.origin) return null;\n return `${url.pathname}${url.search}${url.hash}`;\n } catch {\n return null;\n }\n}\n\n/**\n * Login page with professional design\n * Provides email/password login and social OAuth options\n */\nexport function LoginPage() {\n const navigate = useNavigate();\n const location = useLocation();\n const [searchParams] = useSearchParams();\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n },\n [t],\n );\n const { signIn, isLoading, error, errorCode, resendVerification } =\n useAuthOperations();\n const { completeAuthFlow } = useMyWorkspaces();\n const { setWorkspace, setOrganization } = useActiveContext();\n const { initiateGoogleOAuth, initiateMicrosoftOAuth, isProviderConfigured } =\n useOAuthOperations();\n const mfaRequired = useAuthStore((state) => state.mfaRequired);\n const { executeRecaptcha: executeCaptchaV3 } = useCaptchaV3();\n const [isCompletingAuth, setIsCompletingAuth] = useState(false);\n const [isSsoRedirecting, setIsSsoRedirecting] = useState(false);\n\n /**\n * Build the URL that should receive the post-login redirect.\n *\n * For cross-eTLD+1 SSO on alpha/prod we detour through the central bridge\n * host (alphaapp/app.burdenoff.com) so the freshly-issued tokens land in\n * the bridge origin's first-party (unpartitioned) localStorage. Without\n * this step, a later direct visit to the bridge host has no local tokens\n * and no shared cookie (cross-eTLD+1 cookies don't propagate), so the user\n * sees the login page again.\n *\n * Skipped when:\n * - Already on the bridge host origin (would be a no-op self-redirect).\n * - The user's intended destination is itself on the bridge host\n * (likely an in-product navigation, no deposit needed).\n * - The freshly-stored profile snapshot can't be read (best-effort only —\n * login still succeeds via the direct redirect).\n */\n const buildPostLoginUrl = useCallback((intendedPath: string): string => {\n if (typeof window === \"undefined\") return intendedPath;\n try {\n const bridge = resolveAuthBridgeConfig();\n const bridgeUrl = new URL(bridge.url);\n const currentOrigin = window.location.origin;\n if (bridgeUrl.origin === currentOrigin) return intendedPath;\n\n // intendedPath can be a path or an absolute URL; resolve to absolute\n // against current origin so the bridge host knows where to send the\n // user back.\n const absoluteIntended = new URL(intendedPath, currentOrigin).href;\n\n const profileId = resolveActiveProfileId();\n if (!profileId) return intendedPath;\n const tokens = getProfileTokens(profileId);\n const user = getProfileUser(profileId);\n if (!tokens || !user) return intendedPath;\n const workspaceToken = getProfileWorkspaceToken(profileId);\n\n const payload = {\n profileId,\n tokens: JSON.stringify({ ...tokens, workspaceToken }),\n user: JSON.stringify(user),\n profiles: JSON.stringify(getProfiles()),\n };\n const encoded = btoa(JSON.stringify(payload));\n const deposit = new URL(\"/auth/sso-deposit\", bridgeUrl.origin);\n deposit.searchParams.set(\"returnTo\", absoluteIntended);\n return `${deposit.href}#sso=${encoded}`;\n } catch {\n return intendedPath;\n }\n }, []);\n\n const redirectAfterLogin = useCallback(\n (path: string) => {\n if (typeof window !== \"undefined\") {\n try {\n sessionStorage.setItem(\"bf-post-login-return-to\", path);\n } catch {\n // Ignore storage failures; redirect still proceeds.\n }\n window.location.assign(buildPostLoginUrl(path));\n return;\n }\n navigate(path);\n },\n [navigate, buildPostLoginUrl],\n );\n const [showPassword, setShowPassword] = useState(false);\n const [isAccountInactive, setIsAccountInactive] = useState(false);\n const [isAccountDeleted, setIsAccountDeleted] = useState(false);\n const [isAccountSuspended, setIsAccountSuspended] = useState(false);\n const [isOAuthOnlyAccount, setIsOAuthOnlyAccount] = useState(false);\n const [isEmailNotVerified, setIsEmailNotVerified] = useState(false);\n const [isResendingVerification, setIsResendingVerification] = useState(false);\n const [resendSuccess, setResendSuccess] = useState(false);\n const [oauthError, setOauthError] = useState<string | null>(null);\n const [infoMessage, setInfoMessage] = useState<string | null>(null);\n const [formData, setFormData] = useState<\n SignInInput & { rememberMe?: boolean }\n >({\n email: \"\",\n password: \"\",\n rememberMe: false,\n });\n const [validationError, setValidationError] = useState<string | null>(null);\n const [isDuplicateAccount, setIsDuplicateAccount] = useState(false);\n\n // Detect add-account mode and returnTo from URL params\n const isAddAccountMode = searchParams.get(\"mode\") === \"add-account\";\n const returnTo = searchParams.get(\"returnTo\");\n const initialReturnToRef = useRef<string | null>(returnTo);\n\n useEffect(() => {\n if (!initialReturnToRef.current && returnTo) {\n initialReturnToRef.current = returnTo;\n }\n }, [returnTo]);\n\n useEffect(() => {\n if (isAddAccountMode || isCompletingAuth) return;\n\n const profileId = resolveActiveProfileId();\n if (profileId) {\n const tokens = getProfileTokens(profileId);\n const user = getProfileUser(profileId);\n if (\n tokens?.accessToken &&\n user &&\n hasValidAccessToken(tokens.accessToken)\n ) {\n useAuthStore.getState().loadFromProfile(profileId);\n redirectAfterLogin(decodeSameOriginReturnTo(returnTo) ?? \"/\");\n return;\n }\n }\n\n const ssoUrl = getSSORedirectUrl();\n if (ssoUrl) {\n setIsSsoRedirecting(true);\n window.location.replace(ssoUrl);\n }\n }, [isAddAccountMode, isCompletingAuth, redirectAfterLogin, returnTo]);\n\n // Load remembered email on mount and clean up any legacy plaintext password storage\n useEffect(() => {\n try {\n // Security cleanup: remove any previously stored plaintext passwords\n localStorage.removeItem(\"remembered_password\");\n\n const rememberedEmail = localStorage.getItem(\"remembered_email\");\n\n if (rememberedEmail) {\n setFormData((prev) => ({\n ...prev,\n email: rememberedEmail,\n rememberMe: true,\n }));\n }\n } catch (error) {\n console.warn(\"[LoginPage] Failed to load remembered email:\", error);\n }\n }, []);\n\n // Check for redirect message and email from location state (e.g., from expired MFA or SSO redirect)\n useEffect(() => {\n const state = location.state as { message?: string; email?: string } | null;\n if (state?.message) {\n setInfoMessage(state.message);\n }\n if (state?.email) {\n setFormData((prev) => ({ ...prev, email: state.email }));\n }\n // Clear state to prevent showing message on refresh\n if (state) {\n window.history.replaceState({}, document.title);\n }\n }, [location.state]);\n\n // Determine error type from GraphQL error code (primary) or message (fallback).\n // The backend sends structured codes in extensions.code which are propagated\n // through useAuthOperations → authStore.errorCode.\n const checkErrorType = (\n err: string | null,\n code?: string | null,\n ):\n | \"DELETED\"\n | \"SUSPENDED\"\n | \"OAUTH_ONLY\"\n | \"INACTIVE\"\n | \"EMAIL_NOT_VERIFIED\"\n | null => {\n // Primary: match on GraphQL error code from extensions\n if (code) {\n switch (code) {\n case \"ACCOUNT_DELETED\":\n return \"DELETED\";\n case \"ACCOUNT_SUSPENDED\":\n return \"SUSPENDED\";\n case \"OAUTH_ONLY_ACCOUNT\":\n return \"OAUTH_ONLY\";\n case \"ACCOUNT_INACTIVE\":\n return \"INACTIVE\";\n case \"EMAIL_NOT_VERIFIED\":\n return \"EMAIL_NOT_VERIFIED\";\n default:\n break;\n }\n }\n\n // Fallback: match on error message for non-GraphQL errors or legacy responses\n if (!err) return null;\n const lowerErr = err.toLowerCase();\n\n if (\n lowerErr.includes(\"deleted\") ||\n lowerErr.includes(\"permanently deleted\")\n ) {\n return \"DELETED\";\n }\n if (lowerErr.includes(\"suspended\")) {\n return \"SUSPENDED\";\n }\n if (\n lowerErr.includes(\"oauth provider\") ||\n lowerErr.includes(\"oauth-only\") ||\n lowerErr.includes(\"oauth only\")\n ) {\n return \"OAUTH_ONLY\";\n }\n if (\n lowerErr.includes(\"inactive\") ||\n lowerErr.includes(\"deactivated\") ||\n lowerErr.includes(\"not active\")\n ) {\n return \"INACTIVE\";\n }\n if (\n lowerErr.includes(\"verify your email\") ||\n lowerErr.includes(\"email not verified\") ||\n lowerErr.includes(\"email_not_verified\")\n ) {\n return \"EMAIL_NOT_VERIFIED\";\n }\n return null;\n };\n\n // Pre-fill email from URL query parameter (e.g., after email verification)\n useEffect(() => {\n const emailFromUrl = searchParams.get(\"email\");\n if (emailFromUrl) {\n setFormData((prev) => ({ ...prev, email: emailFromUrl }));\n }\n\n // Check for expired session redirect\n const isExpired = searchParams.get(\"expired\");\n if (isExpired === \"true\") {\n setInfoMessage(\n tr(\n \"auth.login.sessionExpired\",\n \"Your session has expired. Please sign in again.\",\n ),\n );\n // Clear the query param to prevent showing on refresh\n const newUrl = window.location.pathname;\n window.history.replaceState({}, document.title, newUrl);\n }\n }, [searchParams, tr]);\n\n // Redirect to MFA verification when required\n useEffect(() => {\n if (mfaRequired) {\n navigate(\"/auth/mfa-verify\");\n }\n }, [mfaRequired, navigate]);\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setIsAccountInactive(false);\n setIsAccountDeleted(false);\n setIsAccountSuspended(false);\n setIsOAuthOnlyAccount(false);\n setIsEmailNotVerified(false);\n setIsDuplicateAccount(false);\n setResendSuccess(false);\n setValidationError(null);\n\n // Client-side validation\n const emailTrimmed = (formData.email ?? \"\").trim();\n if (!emailTrimmed) {\n setValidationError(\n tr(\n \"auth.login.enterEmailOrUsername\",\n \"Please enter your email or username\",\n ),\n );\n return;\n }\n\n // Basic email format validation (if it contains @)\n if (emailTrimmed.includes(\"@\")) {\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n if (!emailRegex.test(emailTrimmed)) {\n setValidationError(\n tr(\"auth.login.invalidEmail\", \"Please enter a valid email address\"),\n );\n return;\n }\n }\n\n if (!formData.password || formData.password.length < 1) {\n setValidationError(\n tr(\"auth.login.enterPassword\", \"Please enter your password\"),\n );\n return;\n }\n\n // In add-account mode, check if this email is already signed in\n if (isAddAccountMode) {\n const existingProfiles = getProfiles();\n const alreadySignedIn = existingProfiles.some(\n (p) => p.email.toLowerCase() === emailTrimmed.toLowerCase(),\n );\n if (alreadySignedIn) {\n setIsDuplicateAccount(true);\n setValidationError(null);\n return;\n }\n }\n setIsDuplicateAccount(false);\n\n try {\n // Get reCAPTCHA v3 token (no-op when captcha is disabled)\n const recaptchaToken = await executeCaptchaV3(\"login\");\n\n const result = await signIn({\n email: emailTrimmed,\n password: formData.password,\n rememberMe: formData.rememberMe,\n ...(recaptchaToken ? { recaptchaToken } : {}),\n });\n\n // If MFA is required, the useEffect above will handle redirect\n if (result.requiresMFA) {\n return;\n }\n\n // Successful login - complete auth flow (fetch workspaces + issue token)\n if (result.success) {\n try {\n const profileId = result.user?.id;\n if (profileId) {\n const hostname = window.location.hostname;\n const domain = hostname.endsWith(\".local.burdenoff.com\")\n ? \".local.burdenoff.com\"\n : undefined;\n const domainPart = domain ? `; domain=${domain}` : \"\";\n const securePart =\n window.location.protocol === \"https:\" ? \"; secure\" : \"\";\n const ssoValue = JSON.stringify({\n id: profileId,\n origin: window.location.origin,\n });\n document.cookie = `bf-sso=${encodeURIComponent(ssoValue)}${domainPart}; path=/; samesite=lax${securePart}; max-age=86400`;\n document.cookie = `bf-sso-profile=${encodeURIComponent(ssoValue)}${domainPart}; path=/; samesite=lax${securePart}; max-age=86400`;\n }\n } catch {\n // Best effort only. Login can continue without the fast-path cookie.\n }\n\n // Handle Remember Me: Save or clear email only (never store passwords)\n try {\n if (formData.rememberMe) {\n localStorage.setItem(\"remembered_email\", emailTrimmed);\n } else {\n localStorage.removeItem(\"remembered_email\");\n }\n } catch (error) {\n console.warn(\n \"[LoginPage] Failed to save/clear remembered email:\",\n error,\n );\n }\n\n setIsCompletingAuth(true);\n // In add-account mode, redirect back to where the user was\n const effectiveReturnTo = initialReturnToRef.current || returnTo;\n const postLoginPath = effectiveReturnTo\n ? decodeURIComponent(effectiveReturnTo)\n : \"/dashboard\";\n try {\n type AuthFlowResult = Awaited<ReturnType<typeof completeAuthFlow>>;\n // Pass the token directly to avoid race condition with React state updates\n const authFlowResult = await Promise.race<AuthFlowResult>([\n completeAuthFlow(result.token),\n new Promise<AuthFlowResult>((resolve) => {\n window.setTimeout(() => {\n resolve({\n success: false,\n error: \"Auth flow timed out after login\",\n });\n }, 5000);\n }),\n ]);\n\n if (authFlowResult.success) {\n // Sync workspace and organization to ActiveContext\n // This is critical for global search and other workspace-scoped features\n if (authFlowResult.workspace) {\n setWorkspace(authFlowResult.workspace.id);\n if (authFlowResult.workspace.organizationId) {\n setOrganization(authFlowResult.workspace.organizationId);\n }\n }\n\n // Full auth flow completed - redirect to dashboard or returnTo\n redirectAfterLogin(postLoginPath);\n } else if (authFlowResult.error?.includes(\"No workspaces found\")) {\n // No workspaces - redirect to onboarding/workspace creation\n navigate(\"/onboarding/create-workspace\");\n } else {\n // Partial success or error - still sync workspace if available and redirect\n if (authFlowResult.workspace) {\n setWorkspace(authFlowResult.workspace.id);\n if (authFlowResult.workspace.organizationId) {\n setOrganization(authFlowResult.workspace.organizationId);\n }\n }\n console.warn(\"Auth flow warning:\", authFlowResult.error);\n redirectAfterLogin(postLoginPath);\n }\n } catch (authFlowError) {\n // Auth flow failed but login succeeded - still redirect\n console.error(\"Auth flow error (login succeeded):\", authFlowError);\n redirectAfterLogin(postLoginPath);\n } finally {\n setIsCompletingAuth(false);\n }\n }\n } catch (err) {\n if (import.meta.env.DEV) {\n console.error(\"Sign in failed:\", err);\n }\n // Check error type using error code (from store) and message\n const errorMsg = err instanceof Error ? err.message : String(err);\n if (errorMsg.includes(\"reCAPTCHA\")) {\n setValidationError(errorMsg);\n }\n const storeErrorCode = useAuthStore.getState().errorCode;\n const errorType = checkErrorType(errorMsg, storeErrorCode);\n if (errorType === \"DELETED\") {\n setIsAccountDeleted(true);\n } else if (errorType === \"SUSPENDED\") {\n setIsAccountSuspended(true);\n } else if (errorType === \"OAUTH_ONLY\") {\n setIsOAuthOnlyAccount(true);\n } else if (errorType === \"INACTIVE\") {\n setIsAccountInactive(true);\n } else if (errorType === \"EMAIL_NOT_VERIFIED\") {\n setIsEmailNotVerified(true);\n }\n }\n };\n\n // Also check the error from useAuthOperations\n useEffect(() => {\n if (error) {\n const errorType = checkErrorType(error, errorCode);\n if (errorType === \"DELETED\") {\n setIsAccountDeleted(true);\n } else if (errorType === \"SUSPENDED\") {\n setIsAccountSuspended(true);\n } else if (errorType === \"OAUTH_ONLY\") {\n setIsOAuthOnlyAccount(true);\n } else if (errorType === \"INACTIVE\") {\n setIsAccountInactive(true);\n } else if (errorType === \"EMAIL_NOT_VERIFIED\") {\n setIsEmailNotVerified(true);\n }\n }\n }, [error, errorCode]);\n\n /**\n * Handle Google OAuth login\n */\n const handleGoogleLogin = () => {\n setOauthError(null);\n try {\n initiateGoogleOAuth();\n } catch (err) {\n setOauthError(\n err instanceof Error\n ? err.message\n : tr(\n \"auth.login.googleSignInFailed\",\n \"Failed to initiate Google sign in\",\n ),\n );\n }\n };\n\n /**\n * Handle Microsoft OAuth login\n */\n const handleMicrosoftLogin = () => {\n setOauthError(null);\n try {\n initiateMicrosoftOAuth();\n } catch (err) {\n setOauthError(\n err instanceof Error\n ? err.message\n : tr(\n \"auth.login.microsoftSignInFailed\",\n \"Failed to initiate Microsoft sign in\",\n ),\n );\n }\n };\n\n /**\n * Handle resend verification email\n */\n const handleResendVerification = async () => {\n setIsResendingVerification(true);\n setResendSuccess(false);\n try {\n const emailTrimmed = (formData.email ?? \"\").trim();\n const result = await resendVerification(emailTrimmed);\n if (result.success) {\n setResendSuccess(true);\n } else {\n setValidationError(\n tr(\n \"auth.login.resendFailed\",\n \"Failed to resend verification email. Please try again.\",\n ),\n );\n }\n } catch (err) {\n console.error(\"Failed to resend verification email:\", err);\n setValidationError(\n err instanceof Error\n ? err.message\n : tr(\n \"auth.login.resendFailed\",\n \"Failed to resend verification email. Please try again.\",\n ),\n );\n } finally {\n setIsResendingVerification(false);\n }\n };\n\n // Check if OAuth providers are configured\n const isGoogleConfigured = isProviderConfigured(\"google\");\n const isMicrosoftConfigured = isProviderConfigured(\"microsoft\");\n\n const oauthProviders = [\n {\n name: \"Google\",\n icon: GoogleIcon,\n onClick: handleGoogleLogin,\n disabled: !isGoogleConfigured,\n tooltip: isGoogleConfigured\n ? undefined\n : tr(\n \"auth.login.googleNotConfigured\",\n \"Google sign in is not configured\",\n ),\n },\n // {\n // name: \"GitHub\",\n // icon: GitHubIcon,\n // onClick: () => {},\n // disabled: true,\n // tooltip: tr(\n // \"auth.login.githubNotConfigured\",\n // \"GitHub sign in is not configured\",\n // ),\n // },\n {\n name: \"Microsoft\",\n icon: MicrosoftIcon,\n onClick: handleMicrosoftLogin,\n disabled: !isMicrosoftConfigured,\n tooltip: isMicrosoftConfigured\n ? undefined\n : tr(\n \"auth.login.microsoftNotConfigured\",\n \"Microsoft sign in is not configured\",\n ),\n },\n ];\n\n if (isSsoRedirecting) {\n return <SSORedirectSpinner />;\n }\n\n return (\n <AuthLayout\n title={tr(\"auth.login.welcomeBack\", \"Welcome back\")}\n subtitle={tr(\n \"auth.login.enterCredentials\",\n \"Enter your credentials to continue\",\n )}\n >\n <form onSubmit={handleSubmit} className=\"space-y-5\">\n {/* Info Message (e.g., from expired MFA redirect) */}\n {infoMessage && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl border bg-status-info-bg-subtle border-status-info-border\">\n <svg\n className=\"w-5 h-5 flex-shrink-0 mt-0.5 text-text-link\"\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=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <p className=\"text-sm text-status-info-text\">{infoMessage}</p>\n </div>\n )}\n\n {/* Validation Error */}\n {validationError && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl border bg-status-warning-bg-subtle border-status-warning-border\">\n <AlertIcon className=\"w-5 h-5 flex-shrink-0 mt-0.5 text-status-warning-text\" />\n <p className=\"text-sm text-status-warning-text\">\n {validationError}\n </p>\n </div>\n )}\n\n {/* Duplicate Account Warning (Add Account mode) */}\n {isDuplicateAccount && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl border bg-status-warning-bg-subtle border-status-warning-border\">\n <AlertIcon className=\"w-5 h-5 flex-shrink-0 mt-0.5 text-status-warning-text\" />\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-status-warning-text\">\n {tr(\n \"auth.login.duplicateAccount\",\n \"This account is already signed in.\",\n )}\n </p>\n <p className=\"mt-1 text-sm text-status-warning-text/80\">\n {tr(\n \"auth.login.duplicateAccountHint\",\n \"Please use a different account, or go back and switch to this account from the profile menu.\",\n )}\n </p>\n {(initialReturnToRef.current || returnTo) && (\n <button\n type=\"button\"\n onClick={() =>\n navigate(\n decodeURIComponent(\n initialReturnToRef.current || returnTo || \"/dashboard\",\n ),\n )\n }\n className=\"mt-3 inline-flex items-center gap-2 px-4 py-2.5 rounded-xl border border-status-warning-border bg-bg-surface text-status-warning-text text-sm font-medium hover:bg-status-warning-bg-subtle hover:border-status-warning-border transition-all shadow-sm\"\n >\n <svg\n className=\"w-4 h-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=\"M10 19l-7-7m0 0l7-7m-7 7h18\"\n />\n </svg>\n {tr(\n \"auth.login.goBackSwitchAccount\",\n \"Go back and switch account\",\n )}\n </button>\n )}\n </div>\n </div>\n )}\n\n {/* Error Alert */}\n {error && !validationError && !isDuplicateAccount && (\n <div\n className={`flex items-start gap-3 p-4 rounded-xl border ${\n isAccountInactive || isOAuthOnlyAccount || isEmailNotVerified\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={`w-5 h-5 flex-shrink-0 mt-0.5 ${\n isAccountInactive || isOAuthOnlyAccount || isEmailNotVerified\n ? \"text-status-warning-text\"\n : \"text-status-error-text\"\n }`}\n />\n <div className=\"flex-1\">\n <p\n className={`text-sm ${\n isAccountInactive || isOAuthOnlyAccount || isEmailNotVerified\n ? \"text-status-warning-text\"\n : \"text-status-error-text\"\n }`}\n >\n {isAccountDeleted\n ? tr(\n \"auth.login.accountDeleted\",\n \"This account has been permanently deleted and cannot be recovered.\",\n )\n : isAccountSuspended\n ? tr(\n \"auth.login.accountSuspended\",\n \"Your account has been suspended. Please contact support for assistance.\",\n )\n : isAccountInactive\n ? tr(\n \"auth.login.accountDeactivated\",\n \"Your account has been deactivated.\",\n )\n : isOAuthOnlyAccount\n ? tr(\n \"auth.login.oauthOnly\",\n \"This account uses OAuth sign-in. Please use Google or Microsoft to sign in.\",\n )\n : isEmailNotVerified\n ? tr(\n \"auth.login.emailNotVerified\",\n \"Please verify your email before signing in. Check your inbox for the verification link.\",\n )\n : error}\n </p>\n {isAccountInactive && (\n <button\n type=\"button\"\n onClick={() =>\n navigate(\"/auth/reactivate\", {\n state: { email: formData.email },\n })\n }\n className=\"mt-2 text-sm font-semibold text-status-warning-text hover:text-status-warning-text underline underline-offset-2\"\n >\n {tr(\n \"auth.login.clickReactivate\",\n \"Click here to reactivate your account\",\n )}\n </button>\n )}\n {isEmailNotVerified && (\n <div className=\"mt-3\">\n {resendSuccess ? (\n <div className=\"flex items-center gap-2 text-sm text-status-success-text\">\n <svg\n className=\"w-4 h-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=\"M5 13l4 4L19 7\"\n />\n </svg>\n {tr(\n \"auth.login.verificationSent\",\n \"Verification email sent! Please check your inbox.\",\n )}\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={handleResendVerification}\n disabled={isResendingVerification}\n className=\"inline-flex items-center gap-2 px-4 py-2.5 rounded-xl border border-status-warning-border bg-bg-surface text-status-warning-text text-sm font-medium hover:bg-status-warning-bg-subtle hover:border-status-warning-border transition-all shadow-sm disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isResendingVerification ? (\n <>\n <SpinnerIcon className=\"w-4 h-4 animate-spin\" />\n {tr(\"auth.login.sending\", \"Sending...\")}\n </>\n ) : (\n <>\n <svg\n className=\"w-4 h-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=\"M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z\"\n />\n </svg>\n {tr(\n \"auth.login.resendVerification\",\n \"Resend verification email\",\n )}\n </>\n )}\n </button>\n )}\n </div>\n )}\n {isOAuthOnlyAccount && (\n <div className=\"mt-3 flex flex-col gap-2\">\n {isProviderConfigured(\"google\") && (\n <button\n type=\"button\"\n onClick={handleGoogleLogin}\n className=\"inline-flex items-center justify-center gap-2 px-4 py-2.5 rounded-xl border border-border-default bg-bg-surface text-text-primary text-sm font-medium hover:bg-bg-canvas hover:border-border-default transition-all shadow-sm\"\n >\n <GoogleIcon className=\"w-5 h-5\" />\n {tr(\"auth.login.signInWithGoogle\", \"Sign in with Google\")}\n </button>\n )}\n {isProviderConfigured(\"microsoft\") && (\n <button\n type=\"button\"\n onClick={handleMicrosoftLogin}\n className=\"inline-flex items-center justify-center gap-2 px-4 py-2.5 rounded-xl border border-border-default bg-bg-surface text-text-primary text-sm font-medium hover:bg-bg-canvas hover:border-border-default transition-all shadow-sm\"\n >\n <MicrosoftIcon className=\"w-5 h-5\" />\n {tr(\n \"auth.login.signInWithMicrosoft\",\n \"Sign in with Microsoft\",\n )}\n </button>\n )}\n </div>\n )}\n </div>\n </div>\n )}\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.login.emailLabel\", \"Email\")}\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=\"w-5 h-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"email\"\n type=\"text\"\n autoComplete=\"email username\"\n required\n aria-required=\"true\"\n value={formData.email}\n onChange={(e) =>\n setFormData({ ...formData, email: e.target.value })\n }\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(\"auth.login.emailPlaceholder\", \"you@example.com\")}\n />\n </div>\n </div>\n\n {/* Password Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"password\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.login.passwordLabel\", \"Password\")}\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 <LockIcon className=\"w-5 h-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"password\"\n type={showPassword ? \"text\" : \"password\"}\n autoComplete=\"current-password\"\n required\n aria-required=\"true\"\n value={formData.password}\n onChange={(e) =>\n setFormData({ ...formData, password: e.target.value })\n }\n className=\"block w-full pl-[3.5rem] pr-12 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.login.passwordPlaceholder\",\n \"Enter your password\",\n )}\n />\n <button\n type=\"button\"\n onClick={() => setShowPassword(!showPassword)}\n aria-label={showPassword ? \"Hide password\" : \"Show password\"}\n className=\"absolute inset-y-0 right-0 pr-3.5 flex items-center text-[var(--color-text-tertiary)] hover:text-[var(--color-text-secondary)] transition-colors\"\n >\n {showPassword ? (\n <EyeOffIcon className=\"w-5 h-5\" />\n ) : (\n <EyeIcon className=\"w-5 h-5\" />\n )}\n </button>\n </div>\n </div>\n\n {/* Remember & Forgot */}\n <div className=\"flex items-center justify-between\">\n <label className=\"flex items-center gap-2 cursor-pointer group\">\n <div className=\"relative\">\n <input\n type=\"checkbox\"\n checked={formData.rememberMe}\n onChange={(e) => {\n const isChecked = e.target.checked;\n setFormData({ ...formData, rememberMe: isChecked });\n\n // Clear saved email immediately when unchecked\n if (!isChecked) {\n try {\n localStorage.removeItem(\"remembered_email\");\n } catch (error) {\n console.warn(\n \"[LoginPage] Failed to clear remembered email:\",\n error,\n );\n }\n }\n }}\n className=\"sr-only peer\"\n />\n <div className=\"w-5 h-5 rounded-md border-2 border-[var(--color-border-default)] bg-[var(--color-bg-surface)] peer-checked:bg-status-info-bg-subtle0 peer-checked:border-action-primary-bg transition-all peer-focus:ring-2 peer-focus:ring-action-primary-bg/20\">\n <svg\n className=\"w-full h-full text-white opacity-0 peer-checked:opacity-100 transition-opacity p-0.5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={3}\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"M5 13l4 4L19 7\"\n />\n </svg>\n </div>\n </div>\n <span className=\"text-sm text-[var(--color-text-secondary)] group-hover:text-[var(--color-text-primary)] transition-colors\">\n {tr(\"auth.login.rememberMe\", \"Remember me\")}\n </span>\n </label>\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/reset-password\")}\n className=\"text-sm font-medium text-text-link hover:text-text-linkHover transition-colors\"\n >\n {tr(\"auth.login.forgotPassword\", \"Forgot password?\")}\n </button>\n </div>\n\n {/* Submit Button */}\n <button\n type=\"submit\"\n disabled={isLoading || isCompletingAuth}\n className=\"auth-primary-button flex w-full items-center justify-center gap-2 px-4 py-3 font-semibold\"\n >\n {isLoading || isCompletingAuth ? (\n <>\n <SpinnerIcon className=\"w-5 h-5 animate-spin\" />\n <span>\n {isCompletingAuth\n ? tr(\n \"auth.login.settingUpWorkspace\",\n \"Setting up workspace...\",\n )\n : tr(\"auth.login.signingIn\", \"Signing in...\")}\n </span>\n </>\n ) : (\n tr(\"auth.login.signIn\", \"Sign in\")\n )}\n </button>\n </form>\n\n {/* Divider */}\n <div className=\"relative my-8\">\n <div className=\"absolute inset-0 flex items-center\">\n <div className=\"w-full border-t border-[var(--color-border-default)]\" />\n </div>\n <div className=\"relative flex justify-center\">\n <span className=\"px-4 bg-[var(--color-bg-canvas)] text-sm text-[var(--color-text-tertiary)]\">\n {tr(\"auth.login.orContinueWith\", \"or continue with\")}\n </span>\n </div>\n </div>\n\n {/* OAuth Error */}\n {oauthError && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl border bg-status-error-bg-subtle border-status-error-border\">\n <AlertIcon className=\"w-5 h-5 flex-shrink-0 mt-0.5 text-status-error-text\" />\n <p className=\"text-sm text-status-error-text\">{oauthError}</p>\n </div>\n )}\n\n {/* OAuth Buttons */}\n <div className=\"grid grid-cols-3 gap-3\">\n {oauthProviders.map((provider) => (\n <button\n key={provider.name}\n type=\"button\"\n onClick={provider.onClick}\n disabled={provider.disabled}\n title={provider.tooltip}\n className={`flex items-center justify-center gap-2 px-4 py-3 rounded-xl border transition-all duration-200 group ${\n provider.disabled\n ? \"border-[var(--color-border-subtle)] bg-[var(--color-bg-disabled)] cursor-not-allowed opacity-50\"\n : \"border-[var(--color-border-default)] bg-[var(--color-bg-surface)] hover:bg-[var(--color-bg-sunken)] hover:border-[var(--color-border-strong)]\"\n }`}\n >\n <provider.icon\n className={`w-5 h-5 ${provider.disabled ? \"opacity-50\" : \"\"}`}\n />\n <span\n className={`sr-only sm:not-sr-only text-sm font-medium transition-colors hidden sm:inline ${\n provider.disabled\n ? \"text-[var(--color-text-disabled)]\"\n : \"text-[var(--color-text-secondary)] group-hover:text-[var(--color-text-primary)]\"\n }`}\n >\n {provider.name}\n </span>\n </button>\n ))}\n </div>\n\n {/* Enterprise SSO Option — temporarily hidden */}\n {/* <div className=\"mt-6\">\n <button\n onClick={() => navigate(\"/auth/sso/login\")}\n className=\"w-full flex items-center justify-center gap-3 px-4 py-3 rounded-xl border-2 border-dashed border-[var(--color-border-default)] hover:border-action-primary-bg bg-[var(--color-bg-surface)] hover:bg-status-info-bg-subtle transition-all duration-200 group\"\n >\n <svg\n className=\"w-5 h-5 text-[var(--color-text-tertiary)] group-hover:text-text-link\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4\"\n />\n </svg>\n <span className=\"text-sm font-medium text-[var(--color-text-secondary)] group-hover:text-text-link\">\n {tr(\"auth.login.enterpriseSso\", \"Sign in with Enterprise SSO\")}\n </span>\n </button>\n </div> */}\n\n {/* Magic Link Option */}\n <div className=\"mt-4 text-center\">\n <button\n onClick={() => navigate(\"/auth/magic-link\")}\n className=\"text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] transition-colors\"\n >\n {tr(\"auth.login.preferPasswordless\", \"Prefer passwordless login?\")}{\" \"}\n <span className=\"font-semibold text-text-link hover:text-text-linkHover\">\n {tr(\"auth.login.signInMagicLink\", \"Sign in with Magic Link\")}\n </span>\n </button>\n </div>\n\n {/* Sign Up Link */}\n <p className=\"mt-6 text-center text-sm text-[var(--color-text-secondary)]\">\n {tr(\"auth.login.dontHaveAccount\", \"Don't have an account?\")}{\" \"}\n <button\n onClick={() => navigate(\"/auth/register\")}\n className=\"font-semibold text-text-link hover:text-text-linkHover transition-colors\"\n >\n {tr(\"auth.login.signUp\", \"Sign up\")}\n </button>\n </p>\n </AuthLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"LoginPage.js","names":[],"sources":["../../../src/auth/pages/LoginPage.tsx"],"sourcesContent":["import { useState, useEffect, useCallback, useRef } from \"react\";\nimport { useNavigate, useSearchParams, useLocation } from \"react-router-dom\";\nimport {\n useI18n,\n useActiveContext,\n} from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport {\n getSSORedirectUrl,\n SSORedirectSpinner,\n} from \"@burdenoff/fe-libs/shared/components\";\nimport { resolveAuthBridgeConfig } from \"@burdenoff/fe-libs/shared/config\";\n\nimport {\n getProfileTokens,\n getProfileUser,\n getProfiles,\n getProfileWorkspaceToken,\n resolveActiveProfileId,\n} from \"@burdenoff/fe-libs/shared/providers/shell/ProfileStorageManager\";\nimport { useAuthOperations, useMyWorkspaces } from \"../hooks\";\nimport { useOAuthOperations } from \"../hooks/useOAuthOperations\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { AuthLayout } from \"../components/AuthLayout\";\nimport {\n EmailIcon,\n LockIcon,\n EyeIcon,\n EyeOffIcon,\n AlertIcon,\n SpinnerIcon,\n GoogleIcon,\n // GitHubIcon,\n MicrosoftIcon,\n} from \"../components/AuthIcons\";\nimport { useCaptchaV3 } from \"../hooks/useCaptchaV3\";\nimport type { SignInInput } from \"../types\";\n\nfunction hasValidAccessToken(token: string): boolean {\n try {\n const raw = token.split(\".\")[1].replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = raw.padEnd(Math.ceil(raw.length / 4) * 4, \"=\");\n const payload = JSON.parse(atob(padded)) as { exp?: unknown };\n return typeof payload.exp === \"number\" && payload.exp > Date.now() / 1000;\n } catch {\n return false;\n }\n}\n\nfunction decodeSameOriginReturnTo(returnTo: string | null): string | null {\n if (!returnTo) return null;\n try {\n const decoded = decodeURIComponent(returnTo);\n const url = new URL(decoded, window.location.origin);\n if (url.origin !== window.location.origin) return null;\n return `${url.pathname}${url.search}${url.hash}`;\n } catch {\n return null;\n }\n}\n\n/**\n * Login page with professional design\n * Provides email/password login and social OAuth options\n */\nexport function LoginPage() {\n const navigate = useNavigate();\n const location = useLocation();\n const [searchParams] = useSearchParams();\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n },\n [t],\n );\n const { signIn, isLoading, error, errorCode, resendVerification } =\n useAuthOperations();\n const { completeAuthFlow } = useMyWorkspaces();\n const { setWorkspace, setOrganization } = useActiveContext();\n const { initiateGoogleOAuth, initiateMicrosoftOAuth, isProviderConfigured } =\n useOAuthOperations();\n const mfaRequired = useAuthStore((state) => state.mfaRequired);\n const { executeRecaptcha: executeCaptchaV3 } = useCaptchaV3();\n const [isCompletingAuth, setIsCompletingAuth] = useState(false);\n const [isSsoRedirecting, setIsSsoRedirecting] = useState(false);\n\n /**\n * Build the URL that should receive the post-login redirect.\n *\n * For cross-eTLD+1 SSO on alpha/prod we detour through the central bridge\n * host (alphaapp/app.burdenoff.com) so the freshly-issued tokens land in\n * the bridge origin's first-party (unpartitioned) localStorage. Without\n * this step, a later direct visit to the bridge host has no local tokens\n * and no shared cookie (cross-eTLD+1 cookies don't propagate), so the user\n * sees the login page again.\n *\n * Skipped when:\n * - Already on the bridge host origin (would be a no-op self-redirect).\n * - The user's intended destination is itself on the bridge host\n * (likely an in-product navigation, no deposit needed).\n * - The freshly-stored profile snapshot can't be read (best-effort only —\n * login still succeeds via the direct redirect).\n */\n const buildPostLoginUrl = useCallback((intendedPath: string): string => {\n if (typeof window === \"undefined\") return intendedPath;\n try {\n const bridge = resolveAuthBridgeConfig();\n const bridgeUrl = new URL(bridge.url);\n const currentOrigin = window.location.origin;\n if (bridgeUrl.origin === currentOrigin) return intendedPath;\n\n // intendedPath can be a path or an absolute URL; resolve to absolute\n // against current origin so the bridge host knows where to send the\n // user back.\n const absoluteIntended = new URL(intendedPath, currentOrigin).href;\n\n const profileId = resolveActiveProfileId();\n if (!profileId) return intendedPath;\n const tokens = getProfileTokens(profileId);\n const user = getProfileUser(profileId);\n if (!tokens || !user) return intendedPath;\n const workspaceToken = getProfileWorkspaceToken(profileId);\n\n const payload = {\n profileId,\n tokens: JSON.stringify({ ...tokens, workspaceToken }),\n user: JSON.stringify(user),\n profiles: JSON.stringify(getProfiles()),\n };\n const encoded = btoa(JSON.stringify(payload));\n const deposit = new URL(\"/auth/sso-deposit\", bridgeUrl.origin);\n deposit.searchParams.set(\"returnTo\", absoluteIntended);\n return `${deposit.href}#sso=${encoded}`;\n } catch {\n return intendedPath;\n }\n }, []);\n\n const redirectAfterLogin = useCallback(\n (path: string) => {\n if (typeof window !== \"undefined\") {\n try {\n sessionStorage.setItem(\"bf-post-login-return-to\", path);\n } catch {\n // Ignore storage failures; redirect still proceeds.\n }\n window.location.assign(buildPostLoginUrl(path));\n return;\n }\n navigate(path);\n },\n [navigate, buildPostLoginUrl],\n );\n const [showPassword, setShowPassword] = useState(false);\n const [isAccountInactive, setIsAccountInactive] = useState(false);\n const [isAccountDeleted, setIsAccountDeleted] = useState(false);\n const [isAccountSuspended, setIsAccountSuspended] = useState(false);\n const [isOAuthOnlyAccount, setIsOAuthOnlyAccount] = useState(false);\n const [isEmailNotVerified, setIsEmailNotVerified] = useState(false);\n const [isResendingVerification, setIsResendingVerification] = useState(false);\n const [resendSuccess, setResendSuccess] = useState(false);\n const [oauthError, setOauthError] = useState<string | null>(null);\n const [infoMessage, setInfoMessage] = useState<string | null>(null);\n const [formData, setFormData] = useState<\n SignInInput & { rememberMe?: boolean }\n >({\n email: \"\",\n password: \"\",\n rememberMe: false,\n });\n const [validationError, setValidationError] = useState<string | null>(null);\n const [isDuplicateAccount, setIsDuplicateAccount] = useState(false);\n\n // Detect add-account mode and returnTo from URL params\n const isAddAccountMode = searchParams.get(\"mode\") === \"add-account\";\n const returnTo = searchParams.get(\"returnTo\");\n const initialReturnToRef = useRef<string | null>(returnTo);\n\n useEffect(() => {\n if (!initialReturnToRef.current && returnTo) {\n initialReturnToRef.current = returnTo;\n }\n }, [returnTo]);\n\n useEffect(() => {\n if (isAddAccountMode || isCompletingAuth) return;\n\n const profileId = resolveActiveProfileId();\n if (profileId) {\n const tokens = getProfileTokens(profileId);\n const user = getProfileUser(profileId);\n if (\n tokens?.accessToken &&\n user &&\n hasValidAccessToken(tokens.accessToken)\n ) {\n useAuthStore.getState().loadFromProfile(profileId);\n redirectAfterLogin(decodeSameOriginReturnTo(returnTo) ?? \"/\");\n return;\n }\n }\n\n const ssoUrl = getSSORedirectUrl();\n if (ssoUrl) {\n setIsSsoRedirecting(true);\n window.location.replace(ssoUrl);\n }\n }, [isAddAccountMode, isCompletingAuth, redirectAfterLogin, returnTo]);\n\n // Load remembered email on mount and clean up any legacy plaintext password storage\n useEffect(() => {\n try {\n // Security cleanup: remove any previously stored plaintext passwords\n localStorage.removeItem(\"remembered_password\");\n\n const rememberedEmail = localStorage.getItem(\"remembered_email\");\n\n if (rememberedEmail) {\n setFormData((prev) => ({\n ...prev,\n email: rememberedEmail,\n rememberMe: true,\n }));\n }\n } catch (error) {\n console.warn(\"[LoginPage] Failed to load remembered email:\", error);\n }\n }, []);\n\n // Check for redirect message and email from location state (e.g., from expired MFA or SSO redirect)\n useEffect(() => {\n const state = location.state as { message?: string; email?: string } | null;\n if (state?.message) {\n setInfoMessage(state.message);\n }\n if (state?.email) {\n setFormData((prev) => ({ ...prev, email: state.email }));\n }\n // Clear state to prevent showing message on refresh\n if (state) {\n window.history.replaceState({}, document.title);\n }\n }, [location.state]);\n\n // Determine error type from GraphQL error code (primary) or message (fallback).\n // The backend sends structured codes in extensions.code which are propagated\n // through useAuthOperations → authStore.errorCode.\n const checkErrorType = (\n err: string | null,\n code?: string | null,\n ):\n | \"DELETED\"\n | \"SUSPENDED\"\n | \"OAUTH_ONLY\"\n | \"INACTIVE\"\n | \"EMAIL_NOT_VERIFIED\"\n | null => {\n // Primary: match on GraphQL error code from extensions\n if (code) {\n switch (code) {\n case \"ACCOUNT_DELETED\":\n return \"DELETED\";\n case \"ACCOUNT_SUSPENDED\":\n return \"SUSPENDED\";\n case \"OAUTH_ONLY_ACCOUNT\":\n return \"OAUTH_ONLY\";\n case \"ACCOUNT_INACTIVE\":\n return \"INACTIVE\";\n case \"EMAIL_NOT_VERIFIED\":\n return \"EMAIL_NOT_VERIFIED\";\n default:\n break;\n }\n }\n\n // Fallback: match on error message for non-GraphQL errors or legacy responses\n if (!err) return null;\n const lowerErr = err.toLowerCase();\n\n if (\n lowerErr.includes(\"deleted\") ||\n lowerErr.includes(\"permanently deleted\")\n ) {\n return \"DELETED\";\n }\n if (lowerErr.includes(\"suspended\")) {\n return \"SUSPENDED\";\n }\n if (\n lowerErr.includes(\"oauth provider\") ||\n lowerErr.includes(\"oauth-only\") ||\n lowerErr.includes(\"oauth only\")\n ) {\n return \"OAUTH_ONLY\";\n }\n if (\n lowerErr.includes(\"inactive\") ||\n lowerErr.includes(\"deactivated\") ||\n lowerErr.includes(\"not active\")\n ) {\n return \"INACTIVE\";\n }\n if (\n lowerErr.includes(\"verify your email\") ||\n lowerErr.includes(\"email not verified\") ||\n lowerErr.includes(\"email_not_verified\")\n ) {\n return \"EMAIL_NOT_VERIFIED\";\n }\n return null;\n };\n\n // Pre-fill email from URL query parameter (e.g., after email verification)\n useEffect(() => {\n const emailFromUrl = searchParams.get(\"email\");\n if (emailFromUrl) {\n setFormData((prev) => ({ ...prev, email: emailFromUrl }));\n }\n\n // Check for expired session redirect\n const isExpired = searchParams.get(\"expired\");\n if (isExpired === \"true\") {\n setInfoMessage(\n tr(\n \"auth.login.sessionExpired\",\n \"Your session has expired. Please sign in again.\",\n ),\n );\n // Clear the query param to prevent showing on refresh\n const newUrl = window.location.pathname;\n window.history.replaceState({}, document.title, newUrl);\n }\n }, [searchParams, tr]);\n\n // Redirect to MFA verification when required\n useEffect(() => {\n if (mfaRequired) {\n navigate(\"/auth/mfa-verify\");\n }\n }, [mfaRequired, navigate]);\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setIsAccountInactive(false);\n setIsAccountDeleted(false);\n setIsAccountSuspended(false);\n setIsOAuthOnlyAccount(false);\n setIsEmailNotVerified(false);\n setIsDuplicateAccount(false);\n setResendSuccess(false);\n setValidationError(null);\n\n // Client-side validation\n const emailTrimmed = (formData.email ?? \"\").trim();\n if (!emailTrimmed) {\n setValidationError(\n tr(\n \"auth.login.enterEmailOrUsername\",\n \"Please enter your email or username\",\n ),\n );\n return;\n }\n\n // Basic email format validation (if it contains @)\n if (emailTrimmed.includes(\"@\")) {\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n if (!emailRegex.test(emailTrimmed)) {\n setValidationError(\n tr(\"auth.login.invalidEmail\", \"Please enter a valid email address\"),\n );\n return;\n }\n }\n\n if (!formData.password || formData.password.length < 1) {\n setValidationError(\n tr(\"auth.login.enterPassword\", \"Please enter your password\"),\n );\n return;\n }\n\n // In add-account mode, check if this email is already signed in\n if (isAddAccountMode) {\n const existingProfiles = getProfiles();\n const alreadySignedIn = existingProfiles.some(\n (p) => p.email.toLowerCase() === emailTrimmed.toLowerCase(),\n );\n if (alreadySignedIn) {\n setIsDuplicateAccount(true);\n setValidationError(null);\n return;\n }\n }\n setIsDuplicateAccount(false);\n\n try {\n // Get reCAPTCHA v3 token (no-op when captcha is disabled)\n const recaptchaToken = await executeCaptchaV3(\"login\");\n\n const result = await signIn({\n email: emailTrimmed,\n password: formData.password,\n rememberMe: formData.rememberMe,\n ...(recaptchaToken ? { recaptchaToken } : {}),\n });\n\n // If MFA is required, the useEffect above will handle redirect\n if (result.requiresMFA) {\n return;\n }\n\n // Successful login - complete auth flow (fetch workspaces + issue token)\n if (result.success) {\n try {\n const profileId = result.user?.id;\n if (profileId) {\n const hostname = window.location.hostname;\n const domain = hostname.endsWith(\".local.burdenoff.com\")\n ? \".local.burdenoff.com\"\n : undefined;\n const domainPart = domain ? `; domain=${domain}` : \"\";\n const securePart =\n window.location.protocol === \"https:\" ? \"; secure\" : \"\";\n const ssoValue = JSON.stringify({\n id: profileId,\n origin: window.location.origin,\n });\n document.cookie = `bf-sso=${encodeURIComponent(ssoValue)}${domainPart}; path=/; samesite=lax${securePart}; max-age=86400`;\n document.cookie = `bf-sso-profile=${encodeURIComponent(ssoValue)}${domainPart}; path=/; samesite=lax${securePart}; max-age=86400`;\n }\n } catch {\n // Best effort only. Login can continue without the fast-path cookie.\n }\n\n // Handle Remember Me: Save or clear email only (never store passwords)\n try {\n if (formData.rememberMe) {\n localStorage.setItem(\"remembered_email\", emailTrimmed);\n } else {\n localStorage.removeItem(\"remembered_email\");\n }\n } catch (error) {\n console.warn(\n \"[LoginPage] Failed to save/clear remembered email:\",\n error,\n );\n }\n\n setIsCompletingAuth(true);\n // In add-account mode, redirect back to where the user was\n const effectiveReturnTo = initialReturnToRef.current || returnTo;\n const postLoginPath = effectiveReturnTo\n ? decodeURIComponent(effectiveReturnTo)\n : \"/dashboard\";\n try {\n type AuthFlowResult = Awaited<ReturnType<typeof completeAuthFlow>>;\n // Pass the token directly to avoid race condition with React state updates\n const authFlowResult = await Promise.race<AuthFlowResult>([\n completeAuthFlow(result.token),\n new Promise<AuthFlowResult>((resolve) => {\n window.setTimeout(() => {\n resolve({\n success: false,\n error: \"Auth flow timed out after login\",\n });\n }, 5000);\n }),\n ]);\n\n if (authFlowResult.success) {\n // Sync workspace and organization to ActiveContext\n // This is critical for global search and other workspace-scoped features\n if (authFlowResult.workspace) {\n setWorkspace(authFlowResult.workspace.id);\n if (authFlowResult.workspace.organizationId) {\n setOrganization(authFlowResult.workspace.organizationId);\n }\n }\n\n // Full auth flow completed - redirect to dashboard or returnTo\n redirectAfterLogin(postLoginPath);\n } else if (authFlowResult.error?.includes(\"No workspaces found\")) {\n // No workspaces - redirect to onboarding/workspace creation\n navigate(\"/onboarding/create-workspace\");\n } else {\n // Partial success or error - still sync workspace if available and redirect\n if (authFlowResult.workspace) {\n setWorkspace(authFlowResult.workspace.id);\n if (authFlowResult.workspace.organizationId) {\n setOrganization(authFlowResult.workspace.organizationId);\n }\n }\n console.warn(\"Auth flow warning:\", authFlowResult.error);\n redirectAfterLogin(postLoginPath);\n }\n } catch (authFlowError) {\n // Auth flow failed but login succeeded - still redirect\n console.error(\"Auth flow error (login succeeded):\", authFlowError);\n redirectAfterLogin(postLoginPath);\n } finally {\n setIsCompletingAuth(false);\n }\n }\n } catch (err) {\n if (import.meta.env.DEV) {\n console.error(\"Sign in failed:\", err);\n }\n // Check error type using error code (from store) and message\n const errorMsg = err instanceof Error ? err.message : String(err);\n if (errorMsg.includes(\"reCAPTCHA\")) {\n setValidationError(errorMsg);\n }\n const storeErrorCode = useAuthStore.getState().errorCode;\n const errorType = checkErrorType(errorMsg, storeErrorCode);\n if (errorType === \"DELETED\") {\n setIsAccountDeleted(true);\n } else if (errorType === \"SUSPENDED\") {\n setIsAccountSuspended(true);\n } else if (errorType === \"OAUTH_ONLY\") {\n setIsOAuthOnlyAccount(true);\n } else if (errorType === \"INACTIVE\") {\n setIsAccountInactive(true);\n } else if (errorType === \"EMAIL_NOT_VERIFIED\") {\n setIsEmailNotVerified(true);\n }\n }\n };\n\n // Also check the error from useAuthOperations\n useEffect(() => {\n if (error) {\n const errorType = checkErrorType(error, errorCode);\n if (errorType === \"DELETED\") {\n setIsAccountDeleted(true);\n } else if (errorType === \"SUSPENDED\") {\n setIsAccountSuspended(true);\n } else if (errorType === \"OAUTH_ONLY\") {\n setIsOAuthOnlyAccount(true);\n } else if (errorType === \"INACTIVE\") {\n setIsAccountInactive(true);\n } else if (errorType === \"EMAIL_NOT_VERIFIED\") {\n setIsEmailNotVerified(true);\n }\n }\n }, [error, errorCode]);\n\n /**\n * Handle Google OAuth login\n */\n const handleGoogleLogin = () => {\n setOauthError(null);\n try {\n initiateGoogleOAuth();\n } catch (err) {\n setOauthError(\n err instanceof Error\n ? err.message\n : tr(\n \"auth.login.googleSignInFailed\",\n \"Failed to initiate Google sign in\",\n ),\n );\n }\n };\n\n /**\n * Handle Microsoft OAuth login\n */\n const handleMicrosoftLogin = () => {\n setOauthError(null);\n try {\n initiateMicrosoftOAuth();\n } catch (err) {\n setOauthError(\n err instanceof Error\n ? err.message\n : tr(\n \"auth.login.microsoftSignInFailed\",\n \"Failed to initiate Microsoft sign in\",\n ),\n );\n }\n };\n\n /**\n * Handle resend verification email\n */\n const handleResendVerification = async () => {\n setIsResendingVerification(true);\n setResendSuccess(false);\n try {\n const emailTrimmed = (formData.email ?? \"\").trim();\n const result = await resendVerification(emailTrimmed);\n if (result.success) {\n setResendSuccess(true);\n } else {\n setValidationError(\n tr(\n \"auth.login.resendFailed\",\n \"Failed to resend verification email. Please try again.\",\n ),\n );\n }\n } catch (err) {\n console.error(\"Failed to resend verification email:\", err);\n setValidationError(\n err instanceof Error\n ? err.message\n : tr(\n \"auth.login.resendFailed\",\n \"Failed to resend verification email. Please try again.\",\n ),\n );\n } finally {\n setIsResendingVerification(false);\n }\n };\n\n // Check if OAuth providers are configured\n const isGoogleConfigured = isProviderConfigured(\"google\");\n const isMicrosoftConfigured = isProviderConfigured(\"microsoft\");\n\n const oauthProviders = [\n {\n name: \"Google\",\n icon: GoogleIcon,\n onClick: handleGoogleLogin,\n disabled: !isGoogleConfigured,\n tooltip: isGoogleConfigured\n ? undefined\n : tr(\n \"auth.login.googleNotConfigured\",\n \"Google sign in is not configured\",\n ),\n },\n // {\n // name: \"GitHub\",\n // icon: GitHubIcon,\n // onClick: () => {},\n // disabled: true,\n // tooltip: tr(\n // \"auth.login.githubNotConfigured\",\n // \"GitHub sign in is not configured\",\n // ),\n // },\n {\n name: \"Microsoft\",\n icon: MicrosoftIcon,\n onClick: handleMicrosoftLogin,\n disabled: !isMicrosoftConfigured,\n tooltip: isMicrosoftConfigured\n ? undefined\n : tr(\n \"auth.login.microsoftNotConfigured\",\n \"Microsoft sign in is not configured\",\n ),\n },\n ];\n\n if (isSsoRedirecting) {\n return <SSORedirectSpinner />;\n }\n\n return (\n <AuthLayout\n title={tr(\"auth.login.welcomeBack\", \"Welcome back\")}\n subtitle={tr(\n \"auth.login.enterCredentials\",\n \"Enter your credentials to continue\",\n )}\n >\n <form onSubmit={handleSubmit} className=\"space-y-5\">\n {/* Info Message (e.g., from expired MFA redirect) */}\n {infoMessage && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl border bg-status-info-bg-subtle border-status-info-border\">\n <svg\n className=\"w-5 h-5 flex-shrink-0 mt-0.5 text-text-link\"\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=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <p className=\"text-sm text-status-info-text\">{infoMessage}</p>\n </div>\n )}\n\n {/* Validation Error */}\n {validationError && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl border bg-status-warning-bg-subtle border-status-warning-border\">\n <AlertIcon className=\"w-5 h-5 flex-shrink-0 mt-0.5 text-status-warning-text\" />\n <p className=\"text-sm text-status-warning-text\">\n {validationError}\n </p>\n </div>\n )}\n\n {/* Duplicate Account Warning (Add Account mode) */}\n {isDuplicateAccount && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl border bg-status-warning-bg-subtle border-status-warning-border\">\n <AlertIcon className=\"w-5 h-5 flex-shrink-0 mt-0.5 text-status-warning-text\" />\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-status-warning-text\">\n {tr(\n \"auth.login.duplicateAccount\",\n \"This account is already signed in.\",\n )}\n </p>\n <p className=\"mt-1 text-sm text-status-warning-text/80\">\n {tr(\n \"auth.login.duplicateAccountHint\",\n \"Please use a different account, or go back and switch to this account from the profile menu.\",\n )}\n </p>\n {(initialReturnToRef.current || returnTo) && (\n <button\n type=\"button\"\n onClick={() =>\n navigate(\n decodeURIComponent(\n initialReturnToRef.current || returnTo || \"/dashboard\",\n ),\n )\n }\n className=\"mt-3 inline-flex items-center gap-2 px-4 py-2.5 rounded-xl border border-status-warning-border bg-bg-surface text-status-warning-text text-sm font-medium hover:bg-status-warning-bg-subtle hover:border-status-warning-border transition-all shadow-sm\"\n >\n <svg\n className=\"w-4 h-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=\"M10 19l-7-7m0 0l7-7m-7 7h18\"\n />\n </svg>\n {tr(\n \"auth.login.goBackSwitchAccount\",\n \"Go back and switch account\",\n )}\n </button>\n )}\n </div>\n </div>\n )}\n\n {/* Error Alert */}\n {error && !validationError && !isDuplicateAccount && (\n <div\n className={`flex items-start gap-3 p-4 rounded-xl border ${\n isAccountInactive || isOAuthOnlyAccount || isEmailNotVerified\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={`w-5 h-5 flex-shrink-0 mt-0.5 ${\n isAccountInactive || isOAuthOnlyAccount || isEmailNotVerified\n ? \"text-status-warning-text\"\n : \"text-status-error-text\"\n }`}\n />\n <div className=\"flex-1\">\n <p\n className={`text-sm ${\n isAccountInactive || isOAuthOnlyAccount || isEmailNotVerified\n ? \"text-status-warning-text\"\n : \"text-status-error-text\"\n }`}\n >\n {isAccountDeleted\n ? tr(\n \"auth.login.accountDeleted\",\n \"This account has been permanently deleted and cannot be recovered.\",\n )\n : isAccountSuspended\n ? tr(\n \"auth.login.accountSuspended\",\n \"Your account has been suspended. Please contact support for assistance.\",\n )\n : isAccountInactive\n ? tr(\n \"auth.login.accountDeactivated\",\n \"Your account has been deactivated.\",\n )\n : isOAuthOnlyAccount\n ? tr(\n \"auth.login.oauthOnly\",\n \"This account uses OAuth sign-in. Please use Google or Microsoft to sign in.\",\n )\n : isEmailNotVerified\n ? tr(\n \"auth.login.emailNotVerified\",\n \"Please verify your email before signing in. Check your inbox for the verification link.\",\n )\n : error}\n </p>\n {isAccountInactive && (\n <button\n type=\"button\"\n onClick={() =>\n navigate(\"/auth/reactivate\", {\n state: { email: formData.email },\n })\n }\n className=\"mt-2 text-sm font-semibold text-status-warning-text hover:text-status-warning-text underline underline-offset-2\"\n >\n {tr(\n \"auth.login.clickReactivate\",\n \"Click here to reactivate your account\",\n )}\n </button>\n )}\n {isEmailNotVerified && (\n <div className=\"mt-3\">\n {resendSuccess ? (\n <div className=\"flex items-center gap-2 text-sm text-status-success-text\">\n <svg\n className=\"w-4 h-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=\"M5 13l4 4L19 7\"\n />\n </svg>\n {tr(\n \"auth.login.verificationSent\",\n \"Verification email sent! Please check your inbox.\",\n )}\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={handleResendVerification}\n disabled={isResendingVerification}\n className=\"inline-flex items-center gap-2 px-4 py-2.5 rounded-xl border border-status-warning-border bg-bg-surface text-status-warning-text text-sm font-medium hover:bg-status-warning-bg-subtle hover:border-status-warning-border transition-all shadow-sm disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isResendingVerification ? (\n <>\n <SpinnerIcon className=\"w-4 h-4 animate-spin\" />\n {tr(\"auth.login.sending\", \"Sending...\")}\n </>\n ) : (\n <>\n <svg\n className=\"w-4 h-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=\"M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z\"\n />\n </svg>\n {tr(\n \"auth.login.resendVerification\",\n \"Resend verification email\",\n )}\n </>\n )}\n </button>\n )}\n </div>\n )}\n {isOAuthOnlyAccount && (\n <div className=\"mt-3 flex flex-col gap-2\">\n {isProviderConfigured(\"google\") && (\n <button\n type=\"button\"\n onClick={handleGoogleLogin}\n className=\"inline-flex items-center justify-center gap-2 px-4 py-2.5 rounded-xl border border-border-default bg-bg-surface text-text-primary text-sm font-medium hover:bg-bg-canvas hover:border-border-default transition-all shadow-sm\"\n >\n <GoogleIcon className=\"w-5 h-5\" />\n {tr(\"auth.login.signInWithGoogle\", \"Sign in with Google\")}\n </button>\n )}\n {isProviderConfigured(\"microsoft\") && (\n <button\n type=\"button\"\n onClick={handleMicrosoftLogin}\n className=\"inline-flex items-center justify-center gap-2 px-4 py-2.5 rounded-xl border border-border-default bg-bg-surface text-text-primary text-sm font-medium hover:bg-bg-canvas hover:border-border-default transition-all shadow-sm\"\n >\n <MicrosoftIcon className=\"w-5 h-5\" />\n {tr(\n \"auth.login.signInWithMicrosoft\",\n \"Sign in with Microsoft\",\n )}\n </button>\n )}\n </div>\n )}\n </div>\n </div>\n )}\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.login.emailLabel\", \"Email\")}\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=\"w-5 h-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"email\"\n type=\"email\"\n autoComplete=\"email\"\n required\n aria-required=\"true\"\n value={formData.email}\n onChange={(e) =>\n setFormData({ ...formData, email: e.target.value })\n }\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(\"auth.login.emailPlaceholder\", \"you@example.com\")}\n />\n </div>\n </div>\n\n {/* Password Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"password\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.login.passwordLabel\", \"Password\")}\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 <LockIcon className=\"w-5 h-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"password\"\n type={showPassword ? \"text\" : \"password\"}\n autoComplete=\"current-password\"\n required\n aria-required=\"true\"\n value={formData.password}\n onChange={(e) =>\n setFormData({ ...formData, password: e.target.value })\n }\n className=\"block w-full pl-[3.5rem] pr-12 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.login.passwordPlaceholder\",\n \"Enter your password\",\n )}\n />\n <button\n type=\"button\"\n onClick={() => setShowPassword(!showPassword)}\n aria-label={showPassword ? \"Hide password\" : \"Show password\"}\n className=\"absolute inset-y-0 right-0 pr-3.5 flex items-center text-[var(--color-text-tertiary)] hover:text-[var(--color-text-secondary)] transition-colors\"\n >\n {showPassword ? (\n <EyeOffIcon className=\"w-5 h-5\" />\n ) : (\n <EyeIcon className=\"w-5 h-5\" />\n )}\n </button>\n </div>\n </div>\n\n {/* Remember & Forgot */}\n <div className=\"flex items-center justify-between\">\n <label className=\"flex items-center gap-2 cursor-pointer group\">\n <div className=\"relative\">\n <input\n type=\"checkbox\"\n checked={formData.rememberMe}\n onChange={(e) => {\n const isChecked = e.target.checked;\n setFormData({ ...formData, rememberMe: isChecked });\n\n // Clear saved email immediately when unchecked\n if (!isChecked) {\n try {\n localStorage.removeItem(\"remembered_email\");\n } catch (error) {\n console.warn(\n \"[LoginPage] Failed to clear remembered email:\",\n error,\n );\n }\n }\n }}\n className=\"sr-only peer\"\n />\n <div className=\"w-5 h-5 rounded-md border-2 border-[var(--color-border-default)] bg-[var(--color-bg-surface)] peer-checked:bg-status-info-bg-subtle0 peer-checked:border-action-primary-bg transition-all peer-focus:ring-2 peer-focus:ring-action-primary-bg/20\">\n <svg\n className=\"w-full h-full text-white opacity-0 peer-checked:opacity-100 transition-opacity p-0.5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={3}\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"M5 13l4 4L19 7\"\n />\n </svg>\n </div>\n </div>\n <span className=\"text-sm text-[var(--color-text-secondary)] group-hover:text-[var(--color-text-primary)] transition-colors\">\n {tr(\"auth.login.rememberMe\", \"Remember me\")}\n </span>\n </label>\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/reset-password\")}\n className=\"text-sm font-medium text-text-link hover:text-text-linkHover transition-colors\"\n >\n {tr(\"auth.login.forgotPassword\", \"Forgot password?\")}\n </button>\n </div>\n\n {/* Submit Button */}\n <button\n type=\"submit\"\n disabled={isLoading || isCompletingAuth}\n className=\"auth-primary-button flex w-full items-center justify-center gap-2 px-4 py-3 font-semibold\"\n >\n {isLoading || isCompletingAuth ? (\n <>\n <SpinnerIcon className=\"w-5 h-5 animate-spin\" />\n <span>\n {isCompletingAuth\n ? tr(\n \"auth.login.settingUpWorkspace\",\n \"Setting up workspace...\",\n )\n : tr(\"auth.login.signingIn\", \"Signing in...\")}\n </span>\n </>\n ) : (\n tr(\"auth.login.signIn\", \"Sign in\")\n )}\n </button>\n </form>\n\n {/* Divider */}\n <div className=\"relative my-8\">\n <div className=\"absolute inset-0 flex items-center\">\n <div className=\"w-full border-t border-[var(--color-border-default)]\" />\n </div>\n <div className=\"relative flex justify-center\">\n <span className=\"px-4 bg-[var(--color-bg-canvas)] text-sm text-[var(--color-text-tertiary)]\">\n {tr(\"auth.login.orContinueWith\", \"or continue with\")}\n </span>\n </div>\n </div>\n\n {/* OAuth Error */}\n {oauthError && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl border bg-status-error-bg-subtle border-status-error-border\">\n <AlertIcon className=\"w-5 h-5 flex-shrink-0 mt-0.5 text-status-error-text\" />\n <p className=\"text-sm text-status-error-text\">{oauthError}</p>\n </div>\n )}\n\n {/* OAuth Buttons */}\n <div className=\"grid grid-cols-3 gap-3\">\n {oauthProviders.map((provider) => (\n <button\n key={provider.name}\n type=\"button\"\n onClick={provider.onClick}\n disabled={provider.disabled}\n title={provider.tooltip}\n aria-label={provider.name}\n className={`flex items-center justify-center gap-2 px-4 py-3 rounded-xl border transition-all duration-200 group ${\n provider.disabled\n ? \"border-[var(--color-border-subtle)] bg-[var(--color-bg-disabled)] cursor-not-allowed opacity-50\"\n : \"border-[var(--color-border-default)] bg-[var(--color-bg-surface)] hover:bg-[var(--color-bg-sunken)] hover:border-[var(--color-border-strong)]\"\n }`}\n >\n <provider.icon\n className={`w-5 h-5 ${provider.disabled ? \"opacity-50\" : \"\"}`}\n />\n <span\n className={`sr-only sm:not-sr-only text-sm font-medium transition-colors hidden sm:inline ${\n provider.disabled\n ? \"text-[var(--color-text-disabled)]\"\n : \"text-[var(--color-text-secondary)] group-hover:text-[var(--color-text-primary)]\"\n }`}\n >\n {provider.name}\n </span>\n </button>\n ))}\n </div>\n\n {/* Enterprise SSO Option — temporarily hidden */}\n {/* <div className=\"mt-6\">\n <button\n onClick={() => navigate(\"/auth/sso/login\")}\n className=\"w-full flex items-center justify-center gap-3 px-4 py-3 rounded-xl border-2 border-dashed border-[var(--color-border-default)] hover:border-action-primary-bg bg-[var(--color-bg-surface)] hover:bg-status-info-bg-subtle transition-all duration-200 group\"\n >\n <svg\n className=\"w-5 h-5 text-[var(--color-text-tertiary)] group-hover:text-text-link\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4\"\n />\n </svg>\n <span className=\"text-sm font-medium text-[var(--color-text-secondary)] group-hover:text-text-link\">\n {tr(\"auth.login.enterpriseSso\", \"Sign in with Enterprise SSO\")}\n </span>\n </button>\n </div> */}\n\n {/* Magic Link Option */}\n <div className=\"mt-4 text-center\">\n <button\n onClick={() => navigate(\"/auth/magic-link\")}\n className=\"text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] transition-colors\"\n >\n {tr(\"auth.login.preferPasswordless\", \"Prefer passwordless login?\")}{\" \"}\n <span className=\"font-semibold text-text-link hover:text-text-linkHover\">\n {tr(\"auth.login.signInMagicLink\", \"Sign in with Magic Link\")}\n </span>\n </button>\n </div>\n\n {/* Sign Up Link */}\n <p className=\"mt-6 text-center text-sm text-[var(--color-text-secondary)]\">\n {tr(\"auth.login.dontHaveAccount\", \"Don't have an account?\")}{\" \"}\n <button\n onClick={() => navigate(\"/auth/register\")}\n className=\"font-semibold text-text-link hover:text-text-linkHover transition-colors\"\n >\n {tr(\"auth.login.signUp\", \"Sign up\")}\n </button>\n </p>\n </AuthLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -58,7 +58,7 @@ function y() {
|
|
|
58
58
|
width: "w-1/4"
|
|
59
59
|
} : e <= 3 ? {
|
|
60
60
|
level: x("auth.passwordReset.passwordStrengthFair", "fair"),
|
|
61
|
-
color: "bg-
|
|
61
|
+
color: "bg-status-warning-text",
|
|
62
62
|
width: "w-2/4"
|
|
63
63
|
} : e <= 4 ? {
|
|
64
64
|
level: x("auth.passwordReset.passwordStrengthGood", "good"),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PasswordResetPage.js","names":[],"sources":["../../../src/auth/pages/PasswordResetPage.tsx"],"sourcesContent":["import { useState, useEffect } from \"react\";\nimport { useSearchParams, useNavigate } from \"react-router-dom\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport { usePasswordOperations } from \"../hooks\";\nimport { useCaptchaV3 } from \"../hooks/useCaptchaV3\";\nimport { AuthLayout } from \"../components/AuthLayout\";\nimport {\n EmailIcon,\n LockIcon,\n EyeIcon,\n EyeOffIcon,\n CheckIcon,\n XIcon,\n SpinnerIcon,\n} from \"../components/AuthIcons\";\n\nconst REDIRECT_DELAY = 5; // seconds\n\n/**\n * Password reset page with professional design\n * Handles both requesting reset and completing reset with token\n */\nexport function PasswordResetPage() {\n const navigate = useNavigate();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const [searchParams] = useSearchParams();\n const token = searchParams.get(\"token\");\n // Preserve returnTo so after reset→login the user ends up on the right product domain\n const returnTo = searchParams.get(\"returnTo\");\n const { requestPasswordReset, resetPassword, isLoading } =\n usePasswordOperations();\n const { executeRecaptcha: executeCaptchaV3 } = useCaptchaV3();\n const [email, setEmail] = useState(\"\");\n const [newPassword, setNewPassword] = useState(\"\");\n const [confirmPassword, setConfirmPassword] = useState(\"\");\n const [showPassword, setShowPassword] = useState(false);\n const [showConfirmPassword, setShowConfirmPassword] = useState(false);\n const [success, setSuccess] = useState(false);\n const [error, setError] = useState(\"\");\n const [countdown, setCountdown] = useState(REDIRECT_DELAY);\n\n // Auto-redirect countdown after successful password reset (only for token flow)\n useEffect(() => {\n if (!success || !token) return;\n\n const loginPath = returnTo\n ? `/auth/login?returnTo=${encodeURIComponent(returnTo)}`\n : \"/auth/login\";\n const timer = setInterval(() => {\n setCountdown((prev) => {\n if (prev <= 1) {\n clearInterval(timer);\n navigate(loginPath);\n return 0;\n }\n return prev - 1;\n });\n }, 1000);\n\n return () => clearInterval(timer);\n }, [success, token, navigate, returnTo]);\n\n // Password strength indicators\n const passwordChecks = (() => {\n const password = newPassword;\n return [\n {\n label: tr(\"auth.passwordReset.pwMin8\", \"At least 12 characters\"),\n met: password.length >= 12,\n },\n {\n label: tr(\"auth.passwordReset.pwUppercase\", \"One uppercase letter\"),\n met: /[A-Z]/.test(password),\n },\n {\n label: tr(\"auth.passwordReset.pwLowercase\", \"One lowercase letter\"),\n met: /[a-z]/.test(password),\n },\n {\n label: tr(\"auth.passwordReset.pwNumber\", \"One number\"),\n met: /\\d/.test(password),\n },\n {\n label: tr(\"auth.passwordReset.pwSpecial\", \"One special character\"),\n met: /[!@#$%^&*(),.?\":{}|<>]/.test(password),\n },\n ];\n })();\n\n const passwordStrength = (() => {\n const metCount = passwordChecks.filter((c) => c.met).length;\n if (metCount <= 2)\n return {\n level: tr(\"auth.passwordReset.passwordStrengthWeak\", \"weak\"),\n color: \"bg-status-error-bg-subtle0\",\n width: \"w-1/4\",\n };\n if (metCount <= 3)\n return {\n level: tr(\"auth.passwordReset.passwordStrengthFair\", \"fair\"),\n color: \"bg-orange-500\",\n width: \"w-2/4\",\n };\n if (metCount <= 4)\n return {\n level: tr(\"auth.passwordReset.passwordStrengthGood\", \"good\"),\n color: \"bg-status-warning-bg-subtle0\",\n width: \"w-3/4\",\n };\n return {\n level: tr(\"auth.passwordReset.passwordStrengthStrong\", \"strong\"),\n color: \"bg-status-success-bg-subtle0\",\n width: \"w-full\",\n };\n })();\n\n const handleRequestReset = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(\"\");\n try {\n // Get reCAPTCHA v3 token (no-op when captcha is disabled)\n const recaptchaToken = await executeCaptchaV3(\"password_reset\");\n await requestPasswordReset(email, recaptchaToken);\n setSuccess(true);\n } catch (err) {\n setError(\n tr(\n \"auth.passwordReset.sendFailed\",\n \"Failed to send reset email. Please try again.\",\n ),\n );\n console.error(\"Failed to request password reset:\", err);\n }\n };\n\n const handleResetPassword = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(\"\");\n\n if (!token) return;\n\n if (newPassword !== confirmPassword) {\n setError(\n tr(\"auth.passwordReset.passwordsNoMatch\", \"Passwords do not match\"),\n );\n return;\n }\n\n try {\n await resetPassword(token, newPassword);\n setSuccess(true);\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"\";\n if (msg === \"TOKEN_EXPIRED\") {\n setError(\n tr(\n \"auth.passwordReset.errorTokenExpired\",\n \"This password reset link has expired. Please request a new one.\",\n ),\n );\n } else if (msg === \"INVALID_TOKEN\") {\n setError(\n tr(\n \"auth.passwordReset.errorInvalidToken\",\n \"This password reset link is invalid or has already been used.\",\n ),\n );\n } else {\n setError(\n tr(\n \"auth.passwordReset.errorGeneric\",\n \"Failed to reset password. Please try again.\",\n ),\n );\n }\n }\n };\n\n if (success) {\n return (\n <AuthLayout\n title={\n token\n ? tr(\"auth.passwordReset.passwordUpdated\", \"Password updated\")\n : tr(\"auth.passwordReset.checkEmail\", \"Check your email\")\n }\n subtitle={\n token\n ? tr(\n \"auth.passwordReset.successSubtitle\",\n \"Your password has been successfully reset\",\n )\n : tr(\n \"auth.passwordReset.sentInstructions\",\n \"We sent you instructions to reset your password\",\n )\n }\n >\n {/* Success Icon */}\n <div className=\"flex justify-center mb-6\">\n <div className=\"w-16 h-16 rounded-full bg-[var(--color-status-success-bg-subtle)] flex items-center justify-center\">\n <svg\n className=\"w-8 h-8 text-[var(--color-status-success-text)]\"\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=\"M5 13l4 4L19 7\"\n />\n </svg>\n </div>\n </div>\n\n <p className=\"text-center text-[var(--color-text-secondary)] mb-8\">\n {token\n ? tr(\n \"auth.passwordReset.canNowSignIn\",\n \"You can now use your new password to sign in to your account.\",\n )\n : tr(\n \"auth.passwordReset.emailSentTo\",\n \"We sent an email to {{email}}. Click the link in the email to reset your password.\",\n ).replace(\"{{email}}\", email)}\n </p>\n\n {token ? (\n <div className=\"space-y-4\">\n <p className=\"text-center text-sm text-[var(--color-text-tertiary)]\">\n {tr(\n \"auth.passwordReset.redirectingIn\",\n \"Redirecting to sign in in\",\n )}{\" \"}\n <span className=\"font-semibold text-[var(--color-text-primary)]\">\n {countdown}\n </span>{\" \"}\n {tr(\"auth.passwordReset.seconds\", \"seconds...\")}\n </p>\n <button\n onClick={() =>\n navigate(\n returnTo\n ? `/auth/login?returnTo=${encodeURIComponent(returnTo)}`\n : \"/auth/login\",\n )\n }\n className=\"auth-primary-button w-full px-4 py-3 font-semibold\"\n >\n {tr(\"auth.passwordReset.signInNow\", \"Sign in now\")}\n </button>\n </div>\n ) : (\n <div className=\"space-y-4\">\n <button\n onClick={() => setSuccess(false)}\n className=\"w-full py-3 px-4 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] font-medium hover:bg-[var(--color-bg-sunken)] transition-all\"\n >\n {tr(\n \"auth.passwordReset.didntReceive\",\n \"Didn't receive the email? Try again\",\n )}\n </button>\n <button\n onClick={() => navigate(\"/auth/login\")}\n className=\"w-full py-3 px-4 rounded-xl text-[var(--color-text-secondary)] font-medium hover:text-[var(--color-text-primary)] transition-colors\"\n >\n {tr(\"auth.passwordReset.backToSignIn\", \"Back to sign in\")}\n </button>\n </div>\n )}\n </AuthLayout>\n );\n }\n\n return (\n <AuthLayout\n title={\n token\n ? tr(\"auth.passwordReset.createNewPassword\", \"Create new password\")\n : tr(\"auth.passwordReset.resetTitle\", \"Reset your password\")\n }\n subtitle={\n token\n ? tr(\n \"auth.passwordReset.enterNewPassword\",\n \"Enter a new password for your account\",\n )\n : tr(\n \"auth.passwordReset.enterEmail\",\n \"Enter your email and we'll send you a reset link\",\n )\n }\n >\n {token ? (\n <form onSubmit={handleResetPassword} className=\"space-y-5\">\n {/* Error Alert */}\n {error && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl bg-[var(--color-status-error-bg-subtle)] border border-[var(--color-status-error-border)]\">\n <svg\n className=\"w-5 h-5 text-[var(--color-status-error-text)] flex-shrink-0 mt-0.5\"\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=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n <p className=\"text-sm text-[var(--color-status-error-text)]\">\n {error}\n </p>\n </div>\n )}\n\n {/* New Password Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"newPassword\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.passwordReset.newPasswordLabel\", \"New Password\")}\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 <LockIcon className=\"w-5 h-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"newPassword\"\n type={showPassword ? \"text\" : \"password\"}\n required\n aria-required=\"true\"\n autoComplete=\"new-password\"\n value={newPassword}\n onChange={(e) => setNewPassword(e.target.value)}\n className=\"block w-full pl-[3.5rem] pr-12 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.passwordReset.newPasswordPlaceholder\",\n \"Enter new password\",\n )}\n />\n <button\n type=\"button\"\n onClick={() => setShowPassword(!showPassword)}\n aria-label={showPassword ? \"Hide password\" : \"Show password\"}\n className=\"absolute inset-y-0 right-0 pr-3.5 flex items-center text-[var(--color-text-tertiary)] hover:text-[var(--color-text-secondary)] transition-colors\"\n >\n {showPassword ? (\n <EyeOffIcon className=\"w-5 h-5\" />\n ) : (\n <EyeIcon className=\"w-5 h-5\" />\n )}\n </button>\n </div>\n\n {/* Password Strength Indicator */}\n {newPassword && (\n <div className=\"space-y-3 pt-2\">\n <div className=\"space-y-1\">\n <div className=\"h-1.5 bg-[var(--color-bg-sunken)] rounded-full overflow-hidden\">\n <div\n className={`h-full ${passwordStrength.color} ${passwordStrength.width} transition-all duration-300`}\n />\n </div>\n <p className=\"text-xs text-[var(--color-text-tertiary)] capitalize\">\n {tr(\n \"auth.passwordReset.passwordStrength\",\n \"Password strength:\",\n )}{\" \"}\n <span className=\"font-medium\">\n {passwordStrength.level}\n </span>\n </p>\n </div>\n\n <div className=\"grid grid-cols-2 gap-2\">\n {passwordChecks.map((check, i) => (\n <div key={i} className=\"flex items-center gap-2\">\n {check.met ? (\n <CheckIcon className=\"w-4 h-4 text-status-success-text\" />\n ) : (\n <XIcon className=\"w-4 h-4 text-[var(--color-text-tertiary)]\" />\n )}\n <span\n className={`text-xs ${\n check.met\n ? \"text-[var(--color-status-success-text)]\"\n : \"text-[var(--color-text-tertiary)]\"\n }`}\n >\n {check.label}\n </span>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n\n {/* Confirm Password Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"confirmPassword\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\n \"auth.passwordReset.confirmPasswordLabel\",\n \"Confirm Password\",\n )}\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 <LockIcon className=\"w-5 h-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"confirmPassword\"\n type={showConfirmPassword ? \"text\" : \"password\"}\n required\n aria-required=\"true\"\n autoComplete=\"new-password\"\n aria-invalid={\n confirmPassword.length > 0 && newPassword !== confirmPassword\n }\n value={confirmPassword}\n onChange={(e) => setConfirmPassword(e.target.value)}\n className=\"block w-full pl-[3.5rem] pr-12 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.passwordReset.confirmPasswordPlaceholder\",\n \"Confirm new password\",\n )}\n />\n <button\n type=\"button\"\n onClick={() => setShowConfirmPassword(!showConfirmPassword)}\n aria-label={\n showConfirmPassword ? \"Hide password\" : \"Show password\"\n }\n className=\"absolute inset-y-0 right-0 pr-3.5 flex items-center text-[var(--color-text-tertiary)] hover:text-[var(--color-text-secondary)] transition-colors\"\n >\n {showConfirmPassword ? (\n <EyeOffIcon className=\"w-5 h-5\" />\n ) : (\n <EyeIcon className=\"w-5 h-5\" />\n )}\n </button>\n </div>\n {confirmPassword && newPassword !== confirmPassword && (\n <p className=\"text-xs text-[var(--color-status-error-text)]\">\n {tr(\n \"auth.passwordReset.passwordsMismatch\",\n \"Passwords do not match\",\n )}\n </p>\n )}\n </div>\n\n {/* Submit Button */}\n <button\n type=\"submit\"\n disabled={\n isLoading ||\n (confirmPassword.length > 0 && newPassword !== confirmPassword)\n }\n className=\"auth-primary-button flex w-full items-center justify-center gap-2 px-4 py-3 font-semibold\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"w-5 h-5 animate-spin\" />\n <span>\n {tr(\"auth.passwordReset.resetting\", \"Resetting password...\")}\n </span>\n </>\n ) : (\n tr(\"auth.passwordReset.resetButton\", \"Reset password\")\n )}\n </button>\n </form>\n ) : (\n <form onSubmit={handleRequestReset} className=\"space-y-5\">\n {/* Error Alert */}\n {error && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl bg-[var(--color-status-error-bg-subtle)] border border-[var(--color-status-error-border)]\">\n <svg\n className=\"w-5 h-5 text-[var(--color-status-error-text)] flex-shrink-0 mt-0.5\"\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=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n <p className=\"text-sm text-[var(--color-status-error-text)]\">\n {error}\n </p>\n </div>\n )}\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.passwordReset.emailLabel\", \"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=\"w-5 h-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"email\"\n type=\"email\"\n required\n aria-required=\"true\"\n autoComplete=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\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.passwordReset.emailPlaceholder\",\n \"you@example.com\",\n )}\n />\n </div>\n </div>\n\n {/* Submit Button */}\n <button\n type=\"submit\"\n disabled={isLoading}\n className=\"auth-primary-button flex w-full items-center justify-center gap-2 px-4 py-3 font-semibold\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"w-5 h-5 animate-spin\" />\n <span>{tr(\"auth.passwordReset.sending\", \"Sending...\")}</span>\n </>\n ) : (\n tr(\"auth.passwordReset.sendResetLink\", \"Send reset link\")\n )}\n </button>\n </form>\n )}\n\n {/* Back to Sign In */}\n <p className=\"mt-8 text-center text-sm text-[var(--color-text-secondary)]\">\n {tr(\"auth.passwordReset.rememberPassword\", \"Remember your password?\")}{\" \"}\n <button\n onClick={() => navigate(\"/auth/login\")}\n className=\"font-semibold text-text-link hover:text-text-linkHover transition-colors\"\n >\n {tr(\"auth.passwordReset.signIn\", \"Sign in\")}\n </button>\n </p>\n </AuthLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PasswordResetPage.js","names":[],"sources":["../../../src/auth/pages/PasswordResetPage.tsx"],"sourcesContent":["import { useState, useEffect } from \"react\";\nimport { useSearchParams, useNavigate } from \"react-router-dom\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport { usePasswordOperations } from \"../hooks\";\nimport { useCaptchaV3 } from \"../hooks/useCaptchaV3\";\nimport { AuthLayout } from \"../components/AuthLayout\";\nimport {\n EmailIcon,\n LockIcon,\n EyeIcon,\n EyeOffIcon,\n CheckIcon,\n XIcon,\n SpinnerIcon,\n} from \"../components/AuthIcons\";\n\nconst REDIRECT_DELAY = 5; // seconds\n\n/**\n * Password reset page with professional design\n * Handles both requesting reset and completing reset with token\n */\nexport function PasswordResetPage() {\n const navigate = useNavigate();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const [searchParams] = useSearchParams();\n const token = searchParams.get(\"token\");\n // Preserve returnTo so after reset→login the user ends up on the right product domain\n const returnTo = searchParams.get(\"returnTo\");\n const { requestPasswordReset, resetPassword, isLoading } =\n usePasswordOperations();\n const { executeRecaptcha: executeCaptchaV3 } = useCaptchaV3();\n const [email, setEmail] = useState(\"\");\n const [newPassword, setNewPassword] = useState(\"\");\n const [confirmPassword, setConfirmPassword] = useState(\"\");\n const [showPassword, setShowPassword] = useState(false);\n const [showConfirmPassword, setShowConfirmPassword] = useState(false);\n const [success, setSuccess] = useState(false);\n const [error, setError] = useState(\"\");\n const [countdown, setCountdown] = useState(REDIRECT_DELAY);\n\n // Auto-redirect countdown after successful password reset (only for token flow)\n useEffect(() => {\n if (!success || !token) return;\n\n const loginPath = returnTo\n ? `/auth/login?returnTo=${encodeURIComponent(returnTo)}`\n : \"/auth/login\";\n const timer = setInterval(() => {\n setCountdown((prev) => {\n if (prev <= 1) {\n clearInterval(timer);\n navigate(loginPath);\n return 0;\n }\n return prev - 1;\n });\n }, 1000);\n\n return () => clearInterval(timer);\n }, [success, token, navigate, returnTo]);\n\n // Password strength indicators\n const passwordChecks = (() => {\n const password = newPassword;\n return [\n {\n label: tr(\"auth.passwordReset.pwMin8\", \"At least 12 characters\"),\n met: password.length >= 12,\n },\n {\n label: tr(\"auth.passwordReset.pwUppercase\", \"One uppercase letter\"),\n met: /[A-Z]/.test(password),\n },\n {\n label: tr(\"auth.passwordReset.pwLowercase\", \"One lowercase letter\"),\n met: /[a-z]/.test(password),\n },\n {\n label: tr(\"auth.passwordReset.pwNumber\", \"One number\"),\n met: /\\d/.test(password),\n },\n {\n label: tr(\"auth.passwordReset.pwSpecial\", \"One special character\"),\n met: /[!@#$%^&*(),.?\":{}|<>]/.test(password),\n },\n ];\n })();\n\n const passwordStrength = (() => {\n const metCount = passwordChecks.filter((c) => c.met).length;\n if (metCount <= 2)\n return {\n level: tr(\"auth.passwordReset.passwordStrengthWeak\", \"weak\"),\n color: \"bg-status-error-bg-subtle0\",\n width: \"w-1/4\",\n };\n if (metCount <= 3)\n return {\n level: tr(\"auth.passwordReset.passwordStrengthFair\", \"fair\"),\n color: \"bg-status-warning-text\",\n width: \"w-2/4\",\n };\n if (metCount <= 4)\n return {\n level: tr(\"auth.passwordReset.passwordStrengthGood\", \"good\"),\n color: \"bg-status-warning-bg-subtle0\",\n width: \"w-3/4\",\n };\n return {\n level: tr(\"auth.passwordReset.passwordStrengthStrong\", \"strong\"),\n color: \"bg-status-success-bg-subtle0\",\n width: \"w-full\",\n };\n })();\n\n const handleRequestReset = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(\"\");\n try {\n // Get reCAPTCHA v3 token (no-op when captcha is disabled)\n const recaptchaToken = await executeCaptchaV3(\"password_reset\");\n await requestPasswordReset(email, recaptchaToken);\n setSuccess(true);\n } catch (err) {\n setError(\n tr(\n \"auth.passwordReset.sendFailed\",\n \"Failed to send reset email. Please try again.\",\n ),\n );\n console.error(\"Failed to request password reset:\", err);\n }\n };\n\n const handleResetPassword = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(\"\");\n\n if (!token) return;\n\n if (newPassword !== confirmPassword) {\n setError(\n tr(\"auth.passwordReset.passwordsNoMatch\", \"Passwords do not match\"),\n );\n return;\n }\n\n try {\n await resetPassword(token, newPassword);\n setSuccess(true);\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"\";\n if (msg === \"TOKEN_EXPIRED\") {\n setError(\n tr(\n \"auth.passwordReset.errorTokenExpired\",\n \"This password reset link has expired. Please request a new one.\",\n ),\n );\n } else if (msg === \"INVALID_TOKEN\") {\n setError(\n tr(\n \"auth.passwordReset.errorInvalidToken\",\n \"This password reset link is invalid or has already been used.\",\n ),\n );\n } else {\n setError(\n tr(\n \"auth.passwordReset.errorGeneric\",\n \"Failed to reset password. Please try again.\",\n ),\n );\n }\n }\n };\n\n if (success) {\n return (\n <AuthLayout\n title={\n token\n ? tr(\"auth.passwordReset.passwordUpdated\", \"Password updated\")\n : tr(\"auth.passwordReset.checkEmail\", \"Check your email\")\n }\n subtitle={\n token\n ? tr(\n \"auth.passwordReset.successSubtitle\",\n \"Your password has been successfully reset\",\n )\n : tr(\n \"auth.passwordReset.sentInstructions\",\n \"We sent you instructions to reset your password\",\n )\n }\n >\n {/* Success Icon */}\n <div className=\"flex justify-center mb-6\">\n <div className=\"w-16 h-16 rounded-full bg-[var(--color-status-success-bg-subtle)] flex items-center justify-center\">\n <svg\n className=\"w-8 h-8 text-[var(--color-status-success-text)]\"\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=\"M5 13l4 4L19 7\"\n />\n </svg>\n </div>\n </div>\n\n <p className=\"text-center text-[var(--color-text-secondary)] mb-8\">\n {token\n ? tr(\n \"auth.passwordReset.canNowSignIn\",\n \"You can now use your new password to sign in to your account.\",\n )\n : tr(\n \"auth.passwordReset.emailSentTo\",\n \"We sent an email to {{email}}. Click the link in the email to reset your password.\",\n ).replace(\"{{email}}\", email)}\n </p>\n\n {token ? (\n <div className=\"space-y-4\">\n <p className=\"text-center text-sm text-[var(--color-text-tertiary)]\">\n {tr(\n \"auth.passwordReset.redirectingIn\",\n \"Redirecting to sign in in\",\n )}{\" \"}\n <span className=\"font-semibold text-[var(--color-text-primary)]\">\n {countdown}\n </span>{\" \"}\n {tr(\"auth.passwordReset.seconds\", \"seconds...\")}\n </p>\n <button\n onClick={() =>\n navigate(\n returnTo\n ? `/auth/login?returnTo=${encodeURIComponent(returnTo)}`\n : \"/auth/login\",\n )\n }\n className=\"auth-primary-button w-full px-4 py-3 font-semibold\"\n >\n {tr(\"auth.passwordReset.signInNow\", \"Sign in now\")}\n </button>\n </div>\n ) : (\n <div className=\"space-y-4\">\n <button\n onClick={() => setSuccess(false)}\n className=\"w-full py-3 px-4 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] font-medium hover:bg-[var(--color-bg-sunken)] transition-all\"\n >\n {tr(\n \"auth.passwordReset.didntReceive\",\n \"Didn't receive the email? Try again\",\n )}\n </button>\n <button\n onClick={() => navigate(\"/auth/login\")}\n className=\"w-full py-3 px-4 rounded-xl text-[var(--color-text-secondary)] font-medium hover:text-[var(--color-text-primary)] transition-colors\"\n >\n {tr(\"auth.passwordReset.backToSignIn\", \"Back to sign in\")}\n </button>\n </div>\n )}\n </AuthLayout>\n );\n }\n\n return (\n <AuthLayout\n title={\n token\n ? tr(\"auth.passwordReset.createNewPassword\", \"Create new password\")\n : tr(\"auth.passwordReset.resetTitle\", \"Reset your password\")\n }\n subtitle={\n token\n ? tr(\n \"auth.passwordReset.enterNewPassword\",\n \"Enter a new password for your account\",\n )\n : tr(\n \"auth.passwordReset.enterEmail\",\n \"Enter your email and we'll send you a reset link\",\n )\n }\n >\n {token ? (\n <form onSubmit={handleResetPassword} className=\"space-y-5\">\n {/* Error Alert */}\n {error && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl bg-[var(--color-status-error-bg-subtle)] border border-[var(--color-status-error-border)]\">\n <svg\n className=\"w-5 h-5 text-[var(--color-status-error-text)] flex-shrink-0 mt-0.5\"\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=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n <p className=\"text-sm text-[var(--color-status-error-text)]\">\n {error}\n </p>\n </div>\n )}\n\n {/* New Password Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"newPassword\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.passwordReset.newPasswordLabel\", \"New Password\")}\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 <LockIcon className=\"w-5 h-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"newPassword\"\n type={showPassword ? \"text\" : \"password\"}\n required\n aria-required=\"true\"\n autoComplete=\"new-password\"\n value={newPassword}\n onChange={(e) => setNewPassword(e.target.value)}\n className=\"block w-full pl-[3.5rem] pr-12 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.passwordReset.newPasswordPlaceholder\",\n \"Enter new password\",\n )}\n />\n <button\n type=\"button\"\n onClick={() => setShowPassword(!showPassword)}\n aria-label={showPassword ? \"Hide password\" : \"Show password\"}\n className=\"absolute inset-y-0 right-0 pr-3.5 flex items-center text-[var(--color-text-tertiary)] hover:text-[var(--color-text-secondary)] transition-colors\"\n >\n {showPassword ? (\n <EyeOffIcon className=\"w-5 h-5\" />\n ) : (\n <EyeIcon className=\"w-5 h-5\" />\n )}\n </button>\n </div>\n\n {/* Password Strength Indicator */}\n {newPassword && (\n <div className=\"space-y-3 pt-2\">\n <div className=\"space-y-1\">\n <div className=\"h-1.5 bg-[var(--color-bg-sunken)] rounded-full overflow-hidden\">\n <div\n className={`h-full ${passwordStrength.color} ${passwordStrength.width} transition-all duration-300`}\n />\n </div>\n <p className=\"text-xs text-[var(--color-text-tertiary)] capitalize\">\n {tr(\n \"auth.passwordReset.passwordStrength\",\n \"Password strength:\",\n )}{\" \"}\n <span className=\"font-medium\">\n {passwordStrength.level}\n </span>\n </p>\n </div>\n\n <div className=\"grid grid-cols-2 gap-2\">\n {passwordChecks.map((check, i) => (\n <div key={i} className=\"flex items-center gap-2\">\n {check.met ? (\n <CheckIcon className=\"w-4 h-4 text-status-success-text\" />\n ) : (\n <XIcon className=\"w-4 h-4 text-[var(--color-text-tertiary)]\" />\n )}\n <span\n className={`text-xs ${\n check.met\n ? \"text-[var(--color-status-success-text)]\"\n : \"text-[var(--color-text-tertiary)]\"\n }`}\n >\n {check.label}\n </span>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n\n {/* Confirm Password Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"confirmPassword\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\n \"auth.passwordReset.confirmPasswordLabel\",\n \"Confirm Password\",\n )}\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 <LockIcon className=\"w-5 h-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"confirmPassword\"\n type={showConfirmPassword ? \"text\" : \"password\"}\n required\n aria-required=\"true\"\n autoComplete=\"new-password\"\n aria-invalid={\n confirmPassword.length > 0 && newPassword !== confirmPassword\n }\n value={confirmPassword}\n onChange={(e) => setConfirmPassword(e.target.value)}\n className=\"block w-full pl-[3.5rem] pr-12 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.passwordReset.confirmPasswordPlaceholder\",\n \"Confirm new password\",\n )}\n />\n <button\n type=\"button\"\n onClick={() => setShowConfirmPassword(!showConfirmPassword)}\n aria-label={\n showConfirmPassword ? \"Hide password\" : \"Show password\"\n }\n className=\"absolute inset-y-0 right-0 pr-3.5 flex items-center text-[var(--color-text-tertiary)] hover:text-[var(--color-text-secondary)] transition-colors\"\n >\n {showConfirmPassword ? (\n <EyeOffIcon className=\"w-5 h-5\" />\n ) : (\n <EyeIcon className=\"w-5 h-5\" />\n )}\n </button>\n </div>\n {confirmPassword && newPassword !== confirmPassword && (\n <p className=\"text-xs text-[var(--color-status-error-text)]\">\n {tr(\n \"auth.passwordReset.passwordsMismatch\",\n \"Passwords do not match\",\n )}\n </p>\n )}\n </div>\n\n {/* Submit Button */}\n <button\n type=\"submit\"\n disabled={\n isLoading ||\n (confirmPassword.length > 0 && newPassword !== confirmPassword)\n }\n className=\"auth-primary-button flex w-full items-center justify-center gap-2 px-4 py-3 font-semibold\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"w-5 h-5 animate-spin\" />\n <span>\n {tr(\"auth.passwordReset.resetting\", \"Resetting password...\")}\n </span>\n </>\n ) : (\n tr(\"auth.passwordReset.resetButton\", \"Reset password\")\n )}\n </button>\n </form>\n ) : (\n <form onSubmit={handleRequestReset} className=\"space-y-5\">\n {/* Error Alert */}\n {error && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl bg-[var(--color-status-error-bg-subtle)] border border-[var(--color-status-error-border)]\">\n <svg\n className=\"w-5 h-5 text-[var(--color-status-error-text)] flex-shrink-0 mt-0.5\"\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=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n <p className=\"text-sm text-[var(--color-status-error-text)]\">\n {error}\n </p>\n </div>\n )}\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.passwordReset.emailLabel\", \"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=\"w-5 h-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"email\"\n type=\"email\"\n required\n aria-required=\"true\"\n autoComplete=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\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.passwordReset.emailPlaceholder\",\n \"you@example.com\",\n )}\n />\n </div>\n </div>\n\n {/* Submit Button */}\n <button\n type=\"submit\"\n disabled={isLoading}\n className=\"auth-primary-button flex w-full items-center justify-center gap-2 px-4 py-3 font-semibold\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"w-5 h-5 animate-spin\" />\n <span>{tr(\"auth.passwordReset.sending\", \"Sending...\")}</span>\n </>\n ) : (\n tr(\"auth.passwordReset.sendResetLink\", \"Send reset link\")\n )}\n </button>\n </form>\n )}\n\n {/* Back to Sign In */}\n <p className=\"mt-8 text-center text-sm text-[var(--color-text-secondary)]\">\n {tr(\"auth.passwordReset.rememberPassword\", \"Remember your password?\")}{\" \"}\n <button\n onClick={() => navigate(\"/auth/login\")}\n className=\"font-semibold text-text-link hover:text-text-linkHover transition-colors\"\n >\n {tr(\"auth.passwordReset.signIn\", \"Sign in\")}\n </button>\n </p>\n </AuthLayout>\n );\n}\n"],"mappings":";;;;;;;;;;AAgBA,IAAM,IAAiB;AAMvB,SAAgB,IAAoB;CAClC,IAAM,IAAW,GAAa,EACxB,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;IAEnC,CAAC,KAAgB,GAAiB,EAClC,IAAQ,EAAa,IAAI,QAAQ,EAEjC,IAAW,EAAa,IAAI,WAAW,EACvC,EAAE,yBAAsB,kBAAe,iBAC3C,GAAuB,EACnB,EAAE,kBAAkB,MAAqB,GAAc,EACvD,CAAC,GAAO,KAAY,EAAS,GAAG,EAChC,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAiB,KAAsB,EAAS,GAAG,EACpD,CAAC,GAAc,KAAmB,EAAS,GAAM,EACjD,CAAC,GAAqB,KAA0B,EAAS,GAAM,EAC/D,CAAC,GAAS,KAAc,EAAS,GAAM,EACvC,CAAC,GAAO,KAAY,EAAS,GAAG,EAChC,CAAC,GAAW,KAAgB,EAAS,EAAe;AAG1D,SAAgB;AACd,MAAI,CAAC,KAAW,CAAC,EAAO;EAExB,IAAM,IAAY,IACd,wBAAwB,mBAAmB,EAAS,KACpD,eACE,IAAQ,kBAAkB;AAC9B,MAAc,MACR,KAAQ,KACV,cAAc,EAAM,EACpB,EAAS,EAAU,EACZ,KAEF,IAAO,EACd;KACD,IAAK;AAER,eAAa,cAAc,EAAM;IAChC;EAAC;EAAS;EAAO;EAAU;EAAS,CAAC;CAGxC,IAAM,WAAwB;EAC5B,IAAM,IAAW;AACjB,SAAO;GACL;IACE,OAAO,EAAG,6BAA6B,yBAAyB;IAChE,KAAK,EAAS,UAAU;IACzB;GACD;IACE,OAAO,EAAG,kCAAkC,uBAAuB;IACnE,KAAK,QAAQ,KAAK,EAAS;IAC5B;GACD;IACE,OAAO,EAAG,kCAAkC,uBAAuB;IACnE,KAAK,QAAQ,KAAK,EAAS;IAC5B;GACD;IACE,OAAO,EAAG,+BAA+B,aAAa;IACtD,KAAK,KAAK,KAAK,EAAS;IACzB;GACD;IACE,OAAO,EAAG,gCAAgC,wBAAwB;IAClE,KAAK,yBAAyB,KAAK,EAAS;IAC7C;GACF;KACC,EAEE,WAA0B;EAC9B,IAAM,IAAW,EAAe,QAAQ,MAAM,EAAE,IAAI,CAAC;AAmBrD,SAlBI,KAAY,IACP;GACL,OAAO,EAAG,2CAA2C,OAAO;GAC5D,OAAO;GACP,OAAO;GACR,GACC,KAAY,IACP;GACL,OAAO,EAAG,2CAA2C,OAAO;GAC5D,OAAO;GACP,OAAO;GACR,GACC,KAAY,IACP;GACL,OAAO,EAAG,2CAA2C,OAAO;GAC5D,OAAO;GACP,OAAO;GACR,GACI;GACL,OAAO,EAAG,6CAA6C,SAAS;GAChE,OAAO;GACP,OAAO;GACR;KACC;AAmKJ,QAnGI,IAEA,kBAAC,GAAD;EACE,OACE,IACI,EAAG,sCAAsC,mBAAmB,GAC5D,EAAG,iCAAiC,mBAAmB;EAE7D,UACE,IACI,EACE,sCACA,4CACD,GACD,EACE,uCACA,kDACD;YAfT;GAmBE,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MACE,WAAU;MACV,MAAK;MACL,SAAQ;MACR,QAAO;gBAEP,kBAAC,QAAD;OACE,eAAc;OACd,gBAAe;OACf,aAAa;OACb,GAAE;OACF,CAAA;MACE,CAAA;KACF,CAAA;IACF,CAAA;GAEN,kBAAC,KAAD;IAAG,WAAU;cACV,IACG,EACE,mCACA,gEACD,GACD,EACE,kCACA,qFACD,CAAC,QAAQ,aAAa,EAAM;IAC/B,CAAA;GAEH,IACC,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,KAAD;KAAG,WAAU;eAAb;MACG,EACC,oCACA,4BACD;MAAE;MACH,kBAAC,QAAD;OAAM,WAAU;iBACb;OACI,CAAA;MAAC;MACP,EAAG,8BAA8B,aAAa;MAC7C;QACJ,kBAAC,UAAD;KACE,eACE,EACE,IACI,wBAAwB,mBAAmB,EAAS,KACpD,cACL;KAEH,WAAU;eAET,EAAG,gCAAgC,cAAc;KAC3C,CAAA,CACL;QAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,eAAe,EAAW,GAAM;KAChC,WAAU;eAET,EACC,mCACA,sCACD;KACM,CAAA,EACT,kBAAC,UAAD;KACE,eAAe,EAAS,cAAc;KACtC,WAAU;eAET,EAAG,mCAAmC,kBAAkB;KAClD,CAAA,CACL;;GAEG;MAKf,kBAAC,GAAD;EACE,OACE,IACI,EAAG,wCAAwC,sBAAsB,GACjE,EAAG,iCAAiC,sBAAsB;EAEhE,UACE,IACI,EACE,uCACA,wCACD,GACD,EACE,iCACA,mDACD;YAfT,CAkBG,IACC,kBAAC,QAAD;GAAM,UAlKgB,OAAO,MAAuB;AACxD,UAAE,gBAAgB,EAClB,EAAS,GAAG,EAEP,GAEL;SAAI,MAAgB,GAAiB;AACnC,QACE,EAAG,uCAAuC,yBAAyB,CACpE;AACD;;AAGF,SAAI;AAEF,MADA,MAAM,EAAc,GAAO,EAAY,EACvC,EAAW,GAAK;cACT,GAAK;MACZ,IAAM,IAAM,aAAe,QAAQ,EAAI,UAAU;AACjD,MACE,EADE,MAAQ,kBAER,EACE,wCACA,kEACD,GAEM,MAAQ,kBAEf,EACE,wCACA,gEACD,GAID,EACE,mCACA,8CACD,CACF;;;;GA4HoC,WAAU;aAA/C;IAEG,KACC,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MACE,WAAU;MACV,MAAK;MACL,SAAQ;MACR,QAAO;gBAEP,kBAAC,QAAD;OACE,eAAc;OACd,gBAAe;OACf,aAAa;OACb,GAAE;OACF,CAAA;MACE,CAAA,EACN,kBAAC,KAAD;MAAG,WAAU;gBACV;MACC,CAAA,CACA;;IAIR,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,SAAD;OACE,SAAQ;OACR,WAAU;iBAET,EAAG,uCAAuC,eAAe;OACpD,CAAA;MACR,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,GAAD,EAAU,WAAU,6CAA8C,CAAA;SAC9D,CAAA;QACN,kBAAC,SAAD;SACE,IAAG;SACH,MAAM,IAAe,SAAS;SAC9B,UAAA;SACA,iBAAc;SACd,cAAa;SACb,OAAO;SACP,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;SAC/C,WAAU;SACV,aAAa,EACX,6CACA,qBACD;SACD,CAAA;QACF,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAgB,CAAC,EAAa;SAC7C,cAAY,IAAe,kBAAkB;SAC7C,WAAU;mBAGR,EADD,IACE,IAEA,GAFD,EAAY,WAAU,WAAY,CAEH;SAE1B,CAAA;QACL;;MAGL,KACC,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,OAAD,EACE,WAAW,UAAU,EAAiB,MAAM,GAAG,EAAiB,MAAM,+BACtE,CAAA;SACE,CAAA,EACN,kBAAC,KAAD;SAAG,WAAU;mBAAb;UACG,EACC,uCACA,qBACD;UAAE;UACH,kBAAC,QAAD;WAAM,WAAU;qBACb,EAAiB;WACb,CAAA;UACL;WACA;WAEN,kBAAC,OAAD;QAAK,WAAU;kBACZ,EAAe,KAAK,GAAO,MAC1B,kBAAC,OAAD;SAAa,WAAU;mBAAvB,CACG,EAAM,MACL,kBAAC,GAAD,EAAW,WAAU,oCAAqC,CAAA,GAE1D,kBAAC,GAAD,EAAO,WAAU,6CAA8C,CAAA,EAEjE,kBAAC,QAAD;UACE,WAAW,WACT,EAAM,MACF,4CACA;oBAGL,EAAM;UACF,CAAA,CACH;WAfI,EAeJ,CACN;QACE,CAAA,CACF;;MAEJ;;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,SAAD;OACE,SAAQ;OACR,WAAU;iBAET,EACC,2CACA,mBACD;OACK,CAAA;MACR,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,GAAD,EAAU,WAAU,6CAA8C,CAAA;SAC9D,CAAA;QACN,kBAAC,SAAD;SACE,IAAG;SACH,MAAM,IAAsB,SAAS;SACrC,UAAA;SACA,iBAAc;SACd,cAAa;SACb,gBACE,EAAgB,SAAS,KAAK,MAAgB;SAEhD,OAAO;SACP,WAAW,MAAM,EAAmB,EAAE,OAAO,MAAM;SACnD,WAAU;SACV,aAAa,EACX,iDACA,uBACD;SACD,CAAA;QACF,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAuB,CAAC,EAAoB;SAC3D,cACE,IAAsB,kBAAkB;SAE1C,WAAU;mBAGR,EADD,IACE,IAEA,GAFD,EAAY,WAAU,WAAY,CAEH;SAE1B,CAAA;QACL;;MACL,KAAmB,MAAgB,KAClC,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,wCACA,yBACD;OACC,CAAA;MAEF;;IAGN,kBAAC,UAAD;KACE,MAAK;KACL,UACE,KACC,EAAgB,SAAS,KAAK,MAAgB;KAEjD,WAAU;eAET,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAa,WAAU,wBAAyB,CAAA,EAChD,kBAAC,QAAD,EAAA,UACG,EAAG,gCAAgC,wBAAwB,EACvD,CAAA,CACN,EAAA,CAAA,GAEH,EAAG,kCAAkC,iBAAiB;KAEjD,CAAA;IACJ;OAEP,kBAAC,QAAD;GAAM,UA/We,OAAO,MAAuB;AAEvD,IADA,EAAE,gBAAgB,EAClB,EAAS,GAAG;AACZ,QAAI;AAIF,KADA,MAAM,EAAqB,GADJ,MAAM,EAAiB,iBAAiB,CACd,EACjD,EAAW,GAAK;aACT,GAAK;AAOZ,KANA,EACE,EACE,iCACA,gDACD,CACF,EACD,QAAQ,MAAM,qCAAqC,EAAI;;;GAgWjB,WAAU;aAA9C;IAEG,KACC,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MACE,WAAU;MACV,MAAK;MACL,SAAQ;MACR,QAAO;gBAEP,kBAAC,QAAD;OACE,eAAc;OACd,gBAAe;OACf,aAAa;OACb,GAAE;OACF,CAAA;MACE,CAAA,EACN,kBAAC,KAAD;MAAG,WAAU;gBACV;MACC,CAAA,CACA;;IAIR,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,SAAD;MACE,SAAQ;MACR,WAAU;gBAET,EAAG,iCAAiC,gBAAgB;MAC/C,CAAA,EACR,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAW,WAAU,6CAA8C,CAAA;OAC/D,CAAA,EACN,kBAAC,SAAD;OACE,IAAG;OACH,MAAK;OACL,UAAA;OACA,iBAAc;OACd,cAAa;OACb,OAAO;OACP,WAAW,MAAM,EAAS,EAAE,OAAO,MAAM;OACzC,WAAU;OACV,aAAa,EACX,uCACA,kBACD;OACD,CAAA,CACE;QACF;;IAGN,kBAAC,UAAD;KACE,MAAK;KACL,UAAU;KACV,WAAU;eAET,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAa,WAAU,wBAAyB,CAAA,EAChD,kBAAC,QAAD,EAAA,UAAO,EAAG,8BAA8B,aAAa,EAAQ,CAAA,CAC5D,EAAA,CAAA,GAEH,EAAG,oCAAoC,kBAAkB;KAEpD,CAAA;IACJ;MAIT,kBAAC,KAAD;GAAG,WAAU;aAAb;IACG,EAAG,uCAAuC,0BAA0B;IAAE;IACvE,kBAAC,UAAD;KACE,eAAe,EAAS,cAAc;KACtC,WAAU;eAET,EAAG,6BAA6B,UAAU;KACpC,CAAA;IACP;KACO"}
|
|
@@ -150,7 +150,7 @@ function m() {
|
|
|
150
150
|
/* @__PURE__ */ f("button", {
|
|
151
151
|
type: "submit",
|
|
152
152
|
disabled: y || !x || !C,
|
|
153
|
-
className: "w-full py-3 px-4
|
|
153
|
+
className: "auth-primary-button w-full py-3 px-4 flex items-center justify-center gap-2",
|
|
154
154
|
children: y ? /* @__PURE__ */ p(d, { children: [/* @__PURE__ */ f(a, { className: "w-5 h-5 animate-spin" }), /* @__PURE__ */ f("span", { children: "Reactivating..." })] }) : /* @__PURE__ */ p(d, { children: [/* @__PURE__ */ f("svg", {
|
|
155
155
|
className: "w-5 h-5",
|
|
156
156
|
fill: "none",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReactivatePage.js","names":[],"sources":["../../../src/auth/pages/ReactivatePage.tsx"],"sourcesContent":["import { useState, useEffect } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport { useNavigate, useLocation } from \"react-router-dom\";\nimport { useAccountOperations } from \"../hooks/useAccountOperations\";\nimport { AuthLayout } from \"../components/AuthLayout\";\nimport {\n EmailIcon,\n LockIcon,\n SpinnerIcon,\n AlertIcon,\n} from \"../components/AuthIcons\";\n\n/**\n * Account Reactivation Page\n * Allows users to reactivate their deactivated accounts\n */\nexport function ReactivatePage() {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n\n const navigate = useNavigate();\n const location = useLocation();\n const { reactivateAccount, isReactivating } = useAccountOperations();\n\n // Get email from navigation state (passed from login page)\n const emailFromState = (location.state as { email?: string })?.email || \"\";\n\n const [email, setEmail] = useState(emailFromState);\n const [password, setPassword] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n const [success, setSuccess] = useState(false);\n\n // Update email if passed from login page\n useEffect(() => {\n if (emailFromState) {\n setEmail(emailFromState);\n }\n }, [emailFromState]);\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n\n if (!email || !password) {\n setError(\n tr(\"auth.reactivate.enterBoth\", \"Please enter both email and password\"),\n );\n return;\n }\n\n try {\n const result = await reactivateAccount(email, password);\n\n if (result.success) {\n setSuccess(true);\n // Redirect to dashboard after short delay\n setTimeout(() => {\n navigate(\"/dashboard\", { replace: true });\n }, 2000);\n } else {\n setError(result.error || \"Failed to reactivate account\");\n }\n } catch (err) {\n console.error(\"Reactivation error:\", err);\n setError(\n err instanceof Error ? err.message : \"An unexpected error occurred\",\n );\n }\n };\n\n if (success) {\n return (\n <AuthLayout\n title={tr(\"auth.reactivate.successTitle\", \"Account Reactivated!\")}\n subtitle={tr(\"auth.reactivate.successSubtitle\", \"Welcome back\")}\n >\n <div className=\"text-center py-8\">\n <div className=\"w-20 h-20 mx-auto mb-6 rounded-full bg-[var(--color-status-success-bg-subtle)] flex items-center justify-center\">\n <svg\n className=\"w-10 h-10 text-[var(--color-status-success-text)]\"\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=\"M5 13l4 4L19 7\"\n />\n </svg>\n </div>\n\n <h2 className=\"text-xl font-semibold text-[var(--color-text-primary)] mb-2\">\n {tr(\"auth.reactivate.accountActive\", \"Your account is now active\")}\n </h2>\n <p className=\"text-[var(--color-text-secondary)] mb-6\">\n {tr(\n \"auth.reactivate.redirecting\",\n \"You can now access all features. Redirecting to dashboard...\",\n )}\n </p>\n\n <div className=\"flex justify-center\">\n <SpinnerIcon className=\"w-6 h-6 text-status-success-text animate-spin\" />\n </div>\n </div>\n </AuthLayout>\n );\n }\n\n return (\n <AuthLayout\n title={tr(\"auth.reactivate.title\", \"Reactivate Account\")}\n subtitle={tr(\n \"auth.reactivate.subtitle\",\n \"Restore access to your deactivated account\",\n )}\n >\n <form onSubmit={handleSubmit} className=\"space-y-5\">\n {/* Error Alert */}\n {error && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl bg-[var(--color-status-error-bg-subtle)] border border-[var(--color-status-error-border)]\">\n <AlertIcon className=\"w-5 h-5 text-[var(--color-status-error-text)] flex-shrink-0 mt-0.5\" />\n <div className=\"flex-1\">\n <p className=\"text-sm text-[var(--color-status-error-text)]\">\n {error}\n </p>\n </div>\n </div>\n )}\n\n {/* Info Banner */}\n <div className=\"p-4 rounded-xl bg-[var(--color-status-warning-bg-subtle)] border border-[var(--color-status-warning-border)]\">\n <div className=\"flex items-start gap-3\">\n <svg\n className=\"w-5 h-5 text-[var(--color-status-warning-text)] flex-shrink-0 mt-0.5\"\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=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n <div>\n <p className=\"text-sm text-[var(--color-status-warning-text)] font-medium\">\n {tr(\"auth.reactivate.deactivated\", \"Account Deactivated?\")}\n </p>\n <p className=\"text-xs text-[var(--color-status-warning-text)] mt-1\">\n {tr(\n \"auth.reactivate.instructions\",\n \"If you previously deactivated your account, you can restore it here. Enter your email and password to reactivate.\",\n )}\n </p>\n </div>\n </div>\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.reactivate.emailLabel\", \"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=\"w-5 h-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"email\"\n type=\"email\"\n required\n value={email}\n onChange={(e) => setEmail(e.target.value)}\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-status-success-bg/20 focus:border-status-success-border transition-all\"\n placeholder=\"you@example.com\"\n />\n </div>\n </div>\n\n {/* Password Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"password\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.reactivate.passwordLabel\", \"Password\")}\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 <LockIcon className=\"w-5 h-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"password\"\n type=\"password\"\n required\n value={password}\n onChange={(e) => setPassword(e.target.value)}\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-status-success-bg/20 focus:border-status-success-border transition-all\"\n placeholder={tr(\n \"auth.reactivate.passwordPlaceholder\",\n \"Enter your password\",\n )}\n />\n </div>\n </div>\n\n {/* Submit Button */}\n <button\n type=\"submit\"\n disabled={isReactivating || !email || !password}\n className=\"w-full py-3 px-4 rounded-xl bg-gradient-to-r from-green-600 to-emerald-600 text-white font-semibold shadow-lg shadow-green-500/25 hover:shadow-xl hover:shadow-green-500/30 hover:from-green-500 hover:to-emerald-500 disabled:opacity-50 disabled:cursor-not-allowed disabled:shadow-none transition-all duration-200 flex items-center justify-center gap-2\"\n >\n {isReactivating ? (\n <>\n <SpinnerIcon className=\"w-5 h-5 animate-spin\" />\n <span>Reactivating...</span>\n </>\n ) : (\n <>\n <svg\n className=\"w-5 h-5\"\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=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"\n />\n </svg>\n <span>\n {tr(\"auth.reactivate.reactivateBtn\", \"Reactivate Account\")}\n </span>\n </>\n )}\n </button>\n </form>\n\n {/* Links */}\n <div className=\"mt-8 pt-6 border-t border-[var(--color-border-default)] space-y-4\">\n <div className=\"text-center\">\n <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 {tr(\n \"auth.reactivate.accountIsActive\",\n \"Remember your account is active?\",\n )}{\" \"}\n <span className=\"font-semibold text-text-link hover:text-text-linkHover\">\n {tr(\"auth.reactivate.signInInstead\", \"Sign in instead\")}\n </span>\n </button>\n </div>\n\n <div className=\"text-center\">\n <button\n onClick={() => navigate(\"/auth/reset-password\")}\n className=\"text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] transition-colors\"\n >\n {tr(\"auth.reactivate.forgotPassword\", \"Forgot your password?\")}{\" \"}\n <span className=\"font-semibold text-accent-purple hover:text-accent-purple\">\n {tr(\"auth.reactivate.resetIt\", \"Reset it\")}\n </span>\n </button>\n </div>\n </div>\n\n {/* Help Text */}\n <div className=\"mt-6 p-4 rounded-xl bg-[var(--color-bg-muted)] border border-[var(--color-border-default)]\">\n <h4 className=\"text-sm font-medium text-[var(--color-text-primary)] mb-2\">\n {tr(\n \"auth.reactivate.cantReactivate\",\n \"Can't reactivate your account?\",\n )}\n </h4>\n <p className=\"text-xs text-[var(--color-text-secondary)]\">\n {tr(\n \"auth.reactivate.deletedHelp\",\n \"If your account was deleted (not just deactivated), it cannot be restored. Contact support if you believe this is an error.\",\n )}\n </p>\n </div>\n </AuthLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ReactivatePage.js","names":[],"sources":["../../../src/auth/pages/ReactivatePage.tsx"],"sourcesContent":["import { useState, useEffect } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport { useNavigate, useLocation } from \"react-router-dom\";\nimport { useAccountOperations } from \"../hooks/useAccountOperations\";\nimport { AuthLayout } from \"../components/AuthLayout\";\nimport {\n EmailIcon,\n LockIcon,\n SpinnerIcon,\n AlertIcon,\n} from \"../components/AuthIcons\";\n\n/**\n * Account Reactivation Page\n * Allows users to reactivate their deactivated accounts\n */\nexport function ReactivatePage() {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n\n const navigate = useNavigate();\n const location = useLocation();\n const { reactivateAccount, isReactivating } = useAccountOperations();\n\n // Get email from navigation state (passed from login page)\n const emailFromState = (location.state as { email?: string })?.email || \"\";\n\n const [email, setEmail] = useState(emailFromState);\n const [password, setPassword] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n const [success, setSuccess] = useState(false);\n\n // Update email if passed from login page\n useEffect(() => {\n if (emailFromState) {\n setEmail(emailFromState);\n }\n }, [emailFromState]);\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n\n if (!email || !password) {\n setError(\n tr(\"auth.reactivate.enterBoth\", \"Please enter both email and password\"),\n );\n return;\n }\n\n try {\n const result = await reactivateAccount(email, password);\n\n if (result.success) {\n setSuccess(true);\n // Redirect to dashboard after short delay\n setTimeout(() => {\n navigate(\"/dashboard\", { replace: true });\n }, 2000);\n } else {\n setError(result.error || \"Failed to reactivate account\");\n }\n } catch (err) {\n console.error(\"Reactivation error:\", err);\n setError(\n err instanceof Error ? err.message : \"An unexpected error occurred\",\n );\n }\n };\n\n if (success) {\n return (\n <AuthLayout\n title={tr(\"auth.reactivate.successTitle\", \"Account Reactivated!\")}\n subtitle={tr(\"auth.reactivate.successSubtitle\", \"Welcome back\")}\n >\n <div className=\"text-center py-8\">\n <div className=\"w-20 h-20 mx-auto mb-6 rounded-full bg-[var(--color-status-success-bg-subtle)] flex items-center justify-center\">\n <svg\n className=\"w-10 h-10 text-[var(--color-status-success-text)]\"\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=\"M5 13l4 4L19 7\"\n />\n </svg>\n </div>\n\n <h2 className=\"text-xl font-semibold text-[var(--color-text-primary)] mb-2\">\n {tr(\"auth.reactivate.accountActive\", \"Your account is now active\")}\n </h2>\n <p className=\"text-[var(--color-text-secondary)] mb-6\">\n {tr(\n \"auth.reactivate.redirecting\",\n \"You can now access all features. Redirecting to dashboard...\",\n )}\n </p>\n\n <div className=\"flex justify-center\">\n <SpinnerIcon className=\"w-6 h-6 text-status-success-text animate-spin\" />\n </div>\n </div>\n </AuthLayout>\n );\n }\n\n return (\n <AuthLayout\n title={tr(\"auth.reactivate.title\", \"Reactivate Account\")}\n subtitle={tr(\n \"auth.reactivate.subtitle\",\n \"Restore access to your deactivated account\",\n )}\n >\n <form onSubmit={handleSubmit} className=\"space-y-5\">\n {/* Error Alert */}\n {error && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl bg-[var(--color-status-error-bg-subtle)] border border-[var(--color-status-error-border)]\">\n <AlertIcon className=\"w-5 h-5 text-[var(--color-status-error-text)] flex-shrink-0 mt-0.5\" />\n <div className=\"flex-1\">\n <p className=\"text-sm text-[var(--color-status-error-text)]\">\n {error}\n </p>\n </div>\n </div>\n )}\n\n {/* Info Banner */}\n <div className=\"p-4 rounded-xl bg-[var(--color-status-warning-bg-subtle)] border border-[var(--color-status-warning-border)]\">\n <div className=\"flex items-start gap-3\">\n <svg\n className=\"w-5 h-5 text-[var(--color-status-warning-text)] flex-shrink-0 mt-0.5\"\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=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n <div>\n <p className=\"text-sm text-[var(--color-status-warning-text)] font-medium\">\n {tr(\"auth.reactivate.deactivated\", \"Account Deactivated?\")}\n </p>\n <p className=\"text-xs text-[var(--color-status-warning-text)] mt-1\">\n {tr(\n \"auth.reactivate.instructions\",\n \"If you previously deactivated your account, you can restore it here. Enter your email and password to reactivate.\",\n )}\n </p>\n </div>\n </div>\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.reactivate.emailLabel\", \"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=\"w-5 h-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"email\"\n type=\"email\"\n required\n value={email}\n onChange={(e) => setEmail(e.target.value)}\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-status-success-bg/20 focus:border-status-success-border transition-all\"\n placeholder=\"you@example.com\"\n />\n </div>\n </div>\n\n {/* Password Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"password\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.reactivate.passwordLabel\", \"Password\")}\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 <LockIcon className=\"w-5 h-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"password\"\n type=\"password\"\n required\n value={password}\n onChange={(e) => setPassword(e.target.value)}\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-status-success-bg/20 focus:border-status-success-border transition-all\"\n placeholder={tr(\n \"auth.reactivate.passwordPlaceholder\",\n \"Enter your password\",\n )}\n />\n </div>\n </div>\n\n {/* Submit Button */}\n <button\n type=\"submit\"\n disabled={isReactivating || !email || !password}\n className=\"auth-primary-button w-full py-3 px-4 flex items-center justify-center gap-2\"\n >\n {isReactivating ? (\n <>\n <SpinnerIcon className=\"w-5 h-5 animate-spin\" />\n <span>Reactivating...</span>\n </>\n ) : (\n <>\n <svg\n className=\"w-5 h-5\"\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=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"\n />\n </svg>\n <span>\n {tr(\"auth.reactivate.reactivateBtn\", \"Reactivate Account\")}\n </span>\n </>\n )}\n </button>\n </form>\n\n {/* Links */}\n <div className=\"mt-8 pt-6 border-t border-[var(--color-border-default)] space-y-4\">\n <div className=\"text-center\">\n <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 {tr(\n \"auth.reactivate.accountIsActive\",\n \"Remember your account is active?\",\n )}{\" \"}\n <span className=\"font-semibold text-text-link hover:text-text-linkHover\">\n {tr(\"auth.reactivate.signInInstead\", \"Sign in instead\")}\n </span>\n </button>\n </div>\n\n <div className=\"text-center\">\n <button\n onClick={() => navigate(\"/auth/reset-password\")}\n className=\"text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] transition-colors\"\n >\n {tr(\"auth.reactivate.forgotPassword\", \"Forgot your password?\")}{\" \"}\n <span className=\"font-semibold text-accent-purple hover:text-accent-purple\">\n {tr(\"auth.reactivate.resetIt\", \"Reset it\")}\n </span>\n </button>\n </div>\n </div>\n\n {/* Help Text */}\n <div className=\"mt-6 p-4 rounded-xl bg-[var(--color-bg-muted)] border border-[var(--color-border-default)]\">\n <h4 className=\"text-sm font-medium text-[var(--color-text-primary)] mb-2\">\n {tr(\n \"auth.reactivate.cantReactivate\",\n \"Can't reactivate your account?\",\n )}\n </h4>\n <p className=\"text-xs text-[var(--color-text-secondary)]\">\n {tr(\n \"auth.reactivate.deletedHelp\",\n \"If your account was deleted (not just deactivated), it cannot be restored. Contact support if you believe this is an error.\",\n )}\n </p>\n </div>\n </AuthLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -52,7 +52,7 @@ function C() {
|
|
|
52
52
|
width: "w-1/4"
|
|
53
53
|
} : e <= 3 ? {
|
|
54
54
|
level: E("auth.register.passwordStrengthFair", "fair"),
|
|
55
|
-
color: "bg-
|
|
55
|
+
color: "bg-status-warning-text",
|
|
56
56
|
width: "w-2/4"
|
|
57
57
|
} : e <= 4 ? {
|
|
58
58
|
level: E("auth.register.passwordStrengthGood", "good"),
|