cauthy-auth 0.31.0 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.mjs CHANGED
@@ -721,8 +721,8 @@ function definePolicy(given) {
721
721
  }
722
722
 
723
723
  // src/components.tsx
724
- import { useEffect, useId, useRef, useState } from "react";
725
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
724
+ import { Fragment, useEffect, useId, useRef, useState } from "react";
725
+ import { Fragment as Fragment2, jsx, jsxs } from "react/jsx-runtime";
726
726
  var TEXTS = {
727
727
  en: {
728
728
  email: "Email",
@@ -763,6 +763,7 @@ var TEXTS = {
763
763
  removePhone: "Remove phone",
764
764
  bySms: "SMS",
765
765
  byWhatsapp: "WhatsApp",
766
+ channel: "Send codes by",
766
767
  checkEmail: "Almost there: open the link we sent to confirm your address \u2014 it signs you in.",
767
768
  backToSignIn: "Back to sign in",
768
769
  emailMeALink: "Email me a sign-in link",
@@ -852,6 +853,9 @@ var TEXTS = {
852
853
  organizationDeleted: "{org} was deleted.",
853
854
  yes: "Yes",
854
855
  cancel: "Cancel",
856
+ termsChanged: "Our terms changed",
857
+ termsChangedHelp: "To go on, read and accept",
858
+ acceptTerms: "Accept",
855
859
  errors: {
856
860
  not_allowed: "Your role in this organization does not allow that.",
857
861
  last_manager: "Somebody else must manage the members first: give that role to another member.",
@@ -939,6 +943,7 @@ var TEXTS = {
939
943
  removePhone: "Quitar tel\xE9fono",
940
944
  bySms: "SMS",
941
945
  byWhatsapp: "WhatsApp",
946
+ channel: "Enviar los c\xF3digos por",
942
947
  checkEmail: "Casi listo: abre el enlace que te enviamos para confirmar tu correo; te deja adentro.",
943
948
  backToSignIn: "Volver a entrar",
944
949
  emailMeALink: "Env\xEDame un enlace para entrar",
@@ -1028,6 +1033,9 @@ var TEXTS = {
1028
1033
  organizationDeleted: "{org} qued\xF3 eliminada.",
1029
1034
  yes: "S\xED",
1030
1035
  cancel: "Cancelar",
1036
+ termsChanged: "Nuestros t\xE9rminos cambiaron",
1037
+ termsChangedHelp: "Para seguir, lee y acepta",
1038
+ acceptTerms: "Aceptar",
1031
1039
  errors: {
1032
1040
  not_allowed: "Tu rol en esta organizaci\xF3n no permite eso.",
1033
1041
  last_manager: "Primero alguien m\xE1s debe administrar los miembros: dale ese rol a otro miembro.",
@@ -1188,8 +1196,10 @@ function sameOrigin(to) {
1188
1196
  function useForm(props) {
1189
1197
  const t = { ...TEXTS[props.locale ?? "en"], ...props.texts, errors: { ...TEXTS[props.locale ?? "en"].errors, ...props.texts?.errors } };
1190
1198
  const cx = (slot) => props.classNames?.[slot] ?? `cauthy-${slot}`;
1191
- const Button = props.components?.Button ?? PlainButton;
1192
- const Input = props.components?.Input ?? PlainInput;
1199
+ const own = (slot) => props.classNames?.[slot];
1200
+ const C = props.components ?? {};
1201
+ const Button = C.Button ?? PlainButton;
1202
+ const Input = C.Input ?? PlainInput;
1193
1203
  const [error, setError] = useState(null);
1194
1204
  const [busy, setBusy] = useState(false);
1195
1205
  const [policy, setPolicy] = useState(void 0);
@@ -1204,7 +1214,81 @@ function useForm(props) {
1204
1214
  else window.location.assign(sitePath(to) ?? sameOrigin(props.returnTo) ?? "/");
1205
1215
  };
1206
1216
  const root = useRoot(props);
1207
- return { t, cx, Button, Input, error, setError, busy, setBusy, say, done, root, policy, setPolicy, passwordRule };
1217
+ return { t, cx, own, C, Button, Input, error, setError, busy, setBusy, say, done, root, policy, setPolicy, passwordRule };
1218
+ }
1219
+ function Note({ f, children, footer }) {
1220
+ const { Text } = f.C;
1221
+ if (Text) return /* @__PURE__ */ jsx(Text, { as: "p", tone: "muted", size: footer ? "sm" : "md", children });
1222
+ return /* @__PURE__ */ jsx("p", { className: f.cx(footer ? "footer" : "notice"), children });
1223
+ }
1224
+ function Heading({ f, children }) {
1225
+ const { Text } = f.C;
1226
+ if (Text) return /* @__PURE__ */ jsx(Text, { as: "h2", size: "lg", children });
1227
+ return /* @__PURE__ */ jsx("h2", { className: f.cx("heading"), children });
1228
+ }
1229
+ function Say({ f, tone, children }) {
1230
+ const { Alert } = f.C;
1231
+ if (Alert) return /* @__PURE__ */ jsx(Alert, { tone, children });
1232
+ if (tone === "danger") return /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children });
1233
+ return /* @__PURE__ */ jsx("p", { role: "status", className: f.cx("notice"), children });
1234
+ }
1235
+ function Or({ f, children }) {
1236
+ const { Divider } = f.C;
1237
+ if (Divider) return /* @__PURE__ */ jsx(Divider, { label: children });
1238
+ return /* @__PURE__ */ jsx("p", { className: f.cx("separator"), children });
1239
+ }
1240
+ function A({ f, href, newTab, children }) {
1241
+ const { Link } = f.C;
1242
+ if (Link) return /* @__PURE__ */ jsx(Link, { href, newTab, children });
1243
+ return /* @__PURE__ */ jsx("a", { className: f.cx("link"), href, target: newTab ? "_blank" : void 0, rel: newTab ? "noopener" : void 0, children });
1244
+ }
1245
+ function Tag({ f, tone, children, plain }) {
1246
+ const { Badge } = f.C;
1247
+ if (Badge) return /* @__PURE__ */ jsxs(Fragment2, { children: [
1248
+ " ",
1249
+ /* @__PURE__ */ jsx(Badge, { tone, children })
1250
+ ] });
1251
+ return /* @__PURE__ */ jsx(Fragment2, { children: plain });
1252
+ }
1253
+ function Check(p) {
1254
+ const id = useId();
1255
+ const { f } = p;
1256
+ const { Checkbox } = f.C;
1257
+ if (Checkbox) {
1258
+ return /* @__PURE__ */ jsx(Checkbox, { id, name: p.name, checked: p.checked, required: p.required, className: f.own("check"), onChange: p.onChange, children: p.children });
1259
+ }
1260
+ return /* @__PURE__ */ jsxs("label", { className: f.cx("check"), children: [
1261
+ /* @__PURE__ */ jsx("input", { type: "checkbox", name: p.name, checked: p.checked, required: p.required, onChange: (e) => p.onChange(e.target.checked) }),
1262
+ /* @__PURE__ */ jsx("span", { children: p.children })
1263
+ ] });
1264
+ }
1265
+ function Pick(p) {
1266
+ const made = useId();
1267
+ const id = p.id ?? made;
1268
+ const { f } = p;
1269
+ const { Select } = f.C;
1270
+ if (Select) {
1271
+ return /* @__PURE__ */ 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 });
1272
+ }
1273
+ return /* @__PURE__ */ 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__ */ jsx("option", { value: o.value, children: o.label }, o.value)) });
1274
+ }
1275
+ function Labelled({ f, id, label, children }) {
1276
+ const { Field: OwnField } = f.C;
1277
+ if (OwnField) return /* @__PURE__ */ jsx(OwnField, { id, label, className: f.own("field"), children });
1278
+ return /* @__PURE__ */ jsxs("label", { className: f.cx("field"), children: [
1279
+ /* @__PURE__ */ jsx("span", { className: f.cx("label"), children: label }),
1280
+ children
1281
+ ] });
1282
+ }
1283
+ function Documents({ f, documents }) {
1284
+ return /* @__PURE__ */ jsx(Fragment2, { children: documents.map((d, i) => /* @__PURE__ */ jsxs("span", { children: [
1285
+ i > 0 && /* @__PURE__ */ jsxs(Fragment2, { children: [
1286
+ f.t.and,
1287
+ " "
1288
+ ] }),
1289
+ /* @__PURE__ */ jsx(A, { f, href: d.url, newTab: true, children: d.kind === "terms" ? f.t.termsOfService : f.t.privacyPolicy }),
1290
+ " "
1291
+ ] }, d.kind)) });
1208
1292
  }
1209
1293
  var PASSWORD_RULES = ["needs_lowercase", "needs_uppercase", "needs_digit", "needs_symbol", "needs_classes", "breached"];
1210
1294
  function isPasswordRule(e) {
@@ -1222,26 +1306,43 @@ function Field(p) {
1222
1306
  const { f } = p;
1223
1307
  const bad = f.error?.field === p.name ? f.error : null;
1224
1308
  const described = [bad ? `${id}-error` : "", p.hint ? `${id}-hint` : ""].filter(Boolean).join(" ");
1309
+ const input = /* @__PURE__ */ jsx(
1310
+ f.Input,
1311
+ {
1312
+ id,
1313
+ name: p.name,
1314
+ type: p.type,
1315
+ value: p.value,
1316
+ autoComplete: p.autoComplete,
1317
+ required: p.required,
1318
+ className: f.cx("input"),
1319
+ "aria-invalid": bad ? true : void 0,
1320
+ "aria-describedby": described || void 0,
1321
+ onChange: (e) => p.onChange(e.target.value)
1322
+ }
1323
+ );
1324
+ const { Field: OwnField } = f.C;
1325
+ if (OwnField) {
1326
+ return /* @__PURE__ */ jsx(
1327
+ OwnField,
1328
+ {
1329
+ id,
1330
+ label: p.label,
1331
+ aside: p.aside,
1332
+ help: p.hint && !bad ? p.hint : void 0,
1333
+ error: bad ? f.say(bad) : void 0,
1334
+ required: p.required,
1335
+ className: f.own("field"),
1336
+ children: input
1337
+ }
1338
+ );
1339
+ }
1225
1340
  return /* @__PURE__ */ jsxs("div", { className: f.cx("field"), children: [
1226
1341
  /* @__PURE__ */ jsxs("label", { className: f.cx("label"), htmlFor: id, children: [
1227
1342
  p.label,
1228
1343
  p.aside
1229
1344
  ] }),
1230
- /* @__PURE__ */ jsx(
1231
- f.Input,
1232
- {
1233
- id,
1234
- name: p.name,
1235
- type: p.type,
1236
- value: p.value,
1237
- autoComplete: p.autoComplete,
1238
- required: p.required,
1239
- className: f.cx("input"),
1240
- "aria-invalid": bad ? true : void 0,
1241
- "aria-describedby": described || void 0,
1242
- onChange: (e) => p.onChange(e.target.value)
1243
- }
1244
- ),
1345
+ input,
1245
1346
  p.hint && !bad && /* @__PURE__ */ jsx("span", { id: `${id}-hint`, className: f.cx("hint"), children: p.hint }),
1246
1347
  bad && /* @__PURE__ */ jsx("span", { id: `${id}-error`, role: "alert", className: f.cx("fieldError"), children: f.say(bad) })
1247
1348
  ] });
@@ -1325,10 +1426,17 @@ function SecondStepForm(p) {
1325
1426
  const general = f.error && !f.error.field ? f.error : null;
1326
1427
  const to = phoneWay?.phone?.number ?? "";
1327
1428
  return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1328
- /* @__PURE__ */ 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 }),
1329
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1429
+ /* @__PURE__ */ 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 }),
1430
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1330
1431
  phoneWay?.email && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendEmail(), children: f.t.sendEmailAgain }),
