@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 CHANGED
@@ -1,5 +1,15 @@
1
1
  # @stackframe/stack
2
2
 
3
+ ## 2.6.12
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated account settings page
8
+ - Updated dependencies
9
+ - @stackframe/stack-shared@2.6.12
10
+ - @stackframe/stack-sc@2.6.12
11
+ - @stackframe/stack-ui@2.6.12
12
+
3
13
  ## 2.6.11
4
14
 
5
15
  ### Patch Changes
@@ -80,11 +80,11 @@ function AccountSettings(props) {
80
80
  content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProfilePage, {})
81
81
  },
82
82
  {
83
- title: t("Security"),
83
+ title: t("Emails & Auth"),
84
84
  type: "item",
85
- id: "security",
85
+ id: "auth",
86
86
  icon: import_lucide_react.ShieldCheck,
87
- content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SecurityPage, {})
87
+ content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmailsAndAuthPage, {})
88
88
  },
89
89
  {
90
90
  title: t("Settings"),
@@ -181,54 +181,227 @@ function ProfilePage() {
181
181
  )
182
182
  ] });
183
183
  }
184
- function SecurityPage() {
185
- const emailVerificationSection = useEmailVerificationSection();
184
+ function EmailsSection() {
185
+ const { t } = (0, import_translations.useTranslation)();
186
+ const user = (0, import__.useUser)({ or: "redirect" });
187
+ const contactChannels = user.useContactChannels();
188
+ const [addingEmail, setAddingEmail] = (0, import_react.useState)(contactChannels.length === 0);
189
+ const [addingEmailLoading, setAddingEmailLoading] = (0, import_react.useState)(false);
190
+ const [addedEmail, setAddedEmail] = (0, import_react.useState)(null);
191
+ const isLastEmail = contactChannels.filter((x) => x.usedForAuth && x.type === "email").length === 1;
192
+ (0, import_react.useEffect)(() => {
193
+ if (addedEmail) {
194
+ (0, import_promises.runAsynchronously)(async () => {
195
+ const cc = contactChannels.find((x) => x.value === addedEmail);
196
+ if (cc && !cc.isVerified) {
197
+ await cc.sendVerificationEmail();
198
+ }
199
+ setAddedEmail(null);
200
+ });
201
+ }
202
+ }, [contactChannels, addedEmail]);
203
+ const emailSchema = (0, import_schema_fields.yupObject)({
204
+ email: (0, import_schema_fields.yupString)().email(t("Please enter a valid email address")).notOneOf(contactChannels.map((x) => x.value), t("Email already exists")).required(t("Email is required"))
205
+ });
206
+ const { register, handleSubmit, formState: { errors }, reset } = (0, import_react_hook_form.useForm)({
207
+ resolver: (0, import_yup.yupResolver)(emailSchema)
208
+ });
209
+ const onSubmit = async (data) => {
210
+ setAddingEmailLoading(true);
211
+ try {
212
+ await user.createContactChannel({ type: "email", value: data.email, usedForAuth: false });
213
+ setAddedEmail(data.email);
214
+ } finally {
215
+ setAddingEmailLoading(false);
216
+ }
217
+ setAddingEmail(false);
218
+ reset();
219
+ };
220
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
221
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col md:flex-row justify-between mb-4 gap-4", children: [
222
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { className: "font-medium", children: t("Emails") }),
223
+ addingEmail ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
224
+ "form",
225
+ {
226
+ onSubmit: (e) => {
227
+ e.preventDefault();
228
+ (0, import_promises.runAsynchronously)(handleSubmit(onSubmit));
229
+ },
230
+ className: "flex flex-col",
231
+ children: [
232
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-2", children: [
233
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
234
+ import_stack_ui.Input,
235
+ {
236
+ ...register("email"),
237
+ placeholder: t("Enter email")
238
+ }
239
+ ),
240
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Button, { type: "submit", loading: addingEmailLoading, children: t("Add") }),
241
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
242
+ import_stack_ui.Button,
243
+ {
244
+ variant: "secondary",
245
+ onClick: () => {
246
+ setAddingEmail(false);
247
+ reset();
248
+ },
249
+ children: t("Cancel")
250
+ }
251
+ )
252
+ ] }),
253
+ errors.email && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_form_warning.FormWarningText, { text: errors.email.message })
254
+ ]
255
+ }
256
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex md:justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Button, { variant: "secondary", onClick: () => setAddingEmail(true), children: t("Add an email") }) })
257
+ ] }),
258
+ contactChannels.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "border rounded-md", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Table, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableBody, { children: contactChannels.filter((x) => x.type === "email").sort((a, b) => {
259
+ if (a.isPrimary !== b.isPrimary) return a.isPrimary ? -1 : 1;
260
+ if (a.isVerified !== b.isVerified) return a.isVerified ? -1 : 1;
261
+ return 0;
262
+ }).map((x) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.TableRow, { children: [
263
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col md:flex-row gap-2 md:gap-4", children: [
264
+ x.value,
265
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-2", children: [
266
+ x.isPrimary ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Badge, { children: t("Primary") }) : null,
267
+ !x.isVerified ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Badge, { variant: "destructive", children: t("Unverified") }) : null,
268
+ x.usedForAuth ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Badge, { variant: "outline", children: t("Used for sign-in") }) : null
269
+ ] })
270
+ ] }) }),
271
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { className: "flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.ActionCell, { items: [
272
+ ...!x.isVerified ? [{
273
+ item: t("Send verification email"),
274
+ onClick: async () => {
275
+ await x.sendVerificationEmail();
276
+ }
277
+ }] : [],
278
+ ...!x.isPrimary && x.isVerified ? [{
279
+ item: t("Set as primary"),
280
+ onClick: async () => {
281
+ await x.update({ isPrimary: true });
282
+ }
283
+ }] : !x.isPrimary ? [{
284
+ item: t("Set as primary"),
285
+ onClick: async () => {
286
+ },
287
+ disabled: true,
288
+ disabledTooltip: t("Please verify your email first")
289
+ }] : [],
290
+ ...!x.usedForAuth && x.isVerified ? [{
291
+ item: t("Use for sign-in"),
292
+ onClick: async () => {
293
+ await x.update({ usedForAuth: true });
294
+ }
295
+ }] : [],
296
+ ...x.usedForAuth && !isLastEmail ? [{
297
+ item: t("Stop using for sign-in"),
298
+ onClick: async () => {
299
+ await x.update({ usedForAuth: false });
300
+ }
301
+ }] : x.usedForAuth ? [{
302
+ item: t("Stop using for sign-in"),
303
+ onClick: async () => {
304
+ },
305
+ disabled: true,
306
+ disabledTooltip: t("You can not remove your last sign-in email")
307
+ }] : [],
308
+ ...!isLastEmail || !x.usedForAuth ? [{
309
+ item: t("Remove"),
310
+ onClick: async () => {
311
+ await x.delete();
312
+ },
313
+ danger: true
314
+ }] : [{
315
+ item: t("Remove"),
316
+ onClick: async () => {
317
+ },
318
+ disabled: true,
319
+ disabledTooltip: t("You can not remove your last sign-in email")
320
+ }]
321
+ ] }) })
322
+ ] }, x.id)) }) }) }) : null
323
+ ] });
324
+ }
325
+ function EmailsAndAuthPage() {
186
326
  const passwordSection = usePasswordSection();
187
327
  const mfaSection = useMfaSection();
328
+ const otpSection = useOtpSection();
188
329
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(PageLayout, { children: [
189
- emailVerificationSection,
330
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmailsSection, {}),
190
331
  passwordSection,
332
+ otpSection,
191
333
  mfaSection
192
334
  ] });
