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.js CHANGED
@@ -850,6 +850,13 @@ var TEXTS = {
850
850
  recoveryCodes: "Recovery codes",
851
851
  newRecoveryCodes: "New recovery codes",
852
852
  keepCodes: "Keep these codes somewhere safe. Each works once, and they are not shown again.",
853
+ copyCodes: "Copy all",
854
+ downloadCodes: "Download .txt",
855
+ codesCopied: "The codes were copied.",
856
+ codesNotCopied: "The codes could not be copied. Select them and copy them by hand.",
857
+ change: "Change",
858
+ passwordSet: "Your account has a password.",
859
+ addAnotherPasskey: "Add a passkey",
853
860
  passkeys: "Passkeys",
854
861
  managePasskeys: "Add or remove a passkey",
855
862
  passkeysHelp: "Sign in with your fingerprint, your face or your device's lock, without a password.",
@@ -1034,6 +1041,13 @@ var TEXTS = {
1034
1041
  recoveryCodes: "C\xF3digos de recuperaci\xF3n",
1035
1042
  newRecoveryCodes: "Nuevos c\xF3digos de recuperaci\xF3n",
1036
1043
  keepCodes: "Guarda estos c\xF3digos en un lugar seguro. Cada uno sirve una vez y no se vuelven a mostrar.",
1044
+ copyCodes: "Copiar todos",
1045
+ downloadCodes: "Descargar .txt",
1046
+ codesCopied: "Los c\xF3digos quedaron copiados.",
1047
+ codesNotCopied: "No se pudieron copiar los c\xF3digos. Selecci\xF3nalos y c\xF3pialos a mano.",
1048
+ change: "Cambiar",
1049
+ passwordSet: "Tu cuenta tiene contrase\xF1a.",
1050
+ addAnotherPasskey: "Agregar una llave de acceso",
1037
1051
  passkeys: "Llaves de acceso",
1038
1052
  managePasskeys: "Agregar o quitar una llave de acceso",
1039
1053
  passkeysHelp: "Entra con tu huella, tu rostro o el bloqueo de tu dispositivo, sin contrase\xF1a.",
@@ -2209,6 +2223,8 @@ function OrganizationMembers(props) {
2209
2223
  const [role, setRole] = (0, import_react.useState)("");
2210
2224
  const [name, setName] = (0, import_react.useState)("");
2211
2225
  const [asking, setAsking] = (0, import_react.useState)(null);
2226
+ const [adding, setAdding] = (0, import_react.useState)(false);
2227
+ const [renamingOpen, setRenamingOpen] = (0, import_react.useState)(false);
2212
2228
  const [gone, setGone] = (0, import_react.useState)(null);
2213
2229
  const id = props.organizationId;
2214
2230
  const when = props.formatDate ?? ((iso) => new Date(iso).toLocaleDateString());
@@ -2243,6 +2259,7 @@ function OrganizationMembers(props) {
2243
2259
  const r = await run(() => auth.organizations.add(id, { email, role, locale: props.locale }), "add");
2244
2260
  if (!r.ok) return;
2245
2261
  setEmail("");
2262
+ setAdding(false);
2246
2263
  setDone(f.t.memberAdded.replace("{email}", r.member.email));
2247
2264
  saved({ kind: "member.added", organizationId: id, userId: r.member.userId, email: r.member.email, role: r.member.role || role });
2248
2265
  await load();
@@ -2273,6 +2290,7 @@ function OrganizationMembers(props) {
2273
2290
  if (!r.ok) return;
2274
2291
  setSeat(r.organization);
2275
2292
  setName(r.organization.name);
2293
+ setRenamingOpen(false);
2276
2294
  setDone(f.t.organizationRenamed.replace("{name}", r.organization.name));
2277
2295
  saved({ kind: "organization.renamed", organizationId: id, name: r.organization.name });
2278
2296
  };
@@ -2302,6 +2320,24 @@ function OrganizationMembers(props) {
2302
2320
  const archiving = seat.can.archive && !hidden("archive");
2303
2321
  const deleting = seat.can.delete && !hidden("delete");
2304
2322
  const renaming = seat.can.update && !hidden("rename");
2323
+ const alone = members !== null && members.every((m) => m.you);
2324
+ const addOpen = adding || alone;
2325
+ const open = (form) => {
2326
+ setDone(null);
2327
+ f.setError(null);
2328
+ if (form === "add") setAdding(true);
2329
+ else setRenamingOpen(true);
2330
+ };
2331
+ const cancelAdd = () => {
2332
+ setEmail("");
2333
+ f.setError(null);
2334
+ setAdding(false);
2335
+ };
2336
+ const cancelRename = () => {
2337
+ setName(seat.name);
2338
+ f.setError(null);
2339
+ setRenamingOpen(false);
2340
+ };
2305
2341
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "account", root, children: [
2306
2342
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "section", slot: "section", section: "members", children: [
2307
2343
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Heading, { f, children: [
@@ -2365,18 +2401,23 @@ function OrganizationMembers(props) {
2365
2401
  }) }),
2366
2402
  done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: done })
2367
2403
  ] }),
2368
- manage && giveable.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "add-member", onSubmit: add, noValidate: true, children: [
2404
+ manage && giveable.length > 0 && (addOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "add-member", onSubmit: add, noValidate: true, children: [
2369
2405
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.memberAdd }),
2370
2406
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "off", required: true, onChange: setEmail }),
2371
2407
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AddRole, { f, value: role, options: giveable.map((c) => ({ value: c.role, label: roleName(c) })), onChange: setRole }),
2372
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading("add"), disabled: f.busy, className: f.cx("button"), children: f.t.memberAdd })
2373
- ] }),
2408
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading("add"), disabled: f.busy, className: f.cx("button"), children: f.t.memberAdd }),
2409
+ !alone && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cancel, { f, onClick: cancelAdd })
2410
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Wrap, { f, as: "div", slot: "section", section: "add-member", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", disabled: f.busy, className: f.cx("providerButton"), onClick: () => open("add"), children: f.t.memberAdd }) })),
2374
2411
  (renaming || archiving || deleting) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "section", slot: "section", section: "organization", children: [
2375
2412
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.organization }),
2376
- renaming && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", section: "rename", onSubmit: rename, noValidate: true, children: [
2413
+ renaming && (renamingOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", section: "rename", onSubmit: rename, noValidate: true, children: [
2377
2414
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "organization", required: true, onChange: setName }),
2378
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading("rename"), disabled: f.busy, className: f.cx("button"), children: f.t.organizationRename })
2379
- ] }),
2415
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading("rename"), disabled: f.busy, className: f.cx("button"), children: f.t.organizationRename }),
2416
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cancel, { f, onClick: cancelRename })
2417
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2418
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: seat.name }),
2419
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", disabled: f.busy, className: f.cx("providerButton"), onClick: () => open("rename"), children: f.t.organizationRename })
2420
+ ] })),
2380
2421
  asking === "archive" ? confirming(archiveQuestion, f.t.organizationArchive, () => void close("archive")) : asking === "delete" ? confirming(deleteQuestion, f.t.organizationDelete, () => void close("delete")) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2381
