cauthy-auth 0.36.1 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.mjs CHANGED
@@ -801,6 +801,13 @@ var TEXTS = {
801
801
  recoveryCodes: "Recovery codes",
802
802
  newRecoveryCodes: "New recovery codes",
803
803
  keepCodes: "Keep these codes somewhere safe. Each works once, and they are not shown again.",
804
+ copyCodes: "Copy all",
805
+ downloadCodes: "Download .txt",
806
+ codesCopied: "The codes were copied.",
807
+ codesNotCopied: "The codes could not be copied. Select them and copy them by hand.",
808
+ change: "Change",
809
+ passwordSet: "Your account has a password.",
810
+ addAnotherPasskey: "Add a passkey",
804
811
  passkeys: "Passkeys",
805
812
  managePasskeys: "Add or remove a passkey",
806
813
  passkeysHelp: "Sign in with your fingerprint, your face or your device's lock, without a password.",
@@ -985,6 +992,13 @@ var TEXTS = {
985
992
  recoveryCodes: "C\xF3digos de recuperaci\xF3n",
986
993
  newRecoveryCodes: "Nuevos c\xF3digos de recuperaci\xF3n",
987
994
  keepCodes: "Guarda estos c\xF3digos en un lugar seguro. Cada uno sirve una vez y no se vuelven a mostrar.",
995
+ copyCodes: "Copiar todos",
996
+ downloadCodes: "Descargar .txt",
997
+ codesCopied: "Los c\xF3digos quedaron copiados.",
998
+ codesNotCopied: "No se pudieron copiar los c\xF3digos. Selecci\xF3nalos y c\xF3pialos a mano.",
999
+ change: "Cambiar",
1000
+ passwordSet: "Tu cuenta tiene contrase\xF1a.",
1001
+ addAnotherPasskey: "Agregar una llave de acceso",
988
1002
  passkeys: "Llaves de acceso",
989
1003
  managePasskeys: "Agregar o quitar una llave de acceso",
990
1004
  passkeysHelp: "Entra con tu huella, tu rostro o el bloqueo de tu dispositivo, sin contrase\xF1a.",
@@ -2160,6 +2174,8 @@ function OrganizationMembers(props) {
2160
2174
  const [role, setRole] = useState("");
2161
2175
  const [name, setName] = useState("");
2162
2176
  const [asking, setAsking] = useState(null);
2177
+ const [adding, setAdding] = useState(false);
2178
+ const [renamingOpen, setRenamingOpen] = useState(false);
2163
2179
  const [gone, setGone] = useState(null);
2164
2180
  const id = props.organizationId;
2165
2181
  const when = props.formatDate ?? ((iso) => new Date(iso).toLocaleDateString());
@@ -2194,6 +2210,7 @@ function OrganizationMembers(props) {
2194
2210
  const r = await run(() => auth.organizations.add(id, { email, role, locale: props.locale }), "add");
2195
2211
  if (!r.ok) return;
2196
2212
  setEmail("");
2213
+ setAdding(false);
2197
2214
  setDone(f.t.memberAdded.replace("{email}", r.member.email));
2198
2215
  saved({ kind: "member.added", organizationId: id, userId: r.member.userId, email: r.member.email, role: r.member.role || role });
2199
2216
  await load();
@@ -2224,6 +2241,7 @@ function OrganizationMembers(props) {
2224
2241
  if (!r.ok) return;
2225
2242
  setSeat(r.organization);
2226
2243
  setName(r.organization.name);
2244
+ setRenamingOpen(false);
2227
2245
  setDone(f.t.organizationRenamed.replace("{name}", r.organization.name));
2228
2246
  saved({ kind: "organization.renamed", organizationId: id, name: r.organization.name });
2229
2247
  };
@@ -2253,6 +2271,24 @@ function OrganizationMembers(props) {
2253
2271
  const archiving = seat.can.archive && !hidden("archive");
2254
2272
  const deleting = seat.can.delete && !hidden("delete");
2255
2273
  const renaming = seat.can.update && !hidden("rename");
2274
+ const alone = members !== null && members.every((m) => m.you);
2275
+ const addOpen = adding || alone;
2276
+ const open = (form) => {
2277
+ setDone(null);
2278
+ f.setError(null);
2279
+ if (form === "add") setAdding(true);
2280
+ else setRenamingOpen(true);
2281
+ };
2282
+ const cancelAdd = () => {
2283
+ setEmail("");
2284
+ f.setError(null);
2285
+ setAdding(false);
2286
+ };
2287
+ const cancelRename = () => {
2288
+ setName(seat.name);
2289
+ f.setError(null);
2290
+ setRenamingOpen(false);
2291
+ };
2256
2292
  return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "account", root, children: [
2257
2293
  /* @__PURE__ */ jsxs(Wrap, { f, as: "section", slot: "section", section: "members", children: [
2258
2294
  /* @__PURE__ */ jsxs(Heading, { f, children: [
@@ -2316,18 +2352,23 @@ function OrganizationMembers(props) {
2316
2352
  }) }),
2317
2353
  done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2318
2354
  ] }),
2319
- manage && giveable.length > 0 && /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "add-member", onSubmit: add, noValidate: true, children: [
2355
+ manage && giveable.length > 0 && (addOpen ? /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "add-member", onSubmit: add, noValidate: true, children: [
2320
2356
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.memberAdd }),
2321
2357
  /* @__PURE__ */ jsx(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "off", required: true, onChange: setEmail }),
2322
2358
  /* @__PURE__ */ jsx(AddRole, { f, value: role, options: giveable.map((c) => ({ value: c.role, label: roleName(c) })), onChange: setRole }),
2323
- /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading("add"), disabled: f.busy, className: f.cx("button"), children: f.t.memberAdd })
2324
- ] }),
2359
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading("add"), disabled: f.busy, className: f.cx("button"), children: f.t.memberAdd }),
2360
+ !alone && /* @__PURE__ */ jsx(Cancel, { f, onClick: cancelAdd })
2361
+ ] }) : /* @__PURE__ */ jsx(Wrap, { f, as: "div", slot: "section", section: "add-member", children: /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", disabled: f.busy, className: f.cx("providerButton"), onClick: () => open("add"), children: f.t.memberAdd }) })),
2325
2362
  (renaming || archiving || deleting) && /* @__PURE__ */ jsxs(Wrap, { f, as: "section", slot: "section", section: "organization", children: [
2326
2363
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.organization }),
2327
- renaming && /* @__PURE__ */ jsxs(Wrap, { f, as: "form", section: "rename", onSubmit: rename, noValidate: true, children: [
2364
+ renaming && (renamingOpen ? /* @__PURE__ */ jsxs(Wrap, { f, as: "form", section: "rename", onSubmit: rename, noValidate: true, children: [
2328
2365
  /* @__PURE__ */ jsx(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "organization", required: true, onChange: setName }),
2329
- /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading("rename"), disabled: f.busy, className: f.cx("button"), children: f.t.organizationRename })
2330
- ] }),
2366
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading("rename"), disabled: f.busy, className: f.cx("button"), children: f.t.organizationRename }),
2367
+ /* @__PURE__ */ jsx(Cancel, { f, onClick: cancelRename })
2368
+ ] }) : /* @__PURE__ */ jsxs(Fragment2, { children: [
2369
+ /* @__PURE__ */ jsx(Note, { f, children: seat.name }),
2370
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", disabled: f.busy, className: f.cx("providerButton"), onClick: () => open("rename"), children: f.t.organizationRename })
2371
+ ] })),
2331
2372
  asking === "archive" ? confirming(archiveQuestion, f.t.organizationArchive, () => void close("archive")) : asking === "delete" ? confirming(deleteQuestion, f.t.organizationDelete, () => void close("delete")) : /* @__PURE__ */ jsxs(Fragment2, { children: [
2332
2373
  archiving && /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "danger", loading: f.loading("archive"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => ask("archive", f.t.organizationArchive, archiveQuestion, () => void close("archive")), children: f.t.organizationArchive }),
2333
2374
  deleting && /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "danger", loading: f.loading("delete"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => ask("delete", f.t.organizationDelete, deleteQuestion, () => void close("delete")), children: f.t.organizationDelete })
@@ -2346,6 +2387,7 @@ function PasskeysPart({ props, account, accountUrl, when }) {
2346
2387
  const [list, setList] = useState([]);
2347
2388
  const [name, setName] = useState("");
2348
2389
  const [password, setPassword] = useState("");
2390
+ const [adding, setAdding] = useState(false);
2349
2391
  useEffect(() => {
2350
2392
  void auth.methods().then((m) => {
2351
2393
  const can2 = m.passkeyHere && canUsePasskeys();
@@ -2371,6 +2413,7 @@ function PasskeysPart({ props, account, accountUrl, when }) {
2371
2413
  setList([...list, r.passkey]);
2372
2414
  setName("");
2373
2415
  setPassword("");
2416
+ setAdding(false);
2374
2417
  setDone(f.t.passkeyAdded);
2375
2418
  props.onSaved?.({ kind: "passkey.added", id: r.passkey.id });
2376
2419
  };
@@ -2380,43 +2423,123 @@ function PasskeysPart({ props, account, accountUrl, when }) {
2380
2423
  setList(list.filter((p) => p.id !== id));
2381
2424
  props.onSaved?.({ kind: "passkey.removed", id });
2382
2425
  };
2426
+ const cancel = () => {
2427
+ setName("");
2428
+ setPassword("");
2429
+ f.setError(null);
2430
+ setAdding(false);
2431
+ };
2432
+ const listed = list.length === 0 ? /* @__PURE__ */ jsx(Note, { f, children: f.t.noPasskeys }) : /* @__PURE__ */ jsx("ul", { className: f.cx("sessions"), children: list.map((p) => /* @__PURE__ */ jsxs("li", { className: f.cx("session"), children: [
2433
+ p.name || f.t.passkeyUnnamed,
2434
+ p.kind !== "unknown" && /* @__PURE__ */ jsx(Tag, { f, plain: ` \xB7 ${f.t.passkeyKinds[p.kind]}`, children: f.t.passkeyKinds[p.kind] }),
2435
+ " \xB7 ",
2436
+ when(p.createdAt),
2437
+ " ",
2438
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "danger", loading: f.loading(`remove:${p.id}`), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void remove(p.id), children: f.t.removePasskey })
2439
+ ] }, p.id)) });
2440
+ if (list.length > 0 && !adding) {
2441
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "section", section: "passkeys", children: [
2442
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.passkeys }),
2443
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.passkeysHelp }),
2444
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2445
+ listed,
2446
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", disabled: f.busy, className: f.cx("providerButton"), onClick: () => {
2447
+ setDone(null);
2448
+ f.setError(null);
2449
+ setAdding(true);
2450
+ }, children: f.t.addAnotherPasskey }),
2451
+ done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2452
+ ] });
2453
+ }
2383
2454
  return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "passkeys", onSubmit: add, noValidate: true, children: [
2384
2455
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.passkeys }),
2385
2456
  /* @__PURE__ */ jsx(Note, { f, children: f.t.passkeysHelp }),