193
335
  }
194
- function SettingsPage() {
195
- const deleteAccountSection = useDeleteAccountSection();
196
- const signOutSection = useSignOutSection();
197
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(PageLayout, { children: [
198
- deleteAccountSection,
199
- signOutSection
200
- ] });
201
- }
202
- function useEmailVerificationSection() {
336
+ function useOtpSection() {
203
337
  const { t } = (0, import_translations.useTranslation)();
204
- const user = (0, import__.useUser)({ or: "redirect" });
205
- const [emailSent, setEmailSent] = (0, import_react.useState)(false);
206
- if (!user.primaryEmail) {
338
+ const user = (0, import__.useUser)({ or: "throw" });
339
+ const project = (0, import__.useStackApp)().useProject();
340
+ const contactChannels = user.useContactChannels();
341
+ const isLastAuth = user.otpAuthEnabled && !user.hasPassword && user.oauthProviders.length === 0;
342
+ const [disabling, setDisabling] = (0, import_react.useState)(false);
343
+ const hasValidEmail = contactChannels.filter((x) => x.type === "email" && x.isVerified && x.usedForAuth).length > 0;
344
+ if (!project.config.magicLinkEnabled) {
207
345
  return null;
208
346
  }
209
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
210
- Section,
347
+ const handleDisableOTP = async () => {
348
+ await user.update({ otpAuthEnabled: false });
349
+ setDisabling(false);
350
+ };
351
+ return /* @__PURE__ */ (0, import_jsx_runtime.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__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex md:justify-end", children: hasValidEmail ? user.otpAuthEnabled ? !isLastAuth ? !disabling ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
352
+ import_stack_ui.Button,
211
353
  {
212
- title: t("Email Verification"),
213
- description: t("Verify your email address to secure your account"),
214
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: user.primaryEmailVerified ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "success", children: t("Your email has been verified.") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
354
+ variant: "secondary",
355
+ onClick: () => setDisabling(true),
356
+ children: t("Disable OTP")
357
+ }
358
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-2", children: [
359
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.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.") }),
360
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-2", children: [
361
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
215
362
  import_stack_ui.Button,
216
363
  {
217
- disabled: emailSent,
218
- onClick: async () => {
219
- await user.sendVerificationEmail();
220
- setEmailSent(true);
221
- },
222
- children: emailSent ? t("Email sent!") : t("Send Verification Email")
364
+ variant: "destructive",
365
+ onClick: handleDisableOTP,
366
+ children: t("Disable")
223
367
  }
224
- ) }) })
368
+ ),
369
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
370
+ import_stack_ui.Button,
371
+ {
372
+ variant: "secondary",
373
+ onClick: () => setDisabling(false),
374
+ children: t("Cancel")
375
+ }
376
+ )
377
+ ] })
378
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.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__ */ (0, import_jsx_runtime.jsx)(
379
+ import_stack_ui.Button,
380
+ {
381
+ variant: "secondary",
382
+ onClick: async () => {
383
+ await user.update({ otpAuthEnabled: true });
384
+ },
385
+ children: t("Enable OTP")
225
386
  }
226
- );
387
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.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.") }) }) });
388
+ }
389
+ function SettingsPage() {
390
+ const deleteAccountSection = useDeleteAccountSection();
391
+ const signOutSection = useSignOutSection();
392
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(PageLayout, { children: [
393
+ deleteAccountSection,
394
+ signOutSection
395
+ ] });
227
396
  }