2422
  archiving && /* @__PURE__ */ (0, import_jsx_runtime.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 }),
2382
2423
  deleting && /* @__PURE__ */ (0, import_jsx_runtime.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 })
@@ -2395,6 +2436,7 @@ function PasskeysPart({ props, account, accountUrl, when }) {
2395
2436
  const [list, setList] = (0, import_react.useState)([]);
2396
2437
  const [name, setName] = (0, import_react.useState)("");
2397
2438
  const [password, setPassword] = (0, import_react.useState)("");
2439
+ const [adding, setAdding] = (0, import_react.useState)(false);
2398
2440
  (0, import_react.useEffect)(() => {
2399
2441
  void auth.methods().then((m) => {
2400
2442
  const can2 = m.passkeyHere && canUsePasskeys();
@@ -2420,6 +2462,7 @@ function PasskeysPart({ props, account, accountUrl, when }) {
2420
2462
  setList([...list, r.passkey]);
2421
2463
  setName("");
2422
2464
  setPassword("");
2465
+ setAdding(false);
2423
2466
  setDone(f.t.passkeyAdded);
2424
2467
  props.onSaved?.({ kind: "passkey.added", id: r.passkey.id });
2425
2468
  };
@@ -2429,43 +2472,123 @@ function PasskeysPart({ props, account, accountUrl, when }) {
2429
2472
  setList(list.filter((p) => p.id !== id));
2430
2473
  props.onSaved?.({ kind: "passkey.removed", id });
2431
2474
  };
2475
+ const cancel = () => {
2476
+ setName("");
2477
+ setPassword("");
2478
+ f.setError(null);
2479
+ setAdding(false);
2480
+ };
2481
+ const listed = list.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.noPasskeys }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: f.cx("sessions"), children: list.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: f.cx("session"), children: [
2482
+ p.name || f.t.passkeyUnnamed,
2483
+ p.kind !== "unknown" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tag, { f, plain: ` \xB7 ${f.t.passkeyKinds[p.kind]}`, children: f.t.passkeyKinds[p.kind] }),
2484
+ " \xB7 ",
2485
+ when(p.createdAt),
2486
+ " ",
2487
+ /* @__PURE__ */ (0, import_jsx_runtime.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 })
2488
+ ] }, p.id)) });
2489
+ if (list.length > 0 && !adding) {
2490
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "section", section: "passkeys", children: [
2491
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.passkeys }),
2492
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.passkeysHelp }),
2493
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2494
+ listed,
2495
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", disabled: f.busy, className: f.cx("providerButton"), onClick: () => {
2496
+ setDone(null);
2497
+ f.setError(null);
2498
+ setAdding(true);
2499
+ }, children: f.t.addAnotherPasskey }),
2500
+ done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: done })
2501
+ ] });
2502
+ }
2432
2503
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "passkeys", onSubmit: add, noValidate: true, children: [
2433
2504
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.passkeys }),
2434
2505
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.passkeysHelp }),
2435
2506
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2436
- list.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.noPasskeys }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: f.cx("sessions"), children: list.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: f.cx("session"), children: [
2437
- p.name || f.t.passkeyUnnamed,
2438
- p.kind !== "unknown" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tag, { f, plain: ` \xB7 ${f.t.passkeyKinds[p.kind]}`, children: f.t.passkeyKinds[p.kind] }),
2439
- " \xB7 ",
2440
- when(p.createdAt),
2441
- " ",
2442
- /* @__PURE__ */ (0, import_jsx_runtime.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 })
2443
- ] }, p.id)) }),
2507
+ listed,
2444
2508
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "name", type: "text", label: f.t.passkeyName, value: name, autoComplete: "off", onChange: setName }),
2445
2509
  account.hasPassword && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword }),