2386
2457
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2387
- list.length === 0 ? /* @__PURE__ */ jsx(Note, { f, children: f.t.noPasskeys }) : /* @__PURE__ */ jsx("ul", { className: f.cx("sessions"), children: list.map((p) => /* @__PURE__ */ jsxs("li", { className: f.cx("session"), children: [
2388
- p.name || f.t.passkeyUnnamed,
2389
- p.kind !== "unknown" && /* @__PURE__ */ jsx(Tag, { f, plain: ` \xB7 ${f.t.passkeyKinds[p.kind]}`, children: f.t.passkeyKinds[p.kind] }),
2390
- " \xB7 ",
2391
- when(p.createdAt),
2392
- " ",
2393
- /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "danger", loading: f.loading(`remove:${p.id}`), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void remove(p.id), children: f.t.removePasskey })
2394
- ] }, p.id)) }),
2458
+ listed,
2395
2459
  /* @__PURE__ */ jsx(Field, { f, name: "name", type: "text", label: f.t.passkeyName, value: name, autoComplete: "off", onChange: setName }),
2396
2460
  account.hasPassword && /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword }),
2397
2461
  /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.addPasskeyDevice }),
2398
2462
  /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", loading: f.loading("security_key"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void add(null, "security_key"), children: f.t.addSecurityKey }),
