cauthy-auth 0.31.0 → 0.33.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
@@ -23,10 +23,20 @@ var react_exports = {};
23
23
  __export(react_exports, {
24
24
  AcceptInvite: () => AcceptInvite,
25
25
  Account: () => Account,
26
+ AccountAgents: () => AccountAgents,
27
+ AccountEmail: () => AccountEmail,
28
+ AccountLinked: () => AccountLinked,
29
+ AccountPasskeys: () => AccountPasskeys,
30
+ AccountPassword: () => AccountPassword,
31
+ AccountPhone: () => AccountPhone,
32
+ AccountProfile: () => AccountProfile,
33
+ AccountSecondStep: () => AccountSecondStep,
34
+ AccountSessions: () => AccountSessions,
26
35
  CauthyProvider: () => CauthyProvider,
27
36
  ForgotPassword: () => ForgotPassword,
28
37
  GoogleOneTap: () => GoogleOneTap,
29
38
  OrganizationMembers: () => OrganizationMembers,
39
+ PendingTerms: () => PendingTerms,
30
40
  ResetPassword: () => ResetPassword,
31
41
  SignIn: () => SignIn,
32
42
  SignUp: () => SignUp,
@@ -802,6 +812,7 @@ var TEXTS = {
802
812
  removePhone: "Remove phone",
803
813
  bySms: "SMS",
804
814
  byWhatsapp: "WhatsApp",
815
+ channel: "Send codes by",
805
816
  checkEmail: "Almost there: open the link we sent to confirm your address \u2014 it signs you in.",
806
817
  backToSignIn: "Back to sign in",
807
818
  emailMeALink: "Email me a sign-in link",
@@ -891,6 +902,9 @@ var TEXTS = {
891
902
  organizationDeleted: "{org} was deleted.",
892
903
  yes: "Yes",
893
904
  cancel: "Cancel",
905
+ termsChanged: "Our terms changed",
906
+ termsChangedHelp: "To go on, read and accept",
907
+ acceptTerms: "Accept",
894
908
  errors: {
895
909
  not_allowed: "Your role in this organization does not allow that.",
896
910
  last_manager: "Somebody else must manage the members first: give that role to another member.",
@@ -978,6 +992,7 @@ var TEXTS = {
978
992
  removePhone: "Quitar tel\xE9fono",
979
993
  bySms: "SMS",
980
994
  byWhatsapp: "WhatsApp",
995
+ channel: "Enviar los c\xF3digos por",
981
996
  checkEmail: "Casi listo: abre el enlace que te enviamos para confirmar tu correo; te deja adentro.",
982
997
  backToSignIn: "Volver a entrar",
983
998
  emailMeALink: "Env\xEDame un enlace para entrar",
@@ -1067,6 +1082,9 @@ var TEXTS = {
1067
1082
  organizationDeleted: "{org} qued\xF3 eliminada.",
1068
1083
  yes: "S\xED",
1069
1084
  cancel: "Cancelar",
1085
+ termsChanged: "Nuestros t\xE9rminos cambiaron",
1086
+ termsChangedHelp: "Para seguir, lee y acepta",
1087
+ acceptTerms: "Aceptar",
1070
1088
  errors: {
1071
1089
  not_allowed: "Tu rol en esta organizaci\xF3n no permite eso.",
1072
1090
  last_manager: "Primero alguien m\xE1s debe administrar los miembros: dale ese rol a otro miembro.",
@@ -1116,7 +1134,7 @@ var TEXTS = {
1116
1134
  }
1117
1135
  }
1118
1136
  };
1119
- var PlainButton = (p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { ...p });
1137
+ var PlainButton = ({ variant: _variant, loading: _loading, ...p }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { ...p });
1120
1138
  var PlainInput = (p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { ...p });
1121
1139
  var looks = /* @__PURE__ */ new Map();
1122
1140
  var loaded = /* @__PURE__ */ new Set();
@@ -1227,10 +1245,28 @@ function sameOrigin(to) {
1227
1245
  function useForm(props) {
1228
1246
  const t = { ...TEXTS[props.locale ?? "en"], ...props.texts, errors: { ...TEXTS[props.locale ?? "en"].errors, ...props.texts?.errors } };
1229
1247
  const cx = (slot) => props.classNames?.[slot] ?? `cauthy-${slot}`;
1230
- const Button = props.components?.Button ?? PlainButton;
1231
- const Input = props.components?.Input ?? PlainInput;
1232
- const [error, setError] = (0, import_react.useState)(null);
1233
- const [busy, setBusy] = (0, import_react.useState)(false);
1248
+ const own = (slot) => props.classNames?.[slot];
1249
+ const C = props.components ?? {};
1250
+ const Button = C.Button ?? PlainButton;
1251
+ const Input = C.Input ?? PlainInput;
1252
+ const [error, setOnlyError] = (0, import_react.useState)(null);
1253
+ const [problems, setProblems] = (0, import_react.useState)({ list: [], round: 0 });
1254
+ const setError = (0, import_react.useCallback)((e) => {
1255
+ setOnlyError(e);
1256
+ setProblems((p) => p.list.length === 0 ? p : { list: [], round: p.round });
1257
+ }, []);
1258
+ const check = (...found) => {
1259
+ const list = found.filter((e) => Boolean(e));
1260
+ setOnlyError(null);
1261
+ setProblems((p) => ({ list, round: list.length > 0 ? p.round + 1 : p.round }));
1262
+ return list.length === 0;
1263
+ };
1264
+ const fieldError = (name) => problems.list.find((e) => e.field === name) ?? (error?.field === name ? error : null);
1265
+ const firstWrong = (name) => problems.list[0]?.field === name ? problems.round : 0;
1266
+ const [acting, setActing] = (0, import_react.useState)(null);
1267
+ const busy = acting !== null;
1268
+ const setBusy = (0, import_react.useCallback)((on) => setActing(on === false ? null : on === true ? SUBMIT : on), []);
1269
+ const loading = (key = SUBMIT) => acting === key;
1234
1270
  const [policy, setPolicy] = (0, import_react.useState)(void 0);
1235
1271
  const locale = props.locale ?? "en";
1236
1272
  const say = (e) => {
@@ -1243,13 +1279,113 @@ function useForm(props) {
1243
1279
  else window.location.assign(sitePath(to) ?? sameOrigin(props.returnTo) ?? "/");
1244
1280
  };
1245
1281
  const root = useRoot(props);
1246
- return { t, cx, Button, Input, error, setError, busy, setBusy, say, done, root, policy, setPolicy, passwordRule };
1282
+ return { t, cx, own, C, Button, Input, error, setError, check, fieldError, firstWrong, busy, setBusy, loading, say, done, root, policy, setPolicy, passwordRule };
1283
+ }
1284
+ var SUBMIT = "submit";
1285
+ function Wrap(p) {
1286
+ const { Form: OwnForm } = p.f.C;
1287
+ if (OwnForm) {
1288
+ const own = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1289
+ OwnForm,
1290
+ {
1291
+ as: p.as === "form" ? "form" : "div",
1292
+ onSubmit: p.onSubmit,
1293
+ noValidate: p.noValidate,
1294
+ "aria-busy": p.busy,
1295
+ className: p.slot ? p.f.own(p.slot) : void 0,
1296
+ section: p.section,
1297
+ children: p.children
1298
+ }
1299
+ );
1300
+ return p.root?.["data-cauthy"] !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ...p.root, children: own }) : own;
1301
+ }
1302
+ const Tag2 = p.as;
1303
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tag2, { className: p.slot ? p.f.cx(p.slot) : void 0, ...p.root, onSubmit: p.onSubmit, noValidate: p.noValidate, "aria-busy": p.busy, children: p.children });
1304
+ }
1305
+ function Note({ f, children, footer }) {
1306
+ const { Text } = f.C;
1307
+ if (Text) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { as: "p", tone: "muted", size: footer ? "sm" : "md", children });
1308
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx(footer ? "footer" : "notice"), children });
1309
+ }
1310
+ function Heading({ f, children }) {
1311
+ const { Text } = f.C;
1312
+ if (Text) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { as: "h2", size: "lg", children });
1313
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children });
1314
+ }
1315
+ function Say({ f, tone, children }) {
1316
+ const { Alert } = f.C;
1317
+ if (Alert) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Alert, { tone, children });
1318
+ if (tone === "danger") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children });
1319
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "status", className: f.cx("notice"), children });
1320
+ }
1321
+ function Or({ f, children }) {
1322
+ const { Divider } = f.C;
1323
+ if (Divider) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Divider, { label: children });
1324
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("separator"), children });
1325
+ }
1326
+ function A({ f, href, newTab, children }) {
1327
+ const { Link } = f.C;
1328
+ if (Link) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Link, { href, newTab, children });
1329
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href, target: newTab ? "_blank" : void 0, rel: newTab ? "noopener" : void 0, children });
1330
+ }
1331
+ function Tag({ f, tone, children, plain }) {
1332
+ const { Badge } = f.C;
1333
+ if (Badge) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1334
+ " ",
1335
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, { tone, children })
1336
+ ] });
1337
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: plain });
1338
+ }
1339
+ function Check(p) {
1340
+ const id = (0, import_react.useId)();
1341
+ const { f } = p;
1342
+ const { Checkbox } = f.C;
1343
+ if (Checkbox) {
1344
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Checkbox, { id, name: p.name, checked: p.checked, required: p.required, className: f.own("check"), onChange: p.onChange, children: p.children });
1345
+ }
1346
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: f.cx("check"), children: [
1347
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", name: p.name, checked: p.checked, required: p.required, onChange: (e) => p.onChange(e.target.checked) }),
1348
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: p.children })
1349
+ ] });
1350
+ }
1351
+ function Pick(p) {
1352
+ const made = (0, import_react.useId)();
1353
+ const id = p.id ?? made;
1354
+ const { f } = p;
1355
+ const { Select } = f.C;
1356
+ if (Select) {
1357
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Select, { id, name: p.name, value: p.value, options: p.options, disabled: p.disabled, "aria-label": p.label, className: f.own("input"), onChange: p.onChange });
1358
+ }
1359
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { id, name: p.name, "aria-label": p.label, className: f.cx("input"), value: p.value, disabled: p.disabled, onChange: (e) => p.onChange(e.target.value), children: p.options.map((o) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: o.value, children: o.label }, o.value)) });
1360
+ }
1361
+ function Labelled({ f, id, label, children }) {
1362
+ const { Field: OwnField } = f.C;
1363
+ if (OwnField) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OwnField, { id, label, className: f.own("field"), children });
1364
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: f.cx("field"), children: [
1365
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: f.cx("label"), children: label }),
1366
+ children
1367
+ ] });
1368
+ }
1369
+ function Documents({ f, documents }) {
1370
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: documents.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
1371
+ i > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1372
+ f.t.and,
1373
+ " "
1374
+ ] }),
1375
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: d.url, newTab: true, children: d.kind === "terms" ? f.t.termsOfService : f.t.privacyPolicy }),
1376
+ " "
1377
+ ] }, d.kind)) });
1247
1378
  }