2446
2510
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.addPasskeyDevice }),
2447
2511
  /* @__PURE__ */ (0, import_jsx_runtime.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 }),
2512
+ list.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cancel, { f, onClick: cancel }),
2448
2513
  done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: done })
2449
2514
  ] });
2450
2515
  }
2516
+ function Cancel({ f, onClick }) {
2517
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", disabled: f.busy, className: f.cx("providerButton"), onClick, children: f.t.cancel });
2518
+ }
2519
+ function RecoveryCodes({ f, codes }) {
2520
+ const [copied, setCopied] = (0, import_react.useState)(null);
2521
+ (0, import_react.useEffect)(() => setCopied(null), [codes]);
2522
+ const copy = async () => {
2523
+ try {
2524
+ await navigator.clipboard.writeText(codes.join("\n"));
2525
+ setCopied(true);
2526
+ } catch {
2527
+ setCopied(false);
2528
+ }
2529
+ };
2530
+ const download = () => {
2531
+ const file = new Blob([`${f.t.recoveryCodes}
2532
+
2533
+ ${codes.join("\n")}
2534
+ `], { type: "text/plain;charset=utf-8" });
2535
+ const url = URL.createObjectURL(file);
2536
+ const a = document.createElement("a");
2537
+ a.href = url;
2538
+ a.download = "recovery-codes.txt";
2539
+ document.body.appendChild(a);
2540
+ a.click();
2541
+ a.remove();
2542
+ setTimeout(() => URL.revokeObjectURL(url), 0);
2543
+ };
2544
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2545
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.keepCodes }),
2546
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: f.cx("codes"), children: codes.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: c }) }, c)) }),
2547
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", className: f.cx("providerButton"), onClick: () => void copy(), children: f.t.copyCodes }),
2548
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", className: f.cx("providerButton"), onClick: download, children: f.t.downloadCodes }),
2549
+ copied === true && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: f.t.codesCopied }),
2550
+ copied === false && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.t.codesNotCopied })
2551
+ ] });
2552
+ }
2451
2553
  function ProfilePart({ props, account, onSaved }) {
2452
2554
  const auth = useAuth();
2453
2555
  const { f, done, setDone, run, general } = useSection(props);
2454
2556
  const [name, setName] = (0, import_react.useState)(account.name);
2557
+ const [editing, setEditing] = (0, import_react.useState)(account.name.trim() === "");
2455
2558
  const submit = async (e) => {
2456
2559
  e.preventDefault();
2457
2560
  const r = await run(() => auth.account.update({ name }));
2458
2561
  if (!r.ok) return;
2459
2562
  onSaved(r.account);
2563
+ setName(r.account.name);
2564
+ setEditing(r.account.name.trim() === "");
2460
2565
  setDone(f.t.saved);
2461
2566
  props.onSaved?.({ kind: "profile.saved", name: r.account.name });
2462
2567
  };
2568
+ const cancel = () => {
2569
+ setName(account.name);
2570
+ f.setError(null);
2571
+ setEditing(false);
2572
+ };
2573
+ if (!editing) {
2574
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "section", section: "profile", children: [
2575
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.profile }),
2576
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: account.name }),
2577
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: account.email }),
2578
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", disabled: f.busy, className: f.cx("providerButton"), onClick: () => {
2579
+ setDone(null);
2580
+ setEditing(true);
2581
+ }, children: f.t.change }),
2582
+ done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: done })
2583
+ ] });
2584
+ }
2463
2585
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "profile", onSubmit: submit, noValidate: true, children: [
2464
2586
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.profile }),
2465
2587
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: account.email }),
2466
2588
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2467
2589
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "name", onChange: setName }),
2468
2590
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.save }),
2591
+ account.name.trim() !== "" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cancel, { f, onClick: cancel }),
2469
2592
  done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: done })
