@burdenoff/microfe-auth 2026.1031.1 → 2026.1101.1
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/AuthRoot.js +14 -11
- package/dist/auth/AuthRoot.js.map +1 -1
- package/dist/auth/AuthRoutes.js +1 -2
- package/dist/auth/AuthRoutes.js.map +1 -1
- package/dist/auth/components/AuthIcons.js +1 -2
- package/dist/auth/components/AuthIcons.js.map +1 -1
- package/dist/auth/components/AuthLayout.js +1 -2
- package/dist/auth/components/AuthLayout.js.map +1 -1
- package/dist/auth/components/CaptchaV2.js +9 -10
- package/dist/auth/components/CaptchaV2.js.map +1 -1
- package/dist/auth/components/ConfirmActionDialog.js +1 -2
- package/dist/auth/components/ConfirmActionDialog.js.map +1 -1
- package/dist/auth/components/profile/UserProfileMenu.js +7 -6
- package/dist/auth/components/profile/UserProfileMenu.js.map +1 -1
- package/dist/auth/components/workspace/WorkspaceSelector.js +1 -2
- package/dist/auth/components/workspace/WorkspaceSelector.js.map +1 -1
- package/dist/auth/constants/permissions.js +1 -2
- package/dist/auth/constants/permissions.js.map +1 -1
- package/dist/auth/constants/sessionKeys.js +1 -2
- package/dist/auth/constants/sessionKeys.js.map +1 -1
- package/dist/auth/hooks/contextDeepLink.js +1 -2
- package/dist/auth/hooks/contextDeepLink.js.map +1 -1
- package/dist/auth/hooks/index.js +12 -11
- package/dist/auth/hooks/useAccountOperations.js +1 -2
- package/dist/auth/hooks/useAccountOperations.js.map +1 -1
- package/dist/auth/hooks/useActiveContextBootstrapMirror.js +1 -2
- package/dist/auth/hooks/useActiveContextBootstrapMirror.js.map +1 -1
- package/dist/auth/hooks/useAuthOperations.js +37 -37
- package/dist/auth/hooks/useAuthOperations.js.map +1 -1
- package/dist/auth/hooks/useAuthPermissions.js +1 -2
- package/dist/auth/hooks/useAuthPermissions.js.map +1 -1
- package/dist/auth/hooks/useCaptchaV3.js +1 -2
- package/dist/auth/hooks/useCaptchaV3.js.map +1 -1
- package/dist/auth/hooks/useContextSwitcherData.js +81 -78
- package/dist/auth/hooks/useContextSwitcherData.js.map +1 -1
- package/dist/auth/hooks/useMfaOperations.js +4 -5
- package/dist/auth/hooks/useMfaOperations.js.map +1 -1
- package/dist/auth/hooks/useMyWorkspaces.js +4 -5
- package/dist/auth/hooks/useMyWorkspaces.js.map +1 -1
- package/dist/auth/hooks/useOAuthOperations.js +11 -12
- package/dist/auth/hooks/useOAuthOperations.js.map +1 -1
- package/dist/auth/hooks/usePasswordOperations.js +1 -2
- package/dist/auth/hooks/usePasswordOperations.js.map +1 -1
- package/dist/auth/hooks/useSessionOperations.js +3 -4
- package/dist/auth/hooks/useSessionOperations.js.map +1 -1
- package/dist/auth/hooks/useSignupCaptcha.js +1 -2
- package/dist/auth/hooks/useSignupCaptcha.js.map +1 -1
- package/dist/auth/hooks/useSsoOperations.js +80 -81
- package/dist/auth/hooks/useSsoOperations.js.map +1 -1
- package/dist/auth/hooks/useTokenRefresh.js +8 -12
- package/dist/auth/hooks/useTokenRefresh.js.map +1 -1
- package/dist/auth/hooks/useUserOperations.js +1 -2
- package/dist/auth/hooks/useUserOperations.js.map +1 -1
- package/dist/auth/hooks/useWorkspaceOperations.js +1 -2
- package/dist/auth/hooks/useWorkspaceOperations.js.map +1 -1
- package/dist/auth/hooks/useWorkspaceToken.js +1 -2
- package/dist/auth/hooks/useWorkspaceToken.js.map +1 -1
- package/dist/auth/index.js +19 -18
- package/dist/auth/pages/AuditLogPage.js +21 -22
- package/dist/auth/pages/AuditLogPage.js.map +1 -1
- package/dist/auth/pages/EmailVerifyPage.js +15 -17
- package/dist/auth/pages/EmailVerifyPage.js.map +1 -1
- package/dist/auth/pages/LoginPage.js +89 -105
- package/dist/auth/pages/LoginPage.js.map +1 -1
- package/dist/auth/pages/MagicLinkPage.js +30 -31
- package/dist/auth/pages/MagicLinkPage.js.map +1 -1
- package/dist/auth/pages/MfaSetupPage.js +18 -20
- package/dist/auth/pages/MfaSetupPage.js.map +1 -1
- package/dist/auth/pages/MfaVerifyPage.js +31 -35
- package/dist/auth/pages/MfaVerifyPage.js.map +1 -1
- package/dist/auth/pages/OAuthCallbackPage.js +4 -11
- package/dist/auth/pages/OAuthCallbackPage.js.map +1 -1
- package/dist/auth/pages/PasswordResetPage.js +20 -23
- package/dist/auth/pages/PasswordResetPage.js.map +1 -1
- package/dist/auth/pages/ReactivatePage.js +14 -18
- package/dist/auth/pages/ReactivatePage.js.map +1 -1
- package/dist/auth/pages/RegisterPage.js +180 -181
- package/dist/auth/pages/RegisterPage.js.map +1 -1
- package/dist/auth/pages/SSOClearPage.js +9 -10
- package/dist/auth/pages/SSOClearPage.js.map +1 -1
- package/dist/auth/pages/SSODepositPage.js +9 -13
- package/dist/auth/pages/SSODepositPage.js.map +1 -1
- package/dist/auth/pages/SSORelayCallbackPage.js +3 -7
- package/dist/auth/pages/SSORelayCallbackPage.js.map +1 -1
- package/dist/auth/pages/SSORelayPage.js +11 -10
- package/dist/auth/pages/SSORelayPage.js.map +1 -1
- package/dist/auth/pages/SecurityPage.js +19 -21
- package/dist/auth/pages/SecurityPage.js.map +1 -1
- package/dist/auth/pages/SessionBridgePage.js +9 -16
- package/dist/auth/pages/SessionBridgePage.js.map +1 -1
- package/dist/auth/pages/SessionsPage.js +17 -18
- package/dist/auth/pages/SessionsPage.js.map +1 -1
- package/dist/auth/pages/SsoCallbackPage.js +17 -22
- package/dist/auth/pages/SsoCallbackPage.js.map +1 -1
- package/dist/auth/pages/SsoConfigPage.js +69 -69
- package/dist/auth/pages/SsoConfigPage.js.map +1 -1
- package/dist/auth/pages/SsoLoginPage.js +9 -10
- package/dist/auth/pages/SsoLoginPage.js.map +1 -1
- package/dist/auth/pages/UserDetailsPage.js +16 -23
- package/dist/auth/pages/UserDetailsPage.js.map +1 -1
- package/dist/auth/pages/UserProfilePage.js +140 -141
- package/dist/auth/pages/UserProfilePage.js.map +1 -1
- package/dist/auth/pages/UsersPage.js +446 -453
- package/dist/auth/pages/UsersPage.js.map +1 -1
- package/dist/auth/providers/AuthProvider.js +1 -2
- package/dist/auth/providers/AuthProvider.js.map +1 -1
- package/dist/auth/providers/CaptchaProvider.js +1 -2
- package/dist/auth/providers/CaptchaProvider.js.map +1 -1
- package/dist/auth/store/authStore.js +5 -12
- package/dist/auth/store/authStore.js.map +1 -1
- package/dist/auth/utils/captchaConfig.js +5 -8
- package/dist/auth/utils/captchaConfig.js.map +1 -1
- package/dist/auth/utils/formatters.js +1 -2
- package/dist/auth/utils/formatters.js.map +1 -1
- package/dist/auth/utils/gatewayBaseUrls.js +1 -2
- package/dist/auth/utils/gatewayBaseUrls.js.map +1 -1
- package/dist/auth/utils/legalUrls.js +2 -3
- package/dist/auth/utils/legalUrls.js.map +1 -1
- package/dist/auth/utils/redirect.js +1 -2
- package/dist/auth/utils/redirect.js.map +1 -1
- package/dist/auth/utils/ssoOrigins.js +2 -3
- package/dist/auth/utils/ssoOrigins.js.map +1 -1
- package/dist/auth/utils/validation.js +34 -36
- package/dist/auth/utils/validation.js.map +1 -1
- package/dist/generated/global-operations.js +2 -3
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/generated/wspace-operations.js +2 -3
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/microfe-auth.css +1 -1
- package/dist/shared/hooks/useTr.js +1 -2
- package/dist/shared/hooks/useTr.js.map +1 -1
- package/dist/translations/en.js +1 -2
- package/dist/translations/en.js.map +1 -1
- package/dist/utils/nativeBridge.js +1 -2
- package/package.json +7 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserProfilePage.js","names":[],"sources":["../../../src/auth/pages/UserProfilePage.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useId } from \"react\";\nimport { useTr } from \"../../shared/hooks/useTr\";\nimport { useAuth as useShellAuth } from \"@burdenoff/fe-libs/shared/providers/shell/AuthProvider\";\nimport { useNavigate } from \"react-router\";\nimport { useUserOperations } from \"../hooks/useUserOperations\";\nimport { validatePhoneNumber, validateUserFields } from \"../utils/validation\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { AuthLayout } from \"../components/AuthLayout\";\nimport { Button } from \"@burdenoff/fe-libs/button\";\nimport { PagePurpose, NextSteps } from \"@burdenoff/fe-libs/ui\";\nimport {\n SpinnerIcon,\n AlertIcon,\n UserIcon,\n EmailIcon,\n CheckIcon,\n} from \"../components/AuthIcons\";\nimport {\n useInitiateAvatarUploadMutation,\n useCompleteAvatarUploadMutation,\n} from \"../../generated/wspace-operations\";\nimport { useGatewayClient } from \"@burdenoff/fe-libs/shared/graphql\";\n\ninterface ProfileFormData {\n username: string;\n firstName: string;\n lastName: string;\n name: string;\n phoneNumber: string;\n}\n\n/**\n * User Profile page for viewing and editing own profile\n */\nexport function UserProfilePage() {\n const tr = useTr();\n\n const navigate = useNavigate();\n const shellAuth = useShellAuth();\n const isAuthenticated = useAuthStore((state) => state.isAuthenticated);\n const currentUser = useAuthStore((state) => state.user);\n const setStoreUser = useAuthStore((state) => state.setUser);\n const { updateCurrentUserProfile, isLoading } = useUserOperations();\n\n const usernameId = useId();\n const displayNameId = useId();\n const firstNameId = useId();\n const lastNameId = useId();\n const phoneNumberId = useId();\n\n const currentWorkspaceId = useAuthStore((state) => state.currentWorkspaceId);\n const wspaceClient = useGatewayClient(\"workspace\");\n const [initiateUpload] = useInitiateAvatarUploadMutation({\n client: wspaceClient,\n });\n const [completeUpload] = useCompleteAvatarUploadMutation({\n client: wspaceClient,\n });\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [isUploadingAvatar, setIsUploadingAvatar] = useState(false);\n\n const handleAvatarUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file) return;\n if (!file.type.startsWith(\"image/\")) {\n setError(\n tr(\"auth.profile.invalidImageType\", \"Please select an image file\"),\n );\n return;\n }\n if (!currentWorkspaceId) {\n setError(\n tr(\n \"auth.profile.noWorkspace\",\n \"Please select a workspace before uploading a profile picture\",\n ),\n );\n return;\n }\n setIsUploadingAvatar(true);\n setError(null);\n const wsHeaders = { \"x-workspace-id\": currentWorkspaceId };\n try {\n const extension = file.name.split(\".\").pop()?.toLowerCase() ?? \"\";\n const initiateResult = await initiateUpload({\n variables: {\n input: {\n fileName: file.name,\n fileSize: file.size.toString(),\n mimeType: file.type,\n extension,\n },\n },\n context: { headers: wsHeaders },\n });\n const session = initiateResult.data?.initiateUpload.session;\n const uploadUrl = initiateResult.data?.initiateUpload.uploadUrl;\n if (!session || !uploadUrl) throw new Error(\"Failed to initiate upload\");\n\n const uploadResp = await fetch(uploadUrl, {\n method: \"PUT\",\n headers: { \"Content-Type\": file.type },\n body: file,\n });\n if (!uploadResp.ok) throw new Error(\"Failed to upload file\");\n\n const completeResult = await completeUpload({\n variables: { input: { sessionId: session.id } },\n context: { headers: wsHeaders },\n });\n const avatarUrl = completeResult.data?.completeUpload.file?.downloadUrl;\n if (!avatarUrl) throw new Error(\"Upload succeeded but no URL returned\");\n\n await updateCurrentUserProfile({ avatar: avatarUrl });\n // Update both the local authStore and the shell AuthProvider so the nav\n // bar avatar reflects the change immediately without a page reload.\n if (currentUser) {\n setStoreUser({ ...currentUser, avatar: avatarUrl });\n }\n shellAuth.updateUser({ avatar: avatarUrl });\n setSuccessMessage(\n tr(\"auth.profile.avatarUpdated\", \"Profile picture updated!\"),\n );\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr(\"auth.profile.avatarFailed\", \"Failed to upload picture\"),\n );\n } finally {\n setIsUploadingAvatar(false);\n if (fileInputRef.current) fileInputRef.current.value = \"\";\n }\n };\n\n const [isEditing, setIsEditing] = useState(false);\n const [formData, setFormData] = useState<ProfileFormData>({\n username: \"\",\n firstName: \"\",\n lastName: \"\",\n name: \"\",\n phoneNumber: \"\",\n });\n const [error, setError] = useState<string | null>(null);\n const [phoneError, setPhoneError] = useState<string | null>(null);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n\n // Populate form with current user data\n useEffect(() => {\n if (currentUser) {\n setFormData({\n username: currentUser.username || \"\",\n firstName: currentUser.firstName || \"\",\n lastName: currentUser.lastName || \"\",\n name: currentUser.name || \"\",\n phoneNumber: currentUser.phoneNumber || \"\",\n });\n }\n }, [currentUser]);\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n setPhoneError(null);\n setSuccessMessage(null);\n\n const phoneValidation = validatePhoneNumber(formData.phoneNumber);\n if (!phoneValidation.valid) {\n const phoneMsg = tr(\n \"auth.profile.phoneNumber_invalid\",\n \"Invalid phone number\",\n );\n setPhoneError(phoneMsg);\n setError(phoneMsg);\n return;\n }\n\n // BOFF-7214: enforce the same name/username invariants the signup form\n // uses — without this, a user editing their own profile could replace\n // their name with a pure-numeric value and re-open the same bypass.\n // Pass `requireAll: false` so a user whose optional `firstName` /\n // `lastName` is unset (e.g. provisioned via IdP) can still update\n // phone or display name without being forced to invent first/last\n // (Codex P2 review).\n const validation = validateUserFields(formData, { requireAll: false });\n if (!validation.valid) {\n setError(\n validation.errors\n .map((e) => {\n const key = `auth.profile.${e.field}_${e.code}`;\n return tr(key, e.message);\n })\n .join(\" · \"),\n );\n return;\n }\n\n try {\n await updateCurrentUserProfile({\n username: validation.values.username || undefined,\n firstName: validation.values.firstName || undefined,\n lastName: validation.values.lastName || undefined,\n name: formData.name || undefined,\n phoneNumber: formData.phoneNumber || undefined,\n });\n setSuccessMessage(\n tr(\"auth.profile.updateSuccess\", \"Profile updated successfully!\"),\n );\n setIsEditing(false);\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr(\"auth.profile.updateFailed\", \"Failed to update profile\"),\n );\n }\n };\n\n const cancelEdit = () => {\n if (currentUser) {\n setFormData({\n username: currentUser.username || \"\",\n firstName: currentUser.firstName || \"\",\n lastName: currentUser.lastName || \"\",\n name: currentUser.name || \"\",\n phoneNumber: currentUser.phoneNumber || \"\",\n });\n }\n setIsEditing(false);\n setError(null);\n setPhoneError(null);\n };\n\n // Not authenticated\n if (!isAuthenticated || !currentUser) {\n return (\n <AuthLayout\n title={tr(\"auth.profile.title\", \"Profile\")}\n subtitle={tr(\"auth.profile.subtitle\", \"View and manage your profile\")}\n >\n <div className=\"text-center py-12\">\n <div className=\"size-16 mx-auto mb-4 rounded-full bg-[var(--color-status-warning-bg-subtle)] flex items-center justify-center\">\n <AlertIcon className=\"size-8 text-[var(--color-status-warning-text)]\" />\n </div>\n <h2 className=\"text-xl font-semibold text-[var(--color-text-primary)] mb-2\">\n {tr(\"auth.profile.authRequired\", \"Authentication Required\")}\n </h2>\n <p className=\"text-[var(--color-text-secondary)] mb-6\">\n {tr(\n \"auth.profile.signInToView\",\n \"Please sign in to view your profile\",\n )}\n </p>\n <Button\n type=\"button\"\n onClick={() => navigate(\"/auth/login\")}\n variant=\"default\"\n size=\"lg\"\n className=\"font-semibold\"\n >\n {tr(\"auth.profile.signIn\", \"Sign In\")}\n </Button>\n </div>\n </AuthLayout>\n );\n }\n\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n {/* Header */}\n <div className=\"mb-8\">\n <h1 className=\"text-3xl font-semibold text-[var(--color-text-primary)]\">\n {tr(\"auth.profile.myProfile\", \"My Profile\")}\n </h1>\n <p className=\"text-[var(--color-text-secondary)] mt-2\">\n {tr(\n \"auth.profile.viewAndManage\",\n \"View and manage your account information\",\n )}\n </p>\n <PagePurpose className=\"mt-3\">\n {tr(\n \"auth.profile.purpose\",\n \"This is how you appear across the platform. Update your name, photo, and contact details here — teammates see this information, and we use your email for sign-in and important notices.\",\n )}\n </PagePurpose>\n </div>\n\n {/* Success Message */}\n {successMessage && (\n <div className=\"mb-6 flex items-center gap-3 p-4 rounded-xl bg-[var(--color-status-success-bg-subtle)] border border-[var(--color-status-success-border)]\">\n <CheckIcon className=\"size-5 text-[var(--color-status-success-text)] flex-shrink-0\" />\n <p className=\"text-[var(--color-status-success-text)]\">\n {successMessage}\n </p>\n <button\n type=\"button\"\n onClick={() => setSuccessMessage(null)}\n className=\"ml-auto text-sm text-[var(--color-status-success-text)] hover:underline transition-colors duration-200\"\n >\n {tr(\"auth.profile.dismiss\", \"Dismiss\")}\n </button>\n </div>\n )}\n\n {/* Error Message */}\n {error && (\n <div className=\"mb-6 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=\"size-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 <button\n type=\"button\"\n onClick={() => setError(null)}\n className=\"text-sm text-[var(--color-status-error-text)] hover:underline transition-colors duration-200\"\n >\n {tr(\"auth.profile.dismiss\", \"Dismiss\")}\n </button>\n </div>\n )}\n\n {/* Profile Card */}\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-sm overflow-hidden\">\n {/* Profile Header — the one emphasis zone on this page */}\n <div className=\"bg-accent-wash border-b border-border-seam p-6 text-text-primary\">\n <div className=\"flex items-center gap-4\">\n <div className=\"relative group\">\n {currentUser.avatar ? (\n <img\n src={currentUser.avatar}\n alt={tr(\"auth.profile.avatarAlt\", \"Profile picture\")}\n className=\"size-20 rounded-full object-cover shadow-lg\"\n />\n ) : (\n <div className=\"size-20 rounded-full bg-accent-soft backdrop-blur-sm flex items-center justify-center text-[var(--color-action-primary-bg)] text-3xl font-semibold shadow-lg\">\n {(\n currentUser.name ||\n currentUser.username ||\n currentUser.email\n )\n .charAt(0)\n .toUpperCase()}\n </div>\n )}\n <button\n type=\"button\"\n onClick={() => fileInputRef.current?.click()}\n disabled={isUploadingAvatar}\n aria-label={tr(\n \"auth.profile.changeAvatar\",\n \"Change profile picture\",\n )}\n className=\"absolute inset-0 rounded-full flex items-center justify-center bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity duration-200 cursor-pointer\"\n >\n {isUploadingAvatar ? (\n <SpinnerIcon className=\"size-6 text-white animate-spin\" />\n ) : (\n <svg\n className=\"size-6 text-white\"\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 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z\"\n />\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M15 13a3 3 0 11-6 0 3 3 0 016 0z\"\n />\n </svg>\n )}\n </button>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n className=\"hidden\"\n onChange={(e) => void handleAvatarUpload(e)}\n tabIndex={-1}\n aria-label={tr(\n \"auth.profile.avatarUpload\",\n \"Upload avatar image\",\n )}\n />\n </div>\n <div className=\"text-text-primary\">\n <h2 className=\"text-2xl font-semibold\">\n {currentUser.name ||\n `${currentUser.firstName || \"\"} ${currentUser.lastName || \"\"}`.trim() ||\n currentUser.username}\n </h2>\n <p className=\"text-text-secondary flex items-center gap-2\">\n <EmailIcon className=\"size-4\" />\n {currentUser.email}\n </p>\n <div className=\"flex items-center gap-2 mt-2\">\n <span className=\"px-2.5 py-1 rounded-full text-xs font-medium bg-bg-surface/60 text-text-secondary backdrop-blur-sm\">\n {currentUser.role?.replace(\"_\", \" \") ||\n tr(\"auth.profile.member\", \"Member\")}\n </span>\n {currentUser.emailVerified && (\n <span className=\"px-2.5 py-1 rounded-full text-xs font-medium bg-status-success-bg-subtle text-status-success-text flex items-center gap-1\">\n <CheckIcon className=\"size-3\" />\n {tr(\"auth.profile.verified\", \"Verified\")}\n </span>\n )}\n </div>\n </div>\n </div>\n </div>\n\n {/* Profile Body */}\n <div className=\"p-6\">\n {isEditing ? (\n /* Edit Form */\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-6\">\n <div>\n <label\n htmlFor={usernameId}\n className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-2\"\n >\n {tr(\"auth.profile.username\", \"Username\")}\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 <UserIcon\n className=\"size-5 text-[var(--color-text-tertiary)]\"\n aria-hidden=\"true\"\n />\n </div>\n <input\n id={usernameId}\n type=\"text\"\n aria-label={tr(\"auth.profile.username\", \"Username\")}\n value={formData.username}\n onChange={(e) =>\n setFormData({ ...formData, username: e.target.value })\n }\n className=\"block w-full pl-[3.5rem] pr-4 py-3 border border-[var(--color-border-default)] rounded-xl bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all duration-300\"\n placeholder={tr(\n \"auth.profile.usernamePlaceholder\",\n \"Enter username\",\n )}\n />\n </div>\n </div>\n\n <div>\n <label\n htmlFor={displayNameId}\n className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-2\"\n >\n {tr(\"auth.profile.displayName\", \"Display Name\")}\n </label>\n <input\n id={displayNameId}\n type=\"text\"\n aria-label={tr(\"auth.profile.displayName\", \"Display Name\")}\n value={formData.name}\n onChange={(e) =>\n setFormData({ ...formData, name: e.target.value })\n }\n className=\"block w-full px-4 py-3 border border-[var(--color-border-default)] rounded-xl bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all duration-300\"\n placeholder={tr(\n \"auth.profile.displayNamePlaceholder\",\n \"Enter display name\",\n )}\n />\n </div>\n\n <div>\n <label\n htmlFor={firstNameId}\n className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-2\"\n >\n {tr(\"auth.profile.firstName\", \"First Name\")}\n </label>\n <input\n id={firstNameId}\n type=\"text\"\n aria-label={tr(\"auth.profile.firstName\", \"First Name\")}\n value={formData.firstName}\n onChange={(e) =>\n setFormData({ ...formData, firstName: e.target.value })\n }\n className=\"block w-full px-4 py-3 border border-[var(--color-border-default)] rounded-xl bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all duration-300\"\n placeholder={tr(\n \"auth.profile.firstNamePlaceholder\",\n \"Enter first name\",\n )}\n />\n </div>\n\n <div>\n <label\n htmlFor={lastNameId}\n className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-2\"\n >\n {tr(\"auth.profile.lastName\", \"Last Name\")}\n </label>\n <input\n id={lastNameId}\n type=\"text\"\n aria-label={tr(\"auth.profile.lastName\", \"Last Name\")}\n value={formData.lastName}\n onChange={(e) =>\n setFormData({ ...formData, lastName: e.target.value })\n }\n className=\"block w-full px-4 py-3 border border-[var(--color-border-default)] rounded-xl bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all duration-300\"\n placeholder={tr(\n \"auth.profile.lastNamePlaceholder\",\n \"Enter last name\",\n )}\n />\n </div>\n\n <div className=\"md:col-span-2\">\n <label\n htmlFor={phoneNumberId}\n className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-2\"\n >\n {tr(\"auth.profile.phoneNumber\", \"Phone Number\")}\n </label>\n <input\n id={phoneNumberId}\n type=\"tel\"\n aria-label={tr(\"auth.profile.phoneNumber\", \"Phone Number\")}\n aria-invalid={phoneError ? true : undefined}\n aria-describedby={\n phoneError ? `${phoneNumberId}-error` : undefined\n }\n value={formData.phoneNumber}\n onChange={(e) => {\n setFormData({\n ...formData,\n phoneNumber: e.target.value,\n });\n if (phoneError) {\n setPhoneError(null);\n setError(null);\n }\n }}\n className=\"block w-full px-4 py-3 border border-[var(--color-border-default)] rounded-xl bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all duration-300\"\n placeholder={tr(\n \"auth.profile.phonePlaceholder\",\n \"Enter phone number\",\n )}\n />\n {phoneError && (\n <p\n id={`${phoneNumberId}-error`}\n role=\"alert\"\n className=\"mt-2 text-sm text-[var(--color-status-error-text)]\"\n >\n {phoneError}\n </p>\n )}\n </div>\n </div>\n\n <div className=\"flex gap-3 pt-6 mt-6 border-t border-[var(--color-border-default)]\">\n <Button\n type=\"submit\"\n disabled={isLoading}\n variant=\"default\"\n size=\"lg\"\n className=\"font-semibold\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"size-4 animate-spin\" />\n {tr(\"auth.profile.saving\", \"Saving...\")}\n </>\n ) : (\n tr(\"auth.profile.saveChanges\", \"Save Changes\")\n )}\n </Button>\n <button\n type=\"button\"\n onClick={cancelEdit}\n className=\"inline-flex items-center h-10 px-8 rounded-xl border border-[var(--color-border-default)] text-[var(--color-text-secondary)] font-medium hover:bg-[var(--color-bg-muted)] transition-colors duration-200\"\n >\n {tr(\"auth.profile.cancel\", \"Cancel\")}\n </button>\n </div>\n </form>\n ) : (\n /* View Mode */\n <>\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-6\">\n <div className=\"p-4 rounded-xl bg-[var(--color-bg-muted)]\">\n <label className=\"block text-sm font-medium text-[var(--color-text-tertiary)] mb-1\">\n {tr(\"auth.profile.username\", \"Username\")}\n </label>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {currentUser.username || \"-\"}\n </p>\n </div>\n\n <div className=\"p-4 rounded-xl bg-[var(--color-bg-muted)]\">\n <label className=\"block text-sm font-medium text-[var(--color-text-tertiary)] mb-1\">\n {tr(\"auth.profile.displayName\", \"Display Name\")}\n </label>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {currentUser.name || \"-\"}\n </p>\n </div>\n\n <div className=\"p-4 rounded-xl bg-[var(--color-bg-muted)]\">\n <label className=\"block text-sm font-medium text-[var(--color-text-tertiary)] mb-1\">\n {tr(\"auth.profile.firstName\", \"First Name\")}\n </label>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {currentUser.firstName || \"-\"}\n </p>\n </div>\n\n <div className=\"p-4 rounded-xl bg-[var(--color-bg-muted)]\">\n <label className=\"block text-sm font-medium text-[var(--color-text-tertiary)] mb-1\">\n {tr(\"auth.profile.lastName\", \"Last Name\")}\n </label>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {currentUser.lastName || \"-\"}\n </p>\n </div>\n\n <div className=\"p-4 rounded-xl bg-[var(--color-bg-muted)]\">\n <label className=\"block text-sm font-medium text-[var(--color-text-tertiary)] mb-1\">\n {tr(\"auth.profile.email\", \"Email\")}\n </label>\n <p className=\"text-[var(--color-text-primary)] font-medium flex items-center gap-2\">\n {currentUser.email}\n {currentUser.emailVerified && (\n <span className=\"inline-flex items-center gap-1 text-[var(--color-status-success-text)] text-xs\">\n <CheckIcon className=\"size-3\" />\n {tr(\"auth.profile.verified\", \"Verified\")}\n </span>\n )}\n </p>\n </div>\n\n <div className=\"p-4 rounded-xl bg-[var(--color-bg-muted)]\">\n <label className=\"block text-sm font-medium text-[var(--color-text-tertiary)] mb-1\">\n {tr(\"auth.profile.phoneNumber\", \"Phone Number\")}\n </label>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {currentUser.phoneNumber || \"-\"}\n </p>\n </div>\n\n <div className=\"p-4 rounded-xl bg-[var(--color-bg-muted)]\">\n <label className=\"block text-sm font-medium text-[var(--color-text-tertiary)] mb-1\">\n {tr(\"auth.profile.role\", \"Role\")}\n </label>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {currentUser.role?.replace(\"_\", \" \") ||\n tr(\"auth.profile.member\", \"Member\")}\n </p>\n </div>\n\n <div className=\"p-4 rounded-xl bg-[var(--color-bg-muted)]\">\n <label className=\"block text-sm font-medium text-[var(--color-text-tertiary)] mb-1\">\n {tr(\"auth.profile.mfaStatus\", \"MFA Status\")}\n </label>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {currentUser.mfaEnabled ? (\n <span className=\"text-[var(--color-status-success-text)] flex items-center gap-1\">\n <CheckIcon className=\"size-4\" />\n {tr(\"auth.profile.enabled\", \"Enabled\")}\n </span>\n ) : (\n <span className=\"text-[var(--color-status-warning-text)]\">\n {tr(\"auth.profile.notEnabled\", \"Not Enabled\")}\n </span>\n )}\n </p>\n </div>\n\n <div className=\"p-4 rounded-xl bg-[var(--color-bg-muted)]\">\n <label className=\"block text-sm font-medium text-[var(--color-text-tertiary)] mb-1\">\n {tr(\"auth.profile.lastLogin\", \"Last Login\")}\n </label>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {currentUser.lastLoginAt\n ? new Date(currentUser.lastLoginAt).toLocaleString()\n : tr(\"auth.profile.never\", \"Never\")}\n </p>\n </div>\n\n <div className=\"p-4 rounded-xl bg-[var(--color-bg-muted)]\">\n <label className=\"block text-sm font-medium text-[var(--color-text-tertiary)] mb-1\">\n {tr(\"auth.profile.accountCreated\", \"Account Created\")}\n </label>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {currentUser.createdAt\n ? new Date(currentUser.createdAt).toLocaleString()\n : \"-\"}\n </p>\n </div>\n </div>\n\n <div className=\"flex gap-3 pt-6 mt-6 border-t border-[var(--color-border-default)]\">\n <Button\n type=\"button\"\n onClick={() => setIsEditing(true)}\n variant=\"default\"\n size=\"lg\"\n className=\"font-semibold\"\n >\n {tr(\"auth.profile.editProfile\", \"Edit Profile\")}\n </Button>\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/security\")}\n className=\"inline-flex items-center h-10 px-8 rounded-xl border border-[var(--color-border-default)] text-[var(--color-text-secondary)] font-medium hover:bg-[var(--color-bg-muted)] transition-colors duration-200\"\n >\n {tr(\"auth.profile.securitySettings\", \"Security Settings\")}\n </button>\n </div>\n </>\n )}\n </div>\n </div>\n\n <NextSteps\n className=\"mt-8\"\n storageKey=\"auth-profile\"\n steps={[\n {\n id: \"edit-profile\",\n label: tr(\"auth.profile.nextEdit\", \"Complete your profile\"),\n description: tr(\n \"auth.profile.nextEditDesc\",\n \"Add your display name and photo so teammates recognise you.\",\n ),\n onClick: () => setIsEditing(true),\n done: Boolean(currentUser.name && currentUser.avatar),\n },\n {\n id: \"enable-mfa\",\n label: tr(\"auth.profile.nextMfa\", \"Secure your account with 2FA\"),\n description: tr(\n \"auth.profile.nextMfaDesc\",\n \"Turn on two-factor authentication for stronger protection.\",\n ),\n onClick: () => navigate(\"/auth/mfa\"),\n done: Boolean(currentUser.mfaEnabled),\n },\n {\n id: \"security-center\",\n label: tr(\"auth.profile.nextSecurity\", \"Visit the Security Center\"),\n description: tr(\n \"auth.profile.nextSecurityDesc\",\n \"Review sessions, password, and account options.\",\n ),\n onClick: () => navigate(\"/auth/security\"),\n },\n ]}\n />\n\n {/* Quick Links */}\n <div className=\"mt-8 grid grid-cols-1 md:grid-cols-3 gap-4\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/mfa\")}\n className=\"p-4 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] hover:border-[var(--color-action-primary-border)] transition-colors duration-200 text-left group\"\n >\n <div className=\"size-10 rounded-lg bg-[var(--color-bg-accent)] flex items-center justify-center mb-3 group-hover:bg-[var(--color-action-primary-bg-subtle)] transition-colors duration-200\">\n <svg\n className=\"size-5 text-[var(--color-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=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n </div>\n <h3 className=\"font-medium text-[var(--color-text-primary)]\">\n {tr(\"auth.profile.twoFactorAuth\", \"Two-Factor Auth\")}\n </h3>\n <p className=\"text-sm text-[var(--color-text-tertiary)] mt-1\">\n {currentUser.mfaEnabled\n ? tr(\"auth.profile.manageMfa\", \"Manage MFA settings\")\n : tr(\"auth.profile.enableMfa\", \"Enable MFA for extra security\")}\n </p>\n </button>\n\n <button\n type=\"button\"\n onClick={() => navigate(\"/devportal/api-keys\")}\n className=\"p-4 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] hover:border-[var(--color-action-secondary-border)] transition-colors duration-200 text-left group\"\n >\n <div className=\"size-10 rounded-lg bg-[var(--color-action-secondary-bg-subtle)] flex items-center justify-center mb-3 group-hover:bg-[var(--color-action-secondary-bg-subtle)] transition-colors duration-200\">\n <svg\n className=\"size-5 text-[var(--color-action-secondary-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=\"M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z\"\n />\n </svg>\n </div>\n <h3 className=\"font-medium text-[var(--color-text-primary)]\">\n {tr(\"auth.profile.apiKeys\", \"API Keys\")}\n </h3>\n <p className=\"text-sm text-[var(--color-text-tertiary)] mt-1\">\n {tr(\n \"auth.profile.manageApiKeys\",\n \"Manage your API access credentials\",\n )}\n </p>\n </button>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"UserProfilePage.js","names":["useShellAuth","_Fragment","UserProfilePage","tr","useTr","navigate","useNavigate","shellAuth","useAuth","isAuthenticated","useAuthStore","state","currentUser","setStoreUser","useUserOperations","usernameId","useId","displayNameId","firstNameId","lastNameId","phoneNumberId","currentWorkspaceId","wspaceClient","useGatewayClient","initiateUpload","useInitiateAvatarUploadMutation","completeUpload","useCompleteAvatarUploadMutation","fileInputRef","useRef","isUploadingAvatar","setIsUploadingAvatar","useState","handleAvatarUpload","file","setError","wsHeaders","extension","initiateResult","session","uploadUrl","avatarUrl","updateCurrentUserProfile","setSuccessMessage","err","isEditing","setIsEditing","formData","setFormData","error","phoneError","setPhoneError","successMessage","useEffect","AuthLayout","AlertIcon","Button","PagePurpose","CheckIcon","SpinnerIcon","EmailIcon","validatePhoneNumber","phoneMsg","validation","validateUserFields","key","UserIcon","isLoading","Fragment","NextSteps"],"sources":["../../../src/auth/pages/UserProfilePage.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useId } from \"react\";\nimport { useTr } from \"../../shared/hooks/useTr\";\nimport { useAuth as useShellAuth } from \"@burdenoff/fe-libs/shared/providers/shell/AuthProvider\";\nimport { useNavigate } from \"react-router\";\nimport { useUserOperations } from \"../hooks/useUserOperations\";\nimport { validatePhoneNumber, validateUserFields } from \"../utils/validation\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { AuthLayout } from \"../components/AuthLayout\";\nimport { Button } from \"@burdenoff/fe-libs/button\";\nimport { PagePurpose, NextSteps } from \"@burdenoff/fe-libs/ui\";\nimport {\n SpinnerIcon,\n AlertIcon,\n UserIcon,\n EmailIcon,\n CheckIcon,\n} from \"../components/AuthIcons\";\nimport {\n useInitiateAvatarUploadMutation,\n useCompleteAvatarUploadMutation,\n} from \"../../generated/wspace-operations\";\nimport { useGatewayClient } from \"@burdenoff/fe-libs/shared/graphql\";\n\ninterface ProfileFormData {\n username: string;\n firstName: string;\n lastName: string;\n name: string;\n phoneNumber: string;\n}\n\n/**\n * User Profile page for viewing and editing own profile\n */\nexport function UserProfilePage() {\n const tr = useTr();\n\n const navigate = useNavigate();\n const shellAuth = useShellAuth();\n const isAuthenticated = useAuthStore((state) => state.isAuthenticated);\n const currentUser = useAuthStore((state) => state.user);\n const setStoreUser = useAuthStore((state) => state.setUser);\n const { updateCurrentUserProfile, isLoading } = useUserOperations();\n\n const usernameId = useId();\n const displayNameId = useId();\n const firstNameId = useId();\n const lastNameId = useId();\n const phoneNumberId = useId();\n\n const currentWorkspaceId = useAuthStore((state) => state.currentWorkspaceId);\n const wspaceClient = useGatewayClient(\"workspace\");\n const [initiateUpload] = useInitiateAvatarUploadMutation({\n client: wspaceClient,\n });\n const [completeUpload] = useCompleteAvatarUploadMutation({\n client: wspaceClient,\n });\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [isUploadingAvatar, setIsUploadingAvatar] = useState(false);\n\n const handleAvatarUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file) return;\n if (!file.type.startsWith(\"image/\")) {\n setError(\n tr(\"auth.profile.invalidImageType\", \"Please select an image file\"),\n );\n return;\n }\n if (!currentWorkspaceId) {\n setError(\n tr(\n \"auth.profile.noWorkspace\",\n \"Please select a workspace before uploading a profile picture\",\n ),\n );\n return;\n }\n setIsUploadingAvatar(true);\n setError(null);\n const wsHeaders = { \"x-workspace-id\": currentWorkspaceId };\n try {\n const extension = file.name.split(\".\").pop()?.toLowerCase() ?? \"\";\n const initiateResult = await initiateUpload({\n variables: {\n input: {\n fileName: file.name,\n fileSize: file.size.toString(),\n mimeType: file.type,\n extension,\n },\n },\n context: { headers: wsHeaders },\n });\n const session = initiateResult.data?.initiateUpload.session;\n const uploadUrl = initiateResult.data?.initiateUpload.uploadUrl;\n if (!session || !uploadUrl) throw new Error(\"Failed to initiate upload\");\n\n const uploadResp = await fetch(uploadUrl, {\n method: \"PUT\",\n headers: { \"Content-Type\": file.type },\n body: file,\n });\n if (!uploadResp.ok) throw new Error(\"Failed to upload file\");\n\n const completeResult = await completeUpload({\n variables: { input: { sessionId: session.id } },\n context: { headers: wsHeaders },\n });\n const avatarUrl = completeResult.data?.completeUpload.file?.downloadUrl;\n if (!avatarUrl) throw new Error(\"Upload succeeded but no URL returned\");\n\n await updateCurrentUserProfile({ avatar: avatarUrl });\n // Update both the local authStore and the shell AuthProvider so the nav\n // bar avatar reflects the change immediately without a page reload.\n if (currentUser) {\n setStoreUser({ ...currentUser, avatar: avatarUrl });\n }\n shellAuth.updateUser({ avatar: avatarUrl });\n setSuccessMessage(\n tr(\"auth.profile.avatarUpdated\", \"Profile picture updated!\"),\n );\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr(\"auth.profile.avatarFailed\", \"Failed to upload picture\"),\n );\n } finally {\n setIsUploadingAvatar(false);\n if (fileInputRef.current) fileInputRef.current.value = \"\";\n }\n };\n\n const [isEditing, setIsEditing] = useState(false);\n const [formData, setFormData] = useState<ProfileFormData>({\n username: \"\",\n firstName: \"\",\n lastName: \"\",\n name: \"\",\n phoneNumber: \"\",\n });\n const [error, setError] = useState<string | null>(null);\n const [phoneError, setPhoneError] = useState<string | null>(null);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n\n // Populate form with current user data\n useEffect(() => {\n if (currentUser) {\n setFormData({\n username: currentUser.username || \"\",\n firstName: currentUser.firstName || \"\",\n lastName: currentUser.lastName || \"\",\n name: currentUser.name || \"\",\n phoneNumber: currentUser.phoneNumber || \"\",\n });\n }\n }, [currentUser]);\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n setPhoneError(null);\n setSuccessMessage(null);\n\n const phoneValidation = validatePhoneNumber(formData.phoneNumber);\n if (!phoneValidation.valid) {\n const phoneMsg = tr(\n \"auth.profile.phoneNumber_invalid\",\n \"Invalid phone number\",\n );\n setPhoneError(phoneMsg);\n setError(phoneMsg);\n return;\n }\n\n // BOFF-7214: enforce the same name/username invariants the signup form\n // uses — without this, a user editing their own profile could replace\n // their name with a pure-numeric value and re-open the same bypass.\n // Pass `requireAll: false` so a user whose optional `firstName` /\n // `lastName` is unset (e.g. provisioned via IdP) can still update\n // phone or display name without being forced to invent first/last\n // (Codex P2 review).\n const validation = validateUserFields(formData, { requireAll: false });\n if (!validation.valid) {\n setError(\n validation.errors\n .map((e) => {\n const key = `auth.profile.${e.field}_${e.code}`;\n return tr(key, e.message);\n })\n .join(\" · \"),\n );\n return;\n }\n\n try {\n await updateCurrentUserProfile({\n username: validation.values.username || undefined,\n firstName: validation.values.firstName || undefined,\n lastName: validation.values.lastName || undefined,\n name: formData.name || undefined,\n phoneNumber: formData.phoneNumber || undefined,\n });\n setSuccessMessage(\n tr(\"auth.profile.updateSuccess\", \"Profile updated successfully!\"),\n );\n setIsEditing(false);\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr(\"auth.profile.updateFailed\", \"Failed to update profile\"),\n );\n }\n };\n\n const cancelEdit = () => {\n if (currentUser) {\n setFormData({\n username: currentUser.username || \"\",\n firstName: currentUser.firstName || \"\",\n lastName: currentUser.lastName || \"\",\n name: currentUser.name || \"\",\n phoneNumber: currentUser.phoneNumber || \"\",\n });\n }\n setIsEditing(false);\n setError(null);\n setPhoneError(null);\n };\n\n // Not authenticated\n if (!isAuthenticated || !currentUser) {\n return (\n <AuthLayout\n title={tr(\"auth.profile.title\", \"Profile\")}\n subtitle={tr(\"auth.profile.subtitle\", \"View and manage your profile\")}\n >\n <div className=\"text-center py-12\">\n <div className=\"size-16 mx-auto mb-4 rounded-full bg-[var(--color-status-warning-bg-subtle)] flex items-center justify-center\">\n <AlertIcon className=\"size-8 text-[var(--color-status-warning-text)]\" />\n </div>\n <h2 className=\"text-xl font-semibold text-[var(--color-text-primary)] mb-2\">\n {tr(\"auth.profile.authRequired\", \"Authentication Required\")}\n </h2>\n <p className=\"text-[var(--color-text-secondary)] mb-6\">\n {tr(\n \"auth.profile.signInToView\",\n \"Please sign in to view your profile\",\n )}\n </p>\n <Button\n type=\"button\"\n onClick={() => navigate(\"/auth/login\")}\n variant=\"default\"\n size=\"lg\"\n className=\"font-semibold\"\n >\n {tr(\"auth.profile.signIn\", \"Sign In\")}\n </Button>\n </div>\n </AuthLayout>\n );\n }\n\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n {/* Header */}\n <div className=\"mb-8\">\n <h1 className=\"text-3xl font-semibold text-[var(--color-text-primary)]\">\n {tr(\"auth.profile.myProfile\", \"My Profile\")}\n </h1>\n <p className=\"text-[var(--color-text-secondary)] mt-2\">\n {tr(\n \"auth.profile.viewAndManage\",\n \"View and manage your account information\",\n )}\n </p>\n <PagePurpose className=\"mt-3\">\n {tr(\n \"auth.profile.purpose\",\n \"This is how you appear across the platform. Update your name, photo, and contact details here — teammates see this information, and we use your email for sign-in and important notices.\",\n )}\n </PagePurpose>\n </div>\n\n {/* Success Message */}\n {successMessage && (\n <div className=\"mb-6 flex items-center gap-3 p-4 rounded-xl bg-[var(--color-status-success-bg-subtle)] border border-[var(--color-status-success-border)]\">\n <CheckIcon className=\"size-5 text-[var(--color-status-success-text)] flex-shrink-0\" />\n <p className=\"text-[var(--color-status-success-text)]\">\n {successMessage}\n </p>\n <button\n type=\"button\"\n onClick={() => setSuccessMessage(null)}\n className=\"ml-auto text-sm text-[var(--color-status-success-text)] hover:underline transition-colors duration-200\"\n >\n {tr(\"auth.profile.dismiss\", \"Dismiss\")}\n </button>\n </div>\n )}\n\n {/* Error Message */}\n {error && (\n <div className=\"mb-6 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=\"size-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 <button\n type=\"button\"\n onClick={() => setError(null)}\n className=\"text-sm text-[var(--color-status-error-text)] hover:underline transition-colors duration-200\"\n >\n {tr(\"auth.profile.dismiss\", \"Dismiss\")}\n </button>\n </div>\n )}\n\n {/* Profile Card */}\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-sm overflow-hidden\">\n {/* Profile Header — the one emphasis zone on this page */}\n <div className=\"bg-accent-wash border-b border-border-seam p-6 text-text-primary\">\n <div className=\"flex items-center gap-4\">\n <div className=\"relative group\">\n {currentUser.avatar ? (\n <img\n src={currentUser.avatar}\n alt={tr(\"auth.profile.avatarAlt\", \"Profile picture\")}\n className=\"size-20 rounded-full object-cover shadow-lg\"\n />\n ) : (\n <div className=\"size-20 rounded-full bg-accent-soft backdrop-blur-sm flex items-center justify-center text-[var(--color-action-primary-bg)] text-3xl font-semibold shadow-lg\">\n {(\n currentUser.name ||\n currentUser.username ||\n currentUser.email\n )\n .charAt(0)\n .toUpperCase()}\n </div>\n )}\n <button\n type=\"button\"\n onClick={() => fileInputRef.current?.click()}\n disabled={isUploadingAvatar}\n aria-label={tr(\n \"auth.profile.changeAvatar\",\n \"Change profile picture\",\n )}\n className=\"absolute inset-0 rounded-full flex items-center justify-center bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity duration-200 cursor-pointer\"\n >\n {isUploadingAvatar ? (\n <SpinnerIcon className=\"size-6 text-white animate-spin\" />\n ) : (\n <svg\n className=\"size-6 text-white\"\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 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z\"\n />\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M15 13a3 3 0 11-6 0 3 3 0 016 0z\"\n />\n </svg>\n )}\n </button>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n className=\"hidden\"\n onChange={(e) => void handleAvatarUpload(e)}\n tabIndex={-1}\n aria-label={tr(\n \"auth.profile.avatarUpload\",\n \"Upload avatar image\",\n )}\n />\n </div>\n <div className=\"text-text-primary\">\n <h2 className=\"text-2xl font-semibold\">\n {currentUser.name ||\n `${currentUser.firstName || \"\"} ${currentUser.lastName || \"\"}`.trim() ||\n currentUser.username}\n </h2>\n <p className=\"text-text-secondary flex items-center gap-2\">\n <EmailIcon className=\"size-4\" />\n {currentUser.email}\n </p>\n <div className=\"flex items-center gap-2 mt-2\">\n <span className=\"px-2.5 py-1 rounded-full text-xs font-medium bg-bg-surface/60 text-text-secondary backdrop-blur-sm\">\n {currentUser.role?.replace(\"_\", \" \") ||\n tr(\"auth.profile.member\", \"Member\")}\n </span>\n {currentUser.emailVerified && (\n <span className=\"px-2.5 py-1 rounded-full text-xs font-medium bg-status-success-bg-subtle text-status-success-text flex items-center gap-1\">\n <CheckIcon className=\"size-3\" />\n {tr(\"auth.profile.verified\", \"Verified\")}\n </span>\n )}\n </div>\n </div>\n </div>\n </div>\n\n {/* Profile Body */}\n <div className=\"p-6\">\n {isEditing ? (\n /* Edit Form */\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-6\">\n <div>\n <label\n htmlFor={usernameId}\n className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-2\"\n >\n {tr(\"auth.profile.username\", \"Username\")}\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 <UserIcon\n className=\"size-5 text-[var(--color-text-tertiary)]\"\n aria-hidden=\"true\"\n />\n </div>\n <input\n id={usernameId}\n type=\"text\"\n aria-label={tr(\"auth.profile.username\", \"Username\")}\n value={formData.username}\n onChange={(e) =>\n setFormData({ ...formData, username: e.target.value })\n }\n className=\"block w-full pl-[3.5rem] pr-4 py-3 border border-[var(--color-border-default)] rounded-xl bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all duration-300\"\n placeholder={tr(\n \"auth.profile.usernamePlaceholder\",\n \"Enter username\",\n )}\n />\n </div>\n </div>\n\n <div>\n <label\n htmlFor={displayNameId}\n className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-2\"\n >\n {tr(\"auth.profile.displayName\", \"Display Name\")}\n </label>\n <input\n id={displayNameId}\n type=\"text\"\n aria-label={tr(\"auth.profile.displayName\", \"Display Name\")}\n value={formData.name}\n onChange={(e) =>\n setFormData({ ...formData, name: e.target.value })\n }\n className=\"block w-full px-4 py-3 border border-[var(--color-border-default)] rounded-xl bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all duration-300\"\n placeholder={tr(\n \"auth.profile.displayNamePlaceholder\",\n \"Enter display name\",\n )}\n />\n </div>\n\n <div>\n <label\n htmlFor={firstNameId}\n className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-2\"\n >\n {tr(\"auth.profile.firstName\", \"First Name\")}\n </label>\n <input\n id={firstNameId}\n type=\"text\"\n aria-label={tr(\"auth.profile.firstName\", \"First Name\")}\n value={formData.firstName}\n onChange={(e) =>\n setFormData({ ...formData, firstName: e.target.value })\n }\n className=\"block w-full px-4 py-3 border border-[var(--color-border-default)] rounded-xl bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all duration-300\"\n placeholder={tr(\n \"auth.profile.firstNamePlaceholder\",\n \"Enter first name\",\n )}\n />\n </div>\n\n <div>\n <label\n htmlFor={lastNameId}\n className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-2\"\n >\n {tr(\"auth.profile.lastName\", \"Last Name\")}\n </label>\n <input\n id={lastNameId}\n type=\"text\"\n aria-label={tr(\"auth.profile.lastName\", \"Last Name\")}\n value={formData.lastName}\n onChange={(e) =>\n setFormData({ ...formData, lastName: e.target.value })\n }\n className=\"block w-full px-4 py-3 border border-[var(--color-border-default)] rounded-xl bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all duration-300\"\n placeholder={tr(\n \"auth.profile.lastNamePlaceholder\",\n \"Enter last name\",\n )}\n />\n </div>\n\n <div className=\"md:col-span-2\">\n <label\n htmlFor={phoneNumberId}\n className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-2\"\n >\n {tr(\"auth.profile.phoneNumber\", \"Phone Number\")}\n </label>\n <input\n id={phoneNumberId}\n type=\"tel\"\n aria-label={tr(\"auth.profile.phoneNumber\", \"Phone Number\")}\n aria-invalid={phoneError ? true : undefined}\n aria-describedby={\n phoneError ? `${phoneNumberId}-error` : undefined\n }\n value={formData.phoneNumber}\n onChange={(e) => {\n setFormData({\n ...formData,\n phoneNumber: e.target.value,\n });\n if (phoneError) {\n setPhoneError(null);\n setError(null);\n }\n }}\n className=\"block w-full px-4 py-3 border border-[var(--color-border-default)] rounded-xl bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all duration-300\"\n placeholder={tr(\n \"auth.profile.phonePlaceholder\",\n \"Enter phone number\",\n )}\n />\n {phoneError && (\n <p\n id={`${phoneNumberId}-error`}\n role=\"alert\"\n className=\"mt-2 text-sm text-[var(--color-status-error-text)]\"\n >\n {phoneError}\n </p>\n )}\n </div>\n </div>\n\n <div className=\"flex gap-3 pt-6 mt-6 border-t border-[var(--color-border-default)]\">\n <Button\n type=\"submit\"\n disabled={isLoading}\n variant=\"default\"\n size=\"lg\"\n className=\"font-semibold\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"size-4 animate-spin\" />\n {tr(\"auth.profile.saving\", \"Saving...\")}\n </>\n ) : (\n tr(\"auth.profile.saveChanges\", \"Save Changes\")\n )}\n </Button>\n <button\n type=\"button\"\n onClick={cancelEdit}\n className=\"inline-flex items-center h-10 px-8 rounded-xl border border-[var(--color-border-default)] text-[var(--color-text-secondary)] font-medium hover:bg-[var(--color-bg-muted)] transition-colors duration-200\"\n >\n {tr(\"auth.profile.cancel\", \"Cancel\")}\n </button>\n </div>\n </form>\n ) : (\n /* View Mode */\n <>\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-6\">\n <div className=\"p-4 rounded-xl bg-[var(--color-bg-muted)]\">\n <label className=\"block text-sm font-medium text-[var(--color-text-tertiary)] mb-1\">\n {tr(\"auth.profile.username\", \"Username\")}\n </label>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {currentUser.username || \"-\"}\n </p>\n </div>\n\n <div className=\"p-4 rounded-xl bg-[var(--color-bg-muted)]\">\n <label className=\"block text-sm font-medium text-[var(--color-text-tertiary)] mb-1\">\n {tr(\"auth.profile.displayName\", \"Display Name\")}\n </label>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {currentUser.name || \"-\"}\n </p>\n </div>\n\n <div className=\"p-4 rounded-xl bg-[var(--color-bg-muted)]\">\n <label className=\"block text-sm font-medium text-[var(--color-text-tertiary)] mb-1\">\n {tr(\"auth.profile.firstName\", \"First Name\")}\n </label>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {currentUser.firstName || \"-\"}\n </p>\n </div>\n\n <div className=\"p-4 rounded-xl bg-[var(--color-bg-muted)]\">\n <label className=\"block text-sm font-medium text-[var(--color-text-tertiary)] mb-1\">\n {tr(\"auth.profile.lastName\", \"Last Name\")}\n </label>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {currentUser.lastName || \"-\"}\n </p>\n </div>\n\n <div className=\"p-4 rounded-xl bg-[var(--color-bg-muted)]\">\n <label className=\"block text-sm font-medium text-[var(--color-text-tertiary)] mb-1\">\n {tr(\"auth.profile.email\", \"Email\")}\n </label>\n <p className=\"text-[var(--color-text-primary)] font-medium flex items-center gap-2\">\n {currentUser.email}\n {currentUser.emailVerified && (\n <span className=\"inline-flex items-center gap-1 text-[var(--color-status-success-text)] text-xs\">\n <CheckIcon className=\"size-3\" />\n {tr(\"auth.profile.verified\", \"Verified\")}\n </span>\n )}\n </p>\n </div>\n\n <div className=\"p-4 rounded-xl bg-[var(--color-bg-muted)]\">\n <label className=\"block text-sm font-medium text-[var(--color-text-tertiary)] mb-1\">\n {tr(\"auth.profile.phoneNumber\", \"Phone Number\")}\n </label>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {currentUser.phoneNumber || \"-\"}\n </p>\n </div>\n\n <div className=\"p-4 rounded-xl bg-[var(--color-bg-muted)]\">\n <label className=\"block text-sm font-medium text-[var(--color-text-tertiary)] mb-1\">\n {tr(\"auth.profile.role\", \"Role\")}\n </label>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {currentUser.role?.replace(\"_\", \" \") ||\n tr(\"auth.profile.member\", \"Member\")}\n </p>\n </div>\n\n <div className=\"p-4 rounded-xl bg-[var(--color-bg-muted)]\">\n <label className=\"block text-sm font-medium text-[var(--color-text-tertiary)] mb-1\">\n {tr(\"auth.profile.mfaStatus\", \"MFA Status\")}\n </label>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {currentUser.mfaEnabled ? (\n <span className=\"text-[var(--color-status-success-text)] flex items-center gap-1\">\n <CheckIcon className=\"size-4\" />\n {tr(\"auth.profile.enabled\", \"Enabled\")}\n </span>\n ) : (\n <span className=\"text-[var(--color-status-warning-text)]\">\n {tr(\"auth.profile.notEnabled\", \"Not Enabled\")}\n </span>\n )}\n </p>\n </div>\n\n <div className=\"p-4 rounded-xl bg-[var(--color-bg-muted)]\">\n <label className=\"block text-sm font-medium text-[var(--color-text-tertiary)] mb-1\">\n {tr(\"auth.profile.lastLogin\", \"Last Login\")}\n </label>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {currentUser.lastLoginAt\n ? new Date(currentUser.lastLoginAt).toLocaleString()\n : tr(\"auth.profile.never\", \"Never\")}\n </p>\n </div>\n\n <div className=\"p-4 rounded-xl bg-[var(--color-bg-muted)]\">\n <label className=\"block text-sm font-medium text-[var(--color-text-tertiary)] mb-1\">\n {tr(\"auth.profile.accountCreated\", \"Account Created\")}\n </label>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {currentUser.createdAt\n ? new Date(currentUser.createdAt).toLocaleString()\n : \"-\"}\n </p>\n </div>\n </div>\n\n <div className=\"flex gap-3 pt-6 mt-6 border-t border-[var(--color-border-default)]\">\n <Button\n type=\"button\"\n onClick={() => setIsEditing(true)}\n variant=\"default\"\n size=\"lg\"\n className=\"font-semibold\"\n >\n {tr(\"auth.profile.editProfile\", \"Edit Profile\")}\n </Button>\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/security\")}\n className=\"inline-flex items-center h-10 px-8 rounded-xl border border-[var(--color-border-default)] text-[var(--color-text-secondary)] font-medium hover:bg-[var(--color-bg-muted)] transition-colors duration-200\"\n >\n {tr(\"auth.profile.securitySettings\", \"Security Settings\")}\n </button>\n </div>\n </>\n )}\n </div>\n </div>\n\n <NextSteps\n className=\"mt-8\"\n storageKey=\"auth-profile\"\n steps={[\n {\n id: \"edit-profile\",\n label: tr(\"auth.profile.nextEdit\", \"Complete your profile\"),\n description: tr(\n \"auth.profile.nextEditDesc\",\n \"Add your display name and photo so teammates recognise you.\",\n ),\n onClick: () => setIsEditing(true),\n done: Boolean(currentUser.name && currentUser.avatar),\n },\n {\n id: \"enable-mfa\",\n label: tr(\"auth.profile.nextMfa\", \"Secure your account with 2FA\"),\n description: tr(\n \"auth.profile.nextMfaDesc\",\n \"Turn on two-factor authentication for stronger protection.\",\n ),\n onClick: () => navigate(\"/auth/mfa\"),\n done: Boolean(currentUser.mfaEnabled),\n },\n {\n id: \"security-center\",\n label: tr(\"auth.profile.nextSecurity\", \"Visit the Security Center\"),\n description: tr(\n \"auth.profile.nextSecurityDesc\",\n \"Review sessions, password, and account options.\",\n ),\n onClick: () => navigate(\"/auth/security\"),\n },\n ]}\n />\n\n {/* Quick Links */}\n <div className=\"mt-8 grid grid-cols-1 md:grid-cols-3 gap-4\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/mfa\")}\n className=\"p-4 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] hover:border-[var(--color-action-primary-border)] transition-colors duration-200 text-left group\"\n >\n <div className=\"size-10 rounded-lg bg-[var(--color-bg-accent)] flex items-center justify-center mb-3 group-hover:bg-[var(--color-action-primary-bg-subtle)] transition-colors duration-200\">\n <svg\n className=\"size-5 text-[var(--color-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=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n </div>\n <h3 className=\"font-medium text-[var(--color-text-primary)]\">\n {tr(\"auth.profile.twoFactorAuth\", \"Two-Factor Auth\")}\n </h3>\n <p className=\"text-sm text-[var(--color-text-tertiary)] mt-1\">\n {currentUser.mfaEnabled\n ? tr(\"auth.profile.manageMfa\", \"Manage MFA settings\")\n : tr(\"auth.profile.enableMfa\", \"Enable MFA for extra security\")}\n </p>\n </button>\n\n <button\n type=\"button\"\n onClick={() => navigate(\"/devportal/api-keys\")}\n className=\"p-4 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] hover:border-[var(--color-action-secondary-border)] transition-colors duration-200 text-left group\"\n >\n <div className=\"size-10 rounded-lg bg-[var(--color-action-secondary-bg-subtle)] flex items-center justify-center mb-3 group-hover:bg-[var(--color-action-secondary-bg-subtle)] transition-colors duration-200\">\n <svg\n className=\"size-5 text-[var(--color-action-secondary-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=\"M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z\"\n />\n </svg>\n </div>\n <h3 className=\"font-medium text-[var(--color-text-primary)]\">\n {tr(\"auth.profile.apiKeys\", \"API Keys\")}\n </h3>\n <p className=\"text-sm text-[var(--color-text-tertiary)] mt-1\">\n {tr(\n \"auth.profile.manageApiKeys\",\n \"Manage your API access credentials\",\n )}\n </p>\n </button>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|