1331
- pushNumber > 0 ? /* @__PURE__ */ jsxs("div", { className: f.cx("notice"), role: "status", "aria-live": "polite", children: [
1432
+ pushNumber > 0 ? f.C.Text ? (
1433
+ // The application's text: the number to pick drawn large, by its own rules.
1434
+ /* @__PURE__ */ jsxs("div", { role: "status", "aria-live": "polite", children: [
1435
+ /* @__PURE__ */ jsx(f.C.Text, { as: "p", children: f.t.pushPick.replace("{n}", String(pushNumber)) }),
1436
+ /* @__PURE__ */ jsx(f.C.Text, { as: "p", size: "lg", children: pushNumber }),
1437
+ /* @__PURE__ */ jsx(f.C.Text, { as: "p", tone: "muted", children: f.t.pushWaiting })
1438
+ ] })
1439
+ ) : /* @__PURE__ */ jsxs("div", { className: f.cx("notice"), role: "status", "aria-live": "polite", children: [
1332
1440
  /* @__PURE__ */ jsx("p", { children: f.t.pushPick.replace("{n}", String(pushNumber)) }),
1333
1441
  /* @__PURE__ */ jsx("p", { style: { fontSize: 48, fontWeight: 700, margin: "4px 0", textAlign: "center" }, children: pushNumber }),
1334
1442
  /* @__PURE__ */ jsx("p", { children: f.t.pushWaiting })
@@ -1340,7 +1448,7 @@ function SecondStepForm(p) {
1340
1448
  }
1341
1449
  function ProviderButtons(p) {
1342
1450
  const { f } = p;
1343
- return /* @__PURE__ */ jsx(Fragment, { children: p.providers.map((provider) => /* @__PURE__ */ jsxs(f.Button, { type: "button", className: f.cx("providerButton"), onClick: () => p.onPick(provider.id), children: [
1451
+ return /* @__PURE__ */ jsx(Fragment2, { children: p.providers.map((provider) => /* @__PURE__ */ jsxs(f.Button, { type: "button", className: f.cx("providerButton"), onClick: () => p.onPick(provider.id), children: [
1344
1452
  provider.iconUrl && /* @__PURE__ */ jsx("img", { src: provider.iconUrl, alt: "", width: 18, height: 18, className: f.cx("providerIcon") }),
1345
1453
  f.t.continueWith,
1346
1454
  " ",
@@ -1462,27 +1570,27 @@ function SignIn(props) {
1462
1570
  if (step === "opening") return /* @__PURE__ */ jsx("div", { className: f.cx("form"), ...f.root, "aria-busy": "true" });
1463
1571
  if (step === "sent") {
1464
1572
  return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1465
- /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.signInLinkSent }),
1466
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1467
- /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.codeFromMail }),
1573
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.signInLinkSent }),
1574
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1575
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.codeFromMail }),
1468
1576
  /* @__PURE__ */ jsx(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
1469
1577
  /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
1470
1578
  ] });
1471
1579
  }
1472
1580
  if (step === "code") return /* @__PURE__ */ jsx(SecondStepForm, { f, auth });
1473
1581
  return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1474
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1582
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1475
1583
  /* @__PURE__ */ jsx(ProviderButtons, { f, providers: methods?.providers ?? [], onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
1476
1584
  offers.passkey && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void usePasskey(), children: f.t.usePasskey }),
1477
- asksEmail && ((methods?.providers.length ?? 0) > 0 || offers.passkey) && /* @__PURE__ */ jsx("p", { className: f.cx("separator"), children: f.t.orWithEmail }),
1585
+ asksEmail && ((methods?.providers.length ?? 0) > 0 || offers.passkey) && /* @__PURE__ */ jsx(Or, { f, children: f.t.orWithEmail }),
1478
1586
  asksEmail && /* @__PURE__ */ jsx(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: methods?.passkeyHere ? "username webauthn" : "username", required: true, onChange: setEmail }),
1479
1587
  company && /* @__PURE__ */ jsxs(f.Button, { type: "button", disabled: f.busy, className: f.cx(company.enforced ? "button" : "providerButton"), onClick: toCompany, children: [
1480
1588
  f.t.continueWith,
1481
1589
  " ",
1482
1590
  company.name
1483
1591
  ] }),
1484
- company?.enforced && /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.ssoOnly }),
1485
- !company?.enforced && passwordOn && /* @__PURE__ */ jsxs(Fragment, { children: [
1592
+ company?.enforced && /* @__PURE__ */ jsx(Note, { f, children: f.t.ssoOnly }),
1593
+ !company?.enforced && passwordOn && /* @__PURE__ */ jsxs(Fragment2, { children: [
1486
1594
  /* @__PURE__ */ jsx(
1487
1595
  Field,
1488
1596
  {
@@ -1494,7 +1602,7 @@ function SignIn(props) {
1494
1602
  autoComplete: "current-password",
1495
1603
  required: true,
1496
1604
  onChange: setPassword,
1497
- aside: props.links?.forgotPassword ? /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: props.links.forgotPassword, children: f.t.forgotPassword }) : void 0
1605
+ aside: props.links?.forgotPassword ? /* @__PURE__ */ jsx(A, { f, href: props.links.forgotPassword, children: f.t.forgotPassword }) : void 0
1498
1606
  }
1499
1607
  ),
1500
1608
  challenge.node,
@@ -1502,10 +1610,10 @@ function SignIn(props) {
1502
1610
  ] }),
1503
1611
  !company?.enforced && !passwordOn && methods?.magicLink && challenge.node,
1504
1612
  methods?.magicLink && !company?.enforced && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendLink(), children: f.t.emailMeALink }),
1505
- methods?.signUp !== false && props.links?.signUp && /* @__PURE__ */ jsxs("p", { className: f.cx("footer"), children: [
1613
+ methods?.signUp !== false && props.links?.signUp && /* @__PURE__ */ jsxs(Note, { f, footer: true, children: [
1506
1614
  f.t.noAccount,
1507
1615
  " ",
1508
- /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: props.links.signUp, children: f.t.createAccount })
1616
+ /* @__PURE__ */ jsx(A, { f, href: props.links.signUp, children: f.t.createAccount })
1509
1617
  ] })
1510
1618
  ] });
1511
1619
  }
@@ -1564,49 +1672,36 @@ function SignUp(props) {
1564
1672
  const providers = auth.session?.user.isAnonymous === true || methods?.organization?.required ? [] : methods?.providers ?? [];
1565
1673
  if (sent) {
1566
1674
  return /* @__PURE__ */ jsxs("div", { className: f.cx("form"), ...f.root, children: [
1567
- /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.checkEmail }),
1568
- props.links?.signIn && /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn })
1675
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.checkEmail }),
1676
+ props.links?.signIn && /* @__PURE__ */ jsx(A, { f, href: props.links.signIn, children: f.t.backToSignIn })
1569
1677
  ] });
1570
1678
  }