2463
+ list.length > 0 && /* @__PURE__ */ jsx(Cancel, { f, onClick: cancel }),
2399
2464
  done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2400
2465
  ] });
2401
2466
  }
2467
+ function Cancel({ f, onClick }) {
2468
+ return /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", disabled: f.busy, className: f.cx("providerButton"), onClick, children: f.t.cancel });
2469
+ }
2470
+ function RecoveryCodes({ f, codes }) {
2471
+ const [copied, setCopied] = useState(null);
2472
+ useEffect(() => setCopied(null), [codes]);
2473
+ const copy = async () => {
2474
+ try {
2475
+ await navigator.clipboard.writeText(codes.join("\n"));
2476
+ setCopied(true);
2477
+ } catch {
2478
+ setCopied(false);
2479
+ }
2480
+ };
2481
+ const download = () => {
2482
+ const file = new Blob([`${f.t.recoveryCodes}
2483
+
2484
+ ${codes.join("\n")}
2485
+ `], { type: "text/plain;charset=utf-8" });
2486
+ const url = URL.createObjectURL(file);
2487
+ const a = document.createElement("a");
2488
+ a.href = url;
2489
+ a.download = "recovery-codes.txt";
2490
+ document.body.appendChild(a);
2491
+ a.click();
2492
+ a.remove();
2493
+ setTimeout(() => URL.revokeObjectURL(url), 0);
2494
+ };
2495
+ return /* @__PURE__ */ jsxs(Fragment2, { children: [
2496
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.keepCodes }),
2497
+ /* @__PURE__ */ jsx("ul", { className: f.cx("codes"), children: codes.map((c) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("code", { children: c }) }, c)) }),
2498
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", className: f.cx("providerButton"), onClick: () => void copy(), children: f.t.copyCodes }),
2499
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", className: f.cx("providerButton"), onClick: download, children: f.t.downloadCodes }),
2500
+ copied === true && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: f.t.codesCopied }),
2501
+ copied === false && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.t.codesNotCopied })
2502
+ ] });
2503
+ }
2402
2504
  function ProfilePart({ props, account, onSaved }) {
2403
2505
  const auth = useAuth();
2404
2506
  const { f, done, setDone, run, general } = useSection(props);
2405
2507
  const [name, setName] = useState(account.name);
2508
+ const [editing, setEditing] = useState(account.name.trim() === "");
2406
2509
  const submit = async (e) => {
2407
2510
  e.preventDefault();
2408
2511
  const r = await run(() => auth.account.update({ name }));
2409
2512
  if (!r.ok) return;
2410
2513
  onSaved(r.account);
2514
+ setName(r.account.name);
2515
+ setEditing(r.account.name.trim() === "");
2411
2516
  setDone(f.t.saved);
2412
2517
  props.onSaved?.({ kind: "profile.saved", name: r.account.name });
2413
2518
  };
2519
+ const cancel = () => {
2520
+ setName(account.name);
2521
+ f.setError(null);
2522
+ setEditing(false);
2523
+ };
2524
+ if (!editing) {
2525
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "section", section: "profile", children: [
2526
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.profile }),
2527
+ /* @__PURE__ */ jsx(Note, { f, children: account.name }),
2528
+ /* @__PURE__ */ jsx(Note, { f, children: account.email }),
2529
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", disabled: f.busy, className: f.cx("providerButton"), onClick: () => {
2530
+ setDone(null);
2531
+ setEditing(true);
2532
+ }, children: f.t.change }),
2533
+ done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2534
+ ] });
2535
+ }
2414
2536
  return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "profile", onSubmit: submit, noValidate: true, children: [
2415
2537
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.profile }),
2416
2538
  /* @__PURE__ */ jsx(Note, { f, children: account.email }),
