@burdenoff/microfe-auth 2026.830.1 → 2026.904.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 +43 -41
- package/dist/auth/AuthRoot.js.map +1 -1
- package/dist/auth/AuthRoutes.js +7 -0
- package/dist/auth/AuthRoutes.js.map +1 -1
- package/dist/auth/components/AuthLayout.js +65 -67
- package/dist/auth/components/AuthLayout.js.map +1 -1
- package/dist/auth/components/CaptchaV2.js +21 -24
- package/dist/auth/components/CaptchaV2.js.map +1 -1
- package/dist/auth/components/ConfirmActionDialog.js +33 -32
- package/dist/auth/components/ConfirmActionDialog.js.map +1 -1
- package/dist/auth/components/profile/UserProfileMenu.js +44 -46
- package/dist/auth/components/profile/UserProfileMenu.js.map +1 -1
- package/dist/auth/components/workspace/WorkspaceSelector.js +62 -68
- package/dist/auth/components/workspace/WorkspaceSelector.js.map +1 -1
- package/dist/auth/pages/AuditLogPage.js +65 -68
- package/dist/auth/pages/AuditLogPage.js.map +1 -1
- package/dist/auth/pages/EmailVerifyPage.js +51 -54
- package/dist/auth/pages/EmailVerifyPage.js.map +1 -1
- package/dist/auth/pages/LoginPage.js +223 -225
- package/dist/auth/pages/LoginPage.js.map +1 -1
- package/dist/auth/pages/MagicLinkPage.js +67 -69
- package/dist/auth/pages/MagicLinkPage.js.map +1 -1
- package/dist/auth/pages/MfaSetupPage.js +85 -88
- package/dist/auth/pages/MfaSetupPage.js.map +1 -1
- package/dist/auth/pages/MfaVerifyPage.js +60 -63
- package/dist/auth/pages/MfaVerifyPage.js.map +1 -1
- package/dist/auth/pages/OAuthCallbackPage.js +37 -40
- package/dist/auth/pages/OAuthCallbackPage.js.map +1 -1
- package/dist/auth/pages/PasswordResetPage.js +102 -105
- package/dist/auth/pages/PasswordResetPage.js.map +1 -1
- package/dist/auth/pages/ReactivatePage.js +54 -57
- package/dist/auth/pages/ReactivatePage.js.map +1 -1
- package/dist/auth/pages/RegisterPage.js +171 -174
- package/dist/auth/pages/RegisterPage.js.map +1 -1
- package/dist/auth/pages/SSOClearPage.js +21 -20
- package/dist/auth/pages/SSOClearPage.js.map +1 -1
- package/dist/auth/pages/SSODepositPage.js +27 -26
- package/dist/auth/pages/SSODepositPage.js.map +1 -1
- package/dist/auth/pages/SSORelayCallbackPage.js +19 -18
- package/dist/auth/pages/SSORelayCallbackPage.js.map +1 -1
- package/dist/auth/pages/SecurityPage.js +247 -250
- package/dist/auth/pages/SecurityPage.js.map +1 -1
- package/dist/auth/pages/SessionsPage.js +95 -98
- package/dist/auth/pages/SessionsPage.js.map +1 -1
- package/dist/auth/pages/SsoCallbackPage.js +53 -56
- package/dist/auth/pages/SsoCallbackPage.js.map +1 -1
- package/dist/auth/pages/SsoConfigPage.js +296 -299
- package/dist/auth/pages/SsoConfigPage.js.map +1 -1
- package/dist/auth/pages/SsoLoginPage.js +65 -68
- package/dist/auth/pages/SsoLoginPage.js.map +1 -1
- package/dist/auth/pages/UserDetailsPage.js +124 -127
- package/dist/auth/pages/UserDetailsPage.js.map +1 -1
- package/dist/auth/pages/UserProfilePage.js +171 -174
- package/dist/auth/pages/UserProfilePage.js.map +1 -1
- package/dist/auth/pages/UsersPage.js +322 -325
- package/dist/auth/pages/UsersPage.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -1
- package/dist/shared/hooks/index.d.ts +1 -6
- package/dist/shared/hooks/useTr.d.ts +16 -0
- package/dist/shared/hooks/useTr.js +14 -0
- package/dist/shared/hooks/useTr.js.map +1 -0
- package/dist/translations/en.d.ts +806 -248
- package/dist/translations/en.js +870 -0
- package/dist/translations/en.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,79 +1,76 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
1
|
+
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
|
+
import { AuthLayout as t } from "../components/AuthLayout.js";
|
|
3
|
+
import { CheckIcon as n, EmailIcon as r, EyeIcon as i, EyeOffIcon as a, LockIcon as o, SpinnerIcon as s, XIcon as c } from "../components/AuthIcons.js";
|
|
4
|
+
import { useCaptchaV3 as l } from "../hooks/useCaptchaV3.js";
|
|
5
|
+
import { usePasswordOperations as u } from "../hooks/usePasswordOperations.js";
|
|
6
|
+
import { useEffect as d, useState as f } from "react";
|
|
7
|
+
import { useNavigate as p, useSearchParams as m } from "react-router";
|
|
8
8
|
import { Fragment as h, jsx as g, jsxs as _ } from "react/jsx-runtime";
|
|
9
9
|
import { Button as v } from "@burdenoff/fe-libs/button";
|
|
10
10
|
import { PagePurpose as y } from "@burdenoff/fe-libs/ui";
|
|
11
11
|
//#region src/auth/pages/PasswordResetPage.tsx
|
|
12
12
|
var b = 5;
|
|
13
13
|
function x() {
|
|
14
|
-
let x =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (!V || !T) return;
|
|
20
|
-
let e = E ? `/auth/login?returnTo=${encodeURIComponent(E)}` : "/auth/login", t = setInterval(() => {
|
|
21
|
-
K((n) => n <= 1 ? (clearInterval(t), x(e), 0) : n - 1);
|
|
14
|
+
let x = e(), S = p(), [C] = m(), w = C.get("token"), T = C.get("returnTo"), { requestPasswordReset: E, resetPassword: D, isLoading: O } = u(), { executeRecaptcha: k } = l(), [A, j] = f(""), [M, N] = f(""), [P, F] = f(""), [I, L] = f(!1), [R, z] = f(!1), [B, V] = f(!1), [H, U] = f(""), [W, G] = f(b);
|
|
15
|
+
d(() => {
|
|
16
|
+
if (!B || !w) return;
|
|
17
|
+
let e = T ? `/auth/login?returnTo=${encodeURIComponent(T)}` : "/auth/login", t = setInterval(() => {
|
|
18
|
+
G((n) => n <= 1 ? (clearInterval(t), S(e), 0) : n - 1);
|
|
22
19
|
}, 1e3);
|
|
23
20
|
return () => clearInterval(t);
|
|
24
21
|
}, [
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
B,
|
|
23
|
+
w,
|
|
24
|
+
S,
|
|
25
|
+
T
|
|
29
26
|
]);
|
|
30
|
-
let
|
|
31
|
-
let e =
|
|
27
|
+
let K = (() => {
|
|
28
|
+
let e = M;
|
|
32
29
|
return [
|
|
33
30
|
{
|
|
34
|
-
label:
|
|
31
|
+
label: x("auth.passwordReset.pwMin8", "At least 12 characters"),
|
|
35
32
|
met: e.length >= 12
|
|
36
33
|
},
|
|
37
34
|
{
|
|
38
|
-
label:
|
|
35
|
+
label: x("auth.passwordReset.pwUppercase", "One uppercase letter"),
|
|
39
36
|
met: /[A-Z]/.test(e)
|
|
40
37
|
},
|
|
41
38
|
{
|
|
42
|
-
label:
|
|
39
|
+
label: x("auth.passwordReset.pwLowercase", "One lowercase letter"),
|
|
43
40
|
met: /[a-z]/.test(e)
|
|
44
41
|
},
|
|
45
42
|
{
|
|
46
|
-
label:
|
|
43
|
+
label: x("auth.passwordReset.pwNumber", "One number"),
|
|
47
44
|
met: /\d/.test(e)
|
|
48
45
|
},
|
|
49
46
|
{
|
|
50
|
-
label:
|
|
47
|
+
label: x("auth.passwordReset.pwSpecial", "One special character"),
|
|
51
48
|
met: /[!@#$%^&*(),.?":{}|<>]/.test(e)
|
|
52
49
|
}
|
|
53
50
|
];
|
|
54
|
-
})(),
|
|
55
|
-
let e =
|
|
51
|
+
})(), q = (() => {
|
|
52
|
+
let e = K.filter((e) => e.met).length;
|
|
56
53
|
return e <= 2 ? {
|
|
57
|
-
level:
|
|
54
|
+
level: x("auth.passwordReset.passwordStrengthWeak", "weak"),
|
|
58
55
|
color: "bg-status-error-bg-subtle0",
|
|
59
56
|
width: "w-1/4"
|
|
60
57
|
} : e <= 3 ? {
|
|
61
|
-
level:
|
|
58
|
+
level: x("auth.passwordReset.passwordStrengthFair", "fair"),
|
|
62
59
|
color: "bg-status-warning-text",
|
|
63
60
|
width: "w-2/4"
|
|
64
61
|
} : e <= 4 ? {
|
|
65
|
-
level:
|
|
62
|
+
level: x("auth.passwordReset.passwordStrengthGood", "good"),
|
|
66
63
|
color: "bg-status-warning-bg-subtle0",
|
|
67
64
|
width: "w-3/4"
|
|
68
65
|
} : {
|
|
69
|
-
level:
|
|
66
|
+
level: x("auth.passwordReset.passwordStrengthStrong", "strong"),
|
|
70
67
|
color: "bg-status-success-bg-subtle0",
|
|
71
68
|
width: "w-full"
|
|
72
69
|
};
|
|
73
70
|
})();
|
|
74
|
-
return
|
|
75
|
-
title:
|
|
76
|
-
subtitle:
|
|
71
|
+
return B ? /* @__PURE__ */ _(t, {
|
|
72
|
+
title: w ? x("auth.passwordReset.passwordUpdated", "Password updated") : x("auth.passwordReset.checkEmail", "Check your email"),
|
|
73
|
+
subtitle: w ? x("auth.passwordReset.successSubtitle", "Your password has been successfully reset") : x("auth.passwordReset.sentInstructions", "We sent you instructions to reset your password"),
|
|
77
74
|
children: [
|
|
78
75
|
/* @__PURE__ */ g("div", {
|
|
79
76
|
className: "flex justify-center mb-6",
|
|
@@ -95,71 +92,71 @@ function x() {
|
|
|
95
92
|
}),
|
|
96
93
|
/* @__PURE__ */ g("p", {
|
|
97
94
|
className: "text-center text-[var(--color-text-secondary)] mb-8",
|
|
98
|
-
children:
|
|
95
|
+
children: w ? x("auth.passwordReset.canNowSignIn", "You can now use your new password to sign in to your account.") : x("auth.passwordReset.emailSentTo", "We sent an email to {{email}}. Click the link in the email to reset your password.").replace("{{email}}", A)
|
|
99
96
|
}),
|
|
100
|
-
|
|
97
|
+
w ? /* @__PURE__ */ _("div", {
|
|
101
98
|
className: "space-y-4",
|
|
102
99
|
children: [/* @__PURE__ */ _("p", {
|
|
103
100
|
className: "text-center text-sm text-[var(--color-text-tertiary)]",
|
|
104
101
|
children: [
|
|
105
|
-
|
|
102
|
+
x("auth.passwordReset.redirectingIn", "Redirecting to sign in in"),
|
|
106
103
|
" ",
|
|
107
104
|
/* @__PURE__ */ g("span", {
|
|
108
105
|
className: "font-semibold text-[var(--color-text-primary)]",
|
|
109
|
-
children:
|
|
106
|
+
children: W
|
|
110
107
|
}),
|
|
111
108
|
" ",
|
|
112
|
-
|
|
109
|
+
x("auth.passwordReset.seconds", "seconds...")
|
|
113
110
|
]
|
|
114
111
|
}), /* @__PURE__ */ g(v, {
|
|
115
112
|
type: "button",
|
|
116
|
-
onClick: () =>
|
|
113
|
+
onClick: () => S(T ? `/auth/login?returnTo=${encodeURIComponent(T)}` : "/auth/login"),
|
|
117
114
|
variant: "default",
|
|
118
115
|
size: "lg",
|
|
119
116
|
className: "w-full font-semibold",
|
|
120
|
-
children:
|
|
117
|
+
children: x("auth.passwordReset.signInNow", "Sign in now")
|
|
121
118
|
})]
|
|
122
119
|
}) : /* @__PURE__ */ _("div", {
|
|
123
120
|
className: "space-y-4",
|
|
124
121
|
children: [/* @__PURE__ */ g("button", {
|
|
125
122
|
type: "button",
|
|
126
|
-
onClick: () =>
|
|
123
|
+
onClick: () => V(!1),
|
|
127
124
|
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",
|
|
128
|
-
children:
|
|
125
|
+
children: x("auth.passwordReset.didntReceive", "Didn't receive the email? Try again")
|
|
129
126
|
}), /* @__PURE__ */ g("button", {
|
|
130
127
|
type: "button",
|
|
131
|
-
onClick: () =>
|
|
128
|
+
onClick: () => S("/auth/login"),
|
|
132
129
|
className: "w-full py-3 px-4 rounded-xl text-[var(--color-text-secondary)] font-medium hover:text-[var(--color-text-primary)] transition-colors",
|
|
133
|
-
children:
|
|
130
|
+
children: x("auth.passwordReset.backToSignIn", "Back to sign in")
|
|
134
131
|
})]
|
|
135
132
|
})
|
|
136
133
|
]
|
|
137
|
-
}) : /* @__PURE__ */ _(
|
|
138
|
-
title:
|
|
139
|
-
subtitle:
|
|
134
|
+
}) : /* @__PURE__ */ _(t, {
|
|
135
|
+
title: w ? x("auth.passwordReset.createNewPassword", "Create new password") : x("auth.passwordReset.resetTitle", "Reset your password"),
|
|
136
|
+
subtitle: w ? x("auth.passwordReset.enterNewPassword", "Enter a new password for your account") : x("auth.passwordReset.enterEmail", "Enter your email and we'll send you a reset link"),
|
|
140
137
|
children: [
|
|
141
138
|
/* @__PURE__ */ g(y, {
|
|
142
139
|
className: "mb-5",
|
|
143
|
-
children:
|
|
140
|
+
children: w ? x("auth.passwordReset.purposeReset", "Choose a new password for your account. Make it strong and unique — you'll use it to sign in next time.") : x("auth.passwordReset.purposeRequest", "Forgot your password? Enter your email and we'll send a secure link to reset it. The link expires shortly for your safety.")
|
|
144
141
|
}),
|
|
145
|
-
|
|
142
|
+
w ? /* @__PURE__ */ _("form", {
|
|
146
143
|
onSubmit: async (e) => {
|
|
147
|
-
if (e.preventDefault(),
|
|
148
|
-
if (
|
|
149
|
-
|
|
144
|
+
if (e.preventDefault(), U(""), w) {
|
|
145
|
+
if (M !== P) {
|
|
146
|
+
U(x("auth.passwordReset.passwordsNoMatch", "Passwords do not match"));
|
|
150
147
|
return;
|
|
151
148
|
}
|
|
152
149
|
try {
|
|
153
|
-
await
|
|
150
|
+
await D(w, M), V(!0);
|
|
154
151
|
} catch (e) {
|
|
155
152
|
let t = e instanceof Error ? e.message : "";
|
|
156
|
-
|
|
153
|
+
U(t === "TOKEN_EXPIRED" ? x("auth.passwordReset.errorTokenExpired", "This password reset link has expired. Please request a new one.") : t === "INVALID_TOKEN" ? x("auth.passwordReset.errorInvalidToken", "This password reset link is invalid or has already been used.") : x("auth.passwordReset.errorGeneric", "Failed to reset password. Please try again."));
|
|
157
154
|
}
|
|
158
155
|
}
|
|
159
156
|
},
|
|
160
157
|
className: "space-y-5",
|
|
161
158
|
children: [
|
|
162
|
-
|
|
159
|
+
H && /* @__PURE__ */ _("div", {
|
|
163
160
|
className: "flex items-start gap-3 p-4 rounded-xl bg-[var(--color-status-error-bg-subtle)] border border-[var(--color-status-error-border)]",
|
|
164
161
|
children: [/* @__PURE__ */ g("svg", {
|
|
165
162
|
className: "size-5 text-[var(--color-status-error-text)] flex-shrink-0 mt-0.5",
|
|
@@ -174,7 +171,7 @@ function x() {
|
|
|
174
171
|
})
|
|
175
172
|
}), /* @__PURE__ */ g("p", {
|
|
176
173
|
className: "text-sm text-[var(--color-status-error-text)]",
|
|
177
|
-
children:
|
|
174
|
+
children: H
|
|
178
175
|
})]
|
|
179
176
|
}),
|
|
180
177
|
/* @__PURE__ */ _("div", {
|
|
@@ -183,59 +180,59 @@ function x() {
|
|
|
183
180
|
/* @__PURE__ */ g("label", {
|
|
184
181
|
htmlFor: "newPassword",
|
|
185
182
|
className: "block text-sm font-medium text-[var(--color-text-primary)]",
|
|
186
|
-
children:
|
|
183
|
+
children: x("auth.passwordReset.newPasswordLabel", "New Password")
|
|
187
184
|
}),
|
|
188
185
|
/* @__PURE__ */ _("div", {
|
|
189
186
|
className: "relative",
|
|
190
187
|
children: [
|
|
191
188
|
/* @__PURE__ */ g("div", {
|
|
192
189
|
className: "absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none",
|
|
193
|
-
children: /* @__PURE__ */ g(
|
|
190
|
+
children: /* @__PURE__ */ g(o, { className: "size-5 text-[var(--color-text-tertiary)]" })
|
|
194
191
|
}),
|
|
195
192
|
/* @__PURE__ */ g("input", {
|
|
196
193
|
id: "newPassword",
|
|
197
|
-
type:
|
|
194
|
+
type: I ? "text" : "password",
|
|
198
195
|
required: !0,
|
|
199
196
|
"aria-required": "true",
|
|
200
|
-
"aria-label":
|
|
197
|
+
"aria-label": x("auth.passwordReset.newPasswordLabel", "New Password"),
|
|
201
198
|
autoComplete: "new-password",
|
|
202
|
-
value:
|
|
203
|
-
onChange: (e) =>
|
|
199
|
+
value: M,
|
|
200
|
+
onChange: (e) => N(e.target.value),
|
|
204
201
|
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",
|
|
205
|
-
placeholder:
|
|
202
|
+
placeholder: x("auth.passwordReset.newPasswordPlaceholder", "Enter new password")
|
|
206
203
|
}),
|
|
207
204
|
/* @__PURE__ */ g("button", {
|
|
208
205
|
type: "button",
|
|
209
|
-
onClick: () =>
|
|
210
|
-
"aria-label":
|
|
206
|
+
onClick: () => L(!I),
|
|
207
|
+
"aria-label": I ? "Hide password" : "Show password",
|
|
211
208
|
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",
|
|
212
|
-
children: g(
|
|
209
|
+
children: g(I ? a : i, { className: "size-5" })
|
|
213
210
|
})
|
|
214
211
|
]
|
|
215
212
|
}),
|
|
216
|
-
|
|
213
|
+
M && /* @__PURE__ */ _("div", {
|
|
217
214
|
className: "space-y-3 pt-2",
|
|
218
215
|
children: [/* @__PURE__ */ _("div", {
|
|
219
216
|
className: "space-y-1",
|
|
220
217
|
children: [/* @__PURE__ */ g("div", {
|
|
221
218
|
className: "h-1.5 bg-[var(--color-bg-sunken)] rounded-full overflow-hidden",
|
|
222
|
-
children: /* @__PURE__ */ g("div", { className: `h-full ${
|
|
219
|
+
children: /* @__PURE__ */ g("div", { className: `h-full ${q.color} ${q.width} transition-all duration-300` })
|
|
223
220
|
}), /* @__PURE__ */ _("p", {
|
|
224
221
|
className: "text-xs text-[var(--color-text-tertiary)] capitalize",
|
|
225
222
|
children: [
|
|
226
|
-
|
|
223
|
+
x("auth.passwordReset.passwordStrength", "Password strength:"),
|
|
227
224
|
" ",
|
|
228
225
|
/* @__PURE__ */ g("span", {
|
|
229
226
|
className: "font-medium",
|
|
230
|
-
children:
|
|
227
|
+
children: q.level
|
|
231
228
|
})
|
|
232
229
|
]
|
|
233
230
|
})]
|
|
234
231
|
}), /* @__PURE__ */ g("div", {
|
|
235
232
|
className: "grid grid-cols-2 gap-2",
|
|
236
|
-
children:
|
|
233
|
+
children: K.map((e) => /* @__PURE__ */ _("div", {
|
|
237
234
|
className: "flex items-center gap-2",
|
|
238
|
-
children: [e.met ? /* @__PURE__ */ g(
|
|
235
|
+
children: [e.met ? /* @__PURE__ */ g(n, { className: "size-4 text-status-success-text" }) : /* @__PURE__ */ g(c, { className: "size-4 text-[var(--color-text-tertiary)]" }), /* @__PURE__ */ g("span", {
|
|
239
236
|
className: `text-xs ${e.met ? "text-[var(--color-status-success-text)]" : "text-[var(--color-text-tertiary)]"}`,
|
|
240
237
|
children: e.label
|
|
241
238
|
})]
|
|
@@ -250,64 +247,64 @@ function x() {
|
|
|
250
247
|
/* @__PURE__ */ g("label", {
|
|
251
248
|
htmlFor: "confirmPassword",
|
|
252
249
|
className: "block text-sm font-medium text-[var(--color-text-primary)]",
|
|
253
|
-
children:
|
|
250
|
+
children: x("auth.passwordReset.confirmPasswordLabel", "Confirm Password")
|
|
254
251
|
}),
|
|
255
252
|
/* @__PURE__ */ _("div", {
|
|
256
253
|
className: "relative",
|
|
257
254
|
children: [
|
|
258
255
|
/* @__PURE__ */ g("div", {
|
|
259
256
|
className: "absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none",
|
|
260
|
-
children: /* @__PURE__ */ g(
|
|
257
|
+
children: /* @__PURE__ */ g(o, { className: "size-5 text-[var(--color-text-tertiary)]" })
|
|
261
258
|
}),
|
|
262
259
|
/* @__PURE__ */ g("input", {
|
|
263
260
|
id: "confirmPassword",
|
|
264
|
-
type:
|
|
261
|
+
type: R ? "text" : "password",
|
|
265
262
|
required: !0,
|
|
266
263
|
"aria-required": "true",
|
|
267
|
-
"aria-label":
|
|
264
|
+
"aria-label": x("auth.passwordReset.confirmPasswordLabel", "Confirm Password"),
|
|
268
265
|
autoComplete: "new-password",
|
|
269
|
-
"aria-invalid":
|
|
270
|
-
value:
|
|
271
|
-
onChange: (e) =>
|
|
266
|
+
"aria-invalid": P.length > 0 && M !== P,
|
|
267
|
+
value: P,
|
|
268
|
+
onChange: (e) => F(e.target.value),
|
|
272
269
|
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",
|
|
273
|
-
placeholder:
|
|
270
|
+
placeholder: x("auth.passwordReset.confirmPasswordPlaceholder", "Confirm new password")
|
|
274
271
|
}),
|
|
275
272
|
/* @__PURE__ */ g("button", {
|
|
276
273
|
type: "button",
|
|
277
|
-
onClick: () =>
|
|
278
|
-
"aria-label":
|
|
274
|
+
onClick: () => z(!R),
|
|
275
|
+
"aria-label": R ? x("auth.passwordReset.hideConfirmPassword", "Hide confirm password") : x("auth.passwordReset.showConfirmPassword", "Show confirm password"),
|
|
279
276
|
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",
|
|
280
|
-
children: g(
|
|
277
|
+
children: g(R ? a : i, { className: "size-5" })
|
|
281
278
|
})
|
|
282
279
|
]
|
|
283
280
|
}),
|
|
284
|
-
|
|
281
|
+
P && M !== P && /* @__PURE__ */ g("p", {
|
|
285
282
|
className: "text-xs text-[var(--color-status-error-text)]",
|
|
286
|
-
children:
|
|
283
|
+
children: x("auth.passwordReset.passwordsMismatch", "Passwords do not match")
|
|
287
284
|
})
|
|
288
285
|
]
|
|
289
286
|
}),
|
|
290
287
|
/* @__PURE__ */ g(v, {
|
|
291
288
|
type: "submit",
|
|
292
|
-
disabled:
|
|
289
|
+
disabled: O || P.length > 0 && M !== P,
|
|
293
290
|
variant: "default",
|
|
294
291
|
size: "lg",
|
|
295
292
|
className: "w-full font-semibold",
|
|
296
|
-
children:
|
|
293
|
+
children: O ? /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g(s, { className: "size-5 animate-spin" }), /* @__PURE__ */ g("span", { children: x("auth.passwordReset.resetting", "Resetting password...") })] }) : x("auth.passwordReset.resetButton", "Reset password")
|
|
297
294
|
})
|
|
298
295
|
]
|
|
299
296
|
}) : /* @__PURE__ */ _("form", {
|
|
300
297
|
onSubmit: async (e) => {
|
|
301
|
-
e.preventDefault(),
|
|
298
|
+
e.preventDefault(), U("");
|
|
302
299
|
try {
|
|
303
|
-
await
|
|
300
|
+
await E(A, await k("password_reset")), V(!0);
|
|
304
301
|
} catch (e) {
|
|
305
|
-
|
|
302
|
+
U(x("auth.passwordReset.sendFailed", "Failed to send reset email. Please try again.")), console.error("Failed to request password reset:", e);
|
|
306
303
|
}
|
|
307
304
|
},
|
|
308
305
|
className: "space-y-5",
|
|
309
306
|
children: [
|
|
310
|
-
|
|
307
|
+
H && /* @__PURE__ */ _("div", {
|
|
311
308
|
className: "flex items-start gap-3 p-4 rounded-xl bg-[var(--color-status-error-bg-subtle)] border border-[var(--color-status-error-border)]",
|
|
312
309
|
children: [/* @__PURE__ */ g("svg", {
|
|
313
310
|
className: "size-5 text-[var(--color-status-error-text)] flex-shrink-0 mt-0.5",
|
|
@@ -322,7 +319,7 @@ function x() {
|
|
|
322
319
|
})
|
|
323
320
|
}), /* @__PURE__ */ g("p", {
|
|
324
321
|
className: "text-sm text-[var(--color-status-error-text)]",
|
|
325
|
-
children:
|
|
322
|
+
children: H
|
|
326
323
|
})]
|
|
327
324
|
}),
|
|
328
325
|
/* @__PURE__ */ _("div", {
|
|
@@ -330,46 +327,46 @@ function x() {
|
|
|
330
327
|
children: [/* @__PURE__ */ g("label", {
|
|
331
328
|
htmlFor: "email",
|
|
332
329
|
className: "block text-sm font-medium text-[var(--color-text-primary)]",
|
|
333
|
-
children:
|
|
330
|
+
children: x("auth.passwordReset.emailLabel", "Email address")
|
|
334
331
|
}), /* @__PURE__ */ _("div", {
|
|
335
332
|
className: "relative",
|
|
336
333
|
children: [/* @__PURE__ */ g("div", {
|
|
337
334
|
className: "absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none",
|
|
338
|
-
children: /* @__PURE__ */ g(
|
|
335
|
+
children: /* @__PURE__ */ g(r, { className: "size-5 text-[var(--color-text-tertiary)]" })
|
|
339
336
|
}), /* @__PURE__ */ g("input", {
|
|
340
337
|
id: "email",
|
|
341
338
|
type: "email",
|
|
342
339
|
required: !0,
|
|
343
340
|
"aria-required": "true",
|
|
344
|
-
"aria-label":
|
|
341
|
+
"aria-label": x("auth.passwordReset.emailLabel", "Email address"),
|
|
345
342
|
autoComplete: "email",
|
|
346
|
-
value:
|
|
347
|
-
onChange: (e) =>
|
|
343
|
+
value: A,
|
|
344
|
+
onChange: (e) => j(e.target.value),
|
|
348
345
|
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",
|
|
349
|
-
placeholder:
|
|
346
|
+
placeholder: x("auth.passwordReset.emailPlaceholder", "you@example.com")
|
|
350
347
|
})]
|
|
351
348
|
})]
|
|
352
349
|
}),
|
|
353
350
|
/* @__PURE__ */ g(v, {
|
|
354
351
|
type: "submit",
|
|
355
|
-
disabled:
|
|
352
|
+
disabled: O,
|
|
356
353
|
variant: "default",
|
|
357
354
|
size: "lg",
|
|
358
355
|
className: "w-full font-semibold",
|
|
359
|
-
children:
|
|
356
|
+
children: O ? /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g(s, { className: "size-5 animate-spin" }), /* @__PURE__ */ g("span", { children: x("auth.passwordReset.sending", "Sending...") })] }) : x("auth.passwordReset.sendResetLink", "Send reset link")
|
|
360
357
|
})
|
|
361
358
|
]
|
|
362
359
|
}),
|
|
363
360
|
/* @__PURE__ */ _("p", {
|
|
364
361
|
className: "mt-8 text-center text-sm text-[var(--color-text-secondary)]",
|
|
365
362
|
children: [
|
|
366
|
-
|
|
363
|
+
x("auth.passwordReset.rememberPassword", "Remember your password?"),
|
|
367
364
|
" ",
|
|
368
365
|
/* @__PURE__ */ g("button", {
|
|
369
366
|
type: "button",
|
|
370
|
-
onClick: () =>
|
|
367
|
+
onClick: () => S("/auth/login"),
|
|
371
368
|
className: "font-semibold text-text-link hover:text-text-linkHover transition-colors",
|
|
372
|
-
children:
|
|
369
|
+
children: x("auth.passwordReset.signIn", "Sign in")
|
|
373
370
|
})
|
|
374
371
|
]
|
|
375
372
|
})
|
|
@@ -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\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport { usePasswordOperations } from \"../hooks/usePasswordOperations\";\nimport { useCaptchaV3 } from \"../hooks/useCaptchaV3\";\nimport { AuthLayout } from \"../components/AuthLayout\";\nimport { Button } from \"@burdenoff/fe-libs/button\";\nimport { PagePurpose } from \"@burdenoff/fe-libs/ui\";\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=\"size-16 rounded-full bg-[var(--color-status-success-bg-subtle)] flex items-center justify-center\">\n <svg\n className=\"size-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 type=\"button\"\n onClick={() =>\n navigate(\n returnTo\n ? `/auth/login?returnTo=${encodeURIComponent(returnTo)}`\n : \"/auth/login\",\n )\n }\n variant=\"default\"\n size=\"lg\"\n className=\"w-full 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 type=\"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 type=\"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 <PagePurpose className=\"mb-5\">\n {token\n ? tr(\n \"auth.passwordReset.purposeReset\",\n \"Choose a new password for your account. Make it strong and unique — you'll use it to sign in next time.\",\n )\n : tr(\n \"auth.passwordReset.purposeRequest\",\n \"Forgot your password? Enter your email and we'll send a secure link to reset it. The link expires shortly for your safety.\",\n )}\n </PagePurpose>\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=\"size-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=\"size-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 aria-label={tr(\n \"auth.passwordReset.newPasswordLabel\",\n \"New Password\",\n )}\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=\"size-5\" />\n ) : (\n <EyeIcon className=\"size-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) => (\n <div key={check.label} className=\"flex items-center gap-2\">\n {check.met ? (\n <CheckIcon className=\"size-4 text-status-success-text\" />\n ) : (\n <XIcon className=\"size-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=\"size-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 aria-label={tr(\n \"auth.passwordReset.confirmPasswordLabel\",\n \"Confirm Password\",\n )}\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=\"size-5\" />\n ) : (\n <EyeIcon className=\"size-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 variant=\"default\"\n size=\"lg\"\n className=\"w-full font-semibold\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"size-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=\"size-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=\"size-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"email\"\n type=\"email\"\n required\n aria-required=\"true\"\n aria-label={tr(\n \"auth.passwordReset.emailLabel\",\n \"Email address\",\n )}\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 variant=\"default\"\n size=\"lg\"\n className=\"w-full font-semibold\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"size-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 type=\"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\";\nimport { useTr } from \"../../shared/hooks/useTr\";\nimport { usePasswordOperations } from \"../hooks/usePasswordOperations\";\nimport { useCaptchaV3 } from \"../hooks/useCaptchaV3\";\nimport { AuthLayout } from \"../components/AuthLayout\";\nimport { Button } from \"@burdenoff/fe-libs/button\";\nimport { PagePurpose } from \"@burdenoff/fe-libs/ui\";\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 tr = useTr();\n const navigate = useNavigate();\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=\"size-16 rounded-full bg-[var(--color-status-success-bg-subtle)] flex items-center justify-center\">\n <svg\n className=\"size-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 type=\"button\"\n onClick={() =>\n navigate(\n returnTo\n ? `/auth/login?returnTo=${encodeURIComponent(returnTo)}`\n : \"/auth/login\",\n )\n }\n variant=\"default\"\n size=\"lg\"\n className=\"w-full 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 type=\"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 type=\"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 <PagePurpose className=\"mb-5\">\n {token\n ? tr(\n \"auth.passwordReset.purposeReset\",\n \"Choose a new password for your account. Make it strong and unique — you'll use it to sign in next time.\",\n )\n : tr(\n \"auth.passwordReset.purposeRequest\",\n \"Forgot your password? Enter your email and we'll send a secure link to reset it. The link expires shortly for your safety.\",\n )}\n </PagePurpose>\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=\"size-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=\"size-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 aria-label={tr(\n \"auth.passwordReset.newPasswordLabel\",\n \"New Password\",\n )}\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=\"size-5\" />\n ) : (\n <EyeIcon className=\"size-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) => (\n <div key={check.label} className=\"flex items-center gap-2\">\n {check.met ? (\n <CheckIcon className=\"size-4 text-status-success-text\" />\n ) : (\n <XIcon className=\"size-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=\"size-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 aria-label={tr(\n \"auth.passwordReset.confirmPasswordLabel\",\n \"Confirm Password\",\n )}\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\n ? tr(\n \"auth.passwordReset.hideConfirmPassword\",\n \"Hide confirm password\",\n )\n : tr(\n \"auth.passwordReset.showConfirmPassword\",\n \"Show confirm password\",\n )\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=\"size-5\" />\n ) : (\n <EyeIcon className=\"size-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 variant=\"default\"\n size=\"lg\"\n className=\"w-full font-semibold\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"size-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=\"size-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=\"size-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"email\"\n type=\"email\"\n required\n aria-required=\"true\"\n aria-label={tr(\n \"auth.passwordReset.emailLabel\",\n \"Email address\",\n )}\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 variant=\"default\"\n size=\"lg\"\n className=\"w-full font-semibold\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"size-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 type=\"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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"}
|