2470
2593
  ] });
2471
2594
  }
@@ -2474,15 +2597,35 @@ function EmailPart({ props, account }) {
2474
2597
  const { f, done, setDone, run, general } = useSection(props);
2475
2598
  const [email, setEmail] = (0, import_react.useState)("");
2476
2599
  const [password, setPassword] = (0, import_react.useState)("");
2600
+ const [editing, setEditing] = (0, import_react.useState)(false);
2477
2601
  const submit = async (e) => {
2478
2602
  e.preventDefault();
2479
2603
  if (!f.check(checkEmail(email), account.hasPassword && checkPassword(password))) return;
2480
2604
  const r = await run(() => auth.account.changeEmail({ email, password }));
2481
2605
  if (!r.ok) return;
2606
+ setEmail("");
2482
2607
  setPassword("");
2608
+ setEditing(false);
2483
2609
  setDone(f.t.emailSent.replace("{email}", r.email));
2484
2610
  props.onSaved?.({ kind: "email.change_requested", email: r.email });
2485
2611
  };
2612
+ const cancel = () => {
2613
+ setEmail("");
2614
+ setPassword("");
2615
+ f.setError(null);
2616
+ setEditing(false);
2617
+ };
2618
+ if (!editing) {
2619
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "section", section: "email", children: [
2620
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.emailAddress }),
2621
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: account.email }),
2622
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", disabled: f.busy, className: f.cx("providerButton"), onClick: () => {
2623
+ setDone(null);
2624
+ setEditing(true);
2625
+ }, children: f.t.changeEmail }),
2626
+ done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: done })
2627
+ ] });
2628
+ }
2486
2629
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "email", onSubmit: submit, noValidate: true, children: [
2487
2630
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.emailAddress }),
2488
2631
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: account.email }),
@@ -2490,7 +2633,7 @@ function EmailPart({ props, account }) {
2490
2633
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.newEmail, value: email, autoComplete: "email", required: true, onChange: setEmail }),
2491
2634
  account.hasPassword && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword }),
2492
2635
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.changeEmail }),
2493
- done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: done })
2636
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cancel, { f, onClick: cancel })
2494
2637
  ] });
2495
2638
  }