2417
2539
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2418
2540
  /* @__PURE__ */ jsx(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "name", onChange: setName }),
2419
2541
  /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.save }),
2542
+ account.name.trim() !== "" && /* @__PURE__ */ jsx(Cancel, { f, onClick: cancel }),
2420
2543
  done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2421
2544
  ] });
2422
2545
  }
@@ -2425,15 +2548,35 @@ function EmailPart({ props, account }) {
2425
2548
  const { f, done, setDone, run, general } = useSection(props);
2426
2549
  const [email, setEmail] = useState("");
2427
2550
  const [password, setPassword] = useState("");
2551
+ const [editing, setEditing] = useState(false);
2428
2552
  const submit = async (e) => {
2429
2553
  e.preventDefault();
2430
2554
  if (!f.check(checkEmail(email), account.hasPassword && checkPassword(password))) return;
2431
2555
  const r = await run(() => auth.account.changeEmail({ email, password }));
2432
2556
  if (!r.ok) return;
2557
+ setEmail("");
2433
2558
  setPassword("");
2559
+ setEditing(false);
2434
2560
  setDone(f.t.emailSent.replace("{email}", r.email));
2435
2561
  props.onSaved?.({ kind: "email.change_requested", email: r.email });
2436
2562
  };
2563
+ const cancel = () => {
2564
+ setEmail("");
2565
+ setPassword("");
2566
+ f.setError(null);
2567
+ setEditing(false);
2568
+ };
2569
+ if (!editing) {
2570
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "section", section: "email", children: [
2571
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.emailAddress }),
2572
+ /* @__PURE__ */ jsx(Note, { f, children: account.email }),
2573
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", disabled: f.busy, className: f.cx("providerButton"), onClick: () => {
2574
+ setDone(null);
2575
+ setEditing(true);
2576
+ }, children: f.t.changeEmail }),
2577
+ done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2578
+ ] });
2579
+ }
2437
2580
  return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "email", onSubmit: submit, noValidate: true, children: [
2438
2581
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.emailAddress }),
2439
2582
  /* @__PURE__ */ jsx(Note, { f, children: account.email }),
