@burdenoff/microfe-auth 2026.904.2 → 2026.908.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.
|
@@ -3,135 +3,136 @@ import { useTr as t } from "../../shared/hooks/useTr.js";
|
|
|
3
3
|
import { useAuthOperations as n } from "../hooks/useAuthOperations.js";
|
|
4
4
|
import { useOAuthOperations as ee } from "../hooks/useOAuthOperations.js";
|
|
5
5
|
import { AuthLayout as r } from "../components/AuthLayout.js";
|
|
6
|
-
import { AlertIcon as i, CheckIcon as te, EmailIcon as a, EyeIcon as
|
|
7
|
-
import { validateEmail as
|
|
6
|
+
import { AlertIcon as i, CheckIcon as te, EmailIcon as a, EyeIcon as o, EyeOffIcon as s, GitHubIcon as ne, GoogleIcon as re, LockIcon as c, MicrosoftIcon as l, SpinnerIcon as u, UserIcon as d, XIcon as f } from "../components/AuthIcons.js";
|
|
7
|
+
import { validateEmail as p, validatePassword as ie } from "../utils/validation.js";
|
|
8
|
+
import { resolveLegalUrl as m } from "../utils/legalUrls.js";
|
|
8
9
|
import { CaptchaV2 as ae } from "../components/CaptchaV2.js";
|
|
9
10
|
import { useSignupCaptcha as oe } from "../hooks/useSignupCaptcha.js";
|
|
10
|
-
import { useState as
|
|
11
|
-
import { useNavigate as
|
|
12
|
-
import { Fragment as
|
|
13
|
-
import { Button as
|
|
14
|
-
import { PagePurpose as
|
|
11
|
+
import { useState as h } from "react";
|
|
12
|
+
import { useNavigate as g } from "react-router";
|
|
13
|
+
import { Fragment as se, jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
14
|
+
import { Button as y } from "@burdenoff/fe-libs/button";
|
|
15
|
+
import { PagePurpose as ce } from "@burdenoff/fe-libs/ui";
|
|
15
16
|
//#region src/auth/pages/RegisterPage.tsx
|
|
16
|
-
function
|
|
17
|
-
let
|
|
17
|
+
function b() {
|
|
18
|
+
let b = t(), { productName: x } = e(), S = g(), { signUp: C, resendVerification: le, isLoading: w, errorCode: T } = n(), { initiateGoogleOAuth: ue, initiateMicrosoftOAuth: de, isProviderConfigured: E } = ee(), [D, fe] = h(!1), [O, k] = h({
|
|
18
19
|
email: "",
|
|
19
20
|
username: "",
|
|
20
21
|
password: "",
|
|
21
22
|
firstName: "",
|
|
22
23
|
lastName: ""
|
|
23
|
-
}), [
|
|
24
|
-
let e =
|
|
24
|
+
}), [A, j] = h([]), [M, N] = h(!1), [P, F] = h(null), [I, L] = h(null), [R, z] = h(null), [B, V] = h(0), { enabled: H, siteKey: pe } = oe(), [U, W] = h(!1), [G, K] = h(null), q = !!P && T === "ACCOUNT_DEACTIVATED", J = !!P && T === "ACCOUNT_REMOVED", Y = !!P && T === "ACCOUNT_SUSPENDED", X = (() => {
|
|
25
|
+
let e = O.password;
|
|
25
26
|
return [
|
|
26
27
|
{
|
|
27
|
-
label:
|
|
28
|
+
label: b("auth.register.pwMin8", "At least 12 characters"),
|
|
28
29
|
met: e.length >= 12
|
|
29
30
|
},
|
|
30
31
|
{
|
|
31
|
-
label:
|
|
32
|
+
label: b("auth.register.pwUppercase", "One uppercase letter"),
|
|
32
33
|
met: /[A-Z]/.test(e)
|
|
33
34
|
},
|
|
34
35
|
{
|
|
35
|
-
label:
|
|
36
|
+
label: b("auth.register.pwLowercase", "One lowercase letter"),
|
|
36
37
|
met: /[a-z]/.test(e)
|
|
37
38
|
},
|
|
38
39
|
{
|
|
39
|
-
label:
|
|
40
|
+
label: b("auth.register.pwNumber", "One number"),
|
|
40
41
|
met: /\d/.test(e)
|
|
41
42
|
},
|
|
42
43
|
{
|
|
43
|
-
label:
|
|
44
|
+
label: b("auth.register.pwSpecial", "One special character"),
|
|
44
45
|
met: /[!@#$%^&*(),.?":{}|<>]/.test(e)
|
|
45
46
|
}
|
|
46
47
|
];
|
|
47
|
-
})(),
|
|
48
|
-
let e =
|
|
48
|
+
})(), Z = (() => {
|
|
49
|
+
let e = X.filter((e) => e.met).length;
|
|
49
50
|
return e <= 2 ? {
|
|
50
|
-
level:
|
|
51
|
+
level: b("auth.register.passwordStrengthWeak", "weak"),
|
|
51
52
|
color: "bg-status-error-bg-subtle0",
|
|
52
53
|
width: "w-1/4"
|
|
53
54
|
} : e <= 3 ? {
|
|
54
|
-
level:
|
|
55
|
+
level: b("auth.register.passwordStrengthFair", "fair"),
|
|
55
56
|
color: "bg-status-warning-text",
|
|
56
57
|
width: "w-2/4"
|
|
57
58
|
} : e <= 4 ? {
|
|
58
|
-
level:
|
|
59
|
+
level: b("auth.register.passwordStrengthGood", "good"),
|
|
59
60
|
color: "bg-status-warning-bg-subtle0",
|
|
60
61
|
width: "w-3/4"
|
|
61
62
|
} : {
|
|
62
|
-
level:
|
|
63
|
+
level: b("auth.register.passwordStrengthStrong", "strong"),
|
|
63
64
|
color: "bg-status-success-text",
|
|
64
65
|
width: "w-full"
|
|
65
66
|
};
|
|
66
|
-
})(),
|
|
67
|
+
})(), me = async (e) => {
|
|
67
68
|
e.preventDefault();
|
|
68
69
|
let t = [];
|
|
69
|
-
|
|
70
|
-
let n =
|
|
71
|
-
if (n.valid || t.push(...n.errors),
|
|
72
|
-
|
|
70
|
+
p(O.email) || t.push(b("auth.register.invalidEmail", "Please enter a valid email address")), (!O.username || O.username.length < 3) && t.push(b("auth.register.usernameMin3", "Username must be at least 3 characters")), /^[a-zA-Z0-9_-]+$/.test(O.username) || t.push(b("auth.register.usernameChars", "Username can only contain letters, numbers, underscores, and hyphens"));
|
|
71
|
+
let n = ie(O.password);
|
|
72
|
+
if (n.valid || t.push(...n.errors), H && !R && t.push(b("auth.register.completeCaptcha", "Please complete the reCAPTCHA verification")), t.length > 0) {
|
|
73
|
+
j(t);
|
|
73
74
|
return;
|
|
74
75
|
}
|
|
75
|
-
|
|
76
|
+
j([]), F(null);
|
|
76
77
|
try {
|
|
77
|
-
await
|
|
78
|
-
...
|
|
79
|
-
...
|
|
80
|
-
}),
|
|
78
|
+
await C({
|
|
79
|
+
...O,
|
|
80
|
+
...R ? { recaptchaToken: R } : {}
|
|
81
|
+
}), N(!0);
|
|
81
82
|
} catch (e) {
|
|
82
|
-
|
|
83
|
+
F(e instanceof Error ? e.message : b("auth.register.signUpFailed", "Sign up failed")), console.error("Sign up failed:", e), H && (z(null), V((e) => e + 1));
|
|
83
84
|
}
|
|
84
|
-
},
|
|
85
|
-
|
|
85
|
+
}, he = () => {
|
|
86
|
+
L(null);
|
|
86
87
|
try {
|
|
87
88
|
ue();
|
|
88
89
|
} catch (e) {
|
|
89
|
-
|
|
90
|
+
L(e instanceof Error ? e.message : b("auth.register.googleSignUpFailed", "Failed to initiate Google sign up"));
|
|
90
91
|
}
|
|
91
|
-
},
|
|
92
|
-
|
|
92
|
+
}, ge = () => {
|
|
93
|
+
L(null);
|
|
93
94
|
try {
|
|
94
95
|
de();
|
|
95
96
|
} catch (e) {
|
|
96
|
-
|
|
97
|
+
L(e instanceof Error ? e.message : b("auth.register.microsoftSignUpFailed", "Failed to initiate Microsoft sign up"));
|
|
97
98
|
}
|
|
98
|
-
}, Q =
|
|
99
|
+
}, Q = E("google"), $ = E("microsoft"), _e = [
|
|
99
100
|
{
|
|
100
101
|
name: "Google",
|
|
101
|
-
icon:
|
|
102
|
-
onClick:
|
|
102
|
+
icon: re,
|
|
103
|
+
onClick: he,
|
|
103
104
|
disabled: !Q,
|
|
104
|
-
tooltip: Q ? void 0 :
|
|
105
|
+
tooltip: Q ? void 0 : b("auth.register.googleNotConfigured", "Google sign up is not configured")
|
|
105
106
|
},
|
|
106
107
|
{
|
|
107
108
|
name: "GitHub",
|
|
108
|
-
icon:
|
|
109
|
+
icon: ne,
|
|
109
110
|
onClick: () => {},
|
|
110
111
|
disabled: !0,
|
|
111
|
-
tooltip:
|
|
112
|
+
tooltip: b("auth.register.githubNotConfigured", "GitHub sign up is not configured")
|
|
112
113
|
},
|
|
113
114
|
{
|
|
114
115
|
name: "Microsoft",
|
|
115
|
-
icon:
|
|
116
|
-
onClick:
|
|
116
|
+
icon: l,
|
|
117
|
+
onClick: ge,
|
|
117
118
|
disabled: !$,
|
|
118
|
-
tooltip: $ ? void 0 :
|
|
119
|
+
tooltip: $ ? void 0 : b("auth.register.microsoftNotConfigured", "Microsoft sign up is not configured")
|
|
119
120
|
}
|
|
120
|
-
], [
|
|
121
|
-
return
|
|
122
|
-
title:
|
|
123
|
-
subtitle:
|
|
124
|
-
children: /* @__PURE__ */
|
|
121
|
+
], [ve, ye = ""] = b("auth.register.verificationEmailSentTo", "We've sent a verification email to {{email}}. Please check your inbox and click the verification link to activate your account.").split("{{email}}");
|
|
122
|
+
return M ? /* @__PURE__ */ _(r, {
|
|
123
|
+
title: b("auth.register.checkEmail", "Check your email"),
|
|
124
|
+
subtitle: b("auth.register.verificationLinkSent", "We've sent you a verification link"),
|
|
125
|
+
children: /* @__PURE__ */ v("div", {
|
|
125
126
|
className: "flex flex-col items-center justify-center py-6",
|
|
126
127
|
children: [
|
|
127
|
-
/* @__PURE__ */
|
|
128
|
+
/* @__PURE__ */ _("div", {
|
|
128
129
|
className: "size-20 rounded-full bg-status-success-bg-subtle flex items-center justify-center mb-6",
|
|
129
|
-
children: /* @__PURE__ */
|
|
130
|
+
children: /* @__PURE__ */ _("svg", {
|
|
130
131
|
className: "size-10 text-status-success-text",
|
|
131
132
|
fill: "none",
|
|
132
133
|
viewBox: "0 0 24 24",
|
|
133
134
|
stroke: "currentColor",
|
|
134
|
-
children: /* @__PURE__ */
|
|
135
|
+
children: /* @__PURE__ */ _("path", {
|
|
135
136
|
strokeLinecap: "round",
|
|
136
137
|
strokeLinejoin: "round",
|
|
137
138
|
strokeWidth: 2,
|
|
@@ -139,56 +140,56 @@ function v() {
|
|
|
139
140
|
})
|
|
140
141
|
})
|
|
141
142
|
}),
|
|
142
|
-
/* @__PURE__ */
|
|
143
|
+
/* @__PURE__ */ _("h3", {
|
|
143
144
|
className: "text-lg font-semibold text-[var(--color-text-primary)] mb-2",
|
|
144
|
-
children:
|
|
145
|
+
children: b("auth.register.accountCreated", "Account created successfully!")
|
|
145
146
|
}),
|
|
146
|
-
/* @__PURE__ */
|
|
147
|
+
/* @__PURE__ */ v("p", {
|
|
147
148
|
className: "text-center text-[var(--color-text-secondary)] mb-8 max-w-sm",
|
|
148
149
|
children: [
|
|
149
|
-
|
|
150
|
-
/* @__PURE__ */
|
|
150
|
+
ve,
|
|
151
|
+
/* @__PURE__ */ _("span", {
|
|
151
152
|
className: "font-medium text-[var(--color-text-primary)]",
|
|
152
|
-
children:
|
|
153
|
+
children: O.email
|
|
153
154
|
}),
|
|
154
|
-
|
|
155
|
+
ye
|
|
155
156
|
]
|
|
156
157
|
}),
|
|
157
|
-
/* @__PURE__ */
|
|
158
|
+
/* @__PURE__ */ v("div", {
|
|
158
159
|
className: "space-y-3 w-full",
|
|
159
160
|
children: [
|
|
160
|
-
/* @__PURE__ */
|
|
161
|
+
/* @__PURE__ */ _(y, {
|
|
161
162
|
type: "button",
|
|
162
|
-
onClick: () =>
|
|
163
|
+
onClick: () => S("/auth/login"),
|
|
163
164
|
variant: "default",
|
|
164
165
|
size: "lg",
|
|
165
166
|
className: "w-full font-semibold",
|
|
166
|
-
children:
|
|
167
|
+
children: b("auth.register.continueToSignIn", "Continue to sign in")
|
|
167
168
|
}),
|
|
168
|
-
|
|
169
|
+
G && /* @__PURE__ */ _("p", {
|
|
169
170
|
className: "text-center text-sm text-status-error-text",
|
|
170
|
-
children:
|
|
171
|
+
children: G
|
|
171
172
|
}),
|
|
172
|
-
/* @__PURE__ */
|
|
173
|
+
/* @__PURE__ */ v("p", {
|
|
173
174
|
className: "text-center text-sm text-[var(--color-text-tertiary)]",
|
|
174
175
|
children: [
|
|
175
|
-
|
|
176
|
+
b("auth.register.didntReceiveEmail", "Didn't receive the email? Check your spam folder or"),
|
|
176
177
|
" ",
|
|
177
|
-
/* @__PURE__ */
|
|
178
|
+
/* @__PURE__ */ _("button", {
|
|
178
179
|
type: "button",
|
|
179
|
-
disabled:
|
|
180
|
+
disabled: U,
|
|
180
181
|
onClick: async () => {
|
|
181
|
-
|
|
182
|
+
K(null), W(!0);
|
|
182
183
|
try {
|
|
183
|
-
await
|
|
184
|
+
await le(O.email);
|
|
184
185
|
} catch (e) {
|
|
185
|
-
|
|
186
|
+
K(e instanceof Error ? e.message : b("auth.register.resendFailed", "Failed to resend verification email"));
|
|
186
187
|
} finally {
|
|
187
|
-
|
|
188
|
+
W(!1);
|
|
188
189
|
}
|
|
189
190
|
},
|
|
190
191
|
className: "text-text-link hover:text-text-linkHover font-medium disabled:opacity-50 disabled:cursor-not-allowed",
|
|
191
|
-
children:
|
|
192
|
+
children: U ? b("auth.register.resending", "resending...") : b("auth.register.tryAgain", "try again")
|
|
192
193
|
})
|
|
193
194
|
]
|
|
194
195
|
})
|
|
@@ -196,223 +197,223 @@ function v() {
|
|
|
196
197
|
})
|
|
197
198
|
]
|
|
198
199
|
})
|
|
199
|
-
}) : /* @__PURE__ */
|
|
200
|
-
title:
|
|
201
|
-
subtitle:
|
|
202
|
-
productName:
|
|
200
|
+
}) : /* @__PURE__ */ v(r, {
|
|
201
|
+
title: b("auth.register.createAccount", "Create your account"),
|
|
202
|
+
subtitle: b("auth.register.startJourney", "Start your journey with {{productName}}").replace("{{productName}}", x || "Burdenoff Workspaces"),
|
|
203
|
+
productName: x,
|
|
203
204
|
children: [
|
|
204
|
-
/* @__PURE__ */
|
|
205
|
+
/* @__PURE__ */ _(ce, {
|
|
205
206
|
className: "mb-5",
|
|
206
|
-
children:
|
|
207
|
+
children: b("auth.register.purpose", "Create your account to get a personal workspace and start collaborating. You'll verify your email, then you can sign in and invite your team.")
|
|
207
208
|
}),
|
|
208
|
-
/* @__PURE__ */
|
|
209
|
-
onSubmit:
|
|
209
|
+
/* @__PURE__ */ v("form", {
|
|
210
|
+
onSubmit: me,
|
|
210
211
|
className: "space-y-5",
|
|
211
212
|
children: [
|
|
212
|
-
|
|
213
|
+
q && /* @__PURE__ */ v("div", {
|
|
213
214
|
className: "flex items-start gap-3 p-4 rounded-xl bg-status-warning-bg-subtle border border-status-warning-border",
|
|
214
|
-
children: [/* @__PURE__ */
|
|
215
|
+
children: [/* @__PURE__ */ _(i, { className: "size-5 text-status-warning-text flex-shrink-0 mt-0.5" }), /* @__PURE__ */ v("div", {
|
|
215
216
|
className: "space-y-2",
|
|
216
217
|
children: [
|
|
217
|
-
/* @__PURE__ */
|
|
218
|
+
/* @__PURE__ */ _("p", {
|
|
218
219
|
className: "text-sm font-medium text-status-warning-text",
|
|
219
|
-
children:
|
|
220
|
+
children: b("auth.register.accountDeactivated", "This email belongs to a deactivated account.")
|
|
220
221
|
}),
|
|
221
|
-
/* @__PURE__ */
|
|
222
|
+
/* @__PURE__ */ _("p", {
|
|
222
223
|
className: "text-xs text-status-warning-text/80",
|
|
223
|
-
children:
|
|
224
|
+
children: b("auth.register.accountDeactivatedHelp", "If this is your account, you can reactivate it instead of creating a new one.")
|
|
224
225
|
}),
|
|
225
|
-
/* @__PURE__ */
|
|
226
|
+
/* @__PURE__ */ _("button", {
|
|
226
227
|
type: "button",
|
|
227
|
-
onClick: () =>
|
|
228
|
+
onClick: () => S("/auth/reactivate", { state: { email: O.email } }),
|
|
228
229
|
className: "text-sm font-semibold text-status-warning-text underline underline-offset-2",
|
|
229
|
-
children:
|
|
230
|
+
children: b("auth.register.reactivateCta", "Reactivate my account")
|
|
230
231
|
})
|
|
231
232
|
]
|
|
232
233
|
})]
|
|
233
234
|
}),
|
|
234
|
-
(
|
|
235
|
+
(J || Y) && /* @__PURE__ */ v("div", {
|
|
235
236
|
className: "flex items-start gap-3 p-4 rounded-xl bg-status-warning-bg-subtle border border-status-warning-border",
|
|
236
|
-
children: [/* @__PURE__ */
|
|
237
|
+
children: [/* @__PURE__ */ _(i, { className: "size-5 text-status-warning-text flex-shrink-0 mt-0.5" }), /* @__PURE__ */ _("p", {
|
|
237
238
|
className: "text-sm text-status-warning-text",
|
|
238
|
-
children:
|
|
239
|
+
children: P
|
|
239
240
|
})]
|
|
240
241
|
}),
|
|
241
|
-
(
|
|
242
|
+
(P && !q && !J && !Y || A.length > 0) && /* @__PURE__ */ v("div", {
|
|
242
243
|
className: "flex items-start gap-3 p-4 rounded-xl bg-status-error-bg-subtle border border-status-error-border",
|
|
243
|
-
children: [/* @__PURE__ */
|
|
244
|
+
children: [/* @__PURE__ */ _(i, { className: "size-5 text-status-error-text flex-shrink-0 mt-0.5" }), /* @__PURE__ */ v("div", {
|
|
244
245
|
className: "space-y-1",
|
|
245
|
-
children: [
|
|
246
|
+
children: [P && !q && !J && !Y && /* @__PURE__ */ _("p", {
|
|
246
247
|
className: "text-sm text-status-error-text",
|
|
247
|
-
children:
|
|
248
|
-
}),
|
|
248
|
+
children: P
|
|
249
|
+
}), A.map((e) => /* @__PURE__ */ _("p", {
|
|
249
250
|
className: "text-sm text-status-error-text",
|
|
250
251
|
children: e
|
|
251
252
|
}, e))]
|
|
252
253
|
})]
|
|
253
254
|
}),
|
|
254
|
-
/* @__PURE__ */
|
|
255
|
+
/* @__PURE__ */ v("div", {
|
|
255
256
|
className: "grid grid-cols-2 gap-4",
|
|
256
|
-
children: [/* @__PURE__ */
|
|
257
|
+
children: [/* @__PURE__ */ v("div", {
|
|
257
258
|
className: "space-y-2",
|
|
258
|
-
children: [/* @__PURE__ */
|
|
259
|
+
children: [/* @__PURE__ */ _("label", {
|
|
259
260
|
htmlFor: "firstName",
|
|
260
261
|
className: "block text-sm font-medium text-[var(--color-text-primary)]",
|
|
261
|
-
children:
|
|
262
|
-
}), /* @__PURE__ */
|
|
262
|
+
children: b("auth.register.firstName", "First Name")
|
|
263
|
+
}), /* @__PURE__ */ _("input", {
|
|
263
264
|
id: "firstName",
|
|
264
265
|
type: "text",
|
|
265
266
|
required: !0,
|
|
266
267
|
"aria-required": "true",
|
|
267
|
-
"aria-label":
|
|
268
|
-
value:
|
|
269
|
-
onChange: (e) =>
|
|
270
|
-
...
|
|
268
|
+
"aria-label": b("auth.register.firstName", "First Name"),
|
|
269
|
+
value: O.firstName,
|
|
270
|
+
onChange: (e) => k({
|
|
271
|
+
...O,
|
|
271
272
|
firstName: e.target.value
|
|
272
273
|
}),
|
|
273
274
|
className: "block w-full px-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",
|
|
274
|
-
placeholder:
|
|
275
|
+
placeholder: b("auth.register.firstNamePlaceholder", "Enter first name")
|
|
275
276
|
})]
|
|
276
|
-
}), /* @__PURE__ */
|
|
277
|
+
}), /* @__PURE__ */ v("div", {
|
|
277
278
|
className: "space-y-2",
|
|
278
|
-
children: [/* @__PURE__ */
|
|
279
|
+
children: [/* @__PURE__ */ _("label", {
|
|
279
280
|
htmlFor: "lastName",
|
|
280
281
|
className: "block text-sm font-medium text-[var(--color-text-primary)]",
|
|
281
|
-
children:
|
|
282
|
-
}), /* @__PURE__ */
|
|
282
|
+
children: b("auth.register.lastName", "Last Name")
|
|
283
|
+
}), /* @__PURE__ */ _("input", {
|
|
283
284
|
id: "lastName",
|
|
284
285
|
type: "text",
|
|
285
286
|
required: !0,
|
|
286
287
|
"aria-required": "true",
|
|
287
|
-
"aria-label":
|
|
288
|
-
value:
|
|
289
|
-
onChange: (e) =>
|
|
290
|
-
...
|
|
288
|
+
"aria-label": b("auth.register.lastName", "Last Name"),
|
|
289
|
+
value: O.lastName,
|
|
290
|
+
onChange: (e) => k({
|
|
291
|
+
...O,
|
|
291
292
|
lastName: e.target.value
|
|
292
293
|
}),
|
|
293
294
|
className: "block w-full px-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",
|
|
294
|
-
placeholder:
|
|
295
|
+
placeholder: b("auth.register.lastNamePlaceholder", "Enter last name")
|
|
295
296
|
})]
|
|
296
297
|
})]
|
|
297
298
|
}),
|
|
298
|
-
/* @__PURE__ */
|
|
299
|
+
/* @__PURE__ */ v("div", {
|
|
299
300
|
className: "space-y-2",
|
|
300
|
-
children: [/* @__PURE__ */
|
|
301
|
+
children: [/* @__PURE__ */ _("label", {
|
|
301
302
|
htmlFor: "username",
|
|
302
303
|
className: "block text-sm font-medium text-[var(--color-text-primary)]",
|
|
303
|
-
children:
|
|
304
|
-
}), /* @__PURE__ */
|
|
304
|
+
children: b("auth.register.username", "Username")
|
|
305
|
+
}), /* @__PURE__ */ v("div", {
|
|
305
306
|
className: "relative",
|
|
306
|
-
children: [/* @__PURE__ */
|
|
307
|
+
children: [/* @__PURE__ */ _("div", {
|
|
307
308
|
className: "absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none",
|
|
308
|
-
children: /* @__PURE__ */
|
|
309
|
-
}), /* @__PURE__ */
|
|
309
|
+
children: /* @__PURE__ */ _(d, { className: "size-5 text-[var(--color-text-tertiary)]" })
|
|
310
|
+
}), /* @__PURE__ */ _("input", {
|
|
310
311
|
id: "username",
|
|
311
312
|
type: "text",
|
|
312
313
|
required: !0,
|
|
313
314
|
"aria-required": "true",
|
|
314
|
-
"aria-label":
|
|
315
|
+
"aria-label": b("auth.register.username", "Username"),
|
|
315
316
|
autoComplete: "username",
|
|
316
|
-
value:
|
|
317
|
-
onChange: (e) =>
|
|
318
|
-
...
|
|
317
|
+
value: O.username,
|
|
318
|
+
onChange: (e) => k({
|
|
319
|
+
...O,
|
|
319
320
|
username: e.target.value
|
|
320
321
|
}),
|
|
321
322
|
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",
|
|
322
|
-
placeholder:
|
|
323
|
+
placeholder: b("auth.register.usernamePlaceholder", "Choose a username")
|
|
323
324
|
})]
|
|
324
325
|
})]
|
|
325
326
|
}),
|
|
326
|
-
/* @__PURE__ */
|
|
327
|
+
/* @__PURE__ */ v("div", {
|
|
327
328
|
className: "space-y-2",
|
|
328
|
-
children: [/* @__PURE__ */
|
|
329
|
+
children: [/* @__PURE__ */ _("label", {
|
|
329
330
|
htmlFor: "email",
|
|
330
331
|
className: "block text-sm font-medium text-[var(--color-text-primary)]",
|
|
331
|
-
children:
|
|
332
|
-
}), /* @__PURE__ */
|
|
332
|
+
children: b("auth.register.email", "Email")
|
|
333
|
+
}), /* @__PURE__ */ v("div", {
|
|
333
334
|
className: "relative",
|
|
334
|
-
children: [/* @__PURE__ */
|
|
335
|
+
children: [/* @__PURE__ */ _("div", {
|
|
335
336
|
className: "absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none",
|
|
336
|
-
children: /* @__PURE__ */
|
|
337
|
-
}), /* @__PURE__ */
|
|
337
|
+
children: /* @__PURE__ */ _(a, { className: "size-5 text-[var(--color-text-tertiary)]" })
|
|
338
|
+
}), /* @__PURE__ */ _("input", {
|
|
338
339
|
id: "email",
|
|
339
340
|
type: "email",
|
|
340
341
|
required: !0,
|
|
341
342
|
"aria-required": "true",
|
|
342
|
-
"aria-label":
|
|
343
|
+
"aria-label": b("auth.register.email", "Email"),
|
|
343
344
|
autoComplete: "email",
|
|
344
|
-
value:
|
|
345
|
-
onChange: (e) =>
|
|
346
|
-
...
|
|
345
|
+
value: O.email,
|
|
346
|
+
onChange: (e) => k({
|
|
347
|
+
...O,
|
|
347
348
|
email: e.target.value
|
|
348
349
|
}),
|
|
349
350
|
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",
|
|
350
|
-
placeholder:
|
|
351
|
+
placeholder: b("auth.register.emailPlaceholder", "you@example.com")
|
|
351
352
|
})]
|
|
352
353
|
})]
|
|
353
354
|
}),
|
|
354
|
-
/* @__PURE__ */
|
|
355
|
+
/* @__PURE__ */ v("div", {
|
|
355
356
|
className: "space-y-2",
|
|
356
357
|
children: [
|
|
357
|
-
/* @__PURE__ */
|
|
358
|
+
/* @__PURE__ */ _("label", {
|
|
358
359
|
htmlFor: "password",
|
|
359
360
|
className: "block text-sm font-medium text-[var(--color-text-primary)]",
|
|
360
|
-
children:
|
|
361
|
+
children: b("auth.register.password", "Password")
|
|
361
362
|
}),
|
|
362
|
-
/* @__PURE__ */
|
|
363
|
+
/* @__PURE__ */ v("div", {
|
|
363
364
|
className: "relative",
|
|
364
365
|
children: [
|
|
365
|
-
/* @__PURE__ */
|
|
366
|
+
/* @__PURE__ */ _("div", {
|
|
366
367
|
className: "absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none",
|
|
367
|
-
children: /* @__PURE__ */
|
|
368
|
+
children: /* @__PURE__ */ _(c, { className: "size-5 text-[var(--color-text-tertiary)]" })
|
|
368
369
|
}),
|
|
369
|
-
/* @__PURE__ */
|
|
370
|
+
/* @__PURE__ */ _("input", {
|
|
370
371
|
id: "password",
|
|
371
|
-
type:
|
|
372
|
+
type: D ? "text" : "password",
|
|
372
373
|
required: !0,
|
|
373
374
|
"aria-required": "true",
|
|
374
|
-
"aria-label":
|
|
375
|
+
"aria-label": b("auth.register.password", "Password"),
|
|
375
376
|
autoComplete: "new-password",
|
|
376
|
-
value:
|
|
377
|
-
onChange: (e) =>
|
|
378
|
-
...
|
|
377
|
+
value: O.password,
|
|
378
|
+
onChange: (e) => k({
|
|
379
|
+
...O,
|
|
379
380
|
password: e.target.value
|
|
380
381
|
}),
|
|
381
382
|
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",
|
|
382
|
-
placeholder:
|
|
383
|
+
placeholder: b("auth.register.passwordPlaceholder", "Create a strong password")
|
|
383
384
|
}),
|
|
384
|
-
/* @__PURE__ */
|
|
385
|
+
/* @__PURE__ */ _("button", {
|
|
385
386
|
type: "button",
|
|
386
|
-
onClick: () => fe(!
|
|
387
|
-
"aria-label":
|
|
387
|
+
onClick: () => fe(!D),
|
|
388
|
+
"aria-label": D ? b("auth.register.hidePassword", "Hide password") : b("auth.register.showPassword", "Show password"),
|
|
388
389
|
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",
|
|
389
|
-
children:
|
|
390
|
+
children: _(D ? s : o, { className: "size-5" })
|
|
390
391
|
})
|
|
391
392
|
]
|
|
392
393
|
}),
|
|
393
|
-
|
|
394
|
+
O.password && /* @__PURE__ */ v("div", {
|
|
394
395
|
className: "space-y-3 pt-2",
|
|
395
|
-
children: [/* @__PURE__ */
|
|
396
|
+
children: [/* @__PURE__ */ v("div", {
|
|
396
397
|
className: "space-y-1",
|
|
397
|
-
children: [/* @__PURE__ */
|
|
398
|
+
children: [/* @__PURE__ */ _("div", {
|
|
398
399
|
className: "h-1.5 bg-[var(--color-bg-sunken)] rounded-full overflow-hidden",
|
|
399
|
-
children: /* @__PURE__ */
|
|
400
|
-
}), /* @__PURE__ */
|
|
400
|
+
children: /* @__PURE__ */ _("div", { className: `h-full ${Z.color} ${Z.width} transition-all duration-300` })
|
|
401
|
+
}), /* @__PURE__ */ v("p", {
|
|
401
402
|
className: "text-xs text-[var(--color-text-tertiary)] capitalize",
|
|
402
403
|
children: [
|
|
403
|
-
|
|
404
|
+
b("auth.register.passwordStrength", "Password strength:"),
|
|
404
405
|
" ",
|
|
405
|
-
/* @__PURE__ */
|
|
406
|
+
/* @__PURE__ */ _("span", {
|
|
406
407
|
className: "font-medium",
|
|
407
|
-
children:
|
|
408
|
+
children: Z.level
|
|
408
409
|
})
|
|
409
410
|
]
|
|
410
411
|
})]
|
|
411
|
-
}), /* @__PURE__ */
|
|
412
|
+
}), /* @__PURE__ */ _("div", {
|
|
412
413
|
className: "grid grid-cols-2 gap-2",
|
|
413
|
-
children:
|
|
414
|
+
children: X.map((e) => /* @__PURE__ */ v("div", {
|
|
414
415
|
className: "flex items-center gap-2",
|
|
415
|
-
children: [e.met ? /* @__PURE__ */
|
|
416
|
+
children: [e.met ? /* @__PURE__ */ _(te, { className: "size-4 text-status-success-text" }) : /* @__PURE__ */ _(f, { className: "size-4 text-[var(--color-text-tertiary)]" }), /* @__PURE__ */ _("span", {
|
|
416
417
|
className: `text-xs ${e.met ? "text-status-success-text" : "text-[var(--color-text-tertiary)]"}`,
|
|
417
418
|
children: e.label
|
|
418
419
|
})]
|
|
@@ -421,87 +422,91 @@ function v() {
|
|
|
421
422
|
})
|
|
422
423
|
]
|
|
423
424
|
}),
|
|
424
|
-
/* @__PURE__ */
|
|
425
|
+
/* @__PURE__ */ v("p", {
|
|
425
426
|
className: "text-xs text-[var(--color-text-tertiary)] leading-relaxed",
|
|
426
427
|
children: [
|
|
427
|
-
|
|
428
|
+
b("auth.register.agreeToTerms", "By creating an account, you agree to our"),
|
|
428
429
|
" ",
|
|
429
|
-
/* @__PURE__ */
|
|
430
|
-
href: "/
|
|
430
|
+
/* @__PURE__ */ _("a", {
|
|
431
|
+
href: m("/terms"),
|
|
432
|
+
target: "_blank",
|
|
433
|
+
rel: "noopener noreferrer",
|
|
431
434
|
className: "text-text-link underline underline-offset-2",
|
|
432
|
-
children:
|
|
435
|
+
children: b("auth.register.termsOfService", "Terms of Service")
|
|
433
436
|
}),
|
|
434
437
|
" ",
|
|
435
|
-
|
|
438
|
+
b("auth.register.and", "and"),
|
|
436
439
|
" ",
|
|
437
|
-
/* @__PURE__ */
|
|
438
|
-
href: "/
|
|
440
|
+
/* @__PURE__ */ _("a", {
|
|
441
|
+
href: m("/privacy"),
|
|
442
|
+
target: "_blank",
|
|
443
|
+
rel: "noopener noreferrer",
|
|
439
444
|
className: "text-text-link underline underline-offset-2",
|
|
440
|
-
children:
|
|
445
|
+
children: b("auth.register.privacyPolicy", "Privacy Policy")
|
|
441
446
|
}),
|
|
442
447
|
"."
|
|
443
448
|
]
|
|
444
449
|
}),
|
|
445
|
-
/* @__PURE__ */
|
|
446
|
-
enabled:
|
|
450
|
+
/* @__PURE__ */ _(ae, {
|
|
451
|
+
enabled: H,
|
|
447
452
|
siteKey: pe,
|
|
448
|
-
onVerify:
|
|
449
|
-
onExpired: () =>
|
|
450
|
-
},
|
|
451
|
-
/* @__PURE__ */
|
|
453
|
+
onVerify: z,
|
|
454
|
+
onExpired: () => z(null)
|
|
455
|
+
}, B),
|
|
456
|
+
/* @__PURE__ */ _(y, {
|
|
452
457
|
type: "submit",
|
|
453
|
-
disabled:
|
|
458
|
+
disabled: w,
|
|
454
459
|
variant: "default",
|
|
455
460
|
size: "lg",
|
|
456
461
|
className: "w-full font-semibold",
|
|
457
|
-
children:
|
|
462
|
+
children: w ? /* @__PURE__ */ v(se, { children: [/* @__PURE__ */ _(u, { className: "size-5 animate-spin" }), /* @__PURE__ */ _("span", { children: b("auth.register.creatingAccount", "Creating account...") })] }) : b("auth.register.createAccountBtn", "Create account")
|
|
458
463
|
})
|
|
459
464
|
]
|
|
460
465
|
}),
|
|
461
|
-
/* @__PURE__ */
|
|
466
|
+
/* @__PURE__ */ v("div", {
|
|
462
467
|
className: "relative my-8",
|
|
463
|
-
children: [/* @__PURE__ */
|
|
468
|
+
children: [/* @__PURE__ */ _("div", {
|
|
464
469
|
className: "absolute inset-0 flex items-center",
|
|
465
|
-
children: /* @__PURE__ */
|
|
466
|
-
}), /* @__PURE__ */
|
|
470
|
+
children: /* @__PURE__ */ _("div", { className: "w-full border-t border-[var(--color-border-default)]" })
|
|
471
|
+
}), /* @__PURE__ */ _("div", {
|
|
467
472
|
className: "relative flex justify-center",
|
|
468
|
-
children: /* @__PURE__ */
|
|
473
|
+
children: /* @__PURE__ */ _("span", {
|
|
469
474
|
className: "px-4 bg-[var(--color-bg-canvas)] text-sm text-[var(--color-text-tertiary)]",
|
|
470
|
-
children:
|
|
475
|
+
children: b("auth.register.orSignUpWith", "or sign up with")
|
|
471
476
|
})
|
|
472
477
|
})]
|
|
473
478
|
}),
|
|
474
|
-
|
|
479
|
+
I && /* @__PURE__ */ v("div", {
|
|
475
480
|
className: "flex items-start gap-3 p-4 rounded-xl border bg-status-error-bg-subtle border-status-error-border",
|
|
476
|
-
children: [/* @__PURE__ */
|
|
481
|
+
children: [/* @__PURE__ */ _(i, { className: "size-5 flex-shrink-0 mt-0.5 text-status-error-text" }), /* @__PURE__ */ _("p", {
|
|
477
482
|
className: "text-sm text-status-error-text",
|
|
478
|
-
children:
|
|
483
|
+
children: I
|
|
479
484
|
})]
|
|
480
485
|
}),
|
|
481
|
-
/* @__PURE__ */
|
|
486
|
+
/* @__PURE__ */ _("div", {
|
|
482
487
|
className: "grid grid-cols-3 gap-3",
|
|
483
|
-
children:
|
|
488
|
+
children: _e.map((e) => /* @__PURE__ */ v("button", {
|
|
484
489
|
type: "button",
|
|
485
490
|
onClick: e.onClick,
|
|
486
491
|
disabled: e.disabled,
|
|
487
492
|
title: e.tooltip,
|
|
488
493
|
className: `flex items-center justify-center gap-2 px-4 py-3 rounded-xl border transition-all duration-200 group ${e.disabled ? "border-[var(--color-border-subtle)] bg-[var(--color-bg-disabled)] cursor-not-allowed opacity-50" : "border-[var(--color-border-default)] bg-[var(--color-bg-surface)] hover:bg-[var(--color-bg-sunken)] hover:border-[var(--color-border-strong)]"}`,
|
|
489
|
-
children: [/* @__PURE__ */
|
|
494
|
+
children: [/* @__PURE__ */ _(e.icon, { className: `size-5 ${e.disabled ? "opacity-50" : ""}` }), /* @__PURE__ */ _("span", {
|
|
490
495
|
className: `sr-only sm:not-sr-only text-sm font-medium transition-colors ${e.disabled ? "text-[var(--color-text-disabled)]" : "text-[var(--color-text-secondary)] group-hover:text-[var(--color-text-primary)]"}`,
|
|
491
496
|
children: e.name
|
|
492
497
|
})]
|
|
493
498
|
}, e.name))
|
|
494
499
|
}),
|
|
495
|
-
/* @__PURE__ */
|
|
500
|
+
/* @__PURE__ */ v("p", {
|
|
496
501
|
className: "mt-8 text-center text-sm text-[var(--color-text-secondary)]",
|
|
497
502
|
children: [
|
|
498
|
-
|
|
503
|
+
b("auth.register.alreadyHaveAccount", "Already have an account?"),
|
|
499
504
|
" ",
|
|
500
|
-
/* @__PURE__ */
|
|
505
|
+
/* @__PURE__ */ _("button", {
|
|
501
506
|
type: "button",
|
|
502
|
-
onClick: () =>
|
|
507
|
+
onClick: () => S("/auth/login"),
|
|
503
508
|
className: "font-semibold text-text-link hover:text-text-linkHover transition-colors",
|
|
504
|
-
children:
|
|
509
|
+
children: b("auth.register.signIn", "Sign in")
|
|
505
510
|
})
|
|
506
511
|
]
|
|
507
512
|
})
|
|
@@ -509,6 +514,6 @@ function v() {
|
|
|
509
514
|
});
|
|
510
515
|
}
|
|
511
516
|
//#endregion
|
|
512
|
-
export {
|
|
517
|
+
export { b as RegisterPage };
|
|
513
518
|
|
|
514
519
|
//# sourceMappingURL=RegisterPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RegisterPage.js","names":[],"sources":["../../../src/auth/pages/RegisterPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { useNavigate } from \"react-router\";\nimport { useTr } from \"../../shared/hooks/useTr\";\nimport { validateEmail, validatePassword } from \"../utils/validation\";\nimport { useAuthOperations } from \"../hooks/useAuthOperations\";\nimport { useOAuthOperations } from \"../hooks/useOAuthOperations\";\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 UserIcon,\n EyeIcon,\n EyeOffIcon,\n AlertIcon,\n CheckIcon,\n XIcon,\n SpinnerIcon,\n GoogleIcon,\n GitHubIcon,\n MicrosoftIcon,\n} from \"../components/AuthIcons\";\nimport { useAuth } from \"../providers/AuthProvider\";\nimport { CaptchaV2 } from \"../components/CaptchaV2\";\nimport { useSignupCaptcha } from \"../hooks/useSignupCaptcha\";\nimport type { SignUpInput } from \"../types\";\n\n/**\n * Registration page with professional design\n */\nexport function RegisterPage() {\n const tr = useTr();\n const { productName } = useAuth();\n const navigate = useNavigate();\n\n const { signUp, resendVerification, isLoading, errorCode } =\n useAuthOperations();\n const { initiateGoogleOAuth, initiateMicrosoftOAuth, isProviderConfigured } =\n useOAuthOperations();\n const [showPassword, setShowPassword] = useState(false);\n const [formData, setFormData] = useState<SignUpInput>({\n email: \"\",\n username: \"\",\n password: \"\",\n firstName: \"\",\n lastName: \"\",\n });\n const [validationErrors, setValidationErrors] = useState<string[]>([]);\n const [signupSuccess, setSignupSuccess] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [oauthError, setOauthError] = useState<string | null>(null);\n const [captchaToken, setCaptchaToken] = useState<string | null>(null);\n // reCAPTCHA v2 tokens are single-use. Bumping this key remounts the widget so\n // a fresh token is issued after a failed submit (the prior token is consumed\n // server-side; reusing it returns timeout-or-duplicate → CAPTCHA_FAILED loop).\n const [captchaResetKey, setCaptchaResetKey] = useState(0);\n const { enabled: captchaEnabled, siteKey: captchaSiteKey } =\n useSignupCaptcha();\n const [isResendingVerification, setIsResendingVerification] = useState(false);\n const [resendError, setResendError] = useState<string | null>(null);\n\n // Signing up with an email that already belongs to a non-active account:\n // route the user to the right recovery path instead of dead-ending.\n const isAccountDeactivated =\n Boolean(error) && errorCode === \"ACCOUNT_DEACTIVATED\";\n const isAccountRemoved = Boolean(error) && errorCode === \"ACCOUNT_REMOVED\";\n const isAccountSuspended =\n Boolean(error) && errorCode === \"ACCOUNT_SUSPENDED\";\n\n // Password strength indicators\n const passwordChecks = (() => {\n const password = formData.password;\n return [\n {\n label: tr(\"auth.register.pwMin8\", \"At least 12 characters\"),\n met: password.length >= 12,\n },\n {\n label: tr(\"auth.register.pwUppercase\", \"One uppercase letter\"),\n met: /[A-Z]/.test(password),\n },\n {\n label: tr(\"auth.register.pwLowercase\", \"One lowercase letter\"),\n met: /[a-z]/.test(password),\n },\n {\n label: tr(\"auth.register.pwNumber\", \"One number\"),\n met: /\\d/.test(password),\n },\n {\n label: tr(\"auth.register.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.register.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.register.passwordStrengthFair\", \"fair\"),\n color: \"bg-status-warning-text\",\n width: \"w-2/4\",\n };\n if (metCount <= 4)\n return {\n level: tr(\"auth.register.passwordStrengthGood\", \"good\"),\n color: \"bg-status-warning-bg-subtle0\",\n width: \"w-3/4\",\n };\n return {\n level: tr(\"auth.register.passwordStrengthStrong\", \"strong\"),\n color: \"bg-status-success-text\",\n width: \"w-full\",\n };\n })();\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n const errors: string[] = [];\n if (!validateEmail(formData.email)) {\n errors.push(\n tr(\"auth.register.invalidEmail\", \"Please enter a valid email address\"),\n );\n }\n if (!formData.username || formData.username.length < 3) {\n errors.push(\n tr(\n \"auth.register.usernameMin3\",\n \"Username must be at least 3 characters\",\n ),\n );\n }\n if (!/^[a-zA-Z0-9_-]+$/.test(formData.username)) {\n errors.push(\n tr(\n \"auth.register.usernameChars\",\n \"Username can only contain letters, numbers, underscores, and hyphens\",\n ),\n );\n }\n const passwordValidation = validatePassword(formData.password);\n if (!passwordValidation.valid) {\n errors.push(...passwordValidation.errors);\n }\n\n if (captchaEnabled && !captchaToken) {\n errors.push(\n tr(\n \"auth.register.completeCaptcha\",\n \"Please complete the reCAPTCHA verification\",\n ),\n );\n }\n\n if (errors.length > 0) {\n setValidationErrors(errors);\n return;\n }\n\n setValidationErrors([]);\n setError(null);\n try {\n await signUp({\n ...formData,\n ...(captchaToken ? { recaptchaToken: captchaToken } : {}),\n });\n // Sign up successful - check if email verification is required\n // (Backend returns empty tokens when email verification is needed)\n setSignupSuccess(true);\n } catch (err) {\n const message =\n err instanceof Error\n ? err.message\n : tr(\"auth.register.signUpFailed\", \"Sign up failed\");\n setError(message);\n console.error(\"Sign up failed:\", err);\n // The captcha token (if any) was consumed during this attempt — discard it\n // and remount the widget so the user gets a fresh, solvable challenge on\n // retry instead of being trapped in a CAPTCHA_FAILED loop.\n if (captchaEnabled) {\n setCaptchaToken(null);\n setCaptchaResetKey((k) => k + 1);\n }\n }\n };\n\n /**\n * Handle Google OAuth signup\n */\n const handleGoogleSignup = () => {\n setOauthError(null);\n try {\n initiateGoogleOAuth();\n } catch (err) {\n setOauthError(\n err instanceof Error\n ? err.message\n : tr(\n \"auth.register.googleSignUpFailed\",\n \"Failed to initiate Google sign up\",\n ),\n );\n }\n };\n\n /**\n * Handle Microsoft OAuth signup\n */\n const handleMicrosoftSignup = () => {\n setOauthError(null);\n try {\n initiateMicrosoftOAuth();\n } catch (err) {\n setOauthError(\n err instanceof Error\n ? err.message\n : tr(\n \"auth.register.microsoftSignUpFailed\",\n \"Failed to initiate Microsoft sign up\",\n ),\n );\n }\n };\n\n // Check if OAuth providers are configured\n const isGoogleConfigured = isProviderConfigured(\"google\");\n const isMicrosoftConfigured = isProviderConfigured(\"microsoft\");\n\n const oauthProviders = [\n {\n name: \"Google\",\n icon: GoogleIcon,\n onClick: handleGoogleSignup,\n disabled: !isGoogleConfigured,\n tooltip: isGoogleConfigured\n ? undefined\n : tr(\n \"auth.register.googleNotConfigured\",\n \"Google sign up is not configured\",\n ),\n },\n {\n name: \"GitHub\",\n icon: GitHubIcon,\n onClick: () => {},\n disabled: true,\n tooltip: tr(\n \"auth.register.githubNotConfigured\",\n \"GitHub sign up is not configured\",\n ),\n },\n {\n name: \"Microsoft\",\n icon: MicrosoftIcon,\n onClick: handleMicrosoftSignup,\n disabled: !isMicrosoftConfigured,\n tooltip: isMicrosoftConfigured\n ? undefined\n : tr(\n \"auth.register.microsoftNotConfigured\",\n \"Microsoft sign up is not configured\",\n ),\n },\n ];\n\n const verificationEmailMessage = tr(\n \"auth.register.verificationEmailSentTo\",\n \"We've sent a verification email to {{email}}. Please check your inbox and click the verification link to activate your account.\",\n );\n const [verificationEmailPrefix, verificationEmailSuffix = \"\"] =\n verificationEmailMessage.split(\"{{email}}\");\n\n // Show success state after signup\n if (signupSuccess) {\n return (\n <AuthLayout\n title={tr(\"auth.register.checkEmail\", \"Check your email\")}\n subtitle={tr(\n \"auth.register.verificationLinkSent\",\n \"We've sent you a verification link\",\n )}\n >\n <div className=\"flex flex-col items-center justify-center py-6\">\n {/* Success Icon */}\n <div className=\"size-20 rounded-full bg-status-success-bg-subtle flex items-center justify-center mb-6\">\n <svg\n className=\"size-10 text-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=\"M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z\"\n />\n </svg>\n </div>\n\n <h3 className=\"text-lg font-semibold text-[var(--color-text-primary)] mb-2\">\n {tr(\n \"auth.register.accountCreated\",\n \"Account created successfully!\",\n )}\n </h3>\n\n <p className=\"text-center text-[var(--color-text-secondary)] mb-8 max-w-sm\">\n {verificationEmailPrefix}\n <span className=\"font-medium text-[var(--color-text-primary)]\">\n {formData.email}\n </span>\n {verificationEmailSuffix}\n </p>\n\n <div className=\"space-y-3 w-full\">\n <Button\n type=\"button\"\n onClick={() => navigate(\"/auth/login\")}\n variant=\"default\"\n size=\"lg\"\n className=\"w-full font-semibold\"\n >\n {tr(\"auth.register.continueToSignIn\", \"Continue to sign in\")}\n </Button>\n\n {resendError && (\n <p className=\"text-center text-sm text-status-error-text\">\n {resendError}\n </p>\n )}\n <p className=\"text-center text-sm text-[var(--color-text-tertiary)]\">\n {tr(\n \"auth.register.didntReceiveEmail\",\n \"Didn't receive the email? Check your spam folder or\",\n )}{\" \"}\n <button\n type=\"button\"\n disabled={isResendingVerification}\n onClick={async () => {\n setResendError(null);\n setIsResendingVerification(true);\n try {\n await resendVerification(formData.email);\n } catch (err) {\n setResendError(\n err instanceof Error\n ? err.message\n : tr(\n \"auth.register.resendFailed\",\n \"Failed to resend verification email\",\n ),\n );\n } finally {\n setIsResendingVerification(false);\n }\n }}\n className=\"text-text-link hover:text-text-linkHover font-medium disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isResendingVerification\n ? tr(\"auth.register.resending\", \"resending...\")\n : tr(\"auth.register.tryAgain\", \"try again\")}\n </button>\n </p>\n </div>\n </div>\n </AuthLayout>\n );\n }\n\n return (\n <AuthLayout\n title={tr(\"auth.register.createAccount\", \"Create your account\")}\n subtitle={tr(\n \"auth.register.startJourney\",\n \"Start your journey with {{productName}}\",\n ).replace(\"{{productName}}\", productName || \"Burdenoff Workspaces\")}\n productName={productName}\n >\n <PagePurpose className=\"mb-5\">\n {tr(\n \"auth.register.purpose\",\n \"Create your account to get a personal workspace and start collaborating. You'll verify your email, then you can sign in and invite your team.\",\n )}\n </PagePurpose>\n <form onSubmit={handleSubmit} className=\"space-y-5\">\n {/* Deactivated account → offer self-service reactivation */}\n {isAccountDeactivated && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl bg-status-warning-bg-subtle border border-status-warning-border\">\n <AlertIcon className=\"size-5 text-status-warning-text flex-shrink-0 mt-0.5\" />\n <div className=\"space-y-2\">\n <p className=\"text-sm font-medium text-status-warning-text\">\n {tr(\n \"auth.register.accountDeactivated\",\n \"This email belongs to a deactivated account.\",\n )}\n </p>\n <p className=\"text-xs text-status-warning-text/80\">\n {tr(\n \"auth.register.accountDeactivatedHelp\",\n \"If this is your account, you can reactivate it instead of creating a new one.\",\n )}\n </p>\n <button\n type=\"button\"\n onClick={() =>\n navigate(\"/auth/reactivate\", {\n state: { email: formData.email },\n })\n }\n className=\"text-sm font-semibold text-status-warning-text underline underline-offset-2\"\n >\n {tr(\"auth.register.reactivateCta\", \"Reactivate my account\")}\n </button>\n </div>\n </div>\n )}\n\n {/* Admin-removed / suspended → point to support (no self-service path) */}\n {(isAccountRemoved || isAccountSuspended) && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl bg-status-warning-bg-subtle border border-status-warning-border\">\n <AlertIcon className=\"size-5 text-status-warning-text flex-shrink-0 mt-0.5\" />\n <p className=\"text-sm text-status-warning-text\">{error}</p>\n </div>\n )}\n\n {/* Generic error / validation alert */}\n {((error &&\n !isAccountDeactivated &&\n !isAccountRemoved &&\n !isAccountSuspended) ||\n validationErrors.length > 0) && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl bg-status-error-bg-subtle border border-status-error-border\">\n <AlertIcon className=\"size-5 text-status-error-text flex-shrink-0 mt-0.5\" />\n <div className=\"space-y-1\">\n {error &&\n !isAccountDeactivated &&\n !isAccountRemoved &&\n !isAccountSuspended && (\n <p className=\"text-sm text-status-error-text\">{error}</p>\n )}\n {validationErrors.map((err) => (\n <p key={err} className=\"text-sm text-status-error-text\">\n {err}\n </p>\n ))}\n </div>\n </div>\n )}\n\n {/* Name Fields */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-2\">\n <label\n htmlFor=\"firstName\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.register.firstName\", \"First Name\")}\n </label>\n <input\n id=\"firstName\"\n type=\"text\"\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.register.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 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.register.firstNamePlaceholder\",\n \"Enter first name\",\n )}\n />\n </div>\n <div className=\"space-y-2\">\n <label\n htmlFor=\"lastName\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.register.lastName\", \"Last Name\")}\n </label>\n <input\n id=\"lastName\"\n type=\"text\"\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.register.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 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.register.lastNamePlaceholder\",\n \"Enter last name\",\n )}\n />\n </div>\n </div>\n\n {/* Username Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"username\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.register.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 className=\"size-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"username\"\n type=\"text\"\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.register.username\", \"Username\")}\n autoComplete=\"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 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.register.usernamePlaceholder\",\n \"Choose a username\",\n )}\n />\n </div>\n </div>\n\n {/* Email Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"email\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.register.email\", \"Email\")}\n </label>\n <div className=\"relative\">\n <div className=\"absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none\">\n <EmailIcon className=\"size-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"email\"\n type=\"email\"\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.register.email\", \"Email\")}\n autoComplete=\"email\"\n value={formData.email}\n onChange={(e) =>\n setFormData({ ...formData, email: e.target.value })\n }\n className=\"block w-full pl-[3.5rem] pr-4 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all\"\n placeholder={tr(\n \"auth.register.emailPlaceholder\",\n \"you@example.com\",\n )}\n />\n </div>\n </div>\n\n {/* Password Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"password\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.register.password\", \"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=\"password\"\n type={showPassword ? \"text\" : \"password\"}\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.register.password\", \"Password\")}\n autoComplete=\"new-password\"\n value={formData.password}\n onChange={(e) =>\n setFormData({ ...formData, password: e.target.value })\n }\n className=\"block w-full pl-[3.5rem] pr-12 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all\"\n placeholder={tr(\n \"auth.register.passwordPlaceholder\",\n \"Create a strong password\",\n )}\n />\n <button\n type=\"button\"\n onClick={() => setShowPassword(!showPassword)}\n aria-label={\n showPassword\n ? tr(\"auth.register.hidePassword\", \"Hide password\")\n : tr(\"auth.register.showPassword\", \"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 {showPassword ? (\n <EyeOffIcon className=\"size-5\" />\n ) : (\n <EyeIcon className=\"size-5\" />\n )}\n </button>\n </div>\n\n {/* Password Strength Indicator */}\n {formData.password && (\n <div className=\"space-y-3 pt-2\">\n {/* Strength Bar */}\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(\"auth.register.passwordStrength\", \"Password strength:\")}{\" \"}\n <span className=\"font-medium\">{passwordStrength.level}</span>\n </p>\n </div>\n\n {/* Requirements Checklist */}\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-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 {/* Terms Agreement */}\n <p className=\"text-xs text-[var(--color-text-tertiary)] leading-relaxed\">\n {tr(\n \"auth.register.agreeToTerms\",\n \"By creating an account, you agree to our\",\n )}{\" \"}\n <a\n href=\"/legal/terms\"\n className=\"text-text-link underline underline-offset-2\"\n >\n {tr(\"auth.register.termsOfService\", \"Terms of Service\")}\n </a>{\" \"}\n {tr(\"auth.register.and\", \"and\")}{\" \"}\n <a\n href=\"/legal/privacy\"\n className=\"text-text-link underline underline-offset-2\"\n >\n {tr(\"auth.register.privacyPolicy\", \"Privacy Policy\")}\n </a>\n .\n </p>\n\n {/* reCAPTCHA v2 Checkbox — enabled per-product via the platform catalog.\n `key` remounts it after a failed submit so a fresh token is issued. */}\n <CaptchaV2\n key={captchaResetKey}\n enabled={captchaEnabled}\n siteKey={captchaSiteKey}\n onVerify={setCaptchaToken}\n onExpired={() => setCaptchaToken(null)}\n />\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>\n {tr(\"auth.register.creatingAccount\", \"Creating account...\")}\n </span>\n </>\n ) : (\n tr(\"auth.register.createAccountBtn\", \"Create account\")\n )}\n </Button>\n </form>\n\n {/* Divider */}\n <div className=\"relative my-8\">\n <div className=\"absolute inset-0 flex items-center\">\n <div className=\"w-full border-t border-[var(--color-border-default)]\" />\n </div>\n <div className=\"relative flex justify-center\">\n <span className=\"px-4 bg-[var(--color-bg-canvas)] text-sm text-[var(--color-text-tertiary)]\">\n {tr(\"auth.register.orSignUpWith\", \"or sign up with\")}\n </span>\n </div>\n </div>\n\n {/* OAuth Error */}\n {oauthError && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl border bg-status-error-bg-subtle border-status-error-border\">\n <AlertIcon className=\"size-5 flex-shrink-0 mt-0.5 text-status-error-text\" />\n <p className=\"text-sm text-status-error-text\">{oauthError}</p>\n </div>\n )}\n\n {/* OAuth Buttons */}\n <div className=\"grid grid-cols-3 gap-3\">\n {oauthProviders.map((provider) => (\n <button\n key={provider.name}\n type=\"button\"\n onClick={provider.onClick}\n disabled={provider.disabled}\n title={provider.tooltip}\n className={`flex items-center justify-center gap-2 px-4 py-3 rounded-xl border transition-all duration-200 group ${\n provider.disabled\n ? \"border-[var(--color-border-subtle)] bg-[var(--color-bg-disabled)] cursor-not-allowed opacity-50\"\n : \"border-[var(--color-border-default)] bg-[var(--color-bg-surface)] hover:bg-[var(--color-bg-sunken)] hover:border-[var(--color-border-strong)]\"\n }`}\n >\n <provider.icon\n className={`size-5 ${provider.disabled ? \"opacity-50\" : \"\"}`}\n />\n <span\n className={`sr-only sm:not-sr-only text-sm font-medium transition-colors ${\n provider.disabled\n ? \"text-[var(--color-text-disabled)]\"\n : \"text-[var(--color-text-secondary)] group-hover:text-[var(--color-text-primary)]\"\n }`}\n >\n {provider.name}\n </span>\n </button>\n ))}\n </div>\n\n {/* Sign In Link */}\n <p className=\"mt-8 text-center text-sm text-[var(--color-text-secondary)]\">\n {tr(\"auth.register.alreadyHaveAccount\", \"Already have an account?\")}{\" \"}\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.register.signIn\", \"Sign in\")}\n </button>\n </p>\n </AuthLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RegisterPage.js","names":[],"sources":["../../../src/auth/pages/RegisterPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { useNavigate } from \"react-router\";\nimport { useTr } from \"../../shared/hooks/useTr\";\nimport { validateEmail, validatePassword } from \"../utils/validation\";\nimport { resolveLegalUrl } from \"../utils/legalUrls\";\nimport { useAuthOperations } from \"../hooks/useAuthOperations\";\nimport { useOAuthOperations } from \"../hooks/useOAuthOperations\";\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 UserIcon,\n EyeIcon,\n EyeOffIcon,\n AlertIcon,\n CheckIcon,\n XIcon,\n SpinnerIcon,\n GoogleIcon,\n GitHubIcon,\n MicrosoftIcon,\n} from \"../components/AuthIcons\";\nimport { useAuth } from \"../providers/AuthProvider\";\nimport { CaptchaV2 } from \"../components/CaptchaV2\";\nimport { useSignupCaptcha } from \"../hooks/useSignupCaptcha\";\nimport type { SignUpInput } from \"../types\";\n\n/**\n * Registration page with professional design\n */\nexport function RegisterPage() {\n const tr = useTr();\n const { productName } = useAuth();\n const navigate = useNavigate();\n\n const { signUp, resendVerification, isLoading, errorCode } =\n useAuthOperations();\n const { initiateGoogleOAuth, initiateMicrosoftOAuth, isProviderConfigured } =\n useOAuthOperations();\n const [showPassword, setShowPassword] = useState(false);\n const [formData, setFormData] = useState<SignUpInput>({\n email: \"\",\n username: \"\",\n password: \"\",\n firstName: \"\",\n lastName: \"\",\n });\n const [validationErrors, setValidationErrors] = useState<string[]>([]);\n const [signupSuccess, setSignupSuccess] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [oauthError, setOauthError] = useState<string | null>(null);\n const [captchaToken, setCaptchaToken] = useState<string | null>(null);\n // reCAPTCHA v2 tokens are single-use. Bumping this key remounts the widget so\n // a fresh token is issued after a failed submit (the prior token is consumed\n // server-side; reusing it returns timeout-or-duplicate → CAPTCHA_FAILED loop).\n const [captchaResetKey, setCaptchaResetKey] = useState(0);\n const { enabled: captchaEnabled, siteKey: captchaSiteKey } =\n useSignupCaptcha();\n const [isResendingVerification, setIsResendingVerification] = useState(false);\n const [resendError, setResendError] = useState<string | null>(null);\n\n // Signing up with an email that already belongs to a non-active account:\n // route the user to the right recovery path instead of dead-ending.\n const isAccountDeactivated =\n Boolean(error) && errorCode === \"ACCOUNT_DEACTIVATED\";\n const isAccountRemoved = Boolean(error) && errorCode === \"ACCOUNT_REMOVED\";\n const isAccountSuspended =\n Boolean(error) && errorCode === \"ACCOUNT_SUSPENDED\";\n\n // Password strength indicators\n const passwordChecks = (() => {\n const password = formData.password;\n return [\n {\n label: tr(\"auth.register.pwMin8\", \"At least 12 characters\"),\n met: password.length >= 12,\n },\n {\n label: tr(\"auth.register.pwUppercase\", \"One uppercase letter\"),\n met: /[A-Z]/.test(password),\n },\n {\n label: tr(\"auth.register.pwLowercase\", \"One lowercase letter\"),\n met: /[a-z]/.test(password),\n },\n {\n label: tr(\"auth.register.pwNumber\", \"One number\"),\n met: /\\d/.test(password),\n },\n {\n label: tr(\"auth.register.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.register.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.register.passwordStrengthFair\", \"fair\"),\n color: \"bg-status-warning-text\",\n width: \"w-2/4\",\n };\n if (metCount <= 4)\n return {\n level: tr(\"auth.register.passwordStrengthGood\", \"good\"),\n color: \"bg-status-warning-bg-subtle0\",\n width: \"w-3/4\",\n };\n return {\n level: tr(\"auth.register.passwordStrengthStrong\", \"strong\"),\n color: \"bg-status-success-text\",\n width: \"w-full\",\n };\n })();\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n const errors: string[] = [];\n if (!validateEmail(formData.email)) {\n errors.push(\n tr(\"auth.register.invalidEmail\", \"Please enter a valid email address\"),\n );\n }\n if (!formData.username || formData.username.length < 3) {\n errors.push(\n tr(\n \"auth.register.usernameMin3\",\n \"Username must be at least 3 characters\",\n ),\n );\n }\n if (!/^[a-zA-Z0-9_-]+$/.test(formData.username)) {\n errors.push(\n tr(\n \"auth.register.usernameChars\",\n \"Username can only contain letters, numbers, underscores, and hyphens\",\n ),\n );\n }\n const passwordValidation = validatePassword(formData.password);\n if (!passwordValidation.valid) {\n errors.push(...passwordValidation.errors);\n }\n\n if (captchaEnabled && !captchaToken) {\n errors.push(\n tr(\n \"auth.register.completeCaptcha\",\n \"Please complete the reCAPTCHA verification\",\n ),\n );\n }\n\n if (errors.length > 0) {\n setValidationErrors(errors);\n return;\n }\n\n setValidationErrors([]);\n setError(null);\n try {\n await signUp({\n ...formData,\n ...(captchaToken ? { recaptchaToken: captchaToken } : {}),\n });\n // Sign up successful - check if email verification is required\n // (Backend returns empty tokens when email verification is needed)\n setSignupSuccess(true);\n } catch (err) {\n const message =\n err instanceof Error\n ? err.message\n : tr(\"auth.register.signUpFailed\", \"Sign up failed\");\n setError(message);\n console.error(\"Sign up failed:\", err);\n // The captcha token (if any) was consumed during this attempt — discard it\n // and remount the widget so the user gets a fresh, solvable challenge on\n // retry instead of being trapped in a CAPTCHA_FAILED loop.\n if (captchaEnabled) {\n setCaptchaToken(null);\n setCaptchaResetKey((k) => k + 1);\n }\n }\n };\n\n /**\n * Handle Google OAuth signup\n */\n const handleGoogleSignup = () => {\n setOauthError(null);\n try {\n initiateGoogleOAuth();\n } catch (err) {\n setOauthError(\n err instanceof Error\n ? err.message\n : tr(\n \"auth.register.googleSignUpFailed\",\n \"Failed to initiate Google sign up\",\n ),\n );\n }\n };\n\n /**\n * Handle Microsoft OAuth signup\n */\n const handleMicrosoftSignup = () => {\n setOauthError(null);\n try {\n initiateMicrosoftOAuth();\n } catch (err) {\n setOauthError(\n err instanceof Error\n ? err.message\n : tr(\n \"auth.register.microsoftSignUpFailed\",\n \"Failed to initiate Microsoft sign up\",\n ),\n );\n }\n };\n\n // Check if OAuth providers are configured\n const isGoogleConfigured = isProviderConfigured(\"google\");\n const isMicrosoftConfigured = isProviderConfigured(\"microsoft\");\n\n const oauthProviders = [\n {\n name: \"Google\",\n icon: GoogleIcon,\n onClick: handleGoogleSignup,\n disabled: !isGoogleConfigured,\n tooltip: isGoogleConfigured\n ? undefined\n : tr(\n \"auth.register.googleNotConfigured\",\n \"Google sign up is not configured\",\n ),\n },\n {\n name: \"GitHub\",\n icon: GitHubIcon,\n onClick: () => {},\n disabled: true,\n tooltip: tr(\n \"auth.register.githubNotConfigured\",\n \"GitHub sign up is not configured\",\n ),\n },\n {\n name: \"Microsoft\",\n icon: MicrosoftIcon,\n onClick: handleMicrosoftSignup,\n disabled: !isMicrosoftConfigured,\n tooltip: isMicrosoftConfigured\n ? undefined\n : tr(\n \"auth.register.microsoftNotConfigured\",\n \"Microsoft sign up is not configured\",\n ),\n },\n ];\n\n const verificationEmailMessage = tr(\n \"auth.register.verificationEmailSentTo\",\n \"We've sent a verification email to {{email}}. Please check your inbox and click the verification link to activate your account.\",\n );\n const [verificationEmailPrefix, verificationEmailSuffix = \"\"] =\n verificationEmailMessage.split(\"{{email}}\");\n\n // Show success state after signup\n if (signupSuccess) {\n return (\n <AuthLayout\n title={tr(\"auth.register.checkEmail\", \"Check your email\")}\n subtitle={tr(\n \"auth.register.verificationLinkSent\",\n \"We've sent you a verification link\",\n )}\n >\n <div className=\"flex flex-col items-center justify-center py-6\">\n {/* Success Icon */}\n <div className=\"size-20 rounded-full bg-status-success-bg-subtle flex items-center justify-center mb-6\">\n <svg\n className=\"size-10 text-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=\"M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z\"\n />\n </svg>\n </div>\n\n <h3 className=\"text-lg font-semibold text-[var(--color-text-primary)] mb-2\">\n {tr(\n \"auth.register.accountCreated\",\n \"Account created successfully!\",\n )}\n </h3>\n\n <p className=\"text-center text-[var(--color-text-secondary)] mb-8 max-w-sm\">\n {verificationEmailPrefix}\n <span className=\"font-medium text-[var(--color-text-primary)]\">\n {formData.email}\n </span>\n {verificationEmailSuffix}\n </p>\n\n <div className=\"space-y-3 w-full\">\n <Button\n type=\"button\"\n onClick={() => navigate(\"/auth/login\")}\n variant=\"default\"\n size=\"lg\"\n className=\"w-full font-semibold\"\n >\n {tr(\"auth.register.continueToSignIn\", \"Continue to sign in\")}\n </Button>\n\n {resendError && (\n <p className=\"text-center text-sm text-status-error-text\">\n {resendError}\n </p>\n )}\n <p className=\"text-center text-sm text-[var(--color-text-tertiary)]\">\n {tr(\n \"auth.register.didntReceiveEmail\",\n \"Didn't receive the email? Check your spam folder or\",\n )}{\" \"}\n <button\n type=\"button\"\n disabled={isResendingVerification}\n onClick={async () => {\n setResendError(null);\n setIsResendingVerification(true);\n try {\n await resendVerification(formData.email);\n } catch (err) {\n setResendError(\n err instanceof Error\n ? err.message\n : tr(\n \"auth.register.resendFailed\",\n \"Failed to resend verification email\",\n ),\n );\n } finally {\n setIsResendingVerification(false);\n }\n }}\n className=\"text-text-link hover:text-text-linkHover font-medium disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isResendingVerification\n ? tr(\"auth.register.resending\", \"resending...\")\n : tr(\"auth.register.tryAgain\", \"try again\")}\n </button>\n </p>\n </div>\n </div>\n </AuthLayout>\n );\n }\n\n return (\n <AuthLayout\n title={tr(\"auth.register.createAccount\", \"Create your account\")}\n subtitle={tr(\n \"auth.register.startJourney\",\n \"Start your journey with {{productName}}\",\n ).replace(\"{{productName}}\", productName || \"Burdenoff Workspaces\")}\n productName={productName}\n >\n <PagePurpose className=\"mb-5\">\n {tr(\n \"auth.register.purpose\",\n \"Create your account to get a personal workspace and start collaborating. You'll verify your email, then you can sign in and invite your team.\",\n )}\n </PagePurpose>\n <form onSubmit={handleSubmit} className=\"space-y-5\">\n {/* Deactivated account → offer self-service reactivation */}\n {isAccountDeactivated && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl bg-status-warning-bg-subtle border border-status-warning-border\">\n <AlertIcon className=\"size-5 text-status-warning-text flex-shrink-0 mt-0.5\" />\n <div className=\"space-y-2\">\n <p className=\"text-sm font-medium text-status-warning-text\">\n {tr(\n \"auth.register.accountDeactivated\",\n \"This email belongs to a deactivated account.\",\n )}\n </p>\n <p className=\"text-xs text-status-warning-text/80\">\n {tr(\n \"auth.register.accountDeactivatedHelp\",\n \"If this is your account, you can reactivate it instead of creating a new one.\",\n )}\n </p>\n <button\n type=\"button\"\n onClick={() =>\n navigate(\"/auth/reactivate\", {\n state: { email: formData.email },\n })\n }\n className=\"text-sm font-semibold text-status-warning-text underline underline-offset-2\"\n >\n {tr(\"auth.register.reactivateCta\", \"Reactivate my account\")}\n </button>\n </div>\n </div>\n )}\n\n {/* Admin-removed / suspended → point to support (no self-service path) */}\n {(isAccountRemoved || isAccountSuspended) && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl bg-status-warning-bg-subtle border border-status-warning-border\">\n <AlertIcon className=\"size-5 text-status-warning-text flex-shrink-0 mt-0.5\" />\n <p className=\"text-sm text-status-warning-text\">{error}</p>\n </div>\n )}\n\n {/* Generic error / validation alert */}\n {((error &&\n !isAccountDeactivated &&\n !isAccountRemoved &&\n !isAccountSuspended) ||\n validationErrors.length > 0) && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl bg-status-error-bg-subtle border border-status-error-border\">\n <AlertIcon className=\"size-5 text-status-error-text flex-shrink-0 mt-0.5\" />\n <div className=\"space-y-1\">\n {error &&\n !isAccountDeactivated &&\n !isAccountRemoved &&\n !isAccountSuspended && (\n <p className=\"text-sm text-status-error-text\">{error}</p>\n )}\n {validationErrors.map((err) => (\n <p key={err} className=\"text-sm text-status-error-text\">\n {err}\n </p>\n ))}\n </div>\n </div>\n )}\n\n {/* Name Fields */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-2\">\n <label\n htmlFor=\"firstName\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.register.firstName\", \"First Name\")}\n </label>\n <input\n id=\"firstName\"\n type=\"text\"\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.register.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 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.register.firstNamePlaceholder\",\n \"Enter first name\",\n )}\n />\n </div>\n <div className=\"space-y-2\">\n <label\n htmlFor=\"lastName\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.register.lastName\", \"Last Name\")}\n </label>\n <input\n id=\"lastName\"\n type=\"text\"\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.register.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 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.register.lastNamePlaceholder\",\n \"Enter last name\",\n )}\n />\n </div>\n </div>\n\n {/* Username Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"username\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.register.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 className=\"size-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"username\"\n type=\"text\"\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.register.username\", \"Username\")}\n autoComplete=\"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 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.register.usernamePlaceholder\",\n \"Choose a username\",\n )}\n />\n </div>\n </div>\n\n {/* Email Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"email\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.register.email\", \"Email\")}\n </label>\n <div className=\"relative\">\n <div className=\"absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none\">\n <EmailIcon className=\"size-5 text-[var(--color-text-tertiary)]\" />\n </div>\n <input\n id=\"email\"\n type=\"email\"\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.register.email\", \"Email\")}\n autoComplete=\"email\"\n value={formData.email}\n onChange={(e) =>\n setFormData({ ...formData, email: e.target.value })\n }\n className=\"block w-full pl-[3.5rem] pr-4 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all\"\n placeholder={tr(\n \"auth.register.emailPlaceholder\",\n \"you@example.com\",\n )}\n />\n </div>\n </div>\n\n {/* Password Input */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"password\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)]\"\n >\n {tr(\"auth.register.password\", \"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=\"password\"\n type={showPassword ? \"text\" : \"password\"}\n required\n aria-required=\"true\"\n aria-label={tr(\"auth.register.password\", \"Password\")}\n autoComplete=\"new-password\"\n value={formData.password}\n onChange={(e) =>\n setFormData({ ...formData, password: e.target.value })\n }\n className=\"block w-full pl-[3.5rem] pr-12 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all\"\n placeholder={tr(\n \"auth.register.passwordPlaceholder\",\n \"Create a strong password\",\n )}\n />\n <button\n type=\"button\"\n onClick={() => setShowPassword(!showPassword)}\n aria-label={\n showPassword\n ? tr(\"auth.register.hidePassword\", \"Hide password\")\n : tr(\"auth.register.showPassword\", \"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 {showPassword ? (\n <EyeOffIcon className=\"size-5\" />\n ) : (\n <EyeIcon className=\"size-5\" />\n )}\n </button>\n </div>\n\n {/* Password Strength Indicator */}\n {formData.password && (\n <div className=\"space-y-3 pt-2\">\n {/* Strength Bar */}\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(\"auth.register.passwordStrength\", \"Password strength:\")}{\" \"}\n <span className=\"font-medium\">{passwordStrength.level}</span>\n </p>\n </div>\n\n {/* Requirements Checklist */}\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-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 {/* Terms Agreement */}\n <p className=\"text-xs text-[var(--color-text-tertiary)] leading-relaxed\">\n {tr(\n \"auth.register.agreeToTerms\",\n \"By creating an account, you agree to our\",\n )}{\" \"}\n {/* These link to the PUBLIC marketing site, not an app route: the\n reader is signed out, and the app's /legal/:slug route sits behind\n the shell's auth guard. Opened in a new tab so a half-completed\n signup form is not discarded to read the terms. */}\n <a\n href={resolveLegalUrl(\"/terms\")}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"text-text-link underline underline-offset-2\"\n >\n {tr(\"auth.register.termsOfService\", \"Terms of Service\")}\n </a>{\" \"}\n {tr(\"auth.register.and\", \"and\")}{\" \"}\n <a\n href={resolveLegalUrl(\"/privacy\")}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"text-text-link underline underline-offset-2\"\n >\n {tr(\"auth.register.privacyPolicy\", \"Privacy Policy\")}\n </a>\n .\n </p>\n\n {/* reCAPTCHA v2 Checkbox — enabled per-product via the platform catalog.\n `key` remounts it after a failed submit so a fresh token is issued. */}\n <CaptchaV2\n key={captchaResetKey}\n enabled={captchaEnabled}\n siteKey={captchaSiteKey}\n onVerify={setCaptchaToken}\n onExpired={() => setCaptchaToken(null)}\n />\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>\n {tr(\"auth.register.creatingAccount\", \"Creating account...\")}\n </span>\n </>\n ) : (\n tr(\"auth.register.createAccountBtn\", \"Create account\")\n )}\n </Button>\n </form>\n\n {/* Divider */}\n <div className=\"relative my-8\">\n <div className=\"absolute inset-0 flex items-center\">\n <div className=\"w-full border-t border-[var(--color-border-default)]\" />\n </div>\n <div className=\"relative flex justify-center\">\n <span className=\"px-4 bg-[var(--color-bg-canvas)] text-sm text-[var(--color-text-tertiary)]\">\n {tr(\"auth.register.orSignUpWith\", \"or sign up with\")}\n </span>\n </div>\n </div>\n\n {/* OAuth Error */}\n {oauthError && (\n <div className=\"flex items-start gap-3 p-4 rounded-xl border bg-status-error-bg-subtle border-status-error-border\">\n <AlertIcon className=\"size-5 flex-shrink-0 mt-0.5 text-status-error-text\" />\n <p className=\"text-sm text-status-error-text\">{oauthError}</p>\n </div>\n )}\n\n {/* OAuth Buttons */}\n <div className=\"grid grid-cols-3 gap-3\">\n {oauthProviders.map((provider) => (\n <button\n key={provider.name}\n type=\"button\"\n onClick={provider.onClick}\n disabled={provider.disabled}\n title={provider.tooltip}\n className={`flex items-center justify-center gap-2 px-4 py-3 rounded-xl border transition-all duration-200 group ${\n provider.disabled\n ? \"border-[var(--color-border-subtle)] bg-[var(--color-bg-disabled)] cursor-not-allowed opacity-50\"\n : \"border-[var(--color-border-default)] bg-[var(--color-bg-surface)] hover:bg-[var(--color-bg-sunken)] hover:border-[var(--color-border-strong)]\"\n }`}\n >\n <provider.icon\n className={`size-5 ${provider.disabled ? \"opacity-50\" : \"\"}`}\n />\n <span\n className={`sr-only sm:not-sr-only text-sm font-medium transition-colors ${\n provider.disabled\n ? \"text-[var(--color-text-disabled)]\"\n : \"text-[var(--color-text-secondary)] group-hover:text-[var(--color-text-primary)]\"\n }`}\n >\n {provider.name}\n </span>\n </button>\n ))}\n </div>\n\n {/* Sign In Link */}\n <p className=\"mt-8 text-center text-sm text-[var(--color-text-secondary)]\">\n {tr(\"auth.register.alreadyHaveAccount\", \"Already have an account?\")}{\" \"}\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.register.signIn\", \"Sign in\")}\n </button>\n </p>\n </AuthLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public legal-document URLs for the signup consent copy.
|
|
3
|
+
*
|
|
4
|
+
* The signup form is shown to SIGNED-OUT visitors, so its "Terms of Service" and
|
|
5
|
+
* "Privacy Policy" links must resolve to the public marketing site. They used to
|
|
6
|
+
* be written as the app-relative paths `/legal/terms` and `/legal/privacy`, which
|
|
7
|
+
* resolved against the app origin (e.g. `https://app.fluidgrids.ai/legal/terms`).
|
|
8
|
+
* That route lives *inside* the shell's authenticated block, so a visitor who
|
|
9
|
+
* clicked it was bounced to `/auth/login?returnTo=%2Flegal%2Fterms` and the shell
|
|
10
|
+
* footer fired two FORBIDDEN queries on the way out:
|
|
11
|
+
*
|
|
12
|
+
* [Burdenoff backend error] You do not have permission to do that.
|
|
13
|
+
* {code: FORBIDDEN, operationName: GetFooterContentPages}
|
|
14
|
+
* {code: FORBIDDEN, operationName: GetFooterProduct}
|
|
15
|
+
*
|
|
16
|
+
* i.e. the consent links were unreadable for exactly the people being asked to
|
|
17
|
+
* consent. The marketing site is also the authoritative home of the policy text
|
|
18
|
+
* (rendered from the dated drafts in `fluidgrids-website/src/content/legal/`),
|
|
19
|
+
* so linking there avoids maintaining a second copy that can drift.
|
|
20
|
+
*
|
|
21
|
+
* microfe-auth is mounted by every Burdenoff product shell, so the origin is
|
|
22
|
+
* DERIVED from the current host rather than hardcoded — hardcoding one product's
|
|
23
|
+
* domain would send every other product's users to the wrong company's terms.
|
|
24
|
+
* Every product serves its marketing site at `www.<product-domain>` using the
|
|
25
|
+
* same `/terms` and `/privacy` route names (verified against fluidgrids.ai,
|
|
26
|
+
* burdenoff.com, bigconsole.com, planmagnet.com, adaptercloud.com, botlit.ai and
|
|
27
|
+
* timecampus.com).
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the public marketing origin for the product currently being served,
|
|
31
|
+
* or `null` when it cannot be determined (local development, or a host with no
|
|
32
|
+
* registrable domain). Exported for unit testing; prefer `resolveLegalUrl`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveMarketingOrigin(hostname?: string): string | null;
|
|
35
|
+
/** Public legal pages, by the route name every product marketing site uses. */
|
|
36
|
+
export type LegalDocumentPath = "/terms" | "/privacy";
|
|
37
|
+
/**
|
|
38
|
+
* Absolute URL of a public legal document for the current product.
|
|
39
|
+
*
|
|
40
|
+
* Falls back to the bare path when the marketing origin cannot be resolved
|
|
41
|
+
* (local development), which keeps the link valid-looking in dev without
|
|
42
|
+
* inventing a production domain.
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolveLegalUrl(path: LegalDocumentPath): string;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/auth/utils/legalUrls.ts
|
|
2
|
+
var e = [
|
|
3
|
+
"app.",
|
|
4
|
+
"alphaapp.",
|
|
5
|
+
"admin.",
|
|
6
|
+
"alphaadmin.",
|
|
7
|
+
"www."
|
|
8
|
+
];
|
|
9
|
+
function t(t) {
|
|
10
|
+
let n = (t ?? (typeof window < "u" ? window.location.hostname : "")).toLowerCase().trim();
|
|
11
|
+
if (!n || n === "localhost" || n === "127.0.0.1" || n === "0.0.0.0" || n.startsWith("[") || n.endsWith(".local.burdenoff.com") || /^\d+\.\d+\.\d+\.\d+$/.test(n)) return null;
|
|
12
|
+
let r = n;
|
|
13
|
+
for (let t of e) if (r.startsWith(t)) {
|
|
14
|
+
r = r.slice(t.length);
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
return r.includes(".") ? `https://www.${r}` : null;
|
|
18
|
+
}
|
|
19
|
+
function n(e) {
|
|
20
|
+
let n = t();
|
|
21
|
+
return n ? `${n}${e}` : e;
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { n as resolveLegalUrl };
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=legalUrls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legalUrls.js","names":[],"sources":["../../../src/auth/utils/legalUrls.ts"],"sourcesContent":["/**\n * Public legal-document URLs for the signup consent copy.\n *\n * The signup form is shown to SIGNED-OUT visitors, so its \"Terms of Service\" and\n * \"Privacy Policy\" links must resolve to the public marketing site. They used to\n * be written as the app-relative paths `/legal/terms` and `/legal/privacy`, which\n * resolved against the app origin (e.g. `https://app.fluidgrids.ai/legal/terms`).\n * That route lives *inside* the shell's authenticated block, so a visitor who\n * clicked it was bounced to `/auth/login?returnTo=%2Flegal%2Fterms` and the shell\n * footer fired two FORBIDDEN queries on the way out:\n *\n * [Burdenoff backend error] You do not have permission to do that.\n * {code: FORBIDDEN, operationName: GetFooterContentPages}\n * {code: FORBIDDEN, operationName: GetFooterProduct}\n *\n * i.e. the consent links were unreadable for exactly the people being asked to\n * consent. The marketing site is also the authoritative home of the policy text\n * (rendered from the dated drafts in `fluidgrids-website/src/content/legal/`),\n * so linking there avoids maintaining a second copy that can drift.\n *\n * microfe-auth is mounted by every Burdenoff product shell, so the origin is\n * DERIVED from the current host rather than hardcoded — hardcoding one product's\n * domain would send every other product's users to the wrong company's terms.\n * Every product serves its marketing site at `www.<product-domain>` using the\n * same `/terms` and `/privacy` route names (verified against fluidgrids.ai,\n * burdenoff.com, bigconsole.com, planmagnet.com, adaptercloud.com, botlit.ai and\n * timecampus.com).\n */\n\n/**\n * Shell subdomains that front a product's app surface. Stripping one of these\n * leaves the bare product domain whose `www.` host serves the marketing site.\n */\nconst APP_HOST_PREFIXES = [\n \"app.\",\n \"alphaapp.\",\n \"admin.\",\n \"alphaadmin.\",\n \"www.\",\n];\n\n/**\n * Resolve the public marketing origin for the product currently being served,\n * or `null` when it cannot be determined (local development, or a host with no\n * registrable domain). Exported for unit testing; prefer `resolveLegalUrl`.\n */\nexport function resolveMarketingOrigin(hostname?: string): string | null {\n const host = (\n hostname ?? (typeof window !== \"undefined\" ? window.location.hostname : \"\")\n )\n .toLowerCase()\n .trim();\n\n if (!host) return null;\n\n // Local development has no marketing site to link to. `0.0.0.0` and IPv6\n // literals (which `location.hostname` reports bracketed, e.g. \"[::1]\") are\n // included because a bare dotted-quad would otherwise survive the \"has a dot\"\n // check below and produce a nonsense origin like `https://www.0.0.0.0`.\n if (\n host === \"localhost\" ||\n host === \"127.0.0.1\" ||\n host === \"0.0.0.0\" ||\n host.startsWith(\"[\") ||\n host.endsWith(\".local.burdenoff.com\")\n ) {\n return null;\n }\n\n // Any other IPv4 literal is likewise not a registrable domain.\n if (/^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(host)) return null;\n\n let domain = host;\n for (const prefix of APP_HOST_PREFIXES) {\n if (domain.startsWith(prefix)) {\n domain = domain.slice(prefix.length);\n break;\n }\n }\n\n // A bare hostname with no dot is not a registrable domain we can build on.\n if (!domain.includes(\".\")) return null;\n\n return `https://www.${domain}`;\n}\n\n/** Public legal pages, by the route name every product marketing site uses. */\nexport type LegalDocumentPath = \"/terms\" | \"/privacy\";\n\n/**\n * Absolute URL of a public legal document for the current product.\n *\n * Falls back to the bare path when the marketing origin cannot be resolved\n * (local development), which keeps the link valid-looking in dev without\n * inventing a production domain.\n */\nexport function resolveLegalUrl(path: LegalDocumentPath): string {\n const origin = resolveMarketingOrigin();\n return origin ? `${origin}${path}` : path;\n}\n"],"mappings":";AAiCA,IAAM,IAAoB;CACxB;CACA;CACA;CACA;CACA;CACD;AAOD,SAAgB,EAAuB,GAAkC;CACvE,IAAM,KACJ,MAAa,OAAO,SAAW,MAAc,OAAO,SAAS,WAAW,KAEvE,aAAa,CACb,MAAM;AAmBT,KAjBI,CAAC,KAOH,MAAS,eACT,MAAS,eACT,MAAS,aACT,EAAK,WAAW,IAAI,IACpB,EAAK,SAAS,uBAAuB,IAMnC,uBAAuB,KAAK,EAAK,CAAE,QAAO;CAE9C,IAAI,IAAS;AACb,MAAK,IAAM,KAAU,EACnB,KAAI,EAAO,WAAW,EAAO,EAAE;AAC7B,MAAS,EAAO,MAAM,EAAO,OAAO;AACpC;;AAOJ,QAFK,EAAO,SAAS,IAAI,GAElB,eAAe,MAFY;;AAepC,SAAgB,EAAgB,GAAiC;CAC/D,IAAM,IAAS,GAAwB;AACvC,QAAO,IAAS,GAAG,IAAS,MAAS"}
|