1248
1379
  var PASSWORD_RULES = ["needs_lowercase", "needs_uppercase", "needs_digit", "needs_symbol", "needs_classes", "breached"];
1249
1380
  function isPasswordRule(e) {
1250
1381
  if (e.code === "weak_password" || PASSWORD_RULES.includes(e.code)) return true;
1251
1382
  return (e.code === "too_short" || e.code === "too_long") && (e.field === "password" || e.field === "newPassword");
1252
1383
  }
1384
+ function newPasswordProblem(value, policy, field = "password") {
1385
+ const bad = checkNewPassword(value, policy);
1386
+ if (!bad) return null;
1387
+ return { code: bad.code, message: bad.message, field, ...bad.code === "required" ? {} : { reasons: passwordProblems(value, policy) } };
1388
+ }
1253
1389
  function usePasswordPolicy(f, methods) {
1254
1390
  const { setPolicy } = f;
1255
1391
  (0, import_react.useEffect)(() => {
@@ -1259,28 +1395,49 @@ function usePasswordPolicy(f, methods) {
1259
1395
  function Field(p) {
1260
1396
  const id = (0, import_react.useId)();
1261
1397
  const { f } = p;
1262
- const bad = f.error?.field === p.name ? f.error : null;
1398
+ const bad = f.fieldError(p.name);
1399
+ const focus = f.firstWrong(p.name);
1400
+ (0, import_react.useEffect)(() => {
1401
+ if (focus > 0 && typeof document !== "undefined") document.getElementById(id)?.focus();
1402
+ }, [focus, id]);
1263
1403
  const described = [bad ? `${id}-error` : "", p.hint ? `${id}-hint` : ""].filter(Boolean).join(" ");
1404
+ const input = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1405
+ f.Input,
1406
+ {
1407
+ id,
1408
+ name: p.name,
1409
+ type: p.type,
1410
+ value: p.value,
1411
+ autoComplete: p.autoComplete,
1412
+ required: p.required,
1413
+ className: f.cx("input"),
1414
+ "aria-invalid": bad ? true : void 0,
1415
+ "aria-describedby": described || void 0,
1416
+ onChange: (e) => p.onChange(e.target.value)
1417
+ }
1418
+ );
1419
+ const { Field: OwnField } = f.C;
1420
+ if (OwnField) {
1421
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1422
+ OwnField,
1423
+ {
1424
+ id,
1425
+ label: p.label,
1426
+ aside: p.aside,
1427
+ help: p.hint && !bad ? p.hint : void 0,
1428
+ error: bad ? f.say(bad) : void 0,
1429
+ required: p.required,
1430
+ className: f.own("field"),
1431
+ children: input
1432
+ }
1433
+ );
1434
+ }
1264
1435
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("field"), children: [
1265
1436
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: f.cx("label"), htmlFor: id, children: [
1266
1437
  p.label,
1267
1438
  p.aside
1268
1439
  ] }),
1269
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1270
- f.Input,
1271
- {
1272
- id,
1273
- name: p.name,
1274
- type: p.type,
1275
- value: p.value,
1276
- autoComplete: p.autoComplete,
1277
- required: p.required,
1278
- className: f.cx("input"),
1279
- "aria-invalid": bad ? true : void 0,
1280
- "aria-describedby": described || void 0,
1281
- onChange: (e) => p.onChange(e.target.value)
1282
- }
1283
- ),
1440
+ input,
1284
1441
  p.hint && !bad && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { id: `${id}-hint`, className: f.cx("hint"), children: p.hint }),
1285
1442
  bad && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { id: `${id}-error`, role: "alert", className: f.cx("fieldError"), children: f.say(bad) })
1286
1443
  ] });
@@ -1318,7 +1475,7 @@ function SecondStepForm(p) {
1318
1475
  });
1319
1476
  }, []);