@@ -2441,7 +2584,7 @@ function EmailPart({ props, account }) {
2441
2584
  /* @__PURE__ */ jsx(Field, { f, name: "email", type: "email", label: f.t.newEmail, value: email, autoComplete: "email", required: true, onChange: setEmail }),
2442
2585
  account.hasPassword && /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword }),
2443
2586
  /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.changeEmail }),
2444
- done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2587
+ /* @__PURE__ */ jsx(Cancel, { f, onClick: cancel })
2445
2588
  ] });
2446
2589
  }
2447
2590
  function LinkedPart({ props }) {
@@ -2506,6 +2649,7 @@ function PasswordPart({ props }) {
2506
2649
  const { f, done, setDone, run, general } = useSection(props);
2507
2650
  const [password, setPassword] = useState("");
2508
2651
  const [newPassword, setNewPassword] = useState("");
2652
+ const [editing, setEditing] = useState(false);
2509
2653
  usePasswordPolicy(f, useMethods(auth));
2510
2654
  const submit = async (e) => {
2511
2655
  e.preventDefault();
@@ -2514,16 +2658,34 @@ function PasswordPart({ props }) {
2514
2658
  if (!r.ok) return;
2515
2659
  setPassword("");
2516
2660
  setNewPassword("");
2661
+ setEditing(false);
2517
2662
  setDone(f.t.passwordChangedHere);
2518
2663
  props.onSaved?.({ kind: "password.changed" });
2519
2664
  };
2665
+ const cancel = () => {
2666
+ setPassword("");
2667
+ setNewPassword("");
2668
+ f.setError(null);
2669
+ setEditing(false);
2670
+ };
2671
+ if (!editing) {
2672
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "section", section: "password", children: [
2673
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.password }),
2674
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.passwordSet }),
2675
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", disabled: f.busy, className: f.cx("providerButton"), onClick: () => {
2676
+ setDone(null);
2677
+ setEditing(true);
2678
+ }, children: f.t.changePassword }),
2679
+ done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2680
+ ] });
2681
+ }
2520
2682
  return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "password", onSubmit: submit, noValidate: true, children: [
2521
- /* @__PURE__ */ jsx(Heading, { f, children: f.t.changePassword }),
2683
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.password }),
2522
2684
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2523
2685
  /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword }),
2524
2686
  /* @__PURE__ */ jsx(Field, { f, name: "newPassword", type: "password", label: f.t.newPassword, value: newPassword, autoComplete: "new-password", required: true, onChange: setNewPassword, hint: f.passwordRule }),