228
397
  function usePasswordSection() {
229
398
  const { t } = (0, import_translations.useTranslation)();
399
+ const user = (0, import__.useUser)({ or: "throw" });
400
+ const contactChannels = user.useContactChannels();
401
+ const [changingPassword, setChangingPassword] = (0, import_react.useState)(false);
402
+ const [loading, setLoading] = (0, import_react.useState)(false);
230
403
  const passwordSchema = (0, import_schema_fields.yupObject)({
231
- oldPassword: (0, import_schema_fields.yupString)().required(t("Please enter your old password")),
404
+ oldPassword: user.hasPassword ? (0, import_schema_fields.yupString)().required(t("Please enter your old password")) : (0, import_schema_fields.yupString)(),
232
405
  newPassword: (0, import_schema_fields.yupString)().required(t("Please enter your password")).test({
233
406
  name: "is-valid-password",
234
407
  test: (value, ctx) => {
@@ -242,23 +415,20 @@ function usePasswordSection() {
242
415
  }),
243
416
  newPasswordRepeat: (0, import_schema_fields.yupString)().nullable().oneOf([yup.ref("newPassword"), "", null], t("Passwords do not match")).required(t("Please repeat your password"))
244
417
  });
245
- const user = (0, import__.useUser)({ or: "throw" });
246
- const [changingPassword, setChangingPassword] = (0, import_react.useState)(false);
247
418
  const { register, handleSubmit, setError, formState: { errors }, clearErrors, reset } = (0, import_react_hook_form.useForm)({
248
419
  resolver: (0, import_yup.yupResolver)(passwordSchema)
249
420
  });
250
- const [alreadyReset, setAlreadyReset] = (0, import_react.useState)(false);
251
- const [loading, setLoading] = (0, import_react.useState)(false);
421
+ const hasValidEmail = contactChannels.filter((x) => x.type === "email" && x.isVerified && x.usedForAuth).length > 0;
252
422
  const onSubmit = async (data) => {
253
423
  setLoading(true);
254
424
  try {
255
425
  const { oldPassword, newPassword } = data;
256
- const error = await user.updatePassword({ oldPassword, newPassword });
426
+ const error = user.hasPassword ? await user.updatePassword({ oldPassword, newPassword }) : await user.setPassword({ password: newPassword });
257
427
  if (error) {
258
428
  setError("oldPassword", { type: "manual", message: t("Incorrect password") });
259
429
  } else {
260
430
  reset();
261
- setAlreadyReset(true);
431
+ setChangingPassword(false);
262
432
  }
263
433
  } finally {
264
434
  setLoading(false);
@@ -266,69 +436,83 @@ function usePasswordSection() {
266
436
  };
267
437
  const registerPassword = register("newPassword");
268
438
  const registerPasswordRepeat = register("newPasswordRepeat");
269
- if (!user.hasPassword) {
270
- return null;
271
- }
272
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
273
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Label, { children: t("Change password") }),
274
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: alreadyReset ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "success", children: t("Password changed successfully!") }) : !changingPassword ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
275
- import_stack_ui.Button,
276
- {
277
- variant: "secondary",
278
- onClick: async () => {
279
- setChangingPassword(true);
280
- },
281
- children: t("Change Password")
282
- }
283
- ) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
284
- "form",
285
- {
286
- onSubmit: (e) => (0, import_promises.runAsynchronouslyWithAlert)(handleSubmit(onSubmit)(e)),
287
- noValidate: true,
288
- children: [
289
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Label, { htmlFor: "old-password", className: "mb-1", children: t("Old password") }),
290
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
291
- import_stack_ui.Input,
292
- {
293
- id: "old-password",
294
- type: "password",
295
- ...register("oldPassword")
296
- }
297
- ),
298
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_form_warning.FormWarningText, { text: errors.oldPassword?.message?.toString() }),
299
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Label, { htmlFor: "new-password", className: "mt-4 mb-1", children: t("Password") }),
300
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
301
- import_stack_ui.PasswordInput,
302
- {
303
- id: "new-password",
304
- ...registerPassword,
305
- onChange: (e) => {
306
- clearErrors("newPassword");
307
- clearErrors("newPasswordRepeat");
308
- (0, import_promises.runAsynchronously)(registerPassword.onChange(e));
439
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
440
+ Section,
441
+ {
442
+ title: t("Password"),
443
+ description: user.hasPassword ? t("Update your password") : t("Set a password for your account"),
444
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-col gap-4", children: !changingPassword ? hasValidEmail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
445
+ import_stack_ui.Button,
446
+ {
447
+ variant: "secondary",
448
+ onClick: () => setChangingPassword(true),
449
+ children: user.hasPassword ? t("Update password") : t("Set password")
450
+ }
451
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.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__ */ (0, import_jsx_runtime.jsxs)(
452
+ "form",
453
+ {
454
+ onSubmit: (e) => (0, import_promises.runAsynchronouslyWithAlert)(handleSubmit(onSubmit)(e)),
455
+ noValidate: true,
456
+ children: [
457
+ user.hasPassword && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
458
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Label, { htmlFor: "old-password", className: "mb-1", children: t("Old password") }),
459
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
460
+ import_stack_ui.Input,
461
+ {
462
+ id: "old-password",
463
+ type: "password",
464
+ ...register("oldPassword")
465
+ }
466
+ ),
467
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_form_warning.FormWarningText, { text: errors.oldPassword?.message?.toString() })
468
+ ] }),
469
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Label, { htmlFor: "new-password", className: "mt-4 mb-1", children: t("New password") }),
470
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
471
+ import_stack_ui.PasswordInput,
472
+ {
473
+ id: "new-password",
474
+ ...registerPassword,
475
+ onChange: (e) => {
476
+ clearErrors("newPassword");
477
+ clearErrors("newPasswordRepeat");
478
+ (0, import_promises.runAsynchronously)(registerPassword.onChange(e));
479
+ }
309
480
  }