1320
1477
  const sendEmail = async () => {
1321
- f.setBusy(true);
1478
+ f.setBusy("email");
1322
1479
  f.setError(null);
1323
1480
  const r = await auth.signIn.mfaSendEmail();
1324
1481
  f.setBusy(false);
@@ -1329,7 +1486,7 @@ function SecondStepForm(p) {
1329
1486
  const pushWait = (0, import_react.useRef)(null);
1330
1487
  (0, import_react.useEffect)(() => () => pushWait.current?.abort(), []);
1331
1488
  const sendPush = async () => {
1332
- f.setBusy(true);
1489
+ f.setBusy("push");
1333
1490
  f.setError(null);
1334
1491
  const r = await auth.signIn.mfaPush();
1335
1492
  f.setBusy(false);
@@ -1345,7 +1502,7 @@ function SecondStepForm(p) {
1345
1502
  f.done();
1346
1503
  };
1347
1504
  const sendCode = async () => {
1348
- f.setBusy(true);
1505
+ f.setBusy("phone");
1349
1506
  f.setError(null);
1350
1507
  const r = await auth.signIn.mfaSendCode();
1351
1508
  f.setBusy(false);
@@ -1363,23 +1520,30 @@ function SecondStepForm(p) {
1363
1520
  };
1364
1521
  const general = f.error && !f.error.field ? f.error : null;
1365
1522
  const to = phoneWay?.phone?.number ?? "";
1366
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1367
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: phoneSent ? f.t.codeSent.replace("{to}", phoneSent) : phoneWay?.email ? f.t.codeHelpEmail.replace("{to}", phoneWay.email.to) : phoneWay?.phone && !phoneWay.totp ? f.t.codeHelpPhone : f.t.codeHelp }),
1368
- general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1369
- phoneWay?.email && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendEmail(), children: f.t.sendEmailAgain }),
1370
- pushNumber > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("notice"), role: "status", "aria-live": "polite", children: [
1523
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1524
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: phoneSent ? f.t.codeSent.replace("{to}", phoneSent) : phoneWay?.email ? f.t.codeHelpEmail.replace("{to}", phoneWay.email.to) : phoneWay?.phone && !phoneWay.totp ? f.t.codeHelpPhone : f.t.codeHelp }),
1525
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
1526
+ phoneWay?.email && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", loading: f.loading("email"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendEmail(), children: f.t.sendEmailAgain }),
1527
+ pushNumber > 0 ? f.C.Text ? (
1528
+ // The application's text: the number to pick drawn large, by its own rules.
1529
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { role: "status", "aria-live": "polite", children: [
1530
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.C.Text, { as: "p", children: f.t.pushPick.replace("{n}", String(pushNumber)) }),
1531
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.C.Text, { as: "p", size: "lg", children: pushNumber }),
1532
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.C.Text, { as: "p", tone: "muted", children: f.t.pushWaiting })
1533
+ ] })
1534
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("notice"), role: "status", "aria-live": "polite", children: [
1371
1535
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: f.t.pushPick.replace("{n}", String(pushNumber)) }),
1372
1536
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontSize: 48, fontWeight: 700, margin: "4px 0", textAlign: "center" }, children: pushNumber }),
1373
1537
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: f.t.pushWaiting })
1374
- ] }) : phoneWay?.push && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("button"), onClick: () => void sendPush(), children: f.t.pushSend }),
1375
- phoneWay?.phone && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendCode(), children: (phoneWay.phone.channel === "whatsapp" ? f.t.sendCodeWhatsapp : f.t.sendCodeSms).replace("{to}", to) }),
1538
+ ] }) : phoneWay?.push && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", loading: f.loading("push"), disabled: f.busy, className: f.cx("button"), onClick: () => void sendPush(), children: f.t.pushSend }),
1539
+ phoneWay?.phone && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", loading: f.loading("phone"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendCode(), children: (phoneWay.phone.channel === "whatsapp" ? f.t.sendCodeWhatsapp : f.t.sendCodeSms).replace("{to}", to) }),
1376
1540
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
1377
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
1541
+ /* @__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 })
1378
1542
  ] });
1379
1543
  }
1380
1544
  function ProviderButtons(p) {
1381
1545
  const { f } = p;
1382
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: p.providers.map((provider) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(f.Button, { type: "button", className: f.cx("providerButton"), onClick: () => p.onPick(provider.id), children: [
1546
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: p.providers.map((provider) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(f.Button, { type: "button", variant: "secondary", className: f.cx("providerButton"), onClick: () => p.onPick(provider.id), children: [
1383
1547
  provider.iconUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: provider.iconUrl, alt: "", width: 18, height: 18, className: f.cx("providerIcon") }),
1384
1548
  f.t.continueWith,
1385
1549
  " ",
@@ -1419,7 +1583,7 @@ function SignIn(props) {
1419
1583
  }, [email, methods]);
1420
1584
  const toCompany = () => {
1421
1585
  autofill.current?.abort();
1422
- f.setBusy(true);
1586
+ f.setBusy("sso");
1423
1587
  auth.signIn.sso({ email }, { returnTo: props.returnTo });
1424
1588
  };
1425
1589
  (0, import_react.useEffect)(() => {
@@ -1461,6 +1625,7 @@ function SignIn(props) {
1461
1625
  if (methods?.magicLink) return sendLink();
1462
1626
  return;
1463
1627
  }
1628
+ if (step === "password" && !f.check(checkEmail(email), checkPassword(password))) return;
1464
1629
  f.setBusy(true);
1465
1630
  f.setError(null);
1466
1631
  const r = step === "sent" ? await auth.signIn.emailCode({ code }) : await auth.signIn.email({ email, password, captchaToken: challenge.token });
@@ -1476,7 +1641,7 @@ function SignIn(props) {
1476
1641
  };
1477
1642
  const usePasskey = async () => {
1478
1643
  autofill.current?.abort();
1479
- f.setBusy(true);
1644
+ f.setBusy("passkey");
1480
1645
  f.setError(null);
1481
1646
  const r = await auth.signIn.passkey({ returnTo: props.returnTo, locale: props.locale, email });
1482
1647
  if (r.ok && r.status === "redirected") return;
@@ -1487,7 +1652,7 @@ function SignIn(props) {
1487
1652
  };
1488
1653
  const sendLink = async () => {
1489
1654
  autofill.current?.abort();
1490
- f.setBusy(true);
1655
+ f.setBusy("link");
1491
1656
  f.setError(null);
1492
1657
  const r = await auth.signIn.magicLink({ email, captchaToken: challenge.token, create: props.createAccounts, returnTo: props.returnTo });
1493
1658
  f.setBusy(false);
@@ -1498,29 +1663,29 @@ function SignIn(props) {
1498
1663
  };
1499
1664
  const general = f.error && !f.error.field ? f.error : null;
1500
1665
  const asksEmail = offers.email;
1501
- if (step === "opening") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("form"), ...f.root, "aria-busy": "true" });
1666
+ if (step === "opening") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Wrap, { f, as: "div", slot: "form", root: f.root, busy: true });
1502
1667
  if (step === "sent") {
1503
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1504
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.signInLinkSent }),
1505
- general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1506
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.codeFromMail }),
1668
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1669
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.signInLinkSent }),
1670
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
1671
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.codeFromMail }),
1507
1672
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
1508
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
1673
+ /* @__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 })
1509
1674
  ] });
1510
1675
  }
1511
1676
  if (step === "code") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SecondStepForm, { f, auth });
1512
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1513
- general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1677
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1678
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
1514
1679
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProviderButtons, { f, providers: methods?.providers ?? [], onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
1515
- offers.passkey && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void usePasskey(), children: f.t.usePasskey }),
1516
- asksEmail && ((methods?.providers.length ?? 0) > 0 || offers.passkey) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("separator"), children: f.t.orWithEmail }),
1680
+ offers.passkey && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", loading: f.loading("passkey"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void usePasskey(), children: f.t.usePasskey }),
1681
+ asksEmail && ((methods?.providers.length ?? 0) > 0 || offers.passkey) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Or, { f, children: f.t.orWithEmail }),
1517
1682
  asksEmail && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: methods?.passkeyHere ? "username webauthn" : "username", required: true, onChange: setEmail }),
1518
- company && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(f.Button, { type: "button", disabled: f.busy, className: f.cx(company.enforced ? "button" : "providerButton"), onClick: toCompany, children: [
1683
+ company && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(f.Button, { type: "button", variant: company.enforced ? "primary" : "secondary", loading: f.loading("sso"), disabled: f.busy, className: f.cx(company.enforced ? "button" : "providerButton"), onClick: toCompany, children: [
1519
1684
  f.t.continueWith,
1520
1685
  " ",
1521
1686
  company.name
1522
1687
  ] }),
1523
- company?.enforced && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.ssoOnly }),
1688
+ company?.enforced && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.ssoOnly }),
1524
1689
  !company?.enforced && passwordOn && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1525
1690
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1526
1691
  Field,
@@ -1533,18 +1698,18 @@ function SignIn(props) {
1533
1698
  autoComplete: "current-password",
1534
1699
  required: true,
1535
1700
  onChange: setPassword,
1536
- aside: props.links?.forgotPassword ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.forgotPassword, children: f.t.forgotPassword }) : void 0
1701
+ aside: props.links?.forgotPassword ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: props.links.forgotPassword, children: f.t.forgotPassword }) : void 0
1537
1702
  }
1538
1703
  ),
1539
1704
  challenge.node,
1540
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signIn })
1705
+ /* @__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.signIn })
1541
1706
  ] }),
1542
1707
  !company?.enforced && !passwordOn && methods?.magicLink && challenge.node,
1543
- methods?.magicLink && !company?.enforced && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendLink(), children: f.t.emailMeALink }),
1544
- methods?.signUp !== false && props.links?.signUp && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: f.cx("footer"), children: [
1708
+ methods?.magicLink && !company?.enforced && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", loading: f.loading("link"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendLink(), children: f.t.emailMeALink }),
1709
+ methods?.signUp !== false && props.links?.signUp && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Note, { f, footer: true, children: [
1545
1710
  f.t.noAccount,
1546
1711
  " ",
1547
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.signUp, children: f.t.createAccount })
1712
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: props.links.signUp, children: f.t.createAccount })
1548
1713
  ] })
1549
1714
  ] });
1550
1715
  }
@@ -1580,11 +1745,19 @@ function SignUp(props) {
1580
1745
  const [acceptLegal, setAcceptLegal] = (0, import_react.useState)(false);
1581
1746
  const [consents, setConsents] = (0, import_react.useState)({});
1582
1747
  const legal = methods?.legal;
1748
+ const guest = auth.session?.user.isAnonymous === true;
1583
1749
  const submit = async (e) => {
1584
1750
  e.preventDefault();
1751
+ const asksCompany = methods?.organization?.ask === true;
1752
+ if (!f.check(
1753
+ asksCompany && checkOrganization(organization, methods?.organization?.required === true),
1754
+ checkName(name),
1755
+ checkEmail(email),
1756
+ newPasswordProblem(password, f.policy)
1757
+ )) return;
1585
1758
  f.setBusy(true);
1586
1759
  f.setError(null);
1587
- const r = auth.session?.user.isAnonymous === true ? await auth.account.upgrade({ name, email, password }) : await auth.signUp.email({
1760
+ const r = guest ? await auth.account.upgrade({ name, email, password }) : await auth.signUp.email({
1588
1761
  name,
1589
1762
  email,
1590
1763
  password,
@@ -1600,54 +1773,42 @@ function SignUp(props) {
1600
1773
  };
1601
1774
  const general = f.error && !f.error.field ? f.error : null;
1602
1775
  const passwordOn = methods?.passwordSignIn !== false;
1603
- const providers = auth.session?.user.isAnonymous === true || methods?.organization?.required ? [] : methods?.providers ?? [];
1776
+ const providers = guest || methods?.organization?.required ? [] : methods?.providers ?? [];
1604
1777
  if (sent) {
1605
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("form"), ...f.root, children: [
1606
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.checkEmail }),
1607
- props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn })
1778
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1779
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.checkEmail }),
1780
+ props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: props.links.signIn, children: f.t.backToSignIn })
1608
1781
  ] });
1609
1782
  }
1610
- const footer = props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: f.cx("footer"), children: [
1783
+ if (methods === null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Wrap, { f, as: "div", slot: "form", root: f.root, busy: true });
1784
+ const footer = props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Note, { f, footer: true, children: [
1611
1785
  f.t.haveAccount,
1612
1786
  " ",
1613
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.signIn })
1787
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: props.links.signIn, children: f.t.signIn })
1614
1788
  ] });
1615
1789
  if (!passwordOn) {
1616
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("form"), ...f.root, children: [
1617
- general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1790
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1791
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
1618
1792
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProviderButtons, { f, providers, onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
1619
1793
  footer
1620
1794
  ] });
1621
1795
  }
1622
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1623
- general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1796
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1797
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
1624
1798
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProviderButtons, { f, providers, onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
1625
- providers.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("separator"), children: f.t.orWithEmail }),
1626
- methods?.organization?.ask && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "organization", type: "text", label: f.t.company, value: organization, autoComplete: "organization", required: methods.organization.required, onChange: setOrganization }),
1799
+ providers.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Or, { f, children: f.t.orWithEmail }),
1800
+ methods.organization?.ask && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "organization", type: "text", label: f.t.company, value: organization, autoComplete: "organization", required: methods.organization.required, onChange: setOrganization }),
1627
1801
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "name", onChange: setName }),
1628
1802
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "username", required: true, onChange: setEmail }),
1629
1803
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "password", type: "password", label: f.t.password, value: password, autoComplete: "new-password", required: true, onChange: setPassword, hint: f.passwordRule }),
1630
- legal && legal.documents.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: f.cx("check"), children: [
1631
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", name: "acceptLegal", checked: acceptLegal, required: true, onChange: (e) => setAcceptLegal(e.target.checked) }),
1632
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
1633
- f.t.iAccept,
1634
- " ",
1635
- legal.documents.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
1636
- i > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1637
- f.t.and,
1638
- " "
1639
- ] }),
1640
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: d.url, target: "_blank", rel: "noopener", children: d.kind === "terms" ? f.t.termsOfService : f.t.privacyPolicy }),
1641
- " "
1642
- ] }, d.kind))
1643
- ] })
1804
+ legal && legal.documents.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Check, { f, name: "acceptLegal", checked: acceptLegal, required: true, onChange: setAcceptLegal, children: [
1805
+ f.t.iAccept,
1806
+ " ",
1807
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Documents, { f, documents: legal.documents })
1644
1808
  ] }),
1645
- legal?.consentPurposes.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: f.cx("check"), children: [
1646
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", name: `consent_${p.key}`, checked: consents[p.key] === true, required: p.required, onChange: (e) => setConsents({ ...consents, [p.key]: e.target.checked }) }),
1647
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: p.label })
1648
- ] }, p.key)),
1809
+ legal?.consentPurposes.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { f, name: `consent_${p.key}`, checked: consents[p.key] === true, required: p.required, onChange: (on) => setConsents({ ...consents, [p.key]: on }), children: p.label }, p.key)),
1649
1810
  challenge.node,
1650
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signUp }),
1811
+ /* @__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.signUp }),
1651
1812
  footer
1652
1813
  ] });
1653
1814
  }
@@ -1669,17 +1830,17 @@ function ForgotPassword(props) {
1669
1830
  };
1670
1831
  const general = f.error && !f.error.field ? f.error : null;
1671
1832
  if (sent) {
1672
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("form"), ...f.root, children: [
1673
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.linkSent }),
1674
- props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn })
1833
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1834
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.linkSent }),
1835
+ props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: props.links.signIn, children: f.t.backToSignIn })
1675
1836
  ] });
1676
1837
  }
1677
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1678
- general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1838
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1839
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
1679
1840
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "username", required: true, onChange: setEmail }),
1680
1841
  challenge.node,
1681
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.sendLink }),
1682
- props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("footer"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn }) })
1842
+ /* @__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.sendLink }),
1843
+ props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, footer: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: props.links.signIn, children: f.t.backToSignIn }) })
1683
1844
  ] });
1684
1845
  }
1685
1846
  function ResetPassword(props) {
@@ -1703,23 +1864,23 @@ function ResetPassword(props) {
1703
1864
  setChanged(true);
1704
1865
  };
1705
1866
  const general = f.error && !f.error.field ? f.error : null;
1706
- const back = props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn });
1867
+ const back = props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: props.links.signIn, children: f.t.backToSignIn });
1707
1868
  if (changed) {
1708
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("form"), ...f.root, children: [
1709
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.passwordChanged }),
1869
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1870
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.passwordChanged }),
1710
1871
  back
1711
1872
  ] });
1712
1873
  }
1713
1874
  if (token === "") {
1714
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("form"), ...f.root, children: [
1715
- general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1716
- props.links?.forgotPassword && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.forgotPassword, children: f.t.forgotPassword })
1875
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1876
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
1877
+ props.links?.forgotPassword && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: props.links.forgotPassword, children: f.t.forgotPassword })
1717
1878
  ] });
1718
1879
  }
1719
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1720
- general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1880
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1881
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
1721
1882
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "password", type: "password", label: f.t.newPassword, value: password, autoComplete: "new-password", required: true, onChange: setPassword, hint: f.passwordRule }),
1722
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy || token === null, className: f.cx("button"), children: f.t.savePassword })
1883
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy || token === null, className: f.cx("button"), children: f.t.savePassword })
1723
1884
  ] });
1724
1885
  }
1725
1886
  function VerifyEmail(props) {
@@ -1738,11 +1899,11 @@ function VerifyEmail(props) {
1738
1899
  setState("failed");
1739
1900
  });
1740
1901
  });
1741
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("form"), ...f.root, "aria-busy": state === "confirming", children: [
1742
- state === "confirming" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.confirming }),
1743
- state === "confirmed" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.emailConfirmed }),
1744
- state === "failed" && f.error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(f.error) }),
1745
- state !== "confirming" && props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn })
1902
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "form", root: f.root, busy: state === "confirming", children: [
1903
+ state === "confirming" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.confirming }),
1904
+ state === "confirmed" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.emailConfirmed }),
1905
+ state === "failed" && f.error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(f.error) }),
1906
+ state !== "confirming" && props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: props.links.signIn, children: f.t.backToSignIn })
1746
1907
  ] });
1747
1908
  }
1748
1909
  function AcceptInvite(props) {
@@ -1775,37 +1936,67 @@ function AcceptInvite(props) {
1775
1936
  if (r.error.code === "invalid_token") return setState("invalid");
1776
1937
  return f.setError(r.error);
1777
1938
  }
1939
+ props.onSaved?.({ kind: "invitation.accepted", email: invitation?.email ?? "", ...invitation?.organization ? { organization: invitation.organization } : {} });
1778
1940
  if (r.status === "mfa_required") return setState("code");
1779
1941
  f.done();
1780
1942
  };
1781
1943
  const general = f.error && !f.error.field ? f.error : null;
1782
- const back = props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn });
1944
+ const back = props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: props.links.signIn, children: f.t.backToSignIn });
1783
1945
  if (state === "opening") {
1784
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("form"), ...f.root, "aria-busy": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.openingInvitation }) });
1946
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Wrap, { f, as: "div", slot: "form", root: f.root, busy: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.openingInvitation }) });
1785
1947
  }
1786
1948
  if (state === "invalid") {
1787
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("form"), ...f.root, children: [
1788
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: general ? f.say(general) : f.t.invitationInvalid }),
1949
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1950
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: general ? f.say(general) : f.t.invitationInvalid }),
1789
1951
  back
1790
1952
  ] });
1791
1953
  }
1792
1954
  if (state === "code") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SecondStepForm, { f, auth });
1793
1955
  const workspace = invitation?.workspace ?? "";
1794
1956
  const heading = invitation?.organization ? f.t.invitedToOrganization.replace("{organization}", invitation.organization).replace("{workspace}", workspace) : f.t.invitedTo.replace("{workspace}", workspace);
1795
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1796
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: heading }),
1797
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.invitedAs.replace("{email}", invitation?.email ?? "") }),
1798
- general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1957
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1958
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: heading }),
1959
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.invitedAs.replace("{email}", invitation?.email ?? "") }),
1960
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
1799
1961
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "email", name: "username", autoComplete: "username", value: invitation?.email ?? "", readOnly: true, hidden: true }),
1800
1962
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "password", type: "password", label: f.t.choosePassword, value: password, autoComplete: "new-password", required: true, onChange: setPassword, hint: f.passwordRule }),
1801
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.acceptInvite })
1963
+ /* @__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.acceptInvite })
1964
+ ] });
1965
+ }
1966
+ function PendingTerms(props) {
1967
+ const auth = useAuth();
1968
+ const f = useForm(props);
1969
+ const [owed, setOwed] = (0, import_react.useState)(null);
1970
+ (0, import_react.useEffect)(() => {
1971
+ void auth.account.legal().then((r) => setOwed(r.ok ? r.pending : []));
1972
+ }, []);
1973
+ if (!owed || owed.length === 0) return null;
1974
+ const accept = async () => {
1975
+ f.setBusy(true);
1976
+ f.setError(null);
1977
+ const r = await auth.account.acceptLegal({ kinds: owed.map((d) => d.kind) });
1978
+ f.setBusy(false);
1979
+ if (!r.ok) return f.setError(r.error);
1980
+ setOwed([]);
1981
+ props.onSuccess?.();
1982
+ };
1983
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1984
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.termsChanged }),
1985
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Note, { f, children: [
1986
+ f.t.termsChangedHelp,
1987
+ " ",
1988
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Documents, { f, documents: owed })
1989
+ ] }),
1990
+ f.error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(f.error) }),
1991
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), onClick: () => void accept(), children: f.t.acceptTerms })
1802
1992
  ] });
1803
1993
  }
1994
+ var ACCOUNT_SECTIONS = ["profile", "email", "password", "linked", "secondStep", "phone", "passkeys", "agents", "sessions"];
1804
1995
  function useSection(props) {
1805
1996
  const f = useForm({ ...props, unstyled: true });
1806
1997
  const [done, setDone] = (0, import_react.useState)(null);
1807
- const run = async (action) => {
1808
- f.setBusy(true);
1998
+ const run = async (action, key = SUBMIT) => {
1999
+ f.setBusy(key);
1809
2000
  f.setError(null);
1810
2001
  setDone(null);
1811
2002
  const r = await action();
@@ -1819,37 +2010,64 @@ function useSection(props) {
1819
2010
  function Account(props) {
1820
2011
  const auth = useAuth();
1821
2012
  const { accountUrl } = useSession();
1822
- const { t, root } = useForm(props);
1823
- const cx = (slot) => props.classNames?.[slot] ?? `cauthy-${slot}`;
2013
+ const f = useForm(props);
1824
2014
  const [account, setAccount] = (0, import_react.useState)(null);
1825
- const [sessions, setSessions] = (0, import_react.useState)([]);
1826
2015
  (0, import_react.useEffect)(() => {
1827
2016
  void auth.account.get().then(setAccount);
1828
- void auth.account.sessions().then(setSessions);
1829
2017
  }, []);
1830
- if (account === null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cx("account"), ...root, "aria-busy": "true" });
2018
+ if (account === null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Wrap, { f, as: "div", slot: "account", root: f.root, busy: true });
1831
2019
  const when = props.formatDate ?? ((iso) => new Date(iso).toLocaleString());
1832
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: cx("account"), ...root, children: [
1833
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProfilePart, { props, account, onSaved: setAccount }),
1834
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmailPart, { props, account }),
1835
- account.hasPassword && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PasswordPart, { props }),
1836
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkedPart, { props }),
1837
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SecondStepPart, { props, account, onChanged: (mfaEnabled) => setAccount({ ...account, mfaEnabled }) }),
1838
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PhonePart, { props, account, onChanged: (mfaEnabled) => setAccount({ ...account, mfaEnabled }) }),
1839
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PasskeysPart, { props, account, accountUrl: accountUrl(), when }),
1840
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AgentsPart, { props, when }),
1841
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: cx("section"), children: [
1842
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: cx("heading"), children: t.sessions }),
1843
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: cx("sessions"), children: sessions.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: cx("session"), children: [
1844
- s.device || t.unknownDevice,
1845
- " \xB7 ",
1846
- s.ip,
1847
- " \xB7 ",
1848
- when(s.createdAt)
1849
- ] }, i)) }),
1850
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SignOutEverywhere, { props })
1851
- ] })
1852
- ] });
2020
+ const mfa = (mfaEnabled) => setAccount({ ...account, mfaEnabled });
2021
+ const part = (section) => {
2022
+ switch (section) {
2023
+ case "profile":
2024
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProfilePart, { props, account, onSaved: setAccount });
2025
+ case "email":
2026
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmailPart, { props, account });
2027
+ case "password":
2028
+ return account.hasPassword ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PasswordPart, { props }) : null;
2029
+ case "linked":
2030
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkedPart, { props });
2031
+ case "secondStep":
2032
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SecondStepPart, { props, account, onChanged: mfa });
2033
+ case "phone":
2034
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PhonePart, { props, account, onChanged: mfa });
2035
+ case "passkeys":
2036
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PasskeysPart, { props, account, accountUrl: accountUrl(), when });
2037
+ case "agents":
2038
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AgentsPart, { props, when });
2039
+ case "sessions":
2040
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SessionsPart, { props, when });
2041
+ }
2042
+ };
2043
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Wrap, { f, as: "div", slot: "account", root: f.root, children: (props.sections ?? ACCOUNT_SECTIONS).map((section) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Fragment, { children: part(section) }, section)) });
2044
+ }
2045
+ function AccountProfile(props) {
2046
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Account, { ...props, sections: ["profile"] });
2047
+ }
2048
+ function AccountEmail(props) {
2049
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Account, { ...props, sections: ["email"] });
2050
+ }
2051
+ function AccountPassword(props) {
2052
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Account, { ...props, sections: ["password"] });
2053
+ }
2054
+ function AccountLinked(props) {
2055
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Account, { ...props, sections: ["linked"] });
2056
+ }
2057
+ function AccountSecondStep(props) {
2058
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Account, { ...props, sections: ["secondStep"] });
2059
+ }
2060
+ function AccountPhone(props) {
2061
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Account, { ...props, sections: ["phone"] });
2062
+ }
2063
+ function AccountPasskeys(props) {
2064
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Account, { ...props, sections: ["passkeys"] });
2065
+ }
2066
+ function AccountAgents(props) {
2067
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Account, { ...props, sections: ["agents"] });
2068
+ }
2069
+ function AccountSessions(props) {
2070
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Account, { ...props, sections: ["sessions"] });
1853
2071
  }
1854
2072
  var OWNER = "owner";
1855
2073
  function memberActions(seat, m) {
@@ -1861,8 +2079,9 @@ function memberActions(seat, m) {
1861
2079
  inviteAgain: seat.can.manageMembers && m.invited
1862
2080
  };
1863
2081
  }
1864
- function rolesToGive(seat, roles) {
1865
- return seat.role === OWNER ? roles : roles.filter((c) => c.role !== OWNER);
2082
+ function rolesToGive(seat, roles, only) {
2083
+ const mine = seat.role === OWNER ? roles : roles.filter((c) => c.role !== OWNER);
2084
+ return only ? mine.filter((c) => only(c.role, c)) : mine;
1866
2085
  }
1867
2086
  function OrganizationMembers(props) {
1868
2087
  const auth = useAuth();
@@ -1879,6 +2098,8 @@ function OrganizationMembers(props) {
1879
2098
  const [gone, setGone] = (0, import_react.useState)(null);
1880
2099
  const id = props.organizationId;
1881
2100
  const when = props.formatDate ?? ((iso) => new Date(iso).toLocaleDateString());
2101
+ const hidden = (action) => props.hide?.includes(action) === true;
2102
+ const saved = (event) => props.onSaved?.(event);
1882
2103
  const load = async () => {
1883
2104
  const [list, people, choices] = await Promise.all([auth.organizations.list(), auth.organizations.members(id), auth.organizations.roles(id)]);
1884
2105
  const mine = list.find((o) => o.id === id) ?? null;
@@ -1886,135 +2107,173 @@ function OrganizationMembers(props) {
1886
2107
  if (mine) setName(mine.name);
1887
2108
  setMembers(people);
1888
2109
  setRoles(choices ?? []);
1889
- const offered = mine ? rolesToGive(mine, choices ?? []) : [];
2110
+ const offered = mine ? rolesToGive(mine, choices ?? [], props.roles) : [];
1890
2111
  if (offered.length > 0 && !offered.some((c) => c.role === role)) setRole(offered[0].role);
1891
2112
  };
1892
2113
  (0, import_react.useEffect)(() => {
1893
2114
  void load();
1894
2115
  }, [id]);
1895
- if (gone !== null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("account"), ...root, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "status", className: f.cx("notice"), children: gone }) });
1896
- if (seat === void 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("account"), ...root, "aria-busy": "true" });
1897
- if (seat === null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("account"), ...root, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.organizationMissing }) });
2116
+ if (gone !== null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Wrap, { f, as: "div", slot: "account", root, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: gone }) });
2117
+ if (seat === void 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Wrap, { f, as: "div", slot: "account", root, busy: true });
2118
+ if (seat === null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Wrap, { f, as: "div", slot: "account", root, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.organizationMissing }) });
1898
2119
  const manage = seat.can.manageMembers;
1899
- const giveable = rolesToGive(seat, roles);
1900
- const label = (r) => roles.find((c) => c.role === r)?.name || r;
2120
+ const giveable = rolesToGive(seat, roles, props.roles);
2121
+ const roleName = (c) => props.roleLabel?.(c) || c.name || c.role;
2122
+ const label = (r) => {
2123
+ const c = roles.find((x) => x.role === r);
2124
+ return c ? roleName(c) : r;
2125
+ };
1901
2126
  const add = async (e) => {
1902
2127
  e.preventDefault();
1903
- const r = await run(() => auth.organizations.add(id, { email, role, locale: props.locale }));
2128
+ if (!f.check(checkEmail(email))) return;
2129
+ const r = await run(() => auth.organizations.add(id, { email, role, locale: props.locale }), "add");
1904
2130
  if (!r.ok) return;
1905
2131
  setEmail("");
1906
2132
  setDone(f.t.memberAdded.replace("{email}", r.member.email));
2133
+ saved({ kind: "member.added", organizationId: id, userId: r.member.userId, email: r.member.email, role: r.member.role || role });
1907
2134
  await load();
1908
2135
  };
1909
2136
  const change = async (m, next) => {
1910
- const r = await run(() => auth.organizations.setRole(id, m.userId, next));
1911
- if (r.ok) await load();
2137
+ const r = await run(() => auth.organizations.setRole(id, m.userId, next), `role:${m.userId}`);
2138
+ if (!r.ok) return;
2139
+ saved({ kind: "member.role_changed", organizationId: id, userId: m.userId, role: next, previousRole: m.role });
2140
+ await load();
1912
2141
  };
1913
2142
  const remove = async (m) => {
1914
2143
  setAsking(null);
1915
- const r = await run(() => auth.organizations.remove(id, m.userId));
2144
+ const r = await run(() => auth.organizations.remove(id, m.userId), `remove:${m.userId}`);
1916
2145
  if (!r.ok) return;
2146
+ saved({ kind: m.you ? "member.left" : "member.removed", organizationId: id, userId: m.userId });
1917
2147
  if (m.you) setSeat(null);
1918
2148
  else await load();
1919
2149
  };
1920
2150
  const inviteAgain = async (m) => {
1921
- const r = await run(() => auth.organizations.inviteAgain(id, m.userId, { locale: props.locale }));
1922
- if (r.ok) setDone(f.t.memberInvitationSent);
2151
+ const r = await run(() => auth.organizations.inviteAgain(id, m.userId, { locale: props.locale }), `invite:${m.userId}`);
2152
+ if (!r.ok) return;
2153
+ setDone(f.t.memberInvitationSent);
2154
+ saved({ kind: "member.invited_again", organizationId: id, userId: m.userId });
1923
2155
  };
1924
2156
  const rename = async (e) => {
1925
2157
  e.preventDefault();
1926
- const r = await run(() => auth.organizations.rename(id, name));
2158
+ const r = await run(() => auth.organizations.rename(id, name), "rename");
1927
2159
  if (!r.ok) return;
1928
2160
  setSeat(r.organization);
1929
2161
  setName(r.organization.name);
1930
2162
  setDone(f.t.organizationRenamed.replace("{name}", r.organization.name));
2163
+ saved({ kind: "organization.renamed", organizationId: id, name: r.organization.name });
1931
2164
  };
1932
2165
  const close = async (how) => {
1933
2166
  setAsking(null);
1934
- const r = await run(() => how === "archive" ? auth.organizations.archive(id) : auth.organizations.delete(id));
1935
- if (r.ok) setGone((how === "archive" ? f.t.organizationArchived : f.t.organizationDeleted).replace("{org}", seat.name));
2167
+ const r = await run(() => how === "archive" ? auth.organizations.archive(id) : auth.organizations.delete(id), how);
2168
+ if (!r.ok) return;
2169
+ setGone((how === "archive" ? f.t.organizationArchived : f.t.organizationDeleted).replace("{org}", seat.name));
2170
+ saved({ kind: how === "archive" ? "organization.archived" : "organization.deleted", organizationId: id });
2171
+ };
2172
+ const ask = (key, title, question, yes) => {
2173
+ const { Confirm } = f.C;
2174
+ if (!Confirm) return setAsking(key);
2175
+ void Confirm({ title, body: question, confirmLabel: title, cancelLabel: f.t.cancel, danger: true }).then((ok) => {
2176
+ if (ok) yes();
2177
+ });
1936
2178
  };
1937
2179
  const confirming = (text2, label2, yes) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { role: "group", "aria-label": label2, children: [
1938
2180
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: text2 }),
1939
2181
  " ",
1940
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: yes, children: f.t.yes }),
1941
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(null), children: f.t.cancel })
2182
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "danger", disabled: f.busy, className: f.cx("providerButton"), onClick: yes, children: f.t.yes }),
2183
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(null), children: f.t.cancel })
1942
2184
  ] });
1943
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("account"), ...root, children: [
1944
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: f.cx("section"), children: [
1945
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h2", { className: f.cx("heading"), children: [
2185
+ const leaveQuestion = f.t.memberLeaveConfirm.replace("{org}", seat.name);
2186
+ const archiveQuestion = f.t.organizationArchiveConfirm.replace("{org}", seat.name);
2187
+ const deleteQuestion = f.t.organizationDeleteConfirm.replace("{org}", seat.name);
2188
+ const archiving = seat.can.archive && !hidden("archive");
2189
+ const deleting = seat.can.delete && !hidden("delete");
2190
+ const renaming = seat.can.update && !hidden("rename");
2191
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "account", root, children: [
2192
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "section", slot: "section", section: "members", children: [
2193
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Heading, { f, children: [
1946
2194
  f.t.members,
1947
2195
  " \xB7 ",
1948
2196
  seat.name
1949
2197
  ] }),
1950
- problem && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(problem) }),
2198
+ problem && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(problem) }),
1951
2199
  members === null ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1952
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.membersHidden }),
1953
- auth.session && (asking === auth.session.user.id ? confirming(
1954
- f.t.memberLeaveConfirm.replace("{org}", seat.name),
1955
- f.t.memberLeave,
1956
- () => void remove({ userId: auth.session.user.id, you: true })
1957
- ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(auth.session.user.id), children: f.t.memberLeave }))
2200
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.membersHidden }),
2201
+ auth.session && !hidden("leave") && (asking === auth.session.user.id ? confirming(leaveQuestion, f.t.memberLeave, () => void remove({ userId: auth.session.user.id, you: true })) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2202
+ f.Button,
2203
+ {
2204
+ type: "button",
2205
+ variant: "danger",
2206
+ loading: f.loading(`remove:${auth.session.user.id}`),
2207
+ disabled: f.busy,
2208
+ className: f.cx("providerButton"),
2209
+ onClick: () => ask(auth.session.user.id, f.t.memberLeave, leaveQuestion, () => void remove({ userId: auth.session.user.id, you: true })),
2210
+ children: f.t.memberLeave
2211
+ }
2212
+ ))
1958
2213
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: f.cx("sessions"), children: members.map((m) => {
1959
2214
  const who = m.name || m.email;
1960
2215
  const may = memberActions(seat, m);
2216
+ const removable = may.remove && !(m.you && hidden("leave"));
2217
+ const question = m.you ? leaveQuestion : f.t.memberRemoveConfirm.replace("{who}", who);
2218
+ const action = m.you ? f.t.memberLeave : f.t.memberRemove;
2219
+ const choices = giveable.some((c) => c.role === m.role) ? giveable : [...roles.filter((c) => c.role === m.role), ...giveable];
1961
2220
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: f.cx("session"), children: [
1962
2221
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
1963
2222
  who,
1964
2223
  m.name ? ` \xB7 ${m.email}` : "",
1965
- m.you ? ` (${f.t.memberYou})` : "",
1966
- m.invited ? ` \xB7 ${f.t.memberInvited}` : "",
1967
- m.manager ? ` \xB7 ${f.t.memberManager}` : "",
2224
+ m.you && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tag, { f, plain: ` (${f.t.memberYou})`, children: f.t.memberYou }),
2225
+ f.C.Badge ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2226
+ m.invited && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tag, { f, tone: "warning", plain: "", children: f.t.memberInvited }),
2227
+ m.manager && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tag, { f, tone: "success", plain: "", children: f.t.memberManager })
2228
+ ] }) : `${m.invited ? ` \xB7 ${f.t.memberInvited}` : ""}${m.manager ? ` \xB7 ${f.t.memberManager}` : ""}`,
1968
2229
  " \xB7 ",
1969
2230
  when(m.since)
1970
2231
  ] }),
1971
2232
  " ",
1972
2233
  may.changeRole && giveable.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1973
- "select",
2234
+ Pick,
1974
2235
  {
1975
- "aria-label": `${f.t.memberRole}: ${who}`,
1976
- className: f.cx("input"),
2236
+ f,
2237
+ name: `role_${m.userId}`,
2238
+ label: `${f.t.memberRole}: ${who}`,
1977
2239
  value: m.role,
1978
2240
  disabled: f.busy,
1979
- onChange: (e) => void change(m, e.target.value),
1980
- children: (giveable.some((c) => c.role === m.role) ? giveable : [...roles.filter((c) => c.role === m.role), ...giveable]).map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: c.role, children: c.name || c.role }, c.role))
2241
+ options: choices.map((c) => ({ value: c.role, label: roleName(c) })),
2242
+ onChange: (next) => void change(m, next)
1981
2243
  }
1982
- ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: label(m.role) }),
2244
+ ) : f.C.Badge ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.C.Badge, { tone: "neutral", children: label(m.role) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: label(m.role) }),
1983
2245
  " ",
1984
- asking === m.userId ? confirming(
1985
- m.you ? f.t.memberLeaveConfirm.replace("{org}", seat.name) : f.t.memberRemoveConfirm.replace("{who}", who),
1986
- m.you ? f.t.memberLeave : f.t.memberRemove,
1987
- () => void remove(m)
1988
- ) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1989
- may.inviteAgain && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void inviteAgain(m), children: f.t.memberInviteAgain }),
1990
- may.remove && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(m.userId), children: m.you ? f.t.memberLeave : f.t.memberRemove })
2246
+ asking === m.userId ? confirming(question, action, () => void remove(m)) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2247
+ may.inviteAgain && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", loading: f.loading(`invite:${m.userId}`), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void inviteAgain(m), children: f.t.memberInviteAgain }),
2248
+ removable && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "danger", loading: f.loading(`remove:${m.userId}`), disabled: f.busy, className: f.cx("providerButton"), onClick: () => ask(m.userId, action, question, () => void remove(m)), children: action })
1991
2249
  ] })
1992
2250
  ] }, m.userId);
1993
2251
  }) }),
1994
- done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "status", className: f.cx("notice"), children: done })
2252
+ done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: done })
1995
2253
  ] }),
1996
- manage && giveable.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: add, noValidate: true, children: [
1997
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: f.t.memberAdd }),
2254
+ manage && giveable.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "add-member", onSubmit: add, noValidate: true, children: [
2255
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.memberAdd }),
1998
2256
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "off", required: true, onChange: setEmail }),
1999
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: f.cx("field"), children: [
2000
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: f.cx("label"), children: f.t.memberRole }),
2001
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { name: "role", className: f.cx("input"), value: role, onChange: (e) => setRole(e.target.value), children: giveable.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: c.role, children: c.name || c.role }, c.role)) })
2002
- ] }),
2003
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.memberAdd })
2257
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AddRole, { f, value: role, options: giveable.map((c) => ({ value: c.role, label: roleName(c) })), onChange: setRole }),
2258
+ /* @__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 })
2004
2259
  ] }),
2005
- (seat.can.update || seat.can.archive || seat.can.delete) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: f.cx("section"), children: [
2006
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: f.t.organization }),
2007
- seat.can.update && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { onSubmit: rename, noValidate: true, children: [
2260
+ (renaming || archiving || deleting) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "section", slot: "section", section: "organization", children: [
2261
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.organization }),
2262
+ renaming && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", section: "rename", onSubmit: rename, noValidate: true, children: [
2008
2263
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "organization", required: true, onChange: setName }),
2009
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.organizationRename })
2264
+ /* @__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 })
2010
2265
  ] }),
2011
- asking === "archive" ? confirming(f.t.organizationArchiveConfirm.replace("{org}", seat.name), f.t.organizationArchive, () => void close("archive")) : asking === "delete" ? confirming(f.t.organizationDeleteConfirm.replace("{org}", seat.name), f.t.organizationDelete, () => void close("delete")) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2012
- seat.can.archive && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking("archive"), children: f.t.organizationArchive }),
2013
- seat.can.delete && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking("delete"), children: f.t.organizationDelete })
2266
+ 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: [
2267
+ 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 }),
2268
+ 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 })
2014
2269
  ] })
2015
2270
  ] })
2016
2271
  ] });
2017
2272
  }
2273
+ function AddRole({ f, value, options, onChange }) {
2274
+ const id = (0, import_react.useId)();
2275
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Labelled, { f, id, label: f.t.memberRole, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pick, { f, id, name: "role", value, options, onChange }) });
2276
+ }
2018
2277
  function PasskeysPart({ props, account, accountUrl, when }) {
2019
2278
  const auth = useAuth();
2020
2279
  const { f, done, setDone, run, general } = useSection(props);
@@ -2031,14 +2290,15 @@ function PasskeysPart({ props, account, accountUrl, when }) {
2031
2290
  }, []);
2032
2291
  if (here === null) return null;
2033
2292
  if (!here) {
2034
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: f.cx("section"), children: [
2035
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: f.t.passkeys }),
2036
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: accountUrl, children: f.t.managePasskeys })
2293
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "section", slot: "section", section: "passkeys", children: [
2294
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.passkeys }),
2295
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: accountUrl, children: f.t.managePasskeys })
2037
2296
  ] });
2038
2297
  }
2039
2298
  const add = async (e, kind = "device") => {
2040
2299
  e?.preventDefault();
2041
- const r = await run(() => auth.account.addPasskey({ name, password, kind }));
2300
+ if (account.hasPassword && !f.check(checkPassword(password))) return;
2301
+ const r = await run(() => auth.account.addPasskey({ name, password, kind }), kind === "security_key" ? "security_key" : SUBMIT);
2042
2302
  if (!r.ok) {
2043
2303
  if (r.error.code === "cancelled") f.setError(null);
2044
2304
  return;
@@ -2047,28 +2307,31 @@ function PasskeysPart({ props, account, accountUrl, when }) {
2047
2307
  setName("");
2048
2308
  setPassword("");
2049
2309
  setDone(f.t.passkeyAdded);
2310
+ props.onSaved?.({ kind: "passkey.added", id: r.passkey.id });
2050
2311
  };
2051
2312
  const remove = async (id) => {
2052
- const r = await run(() => auth.account.removePasskey(id));
2053
- if (r.ok) setList(list.filter((p) => p.id !== id));
2313
+ const r = await run(() => auth.account.removePasskey(id), `remove:${id}`);
2314
+ if (!r.ok) return;
2315
+ setList(list.filter((p) => p.id !== id));
2316
+ props.onSaved?.({ kind: "passkey.removed", id });
2054
2317
  };
2055
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: add, noValidate: true, children: [
2056
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: f.t.passkeys }),
2057
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.passkeysHelp }),
2058
- general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2059
- list.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), 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: [
2318
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "passkeys", onSubmit: add, noValidate: true, children: [
2319
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.passkeys }),
2320
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.passkeysHelp }),
2321
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2322
+ 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: [
2060
2323
  p.name || f.t.passkeyUnnamed,
2061
- p.kind !== "unknown" ? ` \xB7 ${f.t.passkeyKinds[p.kind]}` : "",
2324
+ p.kind !== "unknown" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tag, { f, plain: ` \xB7 ${f.t.passkeyKinds[p.kind]}`, children: f.t.passkeyKinds[p.kind] }),
2062
2325
  " \xB7 ",
2063
2326
  when(p.createdAt),
2064
2327
  " ",
2065
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void remove(p.id), children: f.t.removePasskey })
2328
+ /* @__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 })
2066
2329
  ] }, p.id)) }),
2067
2330
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "name", type: "text", label: f.t.passkeyName, value: name, autoComplete: "off", onChange: setName }),
2068
2331
  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 }),
2069
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.addPasskeyDevice }),
2070
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void add(null, "security_key"), children: f.t.addSecurityKey }),
2071
- done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "status", className: f.cx("notice"), children: done })
2332
+ /* @__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 }),
2333
+ /* @__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 }),
2334
+ done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: done })
2072
2335
  ] });
2073
2336
  }
2074
2337
  function ProfilePart({ props, account, onSaved }) {
@@ -2081,14 +2344,15 @@ function ProfilePart({ props, account, onSaved }) {
2081
2344
  if (!r.ok) return;
2082
2345
  onSaved(r.account);
2083
2346
  setDone(f.t.saved);
2347
+ props.onSaved?.({ kind: "profile.saved", name: r.account.name });
2084
2348
  };
2085
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: submit, noValidate: true, children: [
2086
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: f.t.profile }),
2087
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: account.email }),
2088
- general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2349
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "profile", onSubmit: submit, noValidate: true, children: [
2350
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.profile }),
2351
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: account.email }),
2352
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2089
2353
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "name", onChange: setName }),
2090
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.save }),
2091
- done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "status", className: f.cx("notice"), children: done })
2354
+ /* @__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 }),
2355
+ done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: done })
2092
2356
  ] });
2093
2357
  }
2094
2358
  function EmailPart({ props, account }) {
@@ -2098,19 +2362,21 @@ function EmailPart({ props, account }) {
2098
2362
  const [password, setPassword] = (0, import_react.useState)("");
2099
2363
  const submit = async (e) => {
2100
2364
  e.preventDefault();
2365
+ if (!f.check(checkEmail(email), account.hasPassword && checkPassword(password))) return;
2101
2366
  const r = await run(() => auth.account.changeEmail({ email, password }));
2102
2367
  if (!r.ok) return;
2103
2368
  setPassword("");
2104
2369
  setDone(f.t.emailSent.replace("{email}", r.email));
2370
+ props.onSaved?.({ kind: "email.change_requested", email: r.email });
2105
2371
  };
2106
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: submit, noValidate: true, children: [
2107
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: f.t.emailAddress }),
2108
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: account.email }),
2109
- general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2372
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "email", onSubmit: submit, noValidate: true, children: [
2373
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.emailAddress }),
2374
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: account.email }),
2375
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2110
2376
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.newEmail, value: email, autoComplete: "email", required: true, onChange: setEmail }),
2111
2377
  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 }),
2112
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.changeEmail }),
2113
- done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "status", className: f.cx("notice"), children: done })
2378
+ /* @__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 }),
2379
+ done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: done })
2114
2380
  ] });
2115
2381
  }
2116
2382
  function LinkedPart({ props }) {
@@ -2123,27 +2389,32 @@ function LinkedPart({ props }) {
2123
2389
  void auth.account.identities().then(setLinked);
2124
2390
  void auth.methods().then((m) => setOffered(m.providers));
2125
2391
  const back = typeof window === "undefined" ? null : new URLSearchParams(window.location.search);
2126
- if (back?.get("linked")) setCame(back.get("linked"));
2127
- else if (back?.get("auth_error")) f.setError({ code: back.get("auth_error"), message: "That account could not be linked. Try again." });
2392
+ const provider = back?.get("linked");
2393
+ if (provider) {
2394
+ setCame(provider);
2395
+ props.onSaved?.({ kind: "identity.linked", provider });
2396
+ } else if (back?.get("auth_error")) f.setError({ code: back.get("auth_error"), message: "That account could not be linked. Try again." });
2128
2397
  }, []);
2129
2398
  if (linked === null) return null;
2130
2399
  const name = (id) => offered.find((p) => p.id === id)?.name ?? id;
2131
2400
  const unlink = async (provider) => {
2132
- const r = await run(() => auth.account.unlink(provider));
2133
- if (r.ok) setLinked(linked.filter((x) => x.provider !== provider));
2401
+ const r = await run(() => auth.account.unlink(provider), `unlink:${provider}`);
2402
+ if (!r.ok) return;
2403
+ setLinked(linked.filter((x) => x.provider !== provider));
2404
+ props.onSaved?.({ kind: "identity.unlinked", provider });
2134
2405
  };
2135
2406
  const here = typeof window === "undefined" ? void 0 : window.location.pathname;
2136
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: f.cx("section"), children: [
2137
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: f.t.linkedAccounts }),
2138
- came && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "status", className: f.cx("notice"), children: f.t.linked }),
2139
- general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2140
- linked.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.noLinkedAccounts }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: f.cx("sessions"), children: linked.map((x) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: f.cx("session"), children: [
2407
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "section", slot: "section", section: "linked", children: [
2408
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.linkedAccounts }),
2409
+ came && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: f.t.linked }),
2410
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2411
+ linked.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.noLinkedAccounts }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: f.cx("sessions"), children: linked.map((x) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: f.cx("session"), children: [
2141
2412
  name(x.provider),
2142
2413
  x.email ? ` \xB7 ${x.email}` : "",
2143
2414
  " ",
2144
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void unlink(x.provider), children: f.t.unlink })
2415
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "danger", loading: f.loading(`unlink:${x.provider}`), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void unlink(x.provider), children: f.t.unlink })
2145
2416
  ] }, x.provider)) }),
2146
- offered.filter((p) => !linked.some((x) => x.provider === p.id)).map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(f.Button, { type: "button", className: f.cx("providerButton"), onClick: () => auth.account.link(p.id, { returnTo: here }), children: [
2417
+ offered.filter((p) => !linked.some((x) => x.provider === p.id)).map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(f.Button, { type: "button", variant: "secondary", className: f.cx("providerButton"), onClick: () => auth.account.link(p.id, { returnTo: here }), children: [
2147
2418
  f.t.linkWith,
2148
2419
  " ",
2149
2420
  p.name
@@ -2158,19 +2429,21 @@ function PasswordPart({ props }) {
2158
2429
  usePasswordPolicy(f, useMethods(auth));
2159
2430
  const submit = async (e) => {
2160
2431
  e.preventDefault();
2432
+ if (!f.check(checkPassword(password), newPasswordProblem(newPassword, f.policy, "newPassword"))) return;
2161
2433
  const r = await run(() => auth.account.changePassword({ password, newPassword }));
2162
2434
  if (!r.ok) return;
2163
2435
  setPassword("");
2164
2436
  setNewPassword("");
2165
2437
  setDone(f.t.passwordChangedHere);
2438
+ props.onSaved?.({ kind: "password.changed" });
2166
2439
  };
2167
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: submit, noValidate: true, children: [
2168
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: f.t.changePassword }),
2169
- general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2440
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "password", onSubmit: submit, noValidate: true, children: [
2441
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.changePassword }),
2442
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2170
2443
  /* @__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 }),
2171
2444
  /* @__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 }),
2172
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.changePassword }),
2173
- done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "status", className: f.cx("notice"), children: done })
2445
+ /* @__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 }),
2446
+ done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: done })
2174
2447
  ] });
2175
2448
  }
2176
2449
  function SecondStepPart({ props, account, onChanged }) {
@@ -2194,6 +2467,7 @@ function SecondStepPart({ props, account, onChanged }) {
2194
2467
  setPassword("");
2195
2468
  setCodes(r.recoveryCodes);
2196
2469
  onChanged(true);
2470
+ props.onSaved?.({ kind: "mfa.enabled" });
2197
2471
  };
2198
2472
  const turnOff = async (e) => {
2199
2473
  e.preventDefault();
@@ -2202,35 +2476,38 @@ function SecondStepPart({ props, account, onChanged }) {
2202
2476
  setPassword("");
2203
2477
  setCodes(null);
2204
2478
  onChanged(false);
2479
+ props.onSaved?.({ kind: "mfa.disabled" });
2205
2480
  };
2206
2481
  const renew = async () => {
2207
- const r = await run(() => auth.account.mfa.recoveryCodes({ password }));
2208
- if (r.ok) setCodes(r.recoveryCodes);
2482
+ const r = await run(() => auth.account.mfa.recoveryCodes({ password }), "codes");
2483
+ if (!r.ok) return;
2484
+ setCodes(r.recoveryCodes);
2485
+ props.onSaved?.({ kind: "recovery_codes.regenerated" });
2209
2486
  };
2210
2487
  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 });
2211
2488
  const shown = codes && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2212
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.keepCodes }),
2489
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.keepCodes }),
2213
2490
  /* @__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)) })
2214
2491
  ] });
2215
2492
  if (setup) {
2216
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: confirm, noValidate: true, children: [
2217
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: f.t.secondStep }),
2218
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.addToApp }),
2493
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "secondStep", onSubmit: confirm, noValidate: true, children: [
2494
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.secondStep }),
2495
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.addToApp }),
2219
2496
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: f.cx("secret"), children: setup.secret }),
2220
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: setup.otpauthUri, children: f.t.openInApp }),
2221
- general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2497
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: setup.otpauthUri, children: f.t.openInApp }),
2498
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2222
2499
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
2223
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
2500
+ /* @__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 })
2224
2501
  ] });
2225
2502
  }
2226
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: account.mfaEnabled ? turnOff : start, noValidate: true, children: [
2227
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: f.t.secondStep }),
2228
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: account.mfaEnabled ? f.t.secondStepOn : f.t.secondStepOff }),
2503
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "secondStep", onSubmit: account.mfaEnabled ? turnOff : start, noValidate: true, children: [
2504
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.secondStep }),
2505
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: account.mfaEnabled ? f.t.secondStepOn : f.t.secondStepOff }),
2229
2506
  shown,
2230
- general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2507
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2231
2508
  passwordField,
2232
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: account.mfaEnabled ? f.t.turnOff : f.t.turnOn }),
2233
- account.mfaEnabled && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void renew(), children: f.t.newRecoveryCodes })
2509
+ /* @__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 }),
2510
+ 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 })
2234
2511
  ] });
2235
2512
  }
2236
2513
  function PhonePart({ props, account, onChanged }) {
@@ -2265,6 +2542,7 @@ function PhonePart({ props, account, onChanged }) {
2265
2542
  setPassword("");
2266
2543
  if (r.recoveryCodes.length > 0) setCodes(r.recoveryCodes);
2267
2544
  onChanged(true);
2545
+ props.onSaved?.({ kind: "phone.added", channel });
2268
2546
  await load();
2269
2547
  };
2270
2548
  const remove = async (e) => {
@@ -2275,33 +2553,35 @@ function PhonePart({ props, account, onChanged }) {
2275
2553
  await load();
2276
2554
  const left = await auth.account.mfa.factors();
2277
2555
  onChanged(Boolean(left?.totp));
2556
+ props.onSaved?.({ kind: "phone.removed" });
2278
2557
  };
2279
2558
  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 });
2280
2559
  if (sentTo) {
2281
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: confirm, noValidate: true, children: [
2282
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: f.t.phone }),
2283
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.codeSent.replace("{to}", sentTo) }),
2284
- general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2560
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "phone", onSubmit: confirm, noValidate: true, children: [
2561
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.phone }),
2562
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.codeSent.replace("{to}", sentTo) }),
2563
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2285
2564
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
2286
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
2565
+ /* @__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 })
2287
2566
  ] });
2288
2567
  }
2289
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: ways.phone ? remove : add, noValidate: true, children: [
2290
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: f.t.phone }),
2291
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: ways.phone ? f.t.phoneOn.replace("{to}", ways.phone.number) : `${f.t.phoneOff} ${f.t.phoneHelp}` }),
2568
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "phone", onSubmit: ways.phone ? remove : add, noValidate: true, children: [
2569
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.phone }),
2570
+ /* @__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}` }),
2292
2571
  codes && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2293
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.keepCodes }),
2572
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.keepCodes }),
2294
2573
  /* @__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)) })
2295
2574
  ] }),
2296
- general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2575
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2297
2576
  !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 }),
2298
- !ways.phone && ways.phoneChannels.length > 1 && /* @__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: [
2577
+ !ways.phone && ways.phoneChannels.length > 1 && f.C.Select && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChannelPick, { f, value: channel, channels: ways.phoneChannels, onChange: setChannel }),
2578
+ !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: [
2299
2579
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "radio", name: "channel", value: c, checked: channel === c, onChange: () => setChannel(c) }),
2300
2580
  " ",
2301
2581
  c === "whatsapp" ? f.t.byWhatsapp : f.t.bySms
2302
2582
  ] }, c)) }),
2303
2583
  passwordField,
2304
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: ways.phone ? f.t.removePhone : f.t.addPhone })
2584
+ /* @__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 })
2305
2585
  ] });
2306
2586
  }
2307
2587
  function AgentsPart({ props, when }) {
@@ -2313,13 +2593,15 @@ function AgentsPart({ props, when }) {
2313
2593
  }, []);
2314
2594
  if (list.length === 0) return null;
2315
2595
  const disconnect = async (id) => {
2316
- const r = await run(() => auth.account.disconnectAgent(id));
2317
- if (r.ok) setList(list.filter((a) => a.id !== id));
2596
+ const r = await run(() => auth.account.disconnectAgent(id), `disconnect:${id}`);
2597
+ if (!r.ok) return;
2598
+ setList(list.filter((a) => a.id !== id));
2599
+ props.onSaved?.({ kind: "agent.disconnected", id });
2318
2600
  };
2319
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: f.cx("section"), children: [
2320
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: f.t.agents }),
2321
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.agentsHelp }),
2322
- general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2601
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "section", slot: "section", section: "agents", children: [
2602
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.agents }),
2603
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.agentsHelp }),
2604
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2323
2605
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: f.cx("sessions"), children: list.map((a) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: f.cx("session"), children: [
2324
2606
  a.name || f.t.anAgent,
2325
2607
  a.redirectHost ? ` (${a.redirectHost})` : "",
@@ -2329,31 +2611,65 @@ function AgentsPart({ props, when }) {
2329
2611
  " \xB7 ",
2330
2612
  when(a.lastUsedAt),
2331
2613
  " ",
2332
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void disconnect(a.id), children: f.t.disconnect })
2614
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "danger", loading: f.loading(`disconnect:${a.id}`), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void disconnect(a.id), children: f.t.disconnect })
2333
2615
  ] }, a.id)) })
2334
2616
  ] });
2335
2617
  }
2336
- function SignOutEverywhere({ props }) {
2618
+ function SessionsPart({ props, when }) {
2337
2619
  const auth = useAuth();
2338
2620
  const { f, run, general } = useSection(props);
2621
+ const [sessions, setSessions] = (0, import_react.useState)([]);
2622
+ (0, import_react.useEffect)(() => {
2623
+ void auth.account.sessions().then(setSessions);
2624
+ }, []);
2339
2625
  const end = async () => {
2340
2626
  const r = await run(() => auth.account.signOutEverywhere());
2341
2627
  if (!r.ok) return;
2628
+ props.onSaved?.({ kind: "sessions.ended" });
2342
2629
  if (props.onSignedOut) props.onSignedOut();
2343
2630
  else window.location.assign("/");
2344
2631
  };
2345
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2346
- general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2347
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("button"), onClick: () => void end(), children: f.t.signOutEverywhere })
2632
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "section", slot: "section", section: "sessions", children: [
2633
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.sessions }),
2634
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: f.cx("sessions"), children: sessions.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: f.cx("session"), children: [
2635
+ s.device || f.t.unknownDevice,
2636
+ " \xB7 ",
2637
+ s.ip,
2638
+ " \xB7 ",
2639
+ when(s.createdAt)
2640
+ ] }, i)) }),
2641
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2642
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "danger", loading: f.loading(), disabled: f.busy, className: f.cx("button"), onClick: () => void end(), children: f.t.signOutEverywhere })
2348
2643
  ] });
2349
2644
  }
2645
+ function ChannelPick({ f, value, channels, onChange }) {
2646
+ const id = (0, import_react.useId)();
2647
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Labelled, { f, id, label: f.t.channel, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2648
+ Pick,
2649
+ {
2650
+ f,
2651
+ id,
2652
+ name: "channel",
2653
+ value,
2654
+ options: channels.map((c) => ({ value: c, label: c === "whatsapp" ? f.t.byWhatsapp : f.t.bySms })),
2655
+ onChange: (c) => onChange(c)
2656
+ }
2657
+ ) });
2658
+ }
2350
2659
 
2351
2660
  // src/react.tsx
2352
2661
  var import_jsx_runtime2 = require("react/jsx-runtime");
2353
2662
  var Ctx = (0, import_react3.createContext)(null);
2663
+ var LocaleCtx = (0, import_react3.createContext)(() => void 0);
2354
2664
  var AHEAD_MS = 6e4;
2355
- function CauthyProvider({ children, basePath = "/api/auth", initialSession }) {
2665
+ function CauthyProvider({ children, basePath = "/api/auth", initialSession, locale }) {
2356
2666
  const base = withoutTrailingSlashes(basePath);
2667
+ const said = (0, import_react3.useRef)(locale);
2668
+ said.current = locale;
2669
+ const readLocale = (0, import_react3.useCallback)(() => {
2670
+ const l = said.current;
2671
+ return typeof l === "function" ? l() : l;
2672
+ }, []);
2357
2673
  const [session, setSession] = (0, import_react3.useState)(initialSession ?? null);
2358
2674
  const [status, setStatus] = (0, import_react3.useState)(
2359
2675
  initialSession === void 0 ? "loading" : initialSession === null ? "unauthenticated" : "authenticated"
@@ -2417,7 +2733,7 @@ function CauthyProvider({ children, basePath = "/api/auth", initialSession }) {
2417
2733
  }),
2418
2734
  [session, status, refresh, base]
2419
2735
  );
2420
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Ctx.Provider, { value, children });
2736
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Ctx.Provider, { value, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LocaleCtx.Provider, { value: readLocale, children }) });
2421
2737
  }
2422
2738
  function useSession() {
2423
2739
  const ctx = (0, import_react3.useContext)(Ctx);
@@ -2426,8 +2742,9 @@ function useSession() {
2426
2742
  }
2427
2743
  function useAuth() {
2428
2744
  const { session, status, refresh, basePath } = useSession();
2745
+ const locale = (0, import_react3.useContext)(LocaleCtx);
2429
2746
  return (0, import_react3.useMemo)(() => {
2430
- const client = createAuthClient({ basePath });
2747
+ const client = createAuthClient({ basePath, locale });
2431
2748
  const then = async (running) => {
2432
2749
  const r = await running;
2433
2750
  await refresh();
@@ -2464,16 +2781,26 @@ function useAuth() {
2464
2781
  status,
2465
2782
  refresh
2466
2783
  };
2467
- }, [basePath, refresh, session, status]);
2784
+ }, [basePath, locale, refresh, session, status]);
2468
2785
  }
2469
2786
  // Annotate the CommonJS export names for ESM import in node:
2470
2787
  0 && (module.exports = {
2471
2788
  AcceptInvite,
2472
2789
  Account,
2790
+ AccountAgents,
2791
+ AccountEmail,
2792
+ AccountLinked,
2793
+ AccountPasskeys,
2794
+ AccountPassword,
2795
+ AccountPhone,
2796
+ AccountProfile,
2797
+ AccountSecondStep,
2798
+ AccountSessions,
2473
2799
  CauthyProvider,
2474
2800
  ForgotPassword,
2475
2801
  GoogleOneTap,
2476
2802
  OrganizationMembers,
2803
+ PendingTerms,
2477
2804
  ResetPassword,
2478
2805
  SignIn,
2479
2806
  SignUp,