2525
2687
  /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.changePassword }),
2526
- done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2688
+ /* @__PURE__ */ jsx(Cancel, { f, onClick: cancel })
2527
2689
  ] });
2528
2690
  }
2529
2691
  function SecondStepPart({ props, account, onChanged }) {
@@ -2533,6 +2695,7 @@ function SecondStepPart({ props, account, onChanged }) {
2533
2695
  const [code, setCode] = useState("");
2534
2696
  const [setup, setSetup] = useState(null);
2535
2697
  const [codes, setCodes] = useState(null);
2698
+ const [asking, setAsking] = useState(null);
2536
2699
  const start = async (e) => {
2537
2700
  e.preventDefault();
2538
2701
  const r = await run(() => auth.account.mfa.setup({ password }));
@@ -2549,26 +2712,36 @@ function SecondStepPart({ props, account, onChanged }) {
2549
2712
  onChanged(true);
2550
2713
  props.onSaved?.({ kind: "mfa.enabled" });
2551
2714
  };
2552
- const turnOff = async (e) => {
2553
- e.preventDefault();
2554
- const r = await run(() => auth.account.mfa.disable({ password }));
2715
+ const turnOff = async (key) => {
2716
+ const r = await run(() => auth.account.mfa.disable({ password }), key);
2555
2717
  if (!r.ok) return;
2556
2718
  setPassword("");
2719
+ setAsking(null);
2557
2720
  setCodes(null);
2558
2721
  onChanged(false);
2559
2722
  props.onSaved?.({ kind: "mfa.disabled" });
2560
2723
  };
2561
- const renew = async () => {
2562
- const r = await run(() => auth.account.mfa.recoveryCodes({ password }), "codes");
2724
+ const renew = async (key) => {
2725
+ const r = await run(() => auth.account.mfa.recoveryCodes({ password }), key);
2563
2726
  if (!r.ok) return;
2727
+ setPassword("");
2728
+ setAsking(null);
2564
2729
  setCodes(r.recoveryCodes);
2565
2730
  props.onSaved?.({ kind: "recovery_codes.regenerated" });
2566
2731
  };
2732
+ const press = (what) => {
2733
+ f.setError(null);
2734
+ if (!account.hasPassword) return void (what === "off" ? turnOff(what) : renew(what));
2735
+ setPassword("");
2736
+ setCodes(null);
2737
+ setAsking(what);
2738
+ };
2739
+ const cancel = () => {
2740
+ setPassword("");
2741
+ f.setError(null);
2742
+ setAsking(null);
2743
+ };
2567
2744
  const passwordField = account.hasPassword && /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword });
2568
- const shown = codes && /* @__PURE__ */ jsxs(Fragment2, { children: [
2569
- /* @__PURE__ */ jsx(Note, { f, children: f.t.keepCodes }),
2570
- /* @__PURE__ */ jsx("ul", { className: f.cx("codes"), children: codes.map((c) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("code", { children: c }) }, c)) })
2571
- ] });
2572
2745
  if (setup) {
2573
2746
  return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "secondStep", onSubmit: confirm, noValidate: true, children: [
2574
2747
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.secondStep }),
@@ -2580,14 +2753,36 @@ function SecondStepPart({ props, account, onChanged }) {
2580
2753
  /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.verify })
2581
2754
  ] });
2582
2755
  }