310
- }
311
- ),
312
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_form_warning.FormWarningText, { text: errors.newPassword?.message?.toString() }),
313
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Label, { htmlFor: "repeat-password", className: "mt-4 mb-1", children: t("Repeat password") }),
314
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
315
- import_stack_ui.PasswordInput,
316
- {
317
- id: "repeat-password",
318
- ...registerPasswordRepeat,
319
- onChange: (e) => {
320
- clearErrors("newPassword");
321
- clearErrors("newPasswordRepeat");
322
- (0, import_promises.runAsynchronously)(registerPasswordRepeat.onChange(e));
481
+ ),
482
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_form_warning.FormWarningText, { text: errors.newPassword?.message?.toString() }),
483
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Label, { htmlFor: "repeat-password", className: "mt-4 mb-1", children: t("Repeat new password") }),
484
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
485
+ import_stack_ui.PasswordInput,
486
+ {
487
+ id: "repeat-password",
488
+ ...registerPasswordRepeat,
489
+ onChange: (e) => {
490
+ clearErrors("newPassword");
491
+ clearErrors("newPasswordRepeat");
492
+ (0, import_promises.runAsynchronously)(registerPasswordRepeat.onChange(e));
493
+ }
323
494
  }
324
- }
325
- ),
326
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_form_warning.FormWarningText, { text: errors.newPasswordRepeat?.message?.toString() }),
327
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Button, { type: "submit", className: "mt-6", loading, children: t("Change Password") })
328
- ]
329
- }
330
- ) })
331
- ] });
495
+ ),
496
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_form_warning.FormWarningText, { text: errors.newPasswordRepeat?.message?.toString() }),
497
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mt-6 flex gap-4", children: [
498
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Button, { type: "submit", loading, children: user.hasPassword ? t("Update Password") : t("Set Password") }),
499
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
500
+ import_stack_ui.Button,
501
+ {
502
+ variant: "secondary",
503
+ onClick: () => {
504
+ setChangingPassword(false);
505
+ reset();
506
+ },
507
+ children: t("Cancel")
508
+ }
509
+ )
510
+ ] })
511
+ ]
512
+ }
513
+ ) })
514
+ }
515
+ );
332
516
  }