1571
- const footer = props.links?.signIn && /* @__PURE__ */ jsxs("p", { className: f.cx("footer"), children: [
1679
+ const footer = props.links?.signIn && /* @__PURE__ */ jsxs(Note, { f, footer: true, children: [
1572
1680
  f.t.haveAccount,
1573
1681
  " ",
1574
- /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.signIn })
1682
+ /* @__PURE__ */ jsx(A, { f, href: props.links.signIn, children: f.t.signIn })
1575
1683
  ] });
1576
1684
  if (!passwordOn) {
1577
1685
  return /* @__PURE__ */ jsxs("div", { className: f.cx("form"), ...f.root, children: [
1578
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1686
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1579
1687
  /* @__PURE__ */ jsx(ProviderButtons, { f, providers, onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
1580
1688
  footer
1581
1689
  ] });
1582
1690
  }
1583
1691
  return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1584
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1692
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1585
1693
  /* @__PURE__ */ jsx(ProviderButtons, { f, providers, onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
1586
- providers.length > 0 && /* @__PURE__ */ jsx("p", { className: f.cx("separator"), children: f.t.orWithEmail }),
1694
+ providers.length > 0 && /* @__PURE__ */ jsx(Or, { f, children: f.t.orWithEmail }),
1587
1695
  methods?.organization?.ask && /* @__PURE__ */ jsx(Field, { f, name: "organization", type: "text", label: f.t.company, value: organization, autoComplete: "organization", required: methods.organization.required, onChange: setOrganization }),
1588
1696
  /* @__PURE__ */ jsx(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "name", onChange: setName }),
1589
1697
  /* @__PURE__ */ jsx(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "username", required: true, onChange: setEmail }),
1590
1698
  /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.password, value: password, autoComplete: "new-password", required: true, onChange: setPassword, hint: f.passwordRule }),
1591
- legal && legal.documents.length > 0 && /* @__PURE__ */ jsxs("label", { className: f.cx("check"), children: [
1592
- /* @__PURE__ */ jsx("input", { type: "checkbox", name: "acceptLegal", checked: acceptLegal, required: true, onChange: (e) => setAcceptLegal(e.target.checked) }),
1593
- /* @__PURE__ */ jsxs("span", { children: [
1594
- f.t.iAccept,
1595
- " ",
1596
- legal.documents.map((d, i) => /* @__PURE__ */ jsxs("span", { children: [
1597
- i > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
1598
- f.t.and,
1599
- " "
1600
- ] }),
1601
- /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: d.url, target: "_blank", rel: "noopener", children: d.kind === "terms" ? f.t.termsOfService : f.t.privacyPolicy }),
1602
- " "
1603
- ] }, d.kind))
1604
- ] })
1699
+ legal && legal.documents.length > 0 && /* @__PURE__ */ jsxs(Check, { f, name: "acceptLegal", checked: acceptLegal, required: true, onChange: setAcceptLegal, children: [
1700
+ f.t.iAccept,
1701
+ " ",
1702
+ /* @__PURE__ */ jsx(Documents, { f, documents: legal.documents })
1605
1703
  ] }),
1606
- legal?.consentPurposes.map((p) => /* @__PURE__ */ jsxs("label", { className: f.cx("check"), children: [
1607
- /* @__PURE__ */ 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 }) }),
1608
- /* @__PURE__ */ jsx("span", { children: p.label })
1609
- ] }, p.key)),
1704
+ legal?.consentPurposes.map((p) => /* @__PURE__ */ 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)),
1610
1705
  challenge.node,
1611
1706
  /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signUp }),
1612
1707
  footer
@@ -1631,16 +1726,16 @@ function ForgotPassword(props) {
1631
1726
  const general = f.error && !f.error.field ? f.error : null;
1632
1727
  if (sent) {
1633
1728
  return /* @__PURE__ */ jsxs("div", { className: f.cx("form"), ...f.root, children: [
1634
- /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.linkSent }),
1635
- props.links?.signIn && /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn })
1729
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.linkSent }),
1730
+ props.links?.signIn && /* @__PURE__ */ jsx(A, { f, href: props.links.signIn, children: f.t.backToSignIn })
1636
1731
  ] });
1637
1732
  }
1638
1733
  return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1639
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1734
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1640
1735
  /* @__PURE__ */ jsx(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "username", required: true, onChange: setEmail }),
1641
1736
  challenge.node,
1642
1737
  /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.sendLink }),
1643
- props.links?.signIn && /* @__PURE__ */ jsx("p", { className: f.cx("footer"), children: /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn }) })
1738
+ props.links?.signIn && /* @__PURE__ */ jsx(Note, { f, footer: true, children: /* @__PURE__ */ jsx(A, { f, href: props.links.signIn, children: f.t.backToSignIn }) })
1644
1739
  ] });
1645
1740
  }
1646
1741
  function ResetPassword(props) {
@@ -1664,21 +1759,21 @@ function ResetPassword(props) {
1664
1759
  setChanged(true);
1665
1760
  };
1666
1761
  const general = f.error && !f.error.field ? f.error : null;
1667
- const back = props.links?.signIn && /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn });
1762
+ const back = props.links?.signIn && /* @__PURE__ */ jsx(A, { f, href: props.links.signIn, children: f.t.backToSignIn });
1668
1763
  if (changed) {
1669
1764
  return /* @__PURE__ */ jsxs("div", { className: f.cx("form"), ...f.root, children: [
1670
- /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.passwordChanged }),
1765
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.passwordChanged }),
1671
1766
  back
1672
1767
  ] });
1673
1768
  }
1674
1769
  if (token === "") {
1675
1770
  return /* @__PURE__ */ jsxs("div", { className: f.cx("form"), ...f.root, children: [
1676
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1677
- props.links?.forgotPassword && /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: props.links.forgotPassword, children: f.t.forgotPassword })
1771
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1772
+ props.links?.forgotPassword && /* @__PURE__ */ jsx(A, { f, href: props.links.forgotPassword, children: f.t.forgotPassword })
1678
1773
  ] });
1679
1774
  }
1680
1775
  return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1681
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1776
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1682
1777
  /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.newPassword, value: password, autoComplete: "new-password", required: true, onChange: setPassword, hint: f.passwordRule }),
1683
1778
  /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy || token === null, className: f.cx("button"), children: f.t.savePassword })
1684
1779
  ] });
@@ -1700,10 +1795,10 @@ function VerifyEmail(props) {
1700
1795
  });
1701
1796
  });
1702
1797
  return /* @__PURE__ */ jsxs("div", { className: f.cx("form"), ...f.root, "aria-busy": state === "confirming", children: [
1703
- state === "confirming" && /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.confirming }),
1704
- state === "confirmed" && /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.emailConfirmed }),
1705
- state === "failed" && f.error && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(f.error) }),
1706
- state !== "confirming" && props.links?.signIn && /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn })
1798
+ state === "confirming" && /* @__PURE__ */ jsx(Note, { f, children: f.t.confirming }),
1799
+ state === "confirmed" && /* @__PURE__ */ jsx(Note, { f, children: f.t.emailConfirmed }),
1800
+ state === "failed" && f.error && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(f.error) }),
1801
+ state !== "confirming" && props.links?.signIn && /* @__PURE__ */ jsx(A, { f, href: props.links.signIn, children: f.t.backToSignIn })
1707
1802
  ] });
1708
1803
  }
1709
1804
  function AcceptInvite(props) {
@@ -1736,17 +1831,18 @@ function AcceptInvite(props) {
1736
1831
  if (r.error.code === "invalid_token") return setState("invalid");
1737
1832
  return f.setError(r.error);
1738
1833
  }
1834
+ props.onSaved?.({ kind: "invitation.accepted", email: invitation?.email ?? "", ...invitation?.organization ? { organization: invitation.organization } : {} });
1739
1835
  if (r.status === "mfa_required") return setState("code");
1740
1836
  f.done();
1741
1837
  };
1742
1838
  const general = f.error && !f.error.field ? f.error : null;
1743
- const back = props.links?.signIn && /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn });
1839
+ const back = props.links?.signIn && /* @__PURE__ */ jsx(A, { f, href: props.links.signIn, children: f.t.backToSignIn });
1744
1840
  if (state === "opening") {
1745
- return /* @__PURE__ */ jsx("div", { className: f.cx("form"), ...f.root, "aria-busy": "true", children: /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.openingInvitation }) });
1841
+ return /* @__PURE__ */ jsx("div", { className: f.cx("form"), ...f.root, "aria-busy": "true", children: /* @__PURE__ */ jsx(Note, { f, children: f.t.openingInvitation }) });
1746
1842
  }
1747
1843
  if (state === "invalid") {
1748
1844
  return /* @__PURE__ */ jsxs("div", { className: f.cx("form"), ...f.root, children: [
1749
- /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: general ? f.say(general) : f.t.invitationInvalid }),
1845
+ /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: general ? f.say(general) : f.t.invitationInvalid }),
1750
1846
  back
1751
1847
  ] });
1752
1848
  }
