@stackframe/stack 2.6.11 → 2.6.12
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/CHANGELOG.md +10 -0
- package/dist/components-page/account-settings.js +287 -103
- package/dist/components-page/account-settings.js.map +1 -1
- package/dist/esm/components-page/account-settings.js +288 -104
- package/dist/esm/components-page/account-settings.js.map +1 -1
- package/dist/esm/generated/global-css.js +1 -1
- package/dist/esm/generated/global-css.js.map +1 -1
- package/dist/esm/generated/quetzal-translations.js +2137 -1813
- package/dist/esm/generated/quetzal-translations.js.map +1 -1
- package/dist/esm/lib/stack-app.js +132 -11
- package/dist/esm/lib/stack-app.js.map +1 -1
- package/dist/generated/global-css.d.mts +1 -1
- package/dist/generated/global-css.d.ts +1 -1
- package/dist/generated/global-css.js +1 -1
- package/dist/generated/global-css.js.map +1 -1
- package/dist/generated/quetzal-translations.d.mts +2 -2
- package/dist/generated/quetzal-translations.d.ts +2 -2
- package/dist/generated/quetzal-translations.js +2137 -1813
- package/dist/generated/quetzal-translations.js.map +1 -1
- package/dist/lib/stack-app.d.mts +51 -17
- package/dist/lib/stack-app.d.ts +51 -17
- package/dist/lib/stack-app.js +132 -11
- package/dist/lib/stack-app.js.map +1 -1
- package/package.json +4 -4
|
@@ -9,7 +9,7 @@ import { yupObject, yupString } from "@stackframe/stack-shared/dist/schema-field
|
|
|
9
9
|
import { generateRandomValues } from "@stackframe/stack-shared/dist/utils/crypto";
|
|
10
10
|
import { throwErr } from "@stackframe/stack-shared/dist/utils/errors";
|
|
11
11
|
import { runAsynchronously, runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises";
|
|
12
|
-
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Button, Input, Label, PasswordInput, Separator, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Typography } from "@stackframe/stack-ui";
|
|
12
|
+
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, ActionCell, Badge, Button, Input, Label, PasswordInput, Separator, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Typography } from "@stackframe/stack-ui";
|
|
13
13
|
import { CirclePlus, Contact, Edit, Settings, ShieldCheck } from "lucide-react";
|
|
14
14
|
import { useRouter } from "next/navigation";
|
|
15
15
|
import { TOTPController, createTOTPKeyURI } from "oslo/otp";
|
|
@@ -44,11 +44,11 @@ function AccountSettings(props) {
|
|
|
44
44
|
content: /* @__PURE__ */ jsx(ProfilePage, {})
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
|
-
title: t("
|
|
47
|
+
title: t("Emails & Auth"),
|
|
48
48
|
type: "item",
|
|
49
|
-
id: "
|
|
49
|
+
id: "auth",
|
|
50
50
|
icon: ShieldCheck,
|
|
51
|
-
content: /* @__PURE__ */ jsx(
|
|
51
|
+
content: /* @__PURE__ */ jsx(EmailsAndAuthPage, {})
|
|
52
52
|
},
|
|
53
53
|
{
|
|
54
54
|
title: t("Settings"),
|
|
@@ -145,54 +145,227 @@ function ProfilePage() {
|
|
|
145
145
|
)
|
|
146
146
|
] });
|
|
147
147
|
}
|
|
148
|
-
function
|
|
149
|
-
const
|
|
148
|
+
function EmailsSection() {
|
|
149
|
+
const { t } = useTranslation();
|
|
150
|
+
const user = useUser({ or: "redirect" });
|
|
151
|
+
const contactChannels = user.useContactChannels();
|
|
152
|
+
const [addingEmail, setAddingEmail] = useState(contactChannels.length === 0);
|
|
153
|
+
const [addingEmailLoading, setAddingEmailLoading] = useState(false);
|
|
154
|
+
const [addedEmail, setAddedEmail] = useState(null);
|
|
155
|
+
const isLastEmail = contactChannels.filter((x) => x.usedForAuth && x.type === "email").length === 1;
|
|
156
|
+
useEffect(() => {
|
|
157
|
+
if (addedEmail) {
|
|
158
|
+
runAsynchronously(async () => {
|
|
159
|
+
const cc = contactChannels.find((x) => x.value === addedEmail);
|
|
160
|
+
if (cc && !cc.isVerified) {
|
|
161
|
+
await cc.sendVerificationEmail();
|
|
162
|
+
}
|
|
163
|
+
setAddedEmail(null);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}, [contactChannels, addedEmail]);
|
|
167
|
+
const emailSchema = yupObject({
|
|
168
|
+
email: yupString().email(t("Please enter a valid email address")).notOneOf(contactChannels.map((x) => x.value), t("Email already exists")).required(t("Email is required"))
|
|
169
|
+
});
|
|
170
|
+
const { register, handleSubmit, formState: { errors }, reset } = useForm({
|
|
171
|
+
resolver: yupResolver(emailSchema)
|
|
172
|
+
});
|
|
173
|
+
const onSubmit = async (data) => {
|
|
174
|
+
setAddingEmailLoading(true);
|
|
175
|
+
try {
|
|
176
|
+
await user.createContactChannel({ type: "email", value: data.email, usedForAuth: false });
|
|
177
|
+
setAddedEmail(data.email);
|
|
178
|
+
} finally {
|
|
179
|
+
setAddingEmailLoading(false);
|
|
180
|
+
}
|
|
181
|
+
setAddingEmail(false);
|
|
182
|
+
reset();
|
|
183
|
+
};
|
|
184
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
185
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col md:flex-row justify-between mb-4 gap-4", children: [
|
|
186
|
+
/* @__PURE__ */ jsx(Typography, { className: "font-medium", children: t("Emails") }),
|
|
187
|
+
addingEmail ? /* @__PURE__ */ jsxs(
|
|
188
|
+
"form",
|
|
189
|
+
{
|
|
190
|
+
onSubmit: (e) => {
|
|
191
|
+
e.preventDefault();
|
|
192
|
+
runAsynchronously(handleSubmit(onSubmit));
|
|
193
|
+
},
|
|
194
|
+
className: "flex flex-col",
|
|
195
|
+
children: [
|
|
196
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
197
|
+
/* @__PURE__ */ jsx(
|
|
198
|
+
Input,
|
|
199
|
+
{
|
|
200
|
+
...register("email"),
|
|
201
|
+
placeholder: t("Enter email")
|
|
202
|
+
}
|
|
203
|
+
),
|
|
204
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", loading: addingEmailLoading, children: t("Add") }),
|
|
205
|
+
/* @__PURE__ */ jsx(
|
|
206
|
+
Button,
|
|
207
|
+
{
|
|
208
|
+
variant: "secondary",
|
|
209
|
+
onClick: () => {
|
|
210
|
+
setAddingEmail(false);
|
|
211
|
+
reset();
|
|
212
|
+
},
|
|
213
|
+
children: t("Cancel")
|
|
214
|
+
}
|
|
215
|
+
)
|
|
216
|
+
] }),
|
|
217
|
+
errors.email && /* @__PURE__ */ jsx(FormWarningText, { text: errors.email.message })
|
|
218
|
+
]
|
|
219
|
+
}
|
|
220
|
+
) : /* @__PURE__ */ jsx("div", { className: "flex md:justify-end", children: /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => setAddingEmail(true), children: t("Add an email") }) })
|
|
221
|
+
] }),
|
|
222
|
+
contactChannels.length > 0 ? /* @__PURE__ */ jsx("div", { className: "border rounded-md", children: /* @__PURE__ */ jsx(Table, { children: /* @__PURE__ */ jsx(TableBody, { children: contactChannels.filter((x) => x.type === "email").sort((a, b) => {
|
|
223
|
+
if (a.isPrimary !== b.isPrimary) return a.isPrimary ? -1 : 1;
|
|
224
|
+
if (a.isVerified !== b.isVerified) return a.isVerified ? -1 : 1;
|
|
225
|
+
return 0;
|
|
226
|
+
}).map((x) => /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
227
|
+
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col md:flex-row gap-2 md:gap-4", children: [
|
|
228
|
+
x.value,
|
|
229
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
230
|
+
x.isPrimary ? /* @__PURE__ */ jsx(Badge, { children: t("Primary") }) : null,
|
|
231
|
+
!x.isVerified ? /* @__PURE__ */ jsx(Badge, { variant: "destructive", children: t("Unverified") }) : null,
|
|
232
|
+
x.usedForAuth ? /* @__PURE__ */ jsx(Badge, { variant: "outline", children: t("Used for sign-in") }) : null
|
|
233
|
+
] })
|
|
234
|
+
] }) }),
|
|
235
|
+
/* @__PURE__ */ jsx(TableCell, { className: "flex justify-end", children: /* @__PURE__ */ jsx(ActionCell, { items: [
|
|
236
|
+
...!x.isVerified ? [{
|
|
237
|
+
item: t("Send verification email"),
|
|
238
|
+
onClick: async () => {
|
|
239
|
+
await x.sendVerificationEmail();
|
|
240
|
+
}
|
|
241
|
+
}] : [],
|
|
242
|
+
...!x.isPrimary && x.isVerified ? [{
|
|
243
|
+
item: t("Set as primary"),
|
|
244
|
+
onClick: async () => {
|
|
245
|
+
await x.update({ isPrimary: true });
|
|
246
|
+
}
|
|
247
|
+
}] : !x.isPrimary ? [{
|
|
248
|
+
item: t("Set as primary"),
|
|
249
|
+
onClick: async () => {
|
|
250
|
+
},
|
|
251
|
+
disabled: true,
|
|
252
|
+
disabledTooltip: t("Please verify your email first")
|
|
253
|
+
}] : [],
|
|
254
|
+
...!x.usedForAuth && x.isVerified ? [{
|
|
255
|
+
item: t("Use for sign-in"),
|
|
256
|
+
onClick: async () => {
|
|
257
|
+
await x.update({ usedForAuth: true });
|
|
258
|
+
}
|
|
259
|
+
}] : [],
|
|
260
|
+
...x.usedForAuth && !isLastEmail ? [{
|
|
261
|
+
item: t("Stop using for sign-in"),
|
|
262
|
+
onClick: async () => {
|
|
263
|
+
await x.update({ usedForAuth: false });
|
|
264
|
+
}
|
|
265
|
+
}] : x.usedForAuth ? [{
|
|
266
|
+
item: t("Stop using for sign-in"),
|
|
267
|
+
onClick: async () => {
|
|
268
|
+
},
|
|
269
|
+
disabled: true,
|
|
270
|
+
disabledTooltip: t("You can not remove your last sign-in email")
|
|
271
|
+
}] : [],
|
|
272
|
+
...!isLastEmail || !x.usedForAuth ? [{
|
|
273
|
+
item: t("Remove"),
|
|
274
|
+
onClick: async () => {
|
|
275
|
+
await x.delete();
|
|
276
|
+
},
|
|
277
|
+
danger: true
|
|
278
|
+
}] : [{
|
|
279
|
+
item: t("Remove"),
|
|
280
|
+
onClick: async () => {
|
|
281
|
+
},
|
|
282
|
+
disabled: true,
|
|
283
|
+
disabledTooltip: t("You can not remove your last sign-in email")
|
|
284
|
+
}]
|
|
285
|
+
] }) })
|
|
286
|
+
] }, x.id)) }) }) }) : null
|
|
287
|
+
] });
|
|
288
|
+
}
|
|
289
|
+
function EmailsAndAuthPage() {
|
|
150
290
|
const passwordSection = usePasswordSection();
|
|
151
291
|
const mfaSection = useMfaSection();
|
|
292
|
+
const otpSection = useOtpSection();
|
|
152
293
|
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
|
153
|
-
|
|
294
|
+
/* @__PURE__ */ jsx(EmailsSection, {}),
|
|
154
295
|
passwordSection,
|
|
296
|
+
otpSection,
|
|
155
297
|
mfaSection
|
|
156
298
|
] });
|
|
157
299
|
}
|
|
158
|
-
function
|
|
159
|
-
const deleteAccountSection = useDeleteAccountSection();
|
|
160
|
-
const signOutSection = useSignOutSection();
|
|
161
|
-
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
|
162
|
-
deleteAccountSection,
|
|
163
|
-
signOutSection
|
|
164
|
-
] });
|
|
165
|
-
}
|
|
166
|
-
function useEmailVerificationSection() {
|
|
300
|
+
function useOtpSection() {
|
|
167
301
|
const { t } = useTranslation();
|
|
168
|
-
const user = useUser({ or: "
|
|
169
|
-
const
|
|
170
|
-
|
|
302
|
+
const user = useUser({ or: "throw" });
|
|
303
|
+
const project = useStackApp().useProject();
|
|
304
|
+
const contactChannels = user.useContactChannels();
|
|
305
|
+
const isLastAuth = user.otpAuthEnabled && !user.hasPassword && user.oauthProviders.length === 0;
|
|
306
|
+
const [disabling, setDisabling] = useState(false);
|
|
307
|
+
const hasValidEmail = contactChannels.filter((x) => x.type === "email" && x.isVerified && x.usedForAuth).length > 0;
|
|
308
|
+
if (!project.config.magicLinkEnabled) {
|
|
171
309
|
return null;
|
|
172
310
|
}
|
|
173
|
-
|
|
174
|
-
|
|
311
|
+
const handleDisableOTP = async () => {
|
|
312
|
+
await user.update({ otpAuthEnabled: false });
|
|
313
|
+
setDisabling(false);
|
|
314
|
+
};
|
|
315
|
+
return /* @__PURE__ */ jsx(Section, { title: t("OTP sign-in"), description: user.otpAuthEnabled ? t("OTP/magic link sign-in is currently enabled.") : t("Enable sign-in via magic link or OTP sent to your sign-in emails."), children: /* @__PURE__ */ jsx("div", { className: "flex md:justify-end", children: hasValidEmail ? user.otpAuthEnabled ? !isLastAuth ? !disabling ? /* @__PURE__ */ jsx(
|
|
316
|
+
Button,
|
|
175
317
|
{
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
children:
|
|
318
|
+
variant: "secondary",
|
|
319
|
+
onClick: () => setDisabling(true),
|
|
320
|
+
children: t("Disable OTP")
|
|
321
|
+
}
|
|
322
|
+
) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
323
|
+
/* @__PURE__ */ jsx(Typography, { variant: "destructive", children: t("Are you sure you want to disable OTP sign-in? You will not be able to sign in with only emails anymore.") }),
|
|
324
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
325
|
+
/* @__PURE__ */ jsx(
|
|
179
326
|
Button,
|
|
180
327
|
{
|
|
181
|
-
|
|
182
|
-
onClick:
|
|
183
|
-
|
|
184
|
-
setEmailSent(true);
|
|
185
|
-
},
|
|
186
|
-
children: emailSent ? t("Email sent!") : t("Send Verification Email")
|
|
328
|
+
variant: "destructive",
|
|
329
|
+
onClick: handleDisableOTP,
|
|
330
|
+
children: t("Disable")
|
|
187
331
|
}
|
|
188
|
-
)
|
|
332
|
+
),
|
|
333
|
+
/* @__PURE__ */ jsx(
|
|
334
|
+
Button,
|
|
335
|
+
{
|
|
336
|
+
variant: "secondary",
|
|
337
|
+
onClick: () => setDisabling(false),
|
|
338
|
+
children: t("Cancel")
|
|
339
|
+
}
|
|
340
|
+
)
|
|
341
|
+
] })
|
|
342
|
+
] }) : /* @__PURE__ */ jsx(Typography, { variant: "secondary", type: "label", children: t("OTP sign-in is enabled and cannot be disabled as it is currently the only sign-in method") }) : /* @__PURE__ */ jsx(
|
|
343
|
+
Button,
|
|
344
|
+
{
|
|
345
|
+
variant: "secondary",
|
|
346
|
+
onClick: async () => {
|
|
347
|
+
await user.update({ otpAuthEnabled: true });
|
|
348
|
+
},
|
|
349
|
+
children: t("Enable OTP")
|
|
189
350
|
}
|
|
190
|
-
);
|
|
351
|
+
) : /* @__PURE__ */ jsx(Typography, { variant: "secondary", type: "label", children: t("To enable OTP sign-in, please add a verified email and set it as your sign-in email.") }) }) });
|
|
352
|
+
}
|
|
353
|
+
function SettingsPage() {
|
|
354
|
+
const deleteAccountSection = useDeleteAccountSection();
|
|
355
|
+
const signOutSection = useSignOutSection();
|
|
356
|
+
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
|
357
|
+
deleteAccountSection,
|
|
358
|
+
signOutSection
|
|
359
|
+
] });
|
|
191
360
|
}
|
|
192
361
|
function usePasswordSection() {
|
|
193
362
|
const { t } = useTranslation();
|
|
363
|
+
const user = useUser({ or: "throw" });
|
|
364
|
+
const contactChannels = user.useContactChannels();
|
|
365
|
+
const [changingPassword, setChangingPassword] = useState(false);
|
|
366
|
+
const [loading, setLoading] = useState(false);
|
|
194
367
|
const passwordSchema = yupObject({
|
|
195
|
-
oldPassword: yupString().required(t("Please enter your old password")),
|
|
368
|
+
oldPassword: user.hasPassword ? yupString().required(t("Please enter your old password")) : yupString(),
|
|
196
369
|
newPassword: yupString().required(t("Please enter your password")).test({
|
|
197
370
|
name: "is-valid-password",
|
|
198
371
|
test: (value, ctx) => {
|
|
@@ -206,23 +379,20 @@ function usePasswordSection() {
|
|
|
206
379
|
}),
|
|
207
380
|
newPasswordRepeat: yupString().nullable().oneOf([yup.ref("newPassword"), "", null], t("Passwords do not match")).required(t("Please repeat your password"))
|
|
208
381
|
});
|
|
209
|
-
const user = useUser({ or: "throw" });
|
|
210
|
-
const [changingPassword, setChangingPassword] = useState(false);
|
|
211
382
|
const { register, handleSubmit, setError, formState: { errors }, clearErrors, reset } = useForm({
|
|
212
383
|
resolver: yupResolver(passwordSchema)
|
|
213
384
|
});
|
|
214
|
-
const
|
|
215
|
-
const [loading, setLoading] = useState(false);
|
|
385
|
+
const hasValidEmail = contactChannels.filter((x) => x.type === "email" && x.isVerified && x.usedForAuth).length > 0;
|
|
216
386
|
const onSubmit = async (data) => {
|
|
217
387
|
setLoading(true);
|
|
218
388
|
try {
|
|
219
389
|
const { oldPassword, newPassword } = data;
|
|
220
|
-
const error = await user.updatePassword({ oldPassword, newPassword });
|
|
390
|
+
const error = user.hasPassword ? await user.updatePassword({ oldPassword, newPassword }) : await user.setPassword({ password: newPassword });
|
|
221
391
|
if (error) {
|
|
222
392
|
setError("oldPassword", { type: "manual", message: t("Incorrect password") });
|
|
223
393
|
} else {
|
|
224
394
|
reset();
|
|
225
|
-
|
|
395
|
+
setChangingPassword(false);
|
|
226
396
|
}
|
|
227
397
|
} finally {
|
|
228
398
|
setLoading(false);
|
|
@@ -230,69 +400,83 @@ function usePasswordSection() {
|
|
|
230
400
|
};
|
|
231
401
|
const registerPassword = register("newPassword");
|
|
232
402
|
const registerPasswordRepeat = register("newPasswordRepeat");
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
403
|
+
return /* @__PURE__ */ jsx(
|
|
404
|
+
Section,
|
|
405
|
+
{
|
|
406
|
+
title: t("Password"),
|
|
407
|
+
description: user.hasPassword ? t("Update your password") : t("Set a password for your account"),
|
|
408
|
+
children: /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-4", children: !changingPassword ? hasValidEmail ? /* @__PURE__ */ jsx(
|
|
409
|
+
Button,
|
|
410
|
+
{
|
|
411
|
+
variant: "secondary",
|
|
412
|
+
onClick: () => setChangingPassword(true),
|
|
413
|
+
children: user.hasPassword ? t("Update password") : t("Set password")
|
|
414
|
+
}
|
|
415
|
+
) : /* @__PURE__ */ jsx(Typography, { variant: "secondary", type: "label", children: t("To set a password, please add a verified email and set it as your sign-in email.") }) : /* @__PURE__ */ jsxs(
|
|
416
|
+
"form",
|
|
417
|
+
{
|
|
418
|
+
onSubmit: (e) => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e)),
|
|
419
|
+
noValidate: true,
|
|
420
|
+
children: [
|
|
421
|
+
user.hasPassword && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
422
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "old-password", className: "mb-1", children: t("Old password") }),
|
|
423
|
+
/* @__PURE__ */ jsx(
|
|
424
|
+
Input,
|
|
425
|
+
{
|
|
426
|
+
id: "old-password",
|
|
427
|
+
type: "password",
|
|
428
|
+
...register("oldPassword")
|
|
429
|
+
}
|
|
430
|
+
),
|
|
431
|
+
/* @__PURE__ */ jsx(FormWarningText, { text: errors.oldPassword?.message?.toString() })
|
|
432
|
+
] }),
|
|
433
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "new-password", className: "mt-4 mb-1", children: t("New password") }),
|
|
434
|
+
/* @__PURE__ */ jsx(
|
|
435
|
+
PasswordInput,
|
|
436
|
+
{
|
|
437
|
+
id: "new-password",
|
|
438
|
+
...registerPassword,
|
|
439
|
+
onChange: (e) => {
|
|
440
|
+
clearErrors("newPassword");
|
|
441
|
+
clearErrors("newPasswordRepeat");
|
|
442
|
+
runAsynchronously(registerPassword.onChange(e));
|
|
443
|
+
}
|
|
273
444
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
445
|
+
),
|
|
446
|
+
/* @__PURE__ */ jsx(FormWarningText, { text: errors.newPassword?.message?.toString() }),
|
|
447
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "repeat-password", className: "mt-4 mb-1", children: t("Repeat new password") }),
|
|
448
|
+
/* @__PURE__ */ jsx(
|
|
449
|
+
PasswordInput,
|
|
450
|
+
{
|
|
451
|
+
id: "repeat-password",
|
|
452
|
+
...registerPasswordRepeat,
|
|
453
|
+
onChange: (e) => {
|
|
454
|
+
clearErrors("newPassword");
|
|
455
|
+
clearErrors("newPasswordRepeat");
|
|
456
|
+
runAsynchronously(registerPasswordRepeat.onChange(e));
|
|
457
|
+
}
|
|
287
458
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
459
|
+
),
|
|
460
|
+
/* @__PURE__ */ jsx(FormWarningText, { text: errors.newPasswordRepeat?.message?.toString() }),
|
|
461
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-6 flex gap-4", children: [
|
|
462
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", loading, children: user.hasPassword ? t("Update Password") : t("Set Password") }),
|
|
463
|
+
/* @__PURE__ */ jsx(
|
|
464
|
+
Button,
|
|
465
|
+
{
|
|
466
|
+
variant: "secondary",
|
|
467
|
+
onClick: () => {
|
|
468
|
+
setChangingPassword(false);
|
|
469
|
+
reset();
|
|
470
|
+
},
|
|
471
|
+
children: t("Cancel")
|
|
472
|
+
}
|
|
473
|
+
)
|
|
474
|
+
] })
|
|
475
|
+
]
|
|
476
|
+
}
|
|
477
|
+
) })
|
|
478
|
+
}
|
|
479
|
+
);
|
|
296
480
|
}
|
|
297
481
|
function useMfaSection() {
|
|
298
482
|
const { t } = useTranslation();
|
|
@@ -323,7 +507,7 @@ function useMfaSection() {
|
|
|
323
507
|
return /* @__PURE__ */ jsx(
|
|
324
508
|
Section,
|
|
325
509
|
{
|
|
326
|
-
title: t("Multi-factor
|
|
510
|
+
title: t("Multi-factor authentication"),
|
|
327
511
|
description: isEnabled ? t("Multi-factor authentication is currently enabled.") : t("Multi-factor authentication is currently disabled."),
|
|
328
512
|
children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
329
513
|
!isEnabled && generatedSecret && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -366,18 +550,18 @@ function useMfaSection() {
|
|
|
366
550
|
totpMultiFactorSecret: null
|
|
367
551
|
});
|
|
368
552
|
},
|
|
369
|
-
children: t("Disable")
|
|
553
|
+
children: t("Disable MFA")
|
|
370
554
|
}
|
|
371
555
|
) : !generatedSecret && /* @__PURE__ */ jsx(
|
|
372
556
|
Button,
|
|
373
557
|
{
|
|
374
|
-
variant: "
|
|
558
|
+
variant: "secondary",
|
|
375
559
|
onClick: async () => {
|
|
376
560
|
const secret = generateRandomValues(new Uint8Array(20));
|
|
377
561
|
setQrCodeUrl(await generateTotpQrCode(project, user, secret));
|
|
378
562
|
setGeneratedSecret(secret);
|
|
379
563
|
},
|
|
380
|
-
children: t("Enable")
|
|
564
|
+
children: t("Enable MFA")
|
|
381
565
|
}
|
|
382
566
|
) })
|
|
383
567
|
] })
|