333
517
  function useMfaSection() {
334
518
  const { t } = (0, import_translations.useTranslation)();
@@ -359,7 +543,7 @@ function useMfaSection() {
359
543
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
360
544
  Section,
361
545
  {
362
- title: t("Multi-factor Authentication"),
546
+ title: t("Multi-factor authentication"),
363
547
  description: isEnabled ? t("Multi-factor authentication is currently enabled.") : t("Multi-factor authentication is currently disabled."),
364
548
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-4", children: [
365
549
  !isEnabled && generatedSecret && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
@@ -402,18 +586,18 @@ function useMfaSection() {
402
586
  totpMultiFactorSecret: null
403
587
  });
404
588
  },
405
- children: t("Disable")
589
+ children: t("Disable MFA")
406
590
  }
407
591
  ) : !generatedSecret && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
408
592
  import_stack_ui.Button,
409
593
  {
410
- variant: "default",
594
+ variant: "secondary",
411
595
  onClick: async () => {
412
596
  const secret = (0, import_crypto.generateRandomValues)(new Uint8Array(20));
413
597
  setQrCodeUrl(await generateTotpQrCode(project, user, secret));
414
598
  setGeneratedSecret(secret);
415
599
  },
416
- children: t("Enable")
600
+ children: t("Enable MFA")
417
601
  }
418
602
  ) })
419
603
  ] })