@@ -1754,14 +1850,43 @@ function AcceptInvite(props) {
1754
1850
  const workspace = invitation?.workspace ?? "";
1755
1851
  const heading = invitation?.organization ? f.t.invitedToOrganization.replace("{organization}", invitation.organization).replace("{workspace}", workspace) : f.t.invitedTo.replace("{workspace}", workspace);
1756
1852
  return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1757
- /* @__PURE__ */ jsx("h2", { className: f.cx("heading"), children: heading }),
1758
- /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.invitedAs.replace("{email}", invitation?.email ?? "") }),
1759
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1853
+ /* @__PURE__ */ jsx(Heading, { f, children: heading }),
1854
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.invitedAs.replace("{email}", invitation?.email ?? "") }),
1855
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1760
1856
  /* @__PURE__ */ jsx("input", { type: "email", name: "username", autoComplete: "username", value: invitation?.email ?? "", readOnly: true, hidden: true }),
1761
1857
  /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.choosePassword, value: password, autoComplete: "new-password", required: true, onChange: setPassword, hint: f.passwordRule }),
1762
1858
  /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.acceptInvite })
1763
1859
  ] });
1764
1860
  }
1861
+ function PendingTerms(props) {
1862
+ const auth = useAuth();
1863
+ const f = useForm(props);
1864
+ const [owed, setOwed] = useState(null);
1865
+ useEffect(() => {
1866
+ void auth.account.legal().then((r) => setOwed(r.ok ? r.pending : []));
1867
+ }, []);
1868
+ if (!owed || owed.length === 0) return null;
1869
+ const accept = async () => {
1870
+ f.setBusy(true);
1871
+ f.setError(null);
1872
+ const r = await auth.account.acceptLegal({ kinds: owed.map((d) => d.kind) });
1873
+ f.setBusy(false);
1874
+ if (!r.ok) return f.setError(r.error);
1875
+ setOwed([]);
1876
+ props.onSuccess?.();
1877
+ };
1878
+ return /* @__PURE__ */ jsxs("div", { className: f.cx("form"), ...f.root, children: [
1879
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.termsChanged }),
1880
+ /* @__PURE__ */ jsxs(Note, { f, children: [
1881
+ f.t.termsChangedHelp,
1882
+ " ",
1883
+ /* @__PURE__ */ jsx(Documents, { f, documents: owed })
1884
+ ] }),
1885
+ f.error && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(f.error) }),
1886
+ /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("button"), onClick: () => void accept(), children: f.t.acceptTerms })
1887
+ ] });
1888
+ }
1889
+ var ACCOUNT_SECTIONS = ["profile", "email", "password", "linked", "secondStep", "phone", "passkeys", "agents", "sessions"];
1765
1890
  function useSection(props) {
1766
1891
  const f = useForm({ ...props, unstyled: true });
1767
1892
  const [done, setDone] = useState(null);
@@ -1780,37 +1905,65 @@ function useSection(props) {
1780
1905
  function Account(props) {
1781
1906
  const auth = useAuth();
1782
1907
  const { accountUrl } = useSession();
1783
- const { t, root } = useForm(props);
1908
+ const { root } = useForm(props);
1784
1909
  const cx = (slot) => props.classNames?.[slot] ?? `cauthy-${slot}`;
1785
1910
  const [account, setAccount] = useState(null);
1786
- const [sessions, setSessions] = useState([]);
1787
1911
  useEffect(() => {
1788
1912
  void auth.account.get().then(setAccount);
1789
- void auth.account.sessions().then(setSessions);
1790
1913
  }, []);
1791
1914
  if (account === null) return /* @__PURE__ */ jsx("div", { className: cx("account"), ...root, "aria-busy": "true" });
1792
1915
  const when = props.formatDate ?? ((iso) => new Date(iso).toLocaleString());
1793
- return /* @__PURE__ */ jsxs("div", { className: cx("account"), ...root, children: [
1794
- /* @__PURE__ */ jsx(ProfilePart, { props, account, onSaved: setAccount }),
1795
- /* @__PURE__ */ jsx(EmailPart, { props, account }),
1796
- account.hasPassword && /* @__PURE__ */ jsx(PasswordPart, { props }),
1797
- /* @__PURE__ */ jsx(LinkedPart, { props }),
1798
- /* @__PURE__ */ jsx(SecondStepPart, { props, account, onChanged: (mfaEnabled) => setAccount({ ...account, mfaEnabled }) }),
1799
- /* @__PURE__ */ jsx(PhonePart, { props, account, onChanged: (mfaEnabled) => setAccount({ ...account, mfaEnabled }) }),
1800
- /* @__PURE__ */ jsx(PasskeysPart, { props, account, accountUrl: accountUrl(), when }),
1801
- /* @__PURE__ */ jsx(AgentsPart, { props, when }),
1802
- /* @__PURE__ */ jsxs("section", { className: cx("section"), children: [
1803
- /* @__PURE__ */ jsx("h2", { className: cx("heading"), children: t.sessions }),
1804
- /* @__PURE__ */ jsx("ul", { className: cx("sessions"), children: sessions.map((s, i) => /* @__PURE__ */ jsxs("li", { className: cx("session"), children: [
1805
- s.device || t.unknownDevice,
1806
- " \xB7 ",
1807
- s.ip,
1808
- " \xB7 ",
1809
- when(s.createdAt)
1810
- ] }, i)) }),
1811
- /* @__PURE__ */ jsx(SignOutEverywhere, { props })
1812
- ] })
1813
- ] });
1916
+ const mfa = (mfaEnabled) => setAccount({ ...account, mfaEnabled });
1917
+ const part = (section) => {
1918
+ switch (section) {
1919
+ case "profile":
1920
+ return /* @__PURE__ */ jsx(ProfilePart, { props, account, onSaved: setAccount });
1921
+ case "email":
1922
+ return /* @__PURE__ */ jsx(EmailPart, { props, account });
1923
+ case "password":
1924
+ return account.hasPassword ? /* @__PURE__ */ jsx(PasswordPart, { props }) : null;
1925
+ case "linked":
1926
+ return /* @__PURE__ */ jsx(LinkedPart, { props });
1927
+ case "secondStep":
1928
+ return /* @__PURE__ */ jsx(SecondStepPart, { props, account, onChanged: mfa });
1929
+ case "phone":
1930
+ return /* @__PURE__ */ jsx(PhonePart, { props, account, onChanged: mfa });
1931
+ case "passkeys":
1932
+ return /* @__PURE__ */ jsx(PasskeysPart, { props, account, accountUrl: accountUrl(), when });
1933
+ case "agents":
1934
+ return /* @__PURE__ */ jsx(AgentsPart, { props, when });
1935
+ case "sessions":
1936
+ return /* @__PURE__ */ jsx(SessionsPart, { props, when });
1937
+ }
1938
+ };
1939
+ return /* @__PURE__ */ jsx("div", { className: cx("account"), ...root, children: (props.sections ?? ACCOUNT_SECTIONS).map((section) => /* @__PURE__ */ jsx(Fragment, { children: part(section) }, section)) });
1940
+ }
1941
+ function AccountProfile(props) {
1942
+ return /* @__PURE__ */ jsx(Account, { ...props, sections: ["profile"] });
1943
+ }
1944
+ function AccountEmail(props) {
1945
+ return /* @__PURE__ */ jsx(Account, { ...props, sections: ["email"] });
1946
+ }
1947
+ function AccountPassword(props) {
1948
+ return /* @__PURE__ */ jsx(Account, { ...props, sections: ["password"] });
1949
+ }
1950
+ function AccountLinked(props) {
1951
+ return /* @__PURE__ */ jsx(Account, { ...props, sections: ["linked"] });
1952
+ }
1953
+ function AccountSecondStep(props) {
1954
+ return /* @__PURE__ */ jsx(Account, { ...props, sections: ["secondStep"] });
1955
+ }
1956
+ function AccountPhone(props) {
1957
+ return /* @__PURE__ */ jsx(Account, { ...props, sections: ["phone"] });
1958
+ }
1959
+ function AccountPasskeys(props) {
1960
+ return /* @__PURE__ */ jsx(Account, { ...props, sections: ["passkeys"] });
1961
+ }
1962
+ function AccountAgents(props) {
1963
+ return /* @__PURE__ */ jsx(Account, { ...props, sections: ["agents"] });
1964
+ }
1965
+ function AccountSessions(props) {
1966
+ return /* @__PURE__ */ jsx(Account, { ...props, sections: ["sessions"] });
1814
1967
  }
1815
1968
  var OWNER = "owner";
1816
1969
  function memberActions(seat, m) {
@@ -1822,8 +1975,9 @@ function memberActions(seat, m) {
1822
1975
  inviteAgain: seat.can.manageMembers && m.invited
1823
1976
  };
1824
1977
  }
1825
- function rolesToGive(seat, roles) {
1826
- return seat.role === OWNER ? roles : roles.filter((c) => c.role !== OWNER);
1978
+ function rolesToGive(seat, roles, only) {
1979
+ const mine = seat.role === OWNER ? roles : roles.filter((c) => c.role !== OWNER);
1980
+ return only ? mine.filter((c) => only(c.role, c)) : mine;
1827
1981
  }
1828
1982
  function OrganizationMembers(props) {
1829
1983
  const auth = useAuth();
@@ -1840,6 +1994,8 @@ function OrganizationMembers(props) {
1840
1994
  const [gone, setGone] = useState(null);
1841
1995
  const id = props.organizationId;
1842
1996
  const when = props.formatDate ?? ((iso) => new Date(iso).toLocaleDateString());
1997
+ const hidden = (action) => props.hide?.includes(action) === true;
1998
+ const saved = (event) => props.onSaved?.(event);
1843
1999
  const load = async () => {
1844
2000
  const [list, people, choices] = await Promise.all([auth.organizations.list(), auth.organizations.members(id), auth.organizations.roles(id)]);
1845
2001
  const mine = list.find((o) => o.id === id) ?? null;
@@ -1847,17 +2003,17 @@ function OrganizationMembers(props) {
1847
2003
  if (mine) setName(mine.name);
1848
2004
  setMembers(people);
1849
2005
  setRoles(choices ?? []);
1850
- const offered = mine ? rolesToGive(mine, choices ?? []) : [];
2006
+ const offered = mine ? rolesToGive(mine, choices ?? [], props.roles) : [];
1851
2007
  if (offered.length > 0 && !offered.some((c) => c.role === role)) setRole(offered[0].role);
1852
2008
  };
1853
2009
  useEffect(() => {
1854
2010
  void load();
1855
2011
  }, [id]);
1856
- if (gone !== null) return /* @__PURE__ */ jsx("div", { className: f.cx("account"), ...root, children: /* @__PURE__ */ jsx("p", { role: "status", className: f.cx("notice"), children: gone }) });
2012
+ if (gone !== null) return /* @__PURE__ */ jsx("div", { className: f.cx("account"), ...root, children: /* @__PURE__ */ jsx(Say, { f, tone: "success", children: gone }) });
1857
2013
  if (seat === void 0) return /* @__PURE__ */ jsx("div", { className: f.cx("account"), ...root, "aria-busy": "true" });
1858
- if (seat === null) return /* @__PURE__ */ jsx("div", { className: f.cx("account"), ...root, children: /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.organizationMissing }) });
2014
+ if (seat === null) return /* @__PURE__ */ jsx("div", { className: f.cx("account"), ...root, children: /* @__PURE__ */ jsx(Note, { f, children: f.t.organizationMissing }) });
1859
2015
  const manage = seat.can.manageMembers;
1860
- const giveable = rolesToGive(seat, roles);
2016
+ const giveable = rolesToGive(seat, roles, props.roles);
1861
2017
  const label = (r) => roles.find((c) => c.role === r)?.name || r;
1862
2018
  const add = async (e) => {
1863
2019
  e.preventDefault();
@@ -1865,22 +2021,28 @@ function OrganizationMembers(props) {
1865
2021
  if (!r.ok) return;
1866
2022
  setEmail("");
1867
2023
  setDone(f.t.memberAdded.replace("{email}", r.member.email));
2024
+ saved({ kind: "member.added", organizationId: id, userId: r.member.userId, email: r.member.email, role: r.member.role || role });
1868
2025
  await load();
1869
2026
  };
1870
2027
  const change = async (m, next) => {
1871
2028
  const r = await run(() => auth.organizations.setRole(id, m.userId, next));
1872
- if (r.ok) await load();
2029
+ if (!r.ok) return;
2030
+ saved({ kind: "member.role_changed", organizationId: id, userId: m.userId, role: next, previousRole: m.role });
2031
+ await load();
1873
2032
  };
1874
2033
  const remove = async (m) => {
1875
2034
  setAsking(null);
1876
2035
  const r = await run(() => auth.organizations.remove(id, m.userId));
1877
2036
  if (!r.ok) return;
2037
+ saved({ kind: m.you ? "member.left" : "member.removed", organizationId: id, userId: m.userId });
1878
2038
  if (m.you) setSeat(null);
1879
2039
  else await load();
1880
2040
  };
1881
2041
  const inviteAgain = async (m) => {
1882
2042
  const r = await run(() => auth.organizations.inviteAgain(id, m.userId, { locale: props.locale }));
1883
- if (r.ok) setDone(f.t.memberInvitationSent);
2043
+ if (!r.ok) return;
2044
+ setDone(f.t.memberInvitationSent);
2045
+ saved({ kind: "member.invited_again", organizationId: id, userId: m.userId });
1884
2046
  };
1885
2047
  const rename = async (e) => {
1886
2048
  e.preventDefault();
@@ -1889,11 +2051,21 @@ function OrganizationMembers(props) {
1889
2051
  setSeat(r.organization);
1890
2052
  setName(r.organization.name);
1891
2053
  setDone(f.t.organizationRenamed.replace("{name}", r.organization.name));
2054
+ saved({ kind: "organization.renamed", organizationId: id, name: r.organization.name });
1892
2055
  };
1893
2056
  const close = async (how) => {
1894
2057
  setAsking(null);
1895
2058
  const r = await run(() => how === "archive" ? auth.organizations.archive(id) : auth.organizations.delete(id));
1896
- if (r.ok) setGone((how === "archive" ? f.t.organizationArchived : f.t.organizationDeleted).replace("{org}", seat.name));
2059
+ if (!r.ok) return;
2060
+ setGone((how === "archive" ? f.t.organizationArchived : f.t.organizationDeleted).replace("{org}", seat.name));
2061
+ saved({ kind: how === "archive" ? "organization.archived" : "organization.deleted", organizationId: id });
2062
+ };
2063
+ const ask = (key, title, question, yes) => {
2064
+ const { Confirm } = f.C;
2065
+ if (!Confirm) return setAsking(key);
2066
+ void Confirm({ title, body: question, confirmLabel: title, cancelLabel: f.t.cancel, danger: true }).then((ok) => {
2067
+ if (ok) yes();
2068
+ });
1897
2069
  };
1898
2070
  const confirming = (text2, label2, yes) => /* @__PURE__ */ jsxs("span", { role: "group", "aria-label": label2, children: [
1899
2071
  /* @__PURE__ */ jsx("span", { children: text2 }),
@@ -1901,81 +2073,96 @@ function OrganizationMembers(props) {
1901
2073
  /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: yes, children: f.t.yes }),
1902
2074
  /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(null), children: f.t.cancel })
1903
2075
  ] });
2076
+ const leaveQuestion = f.t.memberLeaveConfirm.replace("{org}", seat.name);
2077
+ const archiveQuestion = f.t.organizationArchiveConfirm.replace("{org}", seat.name);
2078
+ const deleteQuestion = f.t.organizationDeleteConfirm.replace("{org}", seat.name);
2079
+ const archiving = seat.can.archive && !hidden("archive");
2080
+ const deleting = seat.can.delete && !hidden("delete");
2081
+ const renaming = seat.can.update && !hidden("rename");
1904
2082
  return /* @__PURE__ */ jsxs("div", { className: f.cx("account"), ...root, children: [
1905
2083
  /* @__PURE__ */ jsxs("section", { className: f.cx("section"), children: [
1906
- /* @__PURE__ */ jsxs("h2", { className: f.cx("heading"), children: [
2084
+ /* @__PURE__ */ jsxs(Heading, { f, children: [
1907
2085
  f.t.members,
1908
2086
  " \xB7 ",
1909
2087
  seat.name
1910
2088
  ] }),
1911
- problem && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(problem) }),
1912
- members === null ? /* @__PURE__ */ jsxs(Fragment, { children: [
1913
- /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.membersHidden }),
1914
- auth.session && (asking === auth.session.user.id ? confirming(
1915
- f.t.memberLeaveConfirm.replace("{org}", seat.name),
1916
- f.t.memberLeave,
1917
- () => void remove({ userId: auth.session.user.id, you: true })
1918
- ) : /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(auth.session.user.id), children: f.t.memberLeave }))
2089
+ problem && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(problem) }),
2090
+ members === null ? /* @__PURE__ */ jsxs(Fragment2, { children: [
2091
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.membersHidden }),
2092
+ auth.session && !hidden("leave") && (asking === auth.session.user.id ? confirming(leaveQuestion, f.t.memberLeave, () => void remove({ userId: auth.session.user.id, you: true })) : /* @__PURE__ */ jsx(
2093
+ f.Button,
2094
+ {
2095
+ type: "button",
2096
+ disabled: f.busy,
2097
+ className: f.cx("providerButton"),
2098
+ onClick: () => ask(auth.session.user.id, f.t.memberLeave, leaveQuestion, () => void remove({ userId: auth.session.user.id, you: true })),
2099
+ children: f.t.memberLeave
2100
+ }
2101
+ ))
1919
2102
  ] }) : /* @__PURE__ */ jsx("ul", { className: f.cx("sessions"), children: members.map((m) => {
1920
2103
  const who = m.name || m.email;
1921
2104
  const may = memberActions(seat, m);
2105
+ const removable = may.remove && !(m.you && hidden("leave"));
2106
+ const question = m.you ? leaveQuestion : f.t.memberRemoveConfirm.replace("{who}", who);
2107
+ const action = m.you ? f.t.memberLeave : f.t.memberRemove;
2108
+ const choices = giveable.some((c) => c.role === m.role) ? giveable : [...roles.filter((c) => c.role === m.role), ...giveable];
1922
2109
  return /* @__PURE__ */ jsxs("li", { className: f.cx("session"), children: [
1923
2110
  /* @__PURE__ */ jsxs("span", { children: [
1924
2111
  who,
1925
2112
  m.name ? ` \xB7 ${m.email}` : "",
1926
- m.you ? ` (${f.t.memberYou})` : "",
1927
- m.invited ? ` \xB7 ${f.t.memberInvited}` : "",
1928
- m.manager ? ` \xB7 ${f.t.memberManager}` : "",
2113
+ m.you && /* @__PURE__ */ jsx(Tag, { f, plain: ` (${f.t.memberYou})`, children: f.t.memberYou }),
2114
+ f.C.Badge ? /* @__PURE__ */ jsxs(Fragment2, { children: [
2115
+ m.invited && /* @__PURE__ */ jsx(Tag, { f, tone: "warning", plain: "", children: f.t.memberInvited }),
2116
+ m.manager && /* @__PURE__ */ jsx(Tag, { f, tone: "success", plain: "", children: f.t.memberManager })
2117
+ ] }) : `${m.invited ? ` \xB7 ${f.t.memberInvited}` : ""}${m.manager ? ` \xB7 ${f.t.memberManager}` : ""}`,
1929
2118
  " \xB7 ",
1930
2119
  when(m.since)
1931
2120
  ] }),
1932
2121
  " ",
1933
2122
  may.changeRole && giveable.length > 0 ? /* @__PURE__ */ jsx(
1934
- "select",
2123
+ Pick,
1935
2124
  {
1936
- "aria-label": `${f.t.memberRole}: ${who}`,
1937
- className: f.cx("input"),
2125
+ f,
2126
+ name: `role_${m.userId}`,
2127
+ label: `${f.t.memberRole}: ${who}`,
1938
2128
  value: m.role,
1939
2129
  disabled: f.busy,
1940
- onChange: (e) => void change(m, e.target.value),
1941
- children: (giveable.some((c) => c.role === m.role) ? giveable : [...roles.filter((c) => c.role === m.role), ...giveable]).map((c) => /* @__PURE__ */ jsx("option", { value: c.role, children: c.name || c.role }, c.role))
2130
+ options: choices.map((c) => ({ value: c.role, label: c.name || c.role })),
2131
+ onChange: (next) => void change(m, next)
1942
2132
  }
1943
- ) : /* @__PURE__ */ jsx("span", { children: label(m.role) }),
2133
+ ) : f.C.Badge ? /* @__PURE__ */ jsx(f.C.Badge, { tone: "neutral", children: label(m.role) }) : /* @__PURE__ */ jsx("span", { children: label(m.role) }),
1944
2134
  " ",
1945
- asking === m.userId ? confirming(
1946
- m.you ? f.t.memberLeaveConfirm.replace("{org}", seat.name) : f.t.memberRemoveConfirm.replace("{who}", who),
1947
- m.you ? f.t.memberLeave : f.t.memberRemove,
1948
- () => void remove(m)
1949
- ) : /* @__PURE__ */ jsxs(Fragment, { children: [
2135
+ asking === m.userId ? confirming(question, action, () => void remove(m)) : /* @__PURE__ */ jsxs(Fragment2, { children: [
1950
2136
  may.inviteAgain && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void inviteAgain(m), children: f.t.memberInviteAgain }),
1951
- may.remove && /* @__PURE__ */ 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 })
2137
+ removable && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => ask(m.userId, action, question, () => void remove(m)), children: action })
1952
2138
  ] })
1953
2139
  ] }, m.userId);
1954
2140
  }) }),
1955
- done && /* @__PURE__ */ jsx("p", { role: "status", className: f.cx("notice"), children: done })
2141
+ done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
1956
2142
  ] }),