2496
2639
  function LinkedPart({ props }) {
@@ -2555,6 +2698,7 @@ function PasswordPart({ props }) {
2555
2698
  const { f, done, setDone, run, general } = useSection(props);
2556
2699
  const [password, setPassword] = (0, import_react.useState)("");
2557
2700
  const [newPassword, setNewPassword] = (0, import_react.useState)("");
2701
+ const [editing, setEditing] = (0, import_react.useState)(false);
2558
2702
  usePasswordPolicy(f, useMethods(auth));
2559
2703
  const submit = async (e) => {
2560
2704
  e.preventDefault();
@@ -2563,16 +2707,34 @@ function PasswordPart({ props }) {
2563
2707
  if (!r.ok) return;
2564
2708
  setPassword("");
2565
2709
  setNewPassword("");
2710
+ setEditing(false);
2566
2711
  setDone(f.t.passwordChangedHere);
2567
2712
  props.onSaved?.({ kind: "password.changed" });
2568
2713
  };
2714
+ const cancel = () => {
2715
+ setPassword("");
2716
+ setNewPassword("");
2717
+ f.setError(null);
2718
+ setEditing(false);
2719
+ };
2720
+ if (!editing) {
2721
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "section", section: "password", children: [
2722
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.password }),
2723
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.passwordSet }),
2724
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", disabled: f.busy, className: f.cx("providerButton"), onClick: () => {
2725
+ setDone(null);
2726
+ setEditing(true);
2727
+ }, children: f.t.changePassword }),
2728
+ done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: done })
2729
+ ] });
2730
+ }
2569
2731
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "password", onSubmit: submit, noValidate: true, children: [
2570
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.changePassword }),
2732
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.password }),
2571
2733
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2572
2734
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword }),
2573
2735
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "newPassword", type: "password", label: f.t.newPassword, value: newPassword, autoComplete: "new-password", required: true, onChange: setNewPassword, hint: f.passwordRule }),
2574
2736
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.changePassword }),
2575
- done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: done })
2737
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cancel, { f, onClick: cancel })
2576
2738
  ] });
2577
2739
  }
2578
2740
  function SecondStepPart({ props, account, onChanged }) {
@@ -2582,6 +2744,7 @@ function SecondStepPart({ props, account, onChanged }) {
2582
2744
  const [code, setCode] = (0, import_react.useState)("");
2583
2745
  const [setup, setSetup] = (0, import_react.useState)(null);
2584
2746
  const [codes, setCodes] = (0, import_react.useState)(null);
2747
+ const [asking, setAsking] = (0, import_react.useState)(null);
2585
2748
  const start = async (e) => {
2586
2749
  e.preventDefault();
2587
2750
  const r = await run(() => auth.account.mfa.setup({ password }));
@@ -2598,26 +2761,36 @@ function SecondStepPart({ props, account, onChanged }) {
2598
2761
  onChanged(true);
2599
2762
  props.onSaved?.({ kind: "mfa.enabled" });
2600
2763
  };
2601
- const turnOff = async (e) => {
2602
- e.preventDefault();
2603
- const r = await run(() => auth.account.mfa.disable({ password }));
2764
+ const turnOff = async (key) => {
2765
+ const r = await run(() => auth.account.mfa.disable({ password }), key);
2604
2766
  if (!r.ok) return;
2605
2767
  setPassword("");
2768
+ setAsking(null);
2606
2769
  setCodes(null);
2607
2770
  onChanged(false);
2608
2771
  props.onSaved?.({ kind: "mfa.disabled" });
2609
2772
  };
2610
- const renew = async () => {
2611
- const r = await run(() => auth.account.mfa.recoveryCodes({ password }), "codes");
2773
+ const renew = async (key) => {
2774
+ const r = await run(() => auth.account.mfa.recoveryCodes({ password }), key);
2612
2775
  if (!r.ok) return;
2776
+ setPassword("");
2777
+ setAsking(null);
2613
2778
  setCodes(r.recoveryCodes);
2614
2779
  props.onSaved?.({ kind: "recovery_codes.regenerated" });
2615
2780
  };
2781
+ const press = (what) => {
2782
+ f.setError(null);
2783
+ if (!account.hasPassword) return void (what === "off" ? turnOff(what) : renew(what));
2784
+ setPassword("");
2785
+ setCodes(null);
2786
+ setAsking(what);
2787
+ };
2788
+ const cancel = () => {
2789
+ setPassword("");
2790
+ f.setError(null);
2791
+ setAsking(null);
2792
+ };
2616
2793
  const passwordField = account.hasPassword && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword });
2617
- const shown = codes && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2618
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.keepCodes }),
2619
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: f.cx("codes"), children: codes.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: c }) }, c)) })
2620
- ] });
2621
2794
  if (setup) {
2622
2795
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "secondStep", onSubmit: confirm, noValidate: true, children: [
2623
2796
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.secondStep }),
@@ -2629,14 +2802,36 @@ function SecondStepPart({ props, account, onChanged }) {
2629
2802
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.verify })
2630
2803
  ] });