2583
- return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "secondStep", onSubmit: account.mfaEnabled ? turnOff : start, noValidate: true, children: [
2756
+ if (!account.mfaEnabled) {
2757
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "secondStep", onSubmit: start, noValidate: true, children: [
2758
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.secondStep }),
2759
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.secondStepOff }),
2760
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2761
+ passwordField,
2762
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.turnOn })
2763
+ ] });
2764
+ }
2765
+ if (asking) {
2766
+ const submit = (e) => {
2767
+ e.preventDefault();
2768
+ void (asking === "off" ? turnOff(SUBMIT) : renew(SUBMIT));
2769
+ };
2770
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "secondStep", onSubmit: submit, noValidate: true, children: [
2771
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.secondStep }),
2772
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.secondStepOn }),
2773
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2774
+ passwordField,
2775
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: asking === "off" ? "danger" : "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: asking === "off" ? f.t.turnOff : f.t.newRecoveryCodes }),
2776
+ /* @__PURE__ */ jsx(Cancel, { f, onClick: cancel })
2777
+ ] });
2778
+ }
2779
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "section", section: "secondStep", children: [
2584
2780
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.secondStep }),
2585
- /* @__PURE__ */ jsx(Note, { f, children: account.mfaEnabled ? f.t.secondStepOn : f.t.secondStepOff }),
2586
- shown,
2781
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.secondStepOn }),
2782
+ codes && /* @__PURE__ */ jsx(RecoveryCodes, { f, codes }),
2587
2783
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2588
- passwordField,
2589
- /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: account.mfaEnabled ? "danger" : "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: account.mfaEnabled ? f.t.turnOff : f.t.turnOn }),
2590
- account.mfaEnabled && /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", loading: f.loading("codes"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void renew(), children: f.t.newRecoveryCodes })
2784
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "danger", loading: f.loading("off"), disabled: f.busy, className: f.cx("button"), onClick: () => press("off"), children: f.t.turnOff }),
2785
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", loading: f.loading("codes"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => press("codes"), children: f.t.newRecoveryCodes })
2591
2786
  ] });
2592
2787
  }
2593
2788
  function PhonePart({ props, account, onChanged }) {
@@ -2600,6 +2795,7 @@ function PhonePart({ props, account, onChanged }) {
2600
2795
  const [code, setCode] = useState("");
2601
2796
  const [sentTo, setSentTo] = useState("");
2602
2797
  const [codes, setCodes] = useState(null);
2798
+ const [removing, setRemoving] = useState(false);
2603
2799
  const load = () => auth.account.mfa.factors().then((w) => {
2604
2800
  setWays(w);
2605
2801
  if (w && w.phoneChannels.length > 0 && !w.phoneChannels.includes(channel)) setChannel(w.phoneChannels[0]);
@@ -2620,21 +2816,34 @@ function PhonePart({ props, account, onChanged }) {
2620
2816
  setSentTo("");
2621
2817
  setCode("");
2622
2818
  setPassword("");
2819
+ setNumber("");
2623
2820
  if (r.recoveryCodes.length > 0) setCodes(r.recoveryCodes);
2624
2821
  onChanged(true);
2625
2822
  props.onSaved?.({ kind: "phone.added", channel });
2626
2823
  await load();
2627
2824
  };
2628
- const remove = async (e) => {
2629
- e.preventDefault();
2630
- const r = await run(() => auth.account.mfa.removePhone({ password }));
2825
+ const remove = async (key) => {
2826
+ const r = await run(() => auth.account.mfa.removePhone({ password }), key);
2631
2827
  if (!r.ok) return;
2632
2828
  setPassword("");
2829
+ setRemoving(false);
2830
+ setCodes(null);
2633
2831
  await load();
2634
2832
  const left = await auth.account.mfa.factors();
2635
2833
  onChanged(Boolean(left?.totp));
2636
2834
  props.onSaved?.({ kind: "phone.removed" });
2637
2835
  };
2836
+ const press = () => {
2837
+ f.setError(null);
2838
+ if (!account.hasPassword) return void remove("remove");
2839
+ setPassword("");
2840
+ setRemoving(true);
2841
+ };
2842
+ const cancel = () => {
2843
+ setPassword("");
2844
+ f.setError(null);
2845
+ setRemoving(false);
2846
+ };
2638
2847
  const passwordField = account.hasPassword && /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword });
2639
2848
  if (sentTo) {
2640
2849
  return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "phone", onSubmit: confirm, noValidate: true, children: [
@@ -2645,23 +2854,46 @@ function PhonePart({ props, account, onChanged }) {
2645
2854
  /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.verify })
2646
2855
  ] });
2647
2856
  }