1957
2143
  manage && giveable.length > 0 && /* @__PURE__ */ jsxs("form", { className: f.cx("section"), onSubmit: add, noValidate: true, children: [
1958
- /* @__PURE__ */ jsx("h2", { className: f.cx("heading"), children: f.t.memberAdd }),
2144
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.memberAdd }),
1959
2145
  /* @__PURE__ */ jsx(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "off", required: true, onChange: setEmail }),
1960
- /* @__PURE__ */ jsxs("label", { className: f.cx("field"), children: [
1961
- /* @__PURE__ */ jsx("span", { className: f.cx("label"), children: f.t.memberRole }),
1962
- /* @__PURE__ */ jsx("select", { name: "role", className: f.cx("input"), value: role, onChange: (e) => setRole(e.target.value), children: giveable.map((c) => /* @__PURE__ */ jsx("option", { value: c.role, children: c.name || c.role }, c.role)) })
1963
- ] }),
2146
+ /* @__PURE__ */ jsx(AddRole, { f, value: role, options: giveable.map((c) => ({ value: c.role, label: c.name || c.role })), onChange: setRole }),
1964
2147
  /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.memberAdd })
1965
2148
  ] }),
1966
- (seat.can.update || seat.can.archive || seat.can.delete) && /* @__PURE__ */ jsxs("section", { className: f.cx("section"), children: [
1967
- /* @__PURE__ */ jsx("h2", { className: f.cx("heading"), children: f.t.organization }),
1968
- seat.can.update && /* @__PURE__ */ jsxs("form", { onSubmit: rename, noValidate: true, children: [
2149
+ (renaming || archiving || deleting) && /* @__PURE__ */ jsxs("section", { className: f.cx("section"), children: [
2150
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.organization }),
2151
+ renaming && /* @__PURE__ */ jsxs("form", { onSubmit: rename, noValidate: true, children: [
1969
2152
  /* @__PURE__ */ jsx(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "organization", required: true, onChange: setName }),
1970
2153
  /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.organizationRename })
1971
2154
  ] }),
1972
- 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__ */ jsxs(Fragment, { children: [
1973
- seat.can.archive && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking("archive"), children: f.t.organizationArchive }),
1974
- seat.can.delete && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking("delete"), children: f.t.organizationDelete })
2155
+ asking === "archive" ? confirming(archiveQuestion, f.t.organizationArchive, () => void close("archive")) : asking === "delete" ? confirming(deleteQuestion, f.t.organizationDelete, () => void close("delete")) : /* @__PURE__ */ jsxs(Fragment2, { children: [
2156
+ archiving && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => ask("archive", f.t.organizationArchive, archiveQuestion, () => void close("archive")), children: f.t.organizationArchive }),
2157
+ deleting && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => ask("delete", f.t.organizationDelete, deleteQuestion, () => void close("delete")), children: f.t.organizationDelete })
1975
2158
  ] })