2631
2804
  }
2632
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "secondStep", onSubmit: account.mfaEnabled ? turnOff : start, noValidate: true, children: [
2805
+ if (!account.mfaEnabled) {
2806
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "secondStep", onSubmit: start, noValidate: true, children: [
2807
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.secondStep }),
2808
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.secondStepOff }),
2809
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2810
+ passwordField,
2811
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.turnOn })
2812
+ ] });
2813
+ }
2814
+ if (asking) {
2815
+ const submit = (e) => {
2816
+ e.preventDefault();
2817
+ void (asking === "off" ? turnOff(SUBMIT) : renew(SUBMIT));
2818
+ };
2819
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "secondStep", onSubmit: submit, noValidate: true, children: [
2820
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.secondStep }),
2821
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.secondStepOn }),
2822
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2823
+ passwordField,
2824
+ /* @__PURE__ */ (0, import_jsx_runtime.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 }),
2825
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cancel, { f, onClick: cancel })
2826
+ ] });
2827
+ }
2828
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "section", section: "secondStep", children: [
2633
2829
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.secondStep }),
2634
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: account.mfaEnabled ? f.t.secondStepOn : f.t.secondStepOff }),
2635
- shown,
2830
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.secondStepOn }),
2831
+ codes && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RecoveryCodes, { f, codes }),
2636
2832
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2637
- passwordField,
2638
- /* @__PURE__ */ (0, import_jsx_runtime.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 }),
2639
- account.mfaEnabled && /* @__PURE__ */ (0, import_jsx_runtime.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 })
2833
+ /* @__PURE__ */ (0, import_jsx_runtime.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 }),
2834
+ /* @__PURE__ */ (0, import_jsx_runtime.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 })
2640
2835
  ] });
2641
2836
  }
2642
2837
  function PhonePart({ props, account, onChanged }) {
@@ -2649,6 +2844,7 @@ function PhonePart({ props, account, onChanged }) {
2649
2844
  const [code, setCode] = (0, import_react.useState)("");
2650
2845
  const [sentTo, setSentTo] = (0, import_react.useState)("");
2651
2846
  const [codes, setCodes] = (0, import_react.useState)(null);
2847
+ const [removing, setRemoving] = (0, import_react.useState)(false);
2652
2848
  const load = () => auth.account.mfa.factors().then((w) => {
2653
2849
  setWays(w);
2654
2850
  if (w && w.phoneChannels.length > 0 && !w.phoneChannels.includes(channel)) setChannel(w.phoneChannels[0]);
@@ -2669,21 +2865,34 @@ function PhonePart({ props, account, onChanged }) {
2669
2865
  setSentTo("");
2670
2866
  setCode("");
2671
2867
  setPassword("");
2868
+ setNumber("");
2672
2869
  if (r.recoveryCodes.length > 0) setCodes(r.recoveryCodes);
2673
2870
  onChanged(true);
2674
2871
  props.onSaved?.({ kind: "phone.added", channel });
2675
2872
  await load();
2676
2873
  };
2677
- const remove = async (e) => {
2678
- e.preventDefault();
2679
- const r = await run(() => auth.account.mfa.removePhone({ password }));
2874
+ const remove = async (key) => {
2875
+ const r = await run(() => auth.account.mfa.removePhone({ password }), key);
2680
2876
  if (!r.ok) return;
2681
2877
  setPassword("");
2878
+ setRemoving(false);
2879
+ setCodes(null);
2682
2880
  await load();
2683
2881
  const left = await auth.account.mfa.factors();
2684
2882
  onChanged(Boolean(left?.totp));
2685
2883
  props.onSaved?.({ kind: "phone.removed" });
2686
2884
  };
2885
+ const press = () => {
2886
+ f.setError(null);
2887
+ if (!account.hasPassword) return void remove("remove");
2888
+ setPassword("");
2889
+ setRemoving(true);
2890
+ };
2891
+ const cancel = () => {
2892
+ setPassword("");
2893
+ f.setError(null);
2894
+ setRemoving(false);
2895
+ };
2687
2896
  const passwordField = account.hasPassword && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword });
2688
2897
  if (sentTo) {
2689
2898
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "phone", onSubmit: confirm, noValidate: true, children: [
@@ -2694,23 +2903,46 @@ function PhonePart({ props, account, onChanged }) {
2694
2903
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.verify })
2695
2904
  ] });
2696
2905
  }