2648
- return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "phone", onSubmit: ways.phone ? remove : add, noValidate: true, children: [
2857
+ if (ways.phone) {
2858
+ const by = ways.phone.channel === "whatsapp" ? f.t.byWhatsapp : ways.phone.channel === "sms" ? f.t.bySms : "";
2859
+ const on = /* @__PURE__ */ jsxs(Note, { f, children: [
2860
+ f.t.phoneOn.replace("{to}", ways.phone.number),
2861
+ by && /* @__PURE__ */ jsx(Tag, { f, plain: ` \xB7 ${by}`, children: by })
2862
+ ] });
2863
+ if (removing) {
2864
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "phone", onSubmit: (e) => {
2865
+ e.preventDefault();
2866
+ void remove(SUBMIT);
2867
+ }, noValidate: true, children: [
2868
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.phone }),
2869
+ on,
2870
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2871
+ passwordField,
2872
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "danger", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.removePhone }),
2873
+ /* @__PURE__ */ jsx(Cancel, { f, onClick: cancel })
2874
+ ] });
2875
+ }
2876
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "section", section: "phone", children: [
2877
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.phone }),
2878
+ on,
2879
+ codes && /* @__PURE__ */ jsx(RecoveryCodes, { f, codes }),
2880
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2881
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "danger", loading: f.loading("remove"), disabled: f.busy, className: f.cx("button"), onClick: press, children: f.t.removePhone })
2882
+ ] });
2883
+ }
2884
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "phone", onSubmit: add, noValidate: true, children: [
2649
2885
  /* @__PURE__ */ jsx(Heading, { f, children: f.t.phone }),
2650
- /* @__PURE__ */ jsx(Note, { f, children: ways.phone ? f.t.phoneOn.replace("{to}", ways.phone.number) : `${f.t.phoneOff} ${f.t.phoneHelp}` }),
2651
- codes && /* @__PURE__ */ jsxs(Fragment2, { children: [
2652
- /* @__PURE__ */ jsx(Note, { f, children: f.t.keepCodes }),
2653
- /* @__PURE__ */ jsx("ul", { className: f.cx("codes"), children: codes.map((c) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("code", { children: c }) }, c)) })
2654
- ] }),
2886
+ /* @__PURE__ */ jsx(Note, { f, children: `${f.t.phoneOff} ${f.t.phoneHelp}` }),
2655
2887
  general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2656
- !ways.phone && /* @__PURE__ */ jsx(Field, { f, name: "phone", type: "tel", label: f.t.phone, value: number, autoComplete: "tel", required: true, onChange: setNumber }),
2657
- !ways.phone && ways.phoneChannels.length > 1 && f.C.Select && /* @__PURE__ */ jsx(ChannelPick, { f, value: channel, channels: ways.phoneChannels, onChange: setChannel }),
2658
- !ways.phone && ways.phoneChannels.length > 1 && !f.C.Select && /* @__PURE__ */ jsx("div", { className: f.cx("field"), role: "radiogroup", children: ways.phoneChannels.map((c) => /* @__PURE__ */ jsxs("label", { className: f.cx("label"), children: [
2888
+ /* @__PURE__ */ jsx(Field, { f, name: "phone", type: "tel", label: f.t.phone, value: number, autoComplete: "tel", required: true, onChange: setNumber }),
2889
+ ways.phoneChannels.length > 1 && f.C.Select && /* @__PURE__ */ jsx(ChannelPick, { f, value: channel, channels: ways.phoneChannels, onChange: setChannel }),
2890
+ ways.phoneChannels.length > 1 && !f.C.Select && /* @__PURE__ */ jsx("div", { className: f.cx("field"), role: "radiogroup", children: ways.phoneChannels.map((c) => /* @__PURE__ */ jsxs("label", { className: f.cx("label"), children: [
2659
2891
  /* @__PURE__ */ jsx("input", { type: "radio", name: "channel", value: c, checked: channel === c, disabled: f.busy, onChange: () => setChannel(c) }),
2660
2892
  " ",
2661
2893
  c === "whatsapp" ? f.t.byWhatsapp : f.t.bySms
2662
2894
  ] }, c)) }),
2663
2895
  passwordField,
2664
- /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: ways.phone ? "danger" : "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: ways.phone ? f.t.removePhone : f.t.addPhone })
2896
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.addPhone })
2665
2897
  ] });
2666
2898
  }
2667
2899
  function AgentsPart({ props, when }) {