1976
2159
  ] })
1977
2160
  ] });
1978
2161
  }
2162
+ function AddRole({ f, value, options, onChange }) {
2163
+ const id = useId();
2164
+ return /* @__PURE__ */ jsx(Labelled, { f, id, label: f.t.memberRole, children: /* @__PURE__ */ jsx(Pick, { f, id, name: "role", value, options, onChange }) });
2165
+ }
1979
2166
  function PasskeysPart({ props, account, accountUrl, when }) {
1980
2167
  const auth = useAuth();
1981
2168
  const { f, done, setDone, run, general } = useSection(props);
@@ -1993,8 +2180,8 @@ function PasskeysPart({ props, account, accountUrl, when }) {
1993
2180
  if (here === null) return null;
1994
2181
  if (!here) {
1995
2182
  return /* @__PURE__ */ jsxs("section", { className: f.cx("section"), children: [
1996
- /* @__PURE__ */ jsx("h2", { className: f.cx("heading"), children: f.t.passkeys }),
1997
- /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: accountUrl, children: f.t.managePasskeys })
2183
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.passkeys }),
2184
+ /* @__PURE__ */ jsx(A, { f, href: accountUrl, children: f.t.managePasskeys })
1998
2185
  ] });
1999
2186
  }
2000
2187
  const add = async (e, kind = "device") => {
@@ -2008,18 +2195,21 @@ function PasskeysPart({ props, account, accountUrl, when }) {
2008
2195
  setName("");
2009
2196
  setPassword("");
2010
2197
  setDone(f.t.passkeyAdded);
2198
+ props.onSaved?.({ kind: "passkey.added", id: r.passkey.id });
2011
2199
  };
2012
2200
  const remove = async (id) => {
2013
2201
  const r = await run(() => auth.account.removePasskey(id));
2014
- if (r.ok) setList(list.filter((p) => p.id !== id));
2202
+ if (!r.ok) return;
2203
+ setList(list.filter((p) => p.id !== id));
2204
+ props.onSaved?.({ kind: "passkey.removed", id });
2015
2205
  };
2016
2206
  return /* @__PURE__ */ jsxs("form", { className: f.cx("section"), onSubmit: add, noValidate: true, children: [
2017
- /* @__PURE__ */ jsx("h2", { className: f.cx("heading"), children: f.t.passkeys }),
2018
- /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.passkeysHelp }),
2019
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2020
- list.length === 0 ? /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.noPasskeys }) : /* @__PURE__ */ jsx("ul", { className: f.cx("sessions"), children: list.map((p) => /* @__PURE__ */ jsxs("li", { className: f.cx("session"), children: [
2207
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.passkeys }),
2208
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.passkeysHelp }),
2209
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2210
+ list.length === 0 ? /* @__PURE__ */ jsx(Note, { f, children: f.t.noPasskeys }) : /* @__PURE__ */ jsx("ul", { className: f.cx("sessions"), children: list.map((p) => /* @__PURE__ */ jsxs("li", { className: f.cx("session"), children: [
2021
2211
  p.name || f.t.passkeyUnnamed,
2022
- p.kind !== "unknown" ? ` \xB7 ${f.t.passkeyKinds[p.kind]}` : "",
2212
+ p.kind !== "unknown" && /* @__PURE__ */ jsx(Tag, { f, plain: ` \xB7 ${f.t.passkeyKinds[p.kind]}`, children: f.t.passkeyKinds[p.kind] }),
2023
2213
  " \xB7 ",
2024
2214
  when(p.createdAt),
2025
2215
  " ",
@@ -2029,7 +2219,7 @@ function PasskeysPart({ props, account, accountUrl, when }) {
2029
2219
  account.hasPassword && /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword }),
2030
2220
  /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.addPasskeyDevice }),
2031
2221
  /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void add(null, "security_key"), children: f.t.addSecurityKey }),
2032
- done && /* @__PURE__ */ jsx("p", { role: "status", className: f.cx("notice"), children: done })
2222
+ done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2033
2223
  ] });