2697
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "phone", onSubmit: ways.phone ? remove : add, noValidate: true, children: [
2906
+ if (ways.phone) {
2907
+ const by = ways.phone.channel === "whatsapp" ? f.t.byWhatsapp : ways.phone.channel === "sms" ? f.t.bySms : "";
2908
+ const on = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Note, { f, children: [
2909
+ f.t.phoneOn.replace("{to}", ways.phone.number),
2910
+ by && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tag, { f, plain: ` \xB7 ${by}`, children: by })
2911
+ ] });
2912
+ if (removing) {
2913
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "phone", onSubmit: (e) => {
2914
+ e.preventDefault();
2915
+ void remove(SUBMIT);
2916
+ }, noValidate: true, children: [
2917
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.phone }),
2918
+ on,
2919
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2920
+ passwordField,
2921
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "danger", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.removePhone }),
2922
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cancel, { f, onClick: cancel })
2923
+ ] });
2924
+ }
2925
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "section", section: "phone", children: [
2926
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.phone }),
2927
+ on,
2928
+ codes && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RecoveryCodes, { f, codes }),
2929
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2930
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "danger", loading: f.loading("remove"), disabled: f.busy, className: f.cx("button"), onClick: press, children: f.t.removePhone })
2931
+ ] });
2932
+ }
2933
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "phone", onSubmit: add, noValidate: true, children: [
2698
2934
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.phone }),
2699
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: ways.phone ? f.t.phoneOn.replace("{to}", ways.phone.number) : `${f.t.phoneOff} ${f.t.phoneHelp}` }),
2700
- codes && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2701
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.keepCodes }),
2702
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: f.cx("codes"), children: codes.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: c }) }, c)) })
2703
- ] }),
2935
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: `${f.t.phoneOff} ${f.t.phoneHelp}` }),
2704
2936
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2705
- !ways.phone && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "phone", type: "tel", label: f.t.phone, value: number, autoComplete: "tel", required: true, onChange: setNumber }),
2706
- !ways.phone && ways.phoneChannels.length > 1 && f.C.Select && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChannelPick, { f, value: channel, channels: ways.phoneChannels, onChange: setChannel }),
2707
- !ways.phone && ways.phoneChannels.length > 1 && !f.C.Select && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("field"), role: "radiogroup", children: ways.phoneChannels.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: f.cx("label"), children: [
2937
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "phone", type: "tel", label: f.t.phone, value: number, autoComplete: "tel", required: true, onChange: setNumber }),
2938
+ ways.phoneChannels.length > 1 && f.C.Select && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChannelPick, { f, value: channel, channels: ways.phoneChannels, onChange: setChannel }),
2939
+ ways.phoneChannels.length > 1 && !f.C.Select && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("field"), role: "radiogroup", children: ways.phoneChannels.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: f.cx("label"), children: [
2708
2940
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "radio", name: "channel", value: c, checked: channel === c, disabled: f.busy, onChange: () => setChannel(c) }),
2709
2941
  " ",
2710
2942
  c === "whatsapp" ? f.t.byWhatsapp : f.t.bySms
2711
2943
  ] }, c)) }),
2712
2944
  passwordField,
2713
- /* @__PURE__ */ (0, import_jsx_runtime.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 })
2945
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.addPhone })
2714
2946
  ] });
2715
2947
  }
2716
2948
  function AgentsPart({ props, when }) {