2034
2224
  }
2035
2225
  function ProfilePart({ props, account, onSaved }) {
@@ -2042,14 +2232,15 @@ function ProfilePart({ props, account, onSaved }) {
2042
2232
  if (!r.ok) return;
2043
2233
  onSaved(r.account);
2044
2234
  setDone(f.t.saved);
2235
+ props.onSaved?.({ kind: "profile.saved", name: r.account.name });
2045
2236
  };
2046
2237
  return /* @__PURE__ */ jsxs("form", { className: f.cx("section"), onSubmit: submit, noValidate: true, children: [
2047
- /* @__PURE__ */ jsx("h2", { className: f.cx("heading"), children: f.t.profile }),
2048
- /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: account.email }),
2049
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2238
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.profile }),
2239
+ /* @__PURE__ */ jsx(Note, { f, children: account.email }),
2240
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2050
2241
  /* @__PURE__ */ jsx(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "name", onChange: setName }),
2051
2242
  /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.save }),
2052
- done && /* @__PURE__ */ jsx("p", { role: "status", className: f.cx("notice"), children: done })
2243
+ done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2053
2244
  ] });
2054
2245
  }
2055
2246
  function EmailPart({ props, account }) {
@@ -2063,15 +2254,16 @@ function EmailPart({ props, account }) {
2063
2254
  if (!r.ok) return;
2064
2255
  setPassword("");
2065
2256
  setDone(f.t.emailSent.replace("{email}", r.email));
2257
+ props.onSaved?.({ kind: "email.change_requested", email: r.email });
2066
2258
  };
2067
2259
  return /* @__PURE__ */ jsxs("form", { className: f.cx("section"), onSubmit: submit, noValidate: true, children: [
2068
- /* @__PURE__ */ jsx("h2", { className: f.cx("heading"), children: f.t.emailAddress }),
2069
- /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: account.email }),
2070
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2260
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.emailAddress }),
2261
+ /* @__PURE__ */ jsx(Note, { f, children: account.email }),
2262
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2071
2263
  /* @__PURE__ */ jsx(Field, { f, name: "email", type: "email", label: f.t.newEmail, value: email, autoComplete: "email", required: true, onChange: setEmail }),
2072
2264
  account.hasPassword && /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword }),
2073
2265
  /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.changeEmail }),
2074
- done && /* @__PURE__ */ jsx("p", { role: "status", className: f.cx("notice"), children: done })
2266
+ done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2075
2267
  ] });
2076
2268
  }
2077
2269
  function LinkedPart({ props }) {
@@ -2084,21 +2276,26 @@ function LinkedPart({ props }) {
2084
2276
  void auth.account.identities().then(setLinked);
2085
2277
  void auth.methods().then((m) => setOffered(m.providers));
2086
2278
  const back = typeof window === "undefined" ? null : new URLSearchParams(window.location.search);
2087
- if (back?.get("linked")) setCame(back.get("linked"));
2088
- else if (back?.get("auth_error")) f.setError({ code: back.get("auth_error"), message: "That account could not be linked. Try again." });
2279
+ const provider = back?.get("linked");
2280
+ if (provider) {
2281
+ setCame(provider);
2282
+ props.onSaved?.({ kind: "identity.linked", provider });
2283
+ } else if (back?.get("auth_error")) f.setError({ code: back.get("auth_error"), message: "That account could not be linked. Try again." });
2089
2284
  }, []);
2090
2285
  if (linked === null) return null;
2091
2286
  const name = (id) => offered.find((p) => p.id === id)?.name ?? id;
2092
2287
  const unlink = async (provider) => {
2093
2288
  const r = await run(() => auth.account.unlink(provider));
2094
- if (r.ok) setLinked(linked.filter((x) => x.provider !== provider));
2289
+ if (!r.ok) return;
2290
+ setLinked(linked.filter((x) => x.provider !== provider));
2291
+ props.onSaved?.({ kind: "identity.unlinked", provider });
2095
2292
  };
2096
2293
  const here = typeof window === "undefined" ? void 0 : window.location.pathname;
2097
2294
  return /* @__PURE__ */ jsxs("section", { className: f.cx("section"), children: [
2098
- /* @__PURE__ */ jsx("h2", { className: f.cx("heading"), children: f.t.linkedAccounts }),
2099
- came && /* @__PURE__ */ jsx("p", { role: "status", className: f.cx("notice"), children: f.t.linked }),
2100
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2101
- linked.length === 0 ? /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.noLinkedAccounts }) : /* @__PURE__ */ jsx("ul", { className: f.cx("sessions"), children: linked.map((x) => /* @__PURE__ */ jsxs("li", { className: f.cx("session"), children: [
2295
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.linkedAccounts }),
2296
+ came && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: f.t.linked }),
2297
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2298
+ linked.length === 0 ? /* @__PURE__ */ jsx(Note, { f, children: f.t.noLinkedAccounts }) : /* @__PURE__ */ jsx("ul", { className: f.cx("sessions"), children: linked.map((x) => /* @__PURE__ */ jsxs("li", { className: f.cx("session"), children: [
2102
2299
  name(x.provider),
2103
2300
  x.email ? ` \xB7 ${x.email}` : "",
2104
2301
  " ",
@@ -2124,14 +2321,15 @@ function PasswordPart({ props }) {
2124
2321
  setPassword("");
2125
2322
  setNewPassword("");
2126
2323
  setDone(f.t.passwordChangedHere);
2324
+ props.onSaved?.({ kind: "password.changed" });
2127
2325
  };
2128
2326
  return /* @__PURE__ */ jsxs("form", { className: f.cx("section"), onSubmit: submit, noValidate: true, children: [
2129
- /* @__PURE__ */ jsx("h2", { className: f.cx("heading"), children: f.t.changePassword }),
2130
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2327
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.changePassword }),
2328
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2131
2329
  /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword }),
2132
2330
  /* @__PURE__ */ jsx(Field, { f, name: "newPassword", type: "password", label: f.t.newPassword, value: newPassword, autoComplete: "new-password", required: true, onChange: setNewPassword, hint: f.passwordRule }),
2133
2331
  /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.changePassword }),
2134
- done && /* @__PURE__ */ jsx("p", { role: "status", className: f.cx("notice"), children: done })
2332
+ done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2135
2333
  ] });
2136
2334
  }
2137
2335
  function SecondStepPart({ props, account, onChanged }) {
@@ -2155,6 +2353,7 @@ function SecondStepPart({ props, account, onChanged }) {
2155
2353
  setPassword("");
2156
2354
  setCodes(r.recoveryCodes);
2157
2355
  onChanged(true);
2356
+ props.onSaved?.({ kind: "mfa.enabled" });
2158
2357
  };
2159
2358
  const turnOff = async (e) => {
2160
2359
  e.preventDefault();
@@ -2163,32 +2362,35 @@ function SecondStepPart({ props, account, onChanged }) {
2163
2362
  setPassword("");
2164
2363
  setCodes(null);
2165
2364
  onChanged(false);
2365
+ props.onSaved?.({ kind: "mfa.disabled" });
2166
2366
  };
2167
2367
  const renew = async () => {
2168
2368
  const r = await run(() => auth.account.mfa.recoveryCodes({ password }));
2169
- if (r.ok) setCodes(r.recoveryCodes);
2369
+ if (!r.ok) return;
2370
+ setCodes(r.recoveryCodes);
2371
+ props.onSaved?.({ kind: "recovery_codes.regenerated" });
2170
2372
  };
2171
2373
  const passwordField = account.hasPassword && /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword });
2172
- const shown = codes && /* @__PURE__ */ jsxs(Fragment, { children: [
2173
- /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.keepCodes }),
2374
+ const shown = codes && /* @__PURE__ */ jsxs(Fragment2, { children: [
2375
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.keepCodes }),
2174
2376
  /* @__PURE__ */ jsx("ul", { className: f.cx("codes"), children: codes.map((c) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("code", { children: c }) }, c)) })
2175
2377
  ] });
2176
2378
  if (setup) {
2177
2379
  return /* @__PURE__ */ jsxs("form", { className: f.cx("section"), onSubmit: confirm, noValidate: true, children: [
2178
- /* @__PURE__ */ jsx("h2", { className: f.cx("heading"), children: f.t.secondStep }),
2179
- /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.addToApp }),
2380
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.secondStep }),
2381
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.addToApp }),
2180
2382
  /* @__PURE__ */ jsx("code", { className: f.cx("secret"), children: setup.secret }),
2181
- /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: setup.otpauthUri, children: f.t.openInApp }),
2182
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2383
+ /* @__PURE__ */ jsx(A, { f, href: setup.otpauthUri, children: f.t.openInApp }),
2384
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2183
2385
  /* @__PURE__ */ jsx(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
2184
2386
  /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
2185
2387
  ] });
2186
2388
  }
2187
2389
  return /* @__PURE__ */ jsxs("form", { className: f.cx("section"), onSubmit: account.mfaEnabled ? turnOff : start, noValidate: true, children: [
2188
- /* @__PURE__ */ jsx("h2", { className: f.cx("heading"), children: f.t.secondStep }),
2189
- /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: account.mfaEnabled ? f.t.secondStepOn : f.t.secondStepOff }),
2390
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.secondStep }),
2391
+ /* @__PURE__ */ jsx(Note, { f, children: account.mfaEnabled ? f.t.secondStepOn : f.t.secondStepOff }),
2190
2392
  shown,
2191
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2393
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2192
2394
  passwordField,
2193
2395
  /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: account.mfaEnabled ? f.t.turnOff : f.t.turnOn }),
2194
2396
  account.mfaEnabled && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void renew(), children: f.t.newRecoveryCodes })
@@ -2226,6 +2428,7 @@ function PhonePart({ props, account, onChanged }) {
2226
2428
  setPassword("");
2227
2429
  if (r.recoveryCodes.length > 0) setCodes(r.recoveryCodes);
2228
2430
  onChanged(true);
2431
+ props.onSaved?.({ kind: "phone.added", channel });
2229
2432
  await load();
2230
2433
  };
2231
2434
  const remove = async (e) => {
@@ -2236,27 +2439,29 @@ function PhonePart({ props, account, onChanged }) {
2236
2439
  await load();
2237
2440
  const left = await auth.account.mfa.factors();
2238
2441
  onChanged(Boolean(left?.totp));
2442
+ props.onSaved?.({ kind: "phone.removed" });
2239
2443
  };
2240
2444
  const passwordField = account.hasPassword && /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword });
2241
2445
  if (sentTo) {
2242
2446
  return /* @__PURE__ */ jsxs("form", { className: f.cx("section"), onSubmit: confirm, noValidate: true, children: [
2243
- /* @__PURE__ */ jsx("h2", { className: f.cx("heading"), children: f.t.phone }),
2244
- /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.codeSent.replace("{to}", sentTo) }),
2245
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2447
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.phone }),
2448
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.codeSent.replace("{to}", sentTo) }),
2449
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2246
2450
  /* @__PURE__ */ jsx(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
2247
2451
  /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
2248
2452
  ] });
2249
2453
  }
2250
2454
  return /* @__PURE__ */ jsxs("form", { className: f.cx("section"), onSubmit: ways.phone ? remove : add, noValidate: true, children: [
2251
- /* @__PURE__ */ jsx("h2", { className: f.cx("heading"), children: f.t.phone }),
2252
- /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: ways.phone ? f.t.phoneOn.replace("{to}", ways.phone.number) : `${f.t.phoneOff} ${f.t.phoneHelp}` }),
2253
- codes && /* @__PURE__ */ jsxs(Fragment, { children: [
2254
- /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.keepCodes }),
2455
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.phone }),
2456
+ /* @__PURE__ */ jsx(Note, { f, children: ways.phone ? f.t.phoneOn.replace("{to}", ways.phone.number) : `${f.t.phoneOff} ${f.t.phoneHelp}` }),
2457
+ codes && /* @__PURE__ */ jsxs(Fragment2, { children: [
2458
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.keepCodes }),
2255
2459
  /* @__PURE__ */ jsx("ul", { className: f.cx("codes"), children: codes.map((c) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("code", { children: c }) }, c)) })
2256
2460
  ] }),
2257
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2461
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2258
2462
  !ways.phone && /* @__PURE__ */ jsx(Field, { f, name: "phone", type: "tel", label: f.t.phone, value: number, autoComplete: "tel", required: true, onChange: setNumber }),
2259
- !ways.phone && ways.phoneChannels.length > 1 && /* @__PURE__ */ jsx("div", { className: f.cx("field"), role: "radiogroup", children: ways.phoneChannels.map((c) => /* @__PURE__ */ jsxs("label", { className: f.cx("label"), children: [
2463
+ !ways.phone && ways.phoneChannels.length > 1 && f.C.Select && /* @__PURE__ */ jsx(ChannelPick, { f, value: channel, channels: ways.phoneChannels, onChange: setChannel }),
2464
+ !ways.phone && ways.phoneChannels.length > 1 && !f.C.Select && /* @__PURE__ */ jsx("div", { className: f.cx("field"), role: "radiogroup", children: ways.phoneChannels.map((c) => /* @__PURE__ */ jsxs("label", { className: f.cx("label"), children: [
2260
2465
  /* @__PURE__ */ jsx("input", { type: "radio", name: "channel", value: c, checked: channel === c, onChange: () => setChannel(c) }),
2261
2466
  " ",
2262
2467
  c === "whatsapp" ? f.t.byWhatsapp : f.t.bySms
@@ -2275,12 +2480,14 @@ function AgentsPart({ props, when }) {
2275
2480
  if (list.length === 0) return null;
2276
2481
  const disconnect = async (id) => {
2277
2482
  const r = await run(() => auth.account.disconnectAgent(id));
2278
- if (r.ok) setList(list.filter((a) => a.id !== id));
2483
+ if (!r.ok) return;
2484
+ setList(list.filter((a) => a.id !== id));
2485
+ props.onSaved?.({ kind: "agent.disconnected", id });
2279
2486
  };
2280
2487
  return /* @__PURE__ */ jsxs("section", { className: f.cx("section"), children: [
2281
- /* @__PURE__ */ jsx("h2", { className: f.cx("heading"), children: f.t.agents }),
2282
- /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.agentsHelp }),
2283
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2488
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.agents }),
2489
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.agentsHelp }),
2490
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2284
2491
  /* @__PURE__ */ jsx("ul", { className: f.cx("sessions"), children: list.map((a) => /* @__PURE__ */ jsxs("li", { className: f.cx("session"), children: [
2285
2492
  a.name || f.t.anAgent,
2286
2493
  a.redirectHost ? ` (${a.redirectHost})` : "",
@@ -2294,20 +2501,47 @@ function AgentsPart({ props, when }) {
2294
2501
  ] }, a.id)) })
2295
2502
  ] });
2296
2503
  }
2297
- function SignOutEverywhere({ props }) {
2504
+ function SessionsPart({ props, when }) {
2298
2505
  const auth = useAuth();
2299
2506
  const { f, run, general } = useSection(props);
2507
+ const [sessions, setSessions] = useState([]);
2508
+ useEffect(() => {
2509
+ void auth.account.sessions().then(setSessions);
2510
+ }, []);
2300
2511
  const end = async () => {
2301
2512
  const r = await run(() => auth.account.signOutEverywhere());
2302
2513
  if (!r.ok) return;
2514
+ props.onSaved?.({ kind: "sessions.ended" });
2303
2515
  if (props.onSignedOut) props.onSignedOut();
2304
2516
  else window.location.assign("/");
2305
2517
  };
2306
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2307
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2518
+ return /* @__PURE__ */ jsxs("section", { className: f.cx("section"), children: [
2519
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.sessions }),
2520
+ /* @__PURE__ */ jsx("ul", { className: f.cx("sessions"), children: sessions.map((s, i) => /* @__PURE__ */ jsxs("li", { className: f.cx("session"), children: [
2521
+ s.device || f.t.unknownDevice,
2522
+ " \xB7 ",
2523
+ s.ip,
2524
+ " \xB7 ",
2525
+ when(s.createdAt)
2526
+ ] }, i)) }),
2527
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2308
2528
  /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("button"), onClick: () => void end(), children: f.t.signOutEverywhere })
2309
2529
  ] });
2310
2530
  }
2531
+ function ChannelPick({ f, value, channels, onChange }) {
2532
+ const id = useId();
2533
+ return /* @__PURE__ */ jsx(Labelled, { f, id, label: f.t.channel, children: /* @__PURE__ */ jsx(
2534
+ Pick,
2535
+ {
2536
+ f,
2537
+ id,
2538
+ name: "channel",
2539
+ value,
2540
+ options: channels.map((c) => ({ value: c, label: c === "whatsapp" ? f.t.byWhatsapp : f.t.bySms })),
2541
+ onChange: (c) => onChange(c)
2542
+ }
2543
+ ) });
2544
+ }
2311
2545
 
2312
2546
  // src/react.tsx
2313
2547
  import { jsx as jsx2 } from "react/jsx-runtime";
@@ -2430,10 +2664,20 @@ function useAuth() {
2430
2664
  export {
2431
2665
  AcceptInvite,
2432
2666
  Account,
2667
+ AccountAgents,
2668
+ AccountEmail,
2669
+ AccountLinked,
2670
+ AccountPasskeys,
2671
+ AccountPassword,
2672
+ AccountPhone,
2673
+ AccountProfile,
2674
+ AccountSecondStep,
2675
+ AccountSessions,
2433
2676
  CauthyProvider,
2434
2677
  ForgotPassword,
2435
2678
  GoogleOneTap,
2436
2679
  OrganizationMembers,
2680
+ PendingTerms,
2437
2681
  ResetPassword,
2438
2682
  SignIn,
2439
2683
  SignUp,