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.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  // src/react.tsx
4
- import { createContext, useCallback, useContext, useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState2 } from "react";
4
+ import { createContext, useCallback as useCallback2, useContext, useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState2 } from "react";
5
5
 
6
6
  // src/passkey.ts
7
7
  function canUsePasskeys() {
@@ -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, useCallback, 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.",
@@ -1077,7 +1085,7 @@ var TEXTS = {
1077
1085
  }
1078
1086
  }
1079
1087
  };
1080
- var PlainButton = (p) => /* @__PURE__ */ jsx("button", { ...p });
1088
+ var PlainButton = ({ variant: _variant, loading: _loading, ...p }) => /* @__PURE__ */ jsx("button", { ...p });
1081
1089
  var PlainInput = (p) => /* @__PURE__ */ jsx("input", { ...p });
1082
1090
  var looks = /* @__PURE__ */ new Map();
1083
1091
  var loaded = /* @__PURE__ */ new Set();
@@ -1188,10 +1196,28 @@ 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;
1193
- const [error, setError] = useState(null);
1194
- const [busy, setBusy] = useState(false);
1199
+ const own = (slot) => props.classNames?.[slot];
1200
+ const C = props.components ?? {};
1201
+ const Button = C.Button ?? PlainButton;
1202
+ const Input = C.Input ?? PlainInput;
1203
+ const [error, setOnlyError] = useState(null);
1204
+ const [problems, setProblems] = useState({ list: [], round: 0 });
1205
+ const setError = useCallback((e) => {
1206
+ setOnlyError(e);
1207
+ setProblems((p) => p.list.length === 0 ? p : { list: [], round: p.round });
1208
+ }, []);
1209
+ const check = (...found) => {
1210
+ const list = found.filter((e) => Boolean(e));
1211
+ setOnlyError(null);
1212
+ setProblems((p) => ({ list, round: list.length > 0 ? p.round + 1 : p.round }));
1213
+ return list.length === 0;
1214
+ };
1215
+ const fieldError = (name) => problems.list.find((e) => e.field === name) ?? (error?.field === name ? error : null);
1216
+ const firstWrong = (name) => problems.list[0]?.field === name ? problems.round : 0;
1217
+ const [acting, setActing] = useState(null);
1218
+ const busy = acting !== null;
1219
+ const setBusy = useCallback((on) => setActing(on === false ? null : on === true ? SUBMIT : on), []);
1220
+ const loading = (key = SUBMIT) => acting === key;
1195
1221
  const [policy, setPolicy] = useState(void 0);
1196
1222
  const locale = props.locale ?? "en";
1197
1223
  const say = (e) => {
@@ -1204,13 +1230,113 @@ function useForm(props) {
1204
1230
  else window.location.assign(sitePath(to) ?? sameOrigin(props.returnTo) ?? "/");
1205
1231
  };
1206
1232
  const root = useRoot(props);
1207
- return { t, cx, Button, Input, error, setError, busy, setBusy, say, done, root, policy, setPolicy, passwordRule };
1233
+ return { t, cx, own, C, Button, Input, error, setError, check, fieldError, firstWrong, busy, setBusy, loading, say, done, root, policy, setPolicy, passwordRule };
1234
+ }
1235
+ var SUBMIT = "submit";
1236
+ function Wrap(p) {
1237
+ const { Form: OwnForm } = p.f.C;
1238
+ if (OwnForm) {
1239
+ const own = /* @__PURE__ */ jsx(
1240
+ OwnForm,
1241
+ {
1242
+ as: p.as === "form" ? "form" : "div",
1243
+ onSubmit: p.onSubmit,
1244
+ noValidate: p.noValidate,
1245
+ "aria-busy": p.busy,
1246
+ className: p.slot ? p.f.own(p.slot) : void 0,
1247
+ section: p.section,
1248
+ children: p.children
1249
+ }
1250
+ );
1251
+ return p.root?.["data-cauthy"] !== void 0 ? /* @__PURE__ */ jsx("div", { ...p.root, children: own }) : own;
1252
+ }
1253
+ const Tag2 = p.as;
1254
+ return /* @__PURE__ */ 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 });
1255
+ }
1256
+ function Note({ f, children, footer }) {
1257
+ const { Text } = f.C;
1258
+ if (Text) return /* @__PURE__ */ jsx(Text, { as: "p", tone: "muted", size: footer ? "sm" : "md", children });
1259
+ return /* @__PURE__ */ jsx("p", { className: f.cx(footer ? "footer" : "notice"), children });
1260
+ }
1261
+ function Heading({ f, children }) {
1262
+ const { Text } = f.C;
1263
+ if (Text) return /* @__PURE__ */ jsx(Text, { as: "h2", size: "lg", children });
1264
+ return /* @__PURE__ */ jsx("h2", { className: f.cx("heading"), children });
1265
+ }
1266
+ function Say({ f, tone, children }) {
1267
+ const { Alert } = f.C;
1268
+ if (Alert) return /* @__PURE__ */ jsx(Alert, { tone, children });
1269
+ if (tone === "danger") return /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children });
1270
+ return /* @__PURE__ */ jsx("p", { role: "status", className: f.cx("notice"), children });
1271
+ }
1272
+ function Or({ f, children }) {
1273
+ const { Divider } = f.C;
1274
+ if (Divider) return /* @__PURE__ */ jsx(Divider, { label: children });
1275
+ return /* @__PURE__ */ jsx("p", { className: f.cx("separator"), children });
1276
+ }
1277
+ function A({ f, href, newTab, children }) {
1278
+ const { Link } = f.C;
1279
+ if (Link) return /* @__PURE__ */ jsx(Link, { href, newTab, children });
1280
+ return /* @__PURE__ */ jsx("a", { className: f.cx("link"), href, target: newTab ? "_blank" : void 0, rel: newTab ? "noopener" : void 0, children });
1281
+ }
1282
+ function Tag({ f, tone, children, plain }) {
1283
+ const { Badge } = f.C;
1284
+ if (Badge) return /* @__PURE__ */ jsxs(Fragment2, { children: [
1285
+ " ",
1286
+ /* @__PURE__ */ jsx(Badge, { tone, children })
1287
+ ] });
1288
+ return /* @__PURE__ */ jsx(Fragment2, { children: plain });
1289
+ }
1290
+ function Check(p) {
1291
+ const id = useId();
1292
+ const { f } = p;
1293
+ const { Checkbox } = f.C;
1294
+ if (Checkbox) {
1295
+ return /* @__PURE__ */ jsx(Checkbox, { id, name: p.name, checked: p.checked, required: p.required, className: f.own("check"), onChange: p.onChange, children: p.children });
1296
+ }
1297
+ return /* @__PURE__ */ jsxs("label", { className: f.cx("check"), children: [
1298
+ /* @__PURE__ */ jsx("input", { type: "checkbox", name: p.name, checked: p.checked, required: p.required, onChange: (e) => p.onChange(e.target.checked) }),
1299
+ /* @__PURE__ */ jsx("span", { children: p.children })
1300
+ ] });
1301
+ }
1302
+ function Pick(p) {
1303
+ const made = useId();
1304
+ const id = p.id ?? made;
1305
+ const { f } = p;
1306
+ const { Select } = f.C;
1307
+ if (Select) {
1308
+ 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 });
1309
+ }
1310
+ 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)) });
1311
+ }
1312
+ function Labelled({ f, id, label, children }) {
1313
+ const { Field: OwnField } = f.C;
1314
+ if (OwnField) return /* @__PURE__ */ jsx(OwnField, { id, label, className: f.own("field"), children });
1315
+ return /* @__PURE__ */ jsxs("label", { className: f.cx("field"), children: [
1316
+ /* @__PURE__ */ jsx("span", { className: f.cx("label"), children: label }),
1317
+ children
1318
+ ] });
1319
+ }
1320
+ function Documents({ f, documents }) {
1321
+ return /* @__PURE__ */ jsx(Fragment2, { children: documents.map((d, i) => /* @__PURE__ */ jsxs("span", { children: [
1322
+ i > 0 && /* @__PURE__ */ jsxs(Fragment2, { children: [
1323
+ f.t.and,
1324
+ " "
1325
+ ] }),
1326
+ /* @__PURE__ */ jsx(A, { f, href: d.url, newTab: true, children: d.kind === "terms" ? f.t.termsOfService : f.t.privacyPolicy }),
1327
+ " "
1328
+ ] }, d.kind)) });
1208
1329
  }
1209
1330
  var PASSWORD_RULES = ["needs_lowercase", "needs_uppercase", "needs_digit", "needs_symbol", "needs_classes", "breached"];
1210
1331
  function isPasswordRule(e) {
1211
1332
  if (e.code === "weak_password" || PASSWORD_RULES.includes(e.code)) return true;
1212
1333
  return (e.code === "too_short" || e.code === "too_long") && (e.field === "password" || e.field === "newPassword");
1213
1334
  }
1335
+ function newPasswordProblem(value, policy, field = "password") {
1336
+ const bad = checkNewPassword(value, policy);
1337
+ if (!bad) return null;
1338
+ return { code: bad.code, message: bad.message, field, ...bad.code === "required" ? {} : { reasons: passwordProblems(value, policy) } };
1339
+ }
1214
1340
  function usePasswordPolicy(f, methods) {
1215
1341
  const { setPolicy } = f;
1216
1342
  useEffect(() => {
@@ -1220,28 +1346,49 @@ function usePasswordPolicy(f, methods) {
1220
1346
  function Field(p) {
1221
1347
  const id = useId();
1222
1348
  const { f } = p;
1223
- const bad = f.error?.field === p.name ? f.error : null;
1349
+ const bad = f.fieldError(p.name);
1350
+ const focus = f.firstWrong(p.name);
1351
+ useEffect(() => {
1352
+ if (focus > 0 && typeof document !== "undefined") document.getElementById(id)?.focus();
1353
+ }, [focus, id]);
1224
1354
  const described = [bad ? `${id}-error` : "", p.hint ? `${id}-hint` : ""].filter(Boolean).join(" ");
1355
+ const input = /* @__PURE__ */ jsx(
1356
+ f.Input,
1357
+ {
1358
+ id,
1359
+ name: p.name,
1360
+ type: p.type,
1361
+ value: p.value,
1362
+ autoComplete: p.autoComplete,
1363
+ required: p.required,
1364
+ className: f.cx("input"),
1365
+ "aria-invalid": bad ? true : void 0,
1366
+ "aria-describedby": described || void 0,
1367
+ onChange: (e) => p.onChange(e.target.value)
1368
+ }
1369
+ );
1370
+ const { Field: OwnField } = f.C;
1371
+ if (OwnField) {
1372
+ return /* @__PURE__ */ jsx(
1373
+ OwnField,
1374
+ {
1375
+ id,
1376
+ label: p.label,
1377
+ aside: p.aside,
1378
+ help: p.hint && !bad ? p.hint : void 0,
1379
+ error: bad ? f.say(bad) : void 0,
1380
+ required: p.required,
1381
+ className: f.own("field"),
1382
+ children: input
1383
+ }
1384
+ );
1385
+ }
1225
1386
  return /* @__PURE__ */ jsxs("div", { className: f.cx("field"), children: [
1226
1387
  /* @__PURE__ */ jsxs("label", { className: f.cx("label"), htmlFor: id, children: [
1227
1388
  p.label,
1228
1389
  p.aside
1229
1390
  ] }),
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
- ),
1391
+ input,
1245
1392
  p.hint && !bad && /* @__PURE__ */ jsx("span", { id: `${id}-hint`, className: f.cx("hint"), children: p.hint }),
1246
1393
  bad && /* @__PURE__ */ jsx("span", { id: `${id}-error`, role: "alert", className: f.cx("fieldError"), children: f.say(bad) })
1247
1394
  ] });
@@ -1279,7 +1426,7 @@ function SecondStepForm(p) {
1279
1426
  });
1280
1427
  }, []);
1281
1428
  const sendEmail = async () => {
1282
- f.setBusy(true);
1429
+ f.setBusy("email");
1283
1430
  f.setError(null);
1284
1431
  const r = await auth.signIn.mfaSendEmail();
1285
1432
  f.setBusy(false);
@@ -1290,7 +1437,7 @@ function SecondStepForm(p) {
1290
1437
  const pushWait = useRef(null);
1291
1438
  useEffect(() => () => pushWait.current?.abort(), []);
1292
1439
  const sendPush = async () => {
1293
- f.setBusy(true);
1440
+ f.setBusy("push");
1294
1441
  f.setError(null);
1295
1442
  const r = await auth.signIn.mfaPush();
1296
1443
  f.setBusy(false);
@@ -1306,7 +1453,7 @@ function SecondStepForm(p) {
1306
1453
  f.done();
1307
1454
  };
1308
1455
  const sendCode = async () => {
1309
- f.setBusy(true);
1456
+ f.setBusy("phone");
1310
1457
  f.setError(null);
1311
1458
  const r = await auth.signIn.mfaSendCode();
1312
1459
  f.setBusy(false);
@@ -1324,23 +1471,30 @@ function SecondStepForm(p) {
1324
1471
  };
1325
1472
  const general = f.error && !f.error.field ? f.error : null;
1326
1473
  const to = phoneWay?.phone?.number ?? "";
1327
- 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) }),
1330
- 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: [
1474
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1475
+ /* @__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 }),
1476
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1477
+ phoneWay?.email && /* @__PURE__ */ 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 }),
1478
+ pushNumber > 0 ? f.C.Text ? (
1479
+ // The application's text: the number to pick drawn large, by its own rules.
1480
+ /* @__PURE__ */ jsxs("div", { role: "status", "aria-live": "polite", children: [
1481
+ /* @__PURE__ */ jsx(f.C.Text, { as: "p", children: f.t.pushPick.replace("{n}", String(pushNumber)) }),
1482
+ /* @__PURE__ */ jsx(f.C.Text, { as: "p", size: "lg", children: pushNumber }),
1483
+ /* @__PURE__ */ jsx(f.C.Text, { as: "p", tone: "muted", children: f.t.pushWaiting })
1484
+ ] })
1485
+ ) : /* @__PURE__ */ jsxs("div", { className: f.cx("notice"), role: "status", "aria-live": "polite", children: [
1332
1486
  /* @__PURE__ */ jsx("p", { children: f.t.pushPick.replace("{n}", String(pushNumber)) }),
1333
1487
  /* @__PURE__ */ jsx("p", { style: { fontSize: 48, fontWeight: 700, margin: "4px 0", textAlign: "center" }, children: pushNumber }),
1334
1488
  /* @__PURE__ */ jsx("p", { children: f.t.pushWaiting })
1335
- ] }) : phoneWay?.push && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("button"), onClick: () => void sendPush(), children: f.t.pushSend }),
1336
- phoneWay?.phone && /* @__PURE__ */ 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) }),
1489
+ ] }) : phoneWay?.push && /* @__PURE__ */ 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 }),
1490
+ phoneWay?.phone && /* @__PURE__ */ 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) }),
1337
1491
  /* @__PURE__ */ jsx(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
1338
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
1492
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.verify })
1339
1493
  ] });
1340
1494
  }
1341
1495
  function ProviderButtons(p) {
1342
1496
  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: [
1497
+ return /* @__PURE__ */ jsx(Fragment2, { children: p.providers.map((provider) => /* @__PURE__ */ jsxs(f.Button, { type: "button", variant: "secondary", className: f.cx("providerButton"), onClick: () => p.onPick(provider.id), children: [
1344
1498
  provider.iconUrl && /* @__PURE__ */ jsx("img", { src: provider.iconUrl, alt: "", width: 18, height: 18, className: f.cx("providerIcon") }),
1345
1499
  f.t.continueWith,
1346
1500
  " ",
@@ -1380,7 +1534,7 @@ function SignIn(props) {
1380
1534
  }, [email, methods]);
1381
1535
  const toCompany = () => {
1382
1536
  autofill.current?.abort();
1383
- f.setBusy(true);
1537
+ f.setBusy("sso");
1384
1538
  auth.signIn.sso({ email }, { returnTo: props.returnTo });
1385
1539
  };
1386
1540
  useEffect(() => {
@@ -1422,6 +1576,7 @@ function SignIn(props) {
1422
1576
  if (methods?.magicLink) return sendLink();
1423
1577
  return;
1424
1578
  }
1579
+ if (step === "password" && !f.check(checkEmail(email), checkPassword(password))) return;
1425
1580
  f.setBusy(true);
1426
1581
  f.setError(null);
1427
1582
  const r = step === "sent" ? await auth.signIn.emailCode({ code }) : await auth.signIn.email({ email, password, captchaToken: challenge.token });
@@ -1437,7 +1592,7 @@ function SignIn(props) {
1437
1592
  };
1438
1593
  const usePasskey = async () => {
1439
1594
  autofill.current?.abort();
1440
- f.setBusy(true);
1595
+ f.setBusy("passkey");
1441
1596
  f.setError(null);
1442
1597
  const r = await auth.signIn.passkey({ returnTo: props.returnTo, locale: props.locale, email });
1443
1598
  if (r.ok && r.status === "redirected") return;
@@ -1448,7 +1603,7 @@ function SignIn(props) {
1448
1603
  };
1449
1604
  const sendLink = async () => {
1450
1605
  autofill.current?.abort();
1451
- f.setBusy(true);
1606
+ f.setBusy("link");
1452
1607
  f.setError(null);
1453
1608
  const r = await auth.signIn.magicLink({ email, captchaToken: challenge.token, create: props.createAccounts, returnTo: props.returnTo });
1454
1609
  f.setBusy(false);
@@ -1459,30 +1614,30 @@ function SignIn(props) {
1459
1614
  };
1460
1615
  const general = f.error && !f.error.field ? f.error : null;
1461
1616
  const asksEmail = offers.email;
1462
- if (step === "opening") return /* @__PURE__ */ jsx("div", { className: f.cx("form"), ...f.root, "aria-busy": "true" });
1617
+ if (step === "opening") return /* @__PURE__ */ jsx(Wrap, { f, as: "div", slot: "form", root: f.root, busy: true });
1463
1618
  if (step === "sent") {
1464
- 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 }),
1619
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1620
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.signInLinkSent }),
1621
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1622
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.codeFromMail }),
1468
1623
  /* @__PURE__ */ jsx(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
1469
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
1624
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.verify })
1470
1625
  ] });
1471
1626
  }
1472
1627
  if (step === "code") return /* @__PURE__ */ jsx(SecondStepForm, { f, auth });
1473
- 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) }),
1628
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1629
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1475
1630
  /* @__PURE__ */ jsx(ProviderButtons, { f, providers: methods?.providers ?? [], onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
1476
- 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 }),
1631
+ offers.passkey && /* @__PURE__ */ 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 }),
1632
+ asksEmail && ((methods?.providers.length ?? 0) > 0 || offers.passkey) && /* @__PURE__ */ jsx(Or, { f, children: f.t.orWithEmail }),
1478
1633
  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
- company && /* @__PURE__ */ jsxs(f.Button, { type: "button", disabled: f.busy, className: f.cx(company.enforced ? "button" : "providerButton"), onClick: toCompany, children: [
1634
+ company && /* @__PURE__ */ 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: [
1480
1635
  f.t.continueWith,
1481
1636
  " ",
1482
1637
  company.name
1483
1638
  ] }),
1484
- company?.enforced && /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.ssoOnly }),
1485
- !company?.enforced && passwordOn && /* @__PURE__ */ jsxs(Fragment, { children: [
1639
+ company?.enforced && /* @__PURE__ */ jsx(Note, { f, children: f.t.ssoOnly }),
1640
+ !company?.enforced && passwordOn && /* @__PURE__ */ jsxs(Fragment2, { children: [
1486
1641
  /* @__PURE__ */ jsx(
1487
1642
  Field,
1488
1643
  {
@@ -1494,18 +1649,18 @@ function SignIn(props) {
1494
1649
  autoComplete: "current-password",
1495
1650
  required: true,
1496
1651
  onChange: setPassword,
1497
- aside: props.links?.forgotPassword ? /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: props.links.forgotPassword, children: f.t.forgotPassword }) : void 0
1652
+ aside: props.links?.forgotPassword ? /* @__PURE__ */ jsx(A, { f, href: props.links.forgotPassword, children: f.t.forgotPassword }) : void 0
1498
1653
  }
1499
1654
  ),
1500
1655
  challenge.node,
1501
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signIn })
1656
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.signIn })
1502
1657
  ] }),
1503
1658
  !company?.enforced && !passwordOn && methods?.magicLink && challenge.node,
1504
- 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: [
1659
+ methods?.magicLink && !company?.enforced && /* @__PURE__ */ 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 }),
1660
+ methods?.signUp !== false && props.links?.signUp && /* @__PURE__ */ jsxs(Note, { f, footer: true, children: [
1506
1661
  f.t.noAccount,
1507
1662
  " ",
1508
- /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: props.links.signUp, children: f.t.createAccount })
1663
+ /* @__PURE__ */ jsx(A, { f, href: props.links.signUp, children: f.t.createAccount })
1509
1664
  ] })
1510
1665
  ] });
1511
1666
  }
@@ -1541,11 +1696,19 @@ function SignUp(props) {
1541
1696
  const [acceptLegal, setAcceptLegal] = useState(false);
1542
1697
  const [consents, setConsents] = useState({});
1543
1698
  const legal = methods?.legal;
1699
+ const guest = auth.session?.user.isAnonymous === true;
1544
1700
  const submit = async (e) => {
1545
1701
  e.preventDefault();
1702
+ const asksCompany = methods?.organization?.ask === true;
1703
+ if (!f.check(
1704
+ asksCompany && checkOrganization(organization, methods?.organization?.required === true),
1705
+ checkName(name),
1706
+ checkEmail(email),
1707
+ newPasswordProblem(password, f.policy)
1708
+ )) return;
1546
1709
  f.setBusy(true);
1547
1710
  f.setError(null);
1548
- const r = auth.session?.user.isAnonymous === true ? await auth.account.upgrade({ name, email, password }) : await auth.signUp.email({
1711
+ const r = guest ? await auth.account.upgrade({ name, email, password }) : await auth.signUp.email({
1549
1712
  name,
1550
1713
  email,
1551
1714
  password,
@@ -1561,54 +1724,42 @@ function SignUp(props) {
1561
1724
  };
1562
1725
  const general = f.error && !f.error.field ? f.error : null;
1563
1726
  const passwordOn = methods?.passwordSignIn !== false;
1564
- const providers = auth.session?.user.isAnonymous === true || methods?.organization?.required ? [] : methods?.providers ?? [];
1727
+ const providers = guest || methods?.organization?.required ? [] : methods?.providers ?? [];
1565
1728
  if (sent) {
1566
- 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 })
1729
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1730
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.checkEmail }),
1731
+ props.links?.signIn && /* @__PURE__ */ jsx(A, { f, href: props.links.signIn, children: f.t.backToSignIn })
1569
1732
  ] });
1570
1733
  }
1571
- const footer = props.links?.signIn && /* @__PURE__ */ jsxs("p", { className: f.cx("footer"), children: [
1734
+ if (methods === null) return /* @__PURE__ */ jsx(Wrap, { f, as: "div", slot: "form", root: f.root, busy: true });
1735
+ const footer = props.links?.signIn && /* @__PURE__ */ jsxs(Note, { f, footer: true, children: [
1572
1736
  f.t.haveAccount,
1573
1737
  " ",
1574
- /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.signIn })
1738
+ /* @__PURE__ */ jsx(A, { f, href: props.links.signIn, children: f.t.signIn })
1575
1739
  ] });
1576
1740
  if (!passwordOn) {
1577
- 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) }),
1741
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1742
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1579
1743
  /* @__PURE__ */ jsx(ProviderButtons, { f, providers, onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
1580
1744
  footer
1581
1745
  ] });
1582
1746
  }
1583
- 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) }),
1747
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1748
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1585
1749
  /* @__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 }),
1587
- 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 }),
1750
+ providers.length > 0 && /* @__PURE__ */ jsx(Or, { f, children: f.t.orWithEmail }),
1751
+ 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
1752
  /* @__PURE__ */ jsx(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "name", onChange: setName }),
1589
1753
  /* @__PURE__ */ jsx(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "username", required: true, onChange: setEmail }),
1590
1754
  /* @__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
- ] })
1755
+ legal && legal.documents.length > 0 && /* @__PURE__ */ jsxs(Check, { f, name: "acceptLegal", checked: acceptLegal, required: true, onChange: setAcceptLegal, children: [
1756
+ f.t.iAccept,
1757
+ " ",
1758
+ /* @__PURE__ */ jsx(Documents, { f, documents: legal.documents })
1605
1759
  ] }),
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)),
1760
+ 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
1761
  challenge.node,
1611
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signUp }),
1762
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.signUp }),
1612
1763
  footer
1613
1764
  ] });
1614
1765
  }
@@ -1630,17 +1781,17 @@ function ForgotPassword(props) {
1630
1781
  };
1631
1782
  const general = f.error && !f.error.field ? f.error : null;
1632
1783
  if (sent) {
1633
- 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 })
1784
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1785
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.linkSent }),
1786
+ props.links?.signIn && /* @__PURE__ */ jsx(A, { f, href: props.links.signIn, children: f.t.backToSignIn })
1636
1787
  ] });
1637
1788
  }
1638
- 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) }),
1789
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1790
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1640
1791
  /* @__PURE__ */ jsx(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "username", required: true, onChange: setEmail }),
1641
1792
  challenge.node,
1642
- /* @__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 }) })
1793
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.sendLink }),
1794
+ props.links?.signIn && /* @__PURE__ */ jsx(Note, { f, footer: true, children: /* @__PURE__ */ jsx(A, { f, href: props.links.signIn, children: f.t.backToSignIn }) })
1644
1795
  ] });
1645
1796
  }
1646
1797
  function ResetPassword(props) {
@@ -1664,23 +1815,23 @@ function ResetPassword(props) {
1664
1815
  setChanged(true);
1665
1816
  };
1666
1817
  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 });
1818
+ const back = props.links?.signIn && /* @__PURE__ */ jsx(A, { f, href: props.links.signIn, children: f.t.backToSignIn });
1668
1819
  if (changed) {
1669
- return /* @__PURE__ */ jsxs("div", { className: f.cx("form"), ...f.root, children: [
1670
- /* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.passwordChanged }),
1820
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1821
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.passwordChanged }),
1671
1822
  back
1672
1823
  ] });
1673
1824
  }
1674
1825
  if (token === "") {
1675
- 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 })
1826
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1827
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1828
+ props.links?.forgotPassword && /* @__PURE__ */ jsx(A, { f, href: props.links.forgotPassword, children: f.t.forgotPassword })
1678
1829
  ] });
1679
1830
  }
1680
- 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) }),
1831
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1832
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1682
1833
  /* @__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
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy || token === null, className: f.cx("button"), children: f.t.savePassword })
1834
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy || token === null, className: f.cx("button"), children: f.t.savePassword })
1684
1835
  ] });
1685
1836
  }
1686
1837
  function VerifyEmail(props) {
@@ -1699,11 +1850,11 @@ function VerifyEmail(props) {
1699
1850
  setState("failed");
1700
1851
  });
1701
1852
  });
1702
- 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 })
1853
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "form", root: f.root, busy: state === "confirming", children: [
1854
+ state === "confirming" && /* @__PURE__ */ jsx(Note, { f, children: f.t.confirming }),
1855
+ state === "confirmed" && /* @__PURE__ */ jsx(Note, { f, children: f.t.emailConfirmed }),
1856
+ state === "failed" && f.error && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(f.error) }),
1857
+ state !== "confirming" && props.links?.signIn && /* @__PURE__ */ jsx(A, { f, href: props.links.signIn, children: f.t.backToSignIn })
1707
1858
  ] });
1708
1859
  }
1709
1860
  function AcceptInvite(props) {
@@ -1736,37 +1887,67 @@ function AcceptInvite(props) {
1736
1887
  if (r.error.code === "invalid_token") return setState("invalid");
1737
1888
  return f.setError(r.error);
1738
1889
  }
1890
+ props.onSaved?.({ kind: "invitation.accepted", email: invitation?.email ?? "", ...invitation?.organization ? { organization: invitation.organization } : {} });
1739
1891
  if (r.status === "mfa_required") return setState("code");
1740
1892
  f.done();
1741
1893
  };
1742
1894
  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 });
1895
+ const back = props.links?.signIn && /* @__PURE__ */ jsx(A, { f, href: props.links.signIn, children: f.t.backToSignIn });
1744
1896
  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 }) });
1897
+ return /* @__PURE__ */ jsx(Wrap, { f, as: "div", slot: "form", root: f.root, busy: true, children: /* @__PURE__ */ jsx(Note, { f, children: f.t.openingInvitation }) });
1746
1898
  }
1747
1899
  if (state === "invalid") {
1748
- 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 }),
1900
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1901
+ /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: general ? f.say(general) : f.t.invitationInvalid }),
1750
1902
  back
1751
1903
  ] });
1752
1904
  }
1753
1905
  if (state === "code") return /* @__PURE__ */ jsx(SecondStepForm, { f, auth });
1754
1906
  const workspace = invitation?.workspace ?? "";
1755
1907
  const heading = invitation?.organization ? f.t.invitedToOrganization.replace("{organization}", invitation.organization).replace("{workspace}", workspace) : f.t.invitedTo.replace("{workspace}", workspace);
1756
- 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) }),
1908
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1909
+ /* @__PURE__ */ jsx(Heading, { f, children: heading }),
1910
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.invitedAs.replace("{email}", invitation?.email ?? "") }),
1911
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
1760
1912
  /* @__PURE__ */ jsx("input", { type: "email", name: "username", autoComplete: "username", value: invitation?.email ?? "", readOnly: true, hidden: true }),
1761
1913
  /* @__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
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.acceptInvite })
1914
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.acceptInvite })
1915
+ ] });
1916
+ }
1917
+ function PendingTerms(props) {
1918
+ const auth = useAuth();
1919
+ const f = useForm(props);
1920
+ const [owed, setOwed] = useState(null);
1921
+ useEffect(() => {
1922
+ void auth.account.legal().then((r) => setOwed(r.ok ? r.pending : []));
1923
+ }, []);
1924
+ if (!owed || owed.length === 0) return null;
1925
+ const accept = async () => {
1926
+ f.setBusy(true);
1927
+ f.setError(null);
1928
+ const r = await auth.account.acceptLegal({ kinds: owed.map((d) => d.kind) });
1929
+ f.setBusy(false);
1930
+ if (!r.ok) return f.setError(r.error);
1931
+ setOwed([]);
1932
+ props.onSuccess?.();
1933
+ };
1934
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1935
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.termsChanged }),
1936
+ /* @__PURE__ */ jsxs(Note, { f, children: [
1937
+ f.t.termsChangedHelp,
1938
+ " ",
1939
+ /* @__PURE__ */ jsx(Documents, { f, documents: owed })
1940
+ ] }),
1941
+ f.error && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(f.error) }),
1942
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), onClick: () => void accept(), children: f.t.acceptTerms })
1763
1943
  ] });
1764
1944
  }
1945
+ var ACCOUNT_SECTIONS = ["profile", "email", "password", "linked", "secondStep", "phone", "passkeys", "agents", "sessions"];
1765
1946
  function useSection(props) {
1766
1947
  const f = useForm({ ...props, unstyled: true });
1767
1948
  const [done, setDone] = useState(null);
1768
- const run = async (action) => {
1769
- f.setBusy(true);
1949
+ const run = async (action, key = SUBMIT) => {
1950
+ f.setBusy(key);
1770
1951
  f.setError(null);
1771
1952
  setDone(null);
1772
1953
  const r = await action();
@@ -1780,37 +1961,64 @@ function useSection(props) {
1780
1961
  function Account(props) {
1781
1962
  const auth = useAuth();
1782
1963
  const { accountUrl } = useSession();
1783
- const { t, root } = useForm(props);
1784
- const cx = (slot) => props.classNames?.[slot] ?? `cauthy-${slot}`;
1964
+ const f = useForm(props);
1785
1965
  const [account, setAccount] = useState(null);
1786
- const [sessions, setSessions] = useState([]);
1787
1966
  useEffect(() => {
1788
1967
  void auth.account.get().then(setAccount);
1789
- void auth.account.sessions().then(setSessions);
1790
1968
  }, []);
1791
- if (account === null) return /* @__PURE__ */ jsx("div", { className: cx("account"), ...root, "aria-busy": "true" });
1969
+ if (account === null) return /* @__PURE__ */ jsx(Wrap, { f, as: "div", slot: "account", root: f.root, busy: true });
1792
1970
  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
- ] });
1971
+ const mfa = (mfaEnabled) => setAccount({ ...account, mfaEnabled });
1972
+ const part = (section) => {
1973
+ switch (section) {
1974
+ case "profile":
1975
+ return /* @__PURE__ */ jsx(ProfilePart, { props, account, onSaved: setAccount });
1976
+ case "email":
1977
+ return /* @__PURE__ */ jsx(EmailPart, { props, account });
1978
+ case "password":
1979
+ return account.hasPassword ? /* @__PURE__ */ jsx(PasswordPart, { props }) : null;
1980
+ case "linked":
1981
+ return /* @__PURE__ */ jsx(LinkedPart, { props });
1982
+ case "secondStep":
1983
+ return /* @__PURE__ */ jsx(SecondStepPart, { props, account, onChanged: mfa });
1984
+ case "phone":
1985
+ return /* @__PURE__ */ jsx(PhonePart, { props, account, onChanged: mfa });
1986
+ case "passkeys":
1987
+ return /* @__PURE__ */ jsx(PasskeysPart, { props, account, accountUrl: accountUrl(), when });
1988
+ case "agents":
1989
+ return /* @__PURE__ */ jsx(AgentsPart, { props, when });
1990
+ case "sessions":
1991
+ return /* @__PURE__ */ jsx(SessionsPart, { props, when });
1992
+ }
1993
+ };
1994
+ return /* @__PURE__ */ jsx(Wrap, { f, as: "div", slot: "account", root: f.root, children: (props.sections ?? ACCOUNT_SECTIONS).map((section) => /* @__PURE__ */ jsx(Fragment, { children: part(section) }, section)) });
1995
+ }
1996
+ function AccountProfile(props) {
1997
+ return /* @__PURE__ */ jsx(Account, { ...props, sections: ["profile"] });
1998
+ }
1999
+ function AccountEmail(props) {
2000
+ return /* @__PURE__ */ jsx(Account, { ...props, sections: ["email"] });
2001
+ }
2002
+ function AccountPassword(props) {
2003
+ return /* @__PURE__ */ jsx(Account, { ...props, sections: ["password"] });
2004
+ }
2005
+ function AccountLinked(props) {
2006
+ return /* @__PURE__ */ jsx(Account, { ...props, sections: ["linked"] });
2007
+ }
2008
+ function AccountSecondStep(props) {
2009
+ return /* @__PURE__ */ jsx(Account, { ...props, sections: ["secondStep"] });
2010
+ }
2011
+ function AccountPhone(props) {
2012
+ return /* @__PURE__ */ jsx(Account, { ...props, sections: ["phone"] });
2013
+ }
2014
+ function AccountPasskeys(props) {
2015
+ return /* @__PURE__ */ jsx(Account, { ...props, sections: ["passkeys"] });
2016
+ }
2017
+ function AccountAgents(props) {
2018
+ return /* @__PURE__ */ jsx(Account, { ...props, sections: ["agents"] });
2019
+ }
2020
+ function AccountSessions(props) {
2021
+ return /* @__PURE__ */ jsx(Account, { ...props, sections: ["sessions"] });
1814
2022
  }
1815
2023
  var OWNER = "owner";
1816
2024
  function memberActions(seat, m) {
@@ -1822,8 +2030,9 @@ function memberActions(seat, m) {
1822
2030
  inviteAgain: seat.can.manageMembers && m.invited
1823
2031
  };
1824
2032
  }
1825
- function rolesToGive(seat, roles) {
1826
- return seat.role === OWNER ? roles : roles.filter((c) => c.role !== OWNER);
2033
+ function rolesToGive(seat, roles, only) {
2034
+ const mine = seat.role === OWNER ? roles : roles.filter((c) => c.role !== OWNER);
2035
+ return only ? mine.filter((c) => only(c.role, c)) : mine;
1827
2036
  }
1828
2037
  function OrganizationMembers(props) {
1829
2038
  const auth = useAuth();
@@ -1840,6 +2049,8 @@ function OrganizationMembers(props) {
1840
2049
  const [gone, setGone] = useState(null);
1841
2050
  const id = props.organizationId;
1842
2051
  const when = props.formatDate ?? ((iso) => new Date(iso).toLocaleDateString());
2052
+ const hidden = (action) => props.hide?.includes(action) === true;
2053
+ const saved = (event) => props.onSaved?.(event);
1843
2054
  const load = async () => {
1844
2055
  const [list, people, choices] = await Promise.all([auth.organizations.list(), auth.organizations.members(id), auth.organizations.roles(id)]);
1845
2056
  const mine = list.find((o) => o.id === id) ?? null;
@@ -1847,135 +2058,173 @@ function OrganizationMembers(props) {
1847
2058
  if (mine) setName(mine.name);
1848
2059
  setMembers(people);
1849
2060
  setRoles(choices ?? []);
1850
- const offered = mine ? rolesToGive(mine, choices ?? []) : [];
2061
+ const offered = mine ? rolesToGive(mine, choices ?? [], props.roles) : [];
1851
2062
  if (offered.length > 0 && !offered.some((c) => c.role === role)) setRole(offered[0].role);
1852
2063
  };
1853
2064
  useEffect(() => {
1854
2065
  void load();
1855
2066
  }, [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 }) });
1857
- 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 }) });
2067
+ if (gone !== null) return /* @__PURE__ */ jsx(Wrap, { f, as: "div", slot: "account", root, children: /* @__PURE__ */ jsx(Say, { f, tone: "success", children: gone }) });
2068
+ if (seat === void 0) return /* @__PURE__ */ jsx(Wrap, { f, as: "div", slot: "account", root, busy: true });
2069
+ if (seat === null) return /* @__PURE__ */ jsx(Wrap, { f, as: "div", slot: "account", root, children: /* @__PURE__ */ jsx(Note, { f, children: f.t.organizationMissing }) });
1859
2070
  const manage = seat.can.manageMembers;
1860
- const giveable = rolesToGive(seat, roles);
1861
- const label = (r) => roles.find((c) => c.role === r)?.name || r;
2071
+ const giveable = rolesToGive(seat, roles, props.roles);
2072
+ const roleName = (c) => props.roleLabel?.(c) || c.name || c.role;
2073
+ const label = (r) => {
2074
+ const c = roles.find((x) => x.role === r);
2075
+ return c ? roleName(c) : r;
2076
+ };
1862
2077
  const add = async (e) => {
1863
2078
  e.preventDefault();
1864
- const r = await run(() => auth.organizations.add(id, { email, role, locale: props.locale }));
2079
+ if (!f.check(checkEmail(email))) return;
2080
+ const r = await run(() => auth.organizations.add(id, { email, role, locale: props.locale }), "add");
1865
2081
  if (!r.ok) return;
1866
2082
  setEmail("");
1867
2083
  setDone(f.t.memberAdded.replace("{email}", r.member.email));
2084
+ saved({ kind: "member.added", organizationId: id, userId: r.member.userId, email: r.member.email, role: r.member.role || role });
1868
2085
  await load();
1869
2086
  };
1870
2087
  const change = async (m, next) => {
1871
- const r = await run(() => auth.organizations.setRole(id, m.userId, next));
1872
- if (r.ok) await load();
2088
+ const r = await run(() => auth.organizations.setRole(id, m.userId, next), `role:${m.userId}`);
2089
+ if (!r.ok) return;
2090
+ saved({ kind: "member.role_changed", organizationId: id, userId: m.userId, role: next, previousRole: m.role });
2091
+ await load();
1873
2092
  };
1874
2093
  const remove = async (m) => {
1875
2094
  setAsking(null);
1876
- const r = await run(() => auth.organizations.remove(id, m.userId));
2095
+ const r = await run(() => auth.organizations.remove(id, m.userId), `remove:${m.userId}`);
1877
2096
  if (!r.ok) return;
2097
+ saved({ kind: m.you ? "member.left" : "member.removed", organizationId: id, userId: m.userId });
1878
2098
  if (m.you) setSeat(null);
1879
2099
  else await load();
1880
2100
  };
1881
2101
  const inviteAgain = async (m) => {
1882
- const r = await run(() => auth.organizations.inviteAgain(id, m.userId, { locale: props.locale }));
1883
- if (r.ok) setDone(f.t.memberInvitationSent);
2102
+ const r = await run(() => auth.organizations.inviteAgain(id, m.userId, { locale: props.locale }), `invite:${m.userId}`);
2103
+ if (!r.ok) return;
2104
+ setDone(f.t.memberInvitationSent);
2105
+ saved({ kind: "member.invited_again", organizationId: id, userId: m.userId });
1884
2106
  };
1885
2107
  const rename = async (e) => {
1886
2108
  e.preventDefault();
1887
- const r = await run(() => auth.organizations.rename(id, name));
2109
+ const r = await run(() => auth.organizations.rename(id, name), "rename");
1888
2110
  if (!r.ok) return;
1889
2111
  setSeat(r.organization);
1890
2112
  setName(r.organization.name);
1891
2113
  setDone(f.t.organizationRenamed.replace("{name}", r.organization.name));
2114
+ saved({ kind: "organization.renamed", organizationId: id, name: r.organization.name });
1892
2115
  };
1893
2116
  const close = async (how) => {
1894
2117
  setAsking(null);
1895
- 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));
2118
+ const r = await run(() => how === "archive" ? auth.organizations.archive(id) : auth.organizations.delete(id), how);
2119
+ if (!r.ok) return;
2120
+ setGone((how === "archive" ? f.t.organizationArchived : f.t.organizationDeleted).replace("{org}", seat.name));
2121
+ saved({ kind: how === "archive" ? "organization.archived" : "organization.deleted", organizationId: id });
2122
+ };
2123
+ const ask = (key, title, question, yes) => {
2124
+ const { Confirm } = f.C;
2125
+ if (!Confirm) return setAsking(key);
2126
+ void Confirm({ title, body: question, confirmLabel: title, cancelLabel: f.t.cancel, danger: true }).then((ok) => {
2127
+ if (ok) yes();
2128
+ });
1897
2129
  };
1898
2130
  const confirming = (text2, label2, yes) => /* @__PURE__ */ jsxs("span", { role: "group", "aria-label": label2, children: [
1899
2131
  /* @__PURE__ */ jsx("span", { children: text2 }),
1900
2132
  " ",
1901
- /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: yes, children: f.t.yes }),
1902
- /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(null), children: f.t.cancel })
2133
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "danger", disabled: f.busy, className: f.cx("providerButton"), onClick: yes, children: f.t.yes }),
2134
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(null), children: f.t.cancel })
1903
2135
  ] });
1904
- return /* @__PURE__ */ jsxs("div", { className: f.cx("account"), ...root, children: [
1905
- /* @__PURE__ */ jsxs("section", { className: f.cx("section"), children: [
1906
- /* @__PURE__ */ jsxs("h2", { className: f.cx("heading"), children: [
2136
+ const leaveQuestion = f.t.memberLeaveConfirm.replace("{org}", seat.name);
2137
+ const archiveQuestion = f.t.organizationArchiveConfirm.replace("{org}", seat.name);
2138
+ const deleteQuestion = f.t.organizationDeleteConfirm.replace("{org}", seat.name);
2139
+ const archiving = seat.can.archive && !hidden("archive");
2140
+ const deleting = seat.can.delete && !hidden("delete");
2141
+ const renaming = seat.can.update && !hidden("rename");
2142
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "div", slot: "account", root, children: [
2143
+ /* @__PURE__ */ jsxs(Wrap, { f, as: "section", slot: "section", section: "members", children: [
2144
+ /* @__PURE__ */ jsxs(Heading, { f, children: [
1907
2145
  f.t.members,
1908
2146
  " \xB7 ",
1909
2147
  seat.name
1910
2148
  ] }),
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 }))
2149
+ problem && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(problem) }),
2150
+ members === null ? /* @__PURE__ */ jsxs(Fragment2, { children: [
2151
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.membersHidden }),
2152
+ 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(
2153
+ f.Button,
2154
+ {
2155
+ type: "button",
2156
+ variant: "danger",
2157
+ loading: f.loading(`remove:${auth.session.user.id}`),
2158
+ disabled: f.busy,
2159
+ className: f.cx("providerButton"),
2160
+ onClick: () => ask(auth.session.user.id, f.t.memberLeave, leaveQuestion, () => void remove({ userId: auth.session.user.id, you: true })),
2161
+ children: f.t.memberLeave
2162
+ }
2163
+ ))
1919
2164
  ] }) : /* @__PURE__ */ jsx("ul", { className: f.cx("sessions"), children: members.map((m) => {
1920
2165
  const who = m.name || m.email;
1921
2166
  const may = memberActions(seat, m);
2167
+ const removable = may.remove && !(m.you && hidden("leave"));
2168
+ const question = m.you ? leaveQuestion : f.t.memberRemoveConfirm.replace("{who}", who);
2169
+ const action = m.you ? f.t.memberLeave : f.t.memberRemove;
2170
+ const choices = giveable.some((c) => c.role === m.role) ? giveable : [...roles.filter((c) => c.role === m.role), ...giveable];
1922
2171
  return /* @__PURE__ */ jsxs("li", { className: f.cx("session"), children: [
1923
2172
  /* @__PURE__ */ jsxs("span", { children: [
1924
2173
  who,
1925
2174
  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}` : "",
2175
+ m.you && /* @__PURE__ */ jsx(Tag, { f, plain: ` (${f.t.memberYou})`, children: f.t.memberYou }),
2176
+ f.C.Badge ? /* @__PURE__ */ jsxs(Fragment2, { children: [
2177
+ m.invited && /* @__PURE__ */ jsx(Tag, { f, tone: "warning", plain: "", children: f.t.memberInvited }),
2178
+ m.manager && /* @__PURE__ */ jsx(Tag, { f, tone: "success", plain: "", children: f.t.memberManager })
2179
+ ] }) : `${m.invited ? ` \xB7 ${f.t.memberInvited}` : ""}${m.manager ? ` \xB7 ${f.t.memberManager}` : ""}`,
1929
2180
  " \xB7 ",
1930
2181
  when(m.since)
1931
2182
  ] }),
1932
2183
  " ",
1933
2184
  may.changeRole && giveable.length > 0 ? /* @__PURE__ */ jsx(
1934
- "select",
2185
+ Pick,
1935
2186
  {
1936
- "aria-label": `${f.t.memberRole}: ${who}`,
1937
- className: f.cx("input"),
2187
+ f,
2188
+ name: `role_${m.userId}`,
2189
+ label: `${f.t.memberRole}: ${who}`,
1938
2190
  value: m.role,
1939
2191
  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))
2192
+ options: choices.map((c) => ({ value: c.role, label: roleName(c) })),
2193
+ onChange: (next) => void change(m, next)
1942
2194
  }
1943
- ) : /* @__PURE__ */ jsx("span", { children: label(m.role) }),
2195
+ ) : f.C.Badge ? /* @__PURE__ */ jsx(f.C.Badge, { tone: "neutral", children: label(m.role) }) : /* @__PURE__ */ jsx("span", { children: label(m.role) }),
1944
2196
  " ",
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: [
1950
- 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 })
2197
+ asking === m.userId ? confirming(question, action, () => void remove(m)) : /* @__PURE__ */ jsxs(Fragment2, { children: [
2198
+ may.inviteAgain && /* @__PURE__ */ 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 }),
2199
+ removable && /* @__PURE__ */ 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 })
1952
2200
  ] })
1953
2201
  ] }, m.userId);
1954
2202
  }) }),
1955
- done && /* @__PURE__ */ jsx("p", { role: "status", className: f.cx("notice"), children: done })
2203
+ done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
1956
2204
  ] }),
1957
- 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 }),
2205
+ manage && giveable.length > 0 && /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "add-member", onSubmit: add, noValidate: true, children: [
2206
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.memberAdd }),
1959
2207
  /* @__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
- ] }),
1964
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.memberAdd })
2208
+ /* @__PURE__ */ jsx(AddRole, { f, value: role, options: giveable.map((c) => ({ value: c.role, label: roleName(c) })), onChange: setRole }),
2209
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading("add"), disabled: f.busy, className: f.cx("button"), children: f.t.memberAdd })
1965
2210
  ] }),
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: [
2211
+ (renaming || archiving || deleting) && /* @__PURE__ */ jsxs(Wrap, { f, as: "section", slot: "section", section: "organization", children: [
2212
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.organization }),
2213
+ renaming && /* @__PURE__ */ jsxs(Wrap, { f, as: "form", section: "rename", onSubmit: rename, noValidate: true, children: [
1969
2214
  /* @__PURE__ */ jsx(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "organization", required: true, onChange: setName }),
1970
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.organizationRename })
2215
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading("rename"), disabled: f.busy, className: f.cx("button"), children: f.t.organizationRename })
1971
2216
  ] }),
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 })
2217
+ asking === "archive" ? confirming(archiveQuestion, f.t.organizationArchive, () => void close("archive")) : asking === "delete" ? confirming(deleteQuestion, f.t.organizationDelete, () => void close("delete")) : /* @__PURE__ */ jsxs(Fragment2, { children: [
2218
+ archiving && /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "danger", loading: f.loading("archive"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => ask("archive", f.t.organizationArchive, archiveQuestion, () => void close("archive")), children: f.t.organizationArchive }),
2219
+ deleting && /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "danger", loading: f.loading("delete"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => ask("delete", f.t.organizationDelete, deleteQuestion, () => void close("delete")), children: f.t.organizationDelete })
1975
2220
  ] })
1976
2221
  ] })
1977
2222
  ] });
1978
2223
  }
2224
+ function AddRole({ f, value, options, onChange }) {
2225
+ const id = useId();
2226
+ return /* @__PURE__ */ jsx(Labelled, { f, id, label: f.t.memberRole, children: /* @__PURE__ */ jsx(Pick, { f, id, name: "role", value, options, onChange }) });
2227
+ }
1979
2228
  function PasskeysPart({ props, account, accountUrl, when }) {
1980
2229
  const auth = useAuth();
1981
2230
  const { f, done, setDone, run, general } = useSection(props);
@@ -1992,14 +2241,15 @@ function PasskeysPart({ props, account, accountUrl, when }) {
1992
2241
  }, []);
1993
2242
  if (here === null) return null;
1994
2243
  if (!here) {
1995
- 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 })
2244
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "section", slot: "section", section: "passkeys", children: [
2245
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.passkeys }),
2246
+ /* @__PURE__ */ jsx(A, { f, href: accountUrl, children: f.t.managePasskeys })
1998
2247
  ] });
1999
2248
  }
2000
2249
  const add = async (e, kind = "device") => {
2001
2250
  e?.preventDefault();
2002
- const r = await run(() => auth.account.addPasskey({ name, password, kind }));
2251
+ if (account.hasPassword && !f.check(checkPassword(password))) return;
2252
+ const r = await run(() => auth.account.addPasskey({ name, password, kind }), kind === "security_key" ? "security_key" : SUBMIT);
2003
2253
  if (!r.ok) {
2004
2254
  if (r.error.code === "cancelled") f.setError(null);
2005
2255
  return;
@@ -2008,28 +2258,31 @@ function PasskeysPart({ props, account, accountUrl, when }) {
2008
2258
  setName("");
2009
2259
  setPassword("");
2010
2260
  setDone(f.t.passkeyAdded);
2261
+ props.onSaved?.({ kind: "passkey.added", id: r.passkey.id });
2011
2262
  };
2012
2263
  const remove = async (id) => {
2013
- const r = await run(() => auth.account.removePasskey(id));
2014
- if (r.ok) setList(list.filter((p) => p.id !== id));
2264
+ const r = await run(() => auth.account.removePasskey(id), `remove:${id}`);
2265
+ if (!r.ok) return;
2266
+ setList(list.filter((p) => p.id !== id));
2267
+ props.onSaved?.({ kind: "passkey.removed", id });
2015
2268
  };
2016
- 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: [
2269
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "passkeys", onSubmit: add, noValidate: true, children: [
2270
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.passkeys }),
2271
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.passkeysHelp }),
2272
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2273
+ 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
2274
  p.name || f.t.passkeyUnnamed,
2022
- p.kind !== "unknown" ? ` \xB7 ${f.t.passkeyKinds[p.kind]}` : "",
2275
+ p.kind !== "unknown" && /* @__PURE__ */ jsx(Tag, { f, plain: ` \xB7 ${f.t.passkeyKinds[p.kind]}`, children: f.t.passkeyKinds[p.kind] }),
2023
2276
  " \xB7 ",
2024
2277
  when(p.createdAt),
2025
2278
  " ",
2026
- /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void remove(p.id), children: f.t.removePasskey })
2279
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "danger", loading: f.loading(`remove:${p.id}`), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void remove(p.id), children: f.t.removePasskey })
2027
2280
  ] }, p.id)) }),
2028
2281
  /* @__PURE__ */ jsx(Field, { f, name: "name", type: "text", label: f.t.passkeyName, value: name, autoComplete: "off", onChange: setName }),
2029
2282
  account.hasPassword && /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword }),
2030
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.addPasskeyDevice }),
2031
- /* @__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 })
2283
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.addPasskeyDevice }),
2284
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", loading: f.loading("security_key"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void add(null, "security_key"), children: f.t.addSecurityKey }),
2285
+ done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2033
2286
  ] });
2034
2287
  }
2035
2288
  function ProfilePart({ props, account, onSaved }) {
@@ -2042,14 +2295,15 @@ function ProfilePart({ props, account, onSaved }) {
2042
2295
  if (!r.ok) return;
2043
2296
  onSaved(r.account);
2044
2297
  setDone(f.t.saved);
2298
+ props.onSaved?.({ kind: "profile.saved", name: r.account.name });
2045
2299
  };
2046
- 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) }),
2300
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "profile", onSubmit: submit, noValidate: true, children: [
2301
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.profile }),
2302
+ /* @__PURE__ */ jsx(Note, { f, children: account.email }),
2303
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2050
2304
  /* @__PURE__ */ jsx(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "name", onChange: setName }),
2051
- /* @__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 })
2305
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.save }),
2306
+ done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2053
2307
  ] });
2054
2308
  }
2055
2309
  function EmailPart({ props, account }) {
@@ -2059,19 +2313,21 @@ function EmailPart({ props, account }) {
2059
2313
  const [password, setPassword] = useState("");
2060
2314
  const submit = async (e) => {
2061
2315
  e.preventDefault();
2316
+ if (!f.check(checkEmail(email), account.hasPassword && checkPassword(password))) return;
2062
2317
  const r = await run(() => auth.account.changeEmail({ email, password }));
2063
2318
  if (!r.ok) return;
2064
2319
  setPassword("");
2065
2320
  setDone(f.t.emailSent.replace("{email}", r.email));
2321
+ props.onSaved?.({ kind: "email.change_requested", email: r.email });
2066
2322
  };
2067
- 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) }),
2323
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "email", onSubmit: submit, noValidate: true, children: [
2324
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.emailAddress }),
2325
+ /* @__PURE__ */ jsx(Note, { f, children: account.email }),
2326
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2071
2327
  /* @__PURE__ */ jsx(Field, { f, name: "email", type: "email", label: f.t.newEmail, value: email, autoComplete: "email", required: true, onChange: setEmail }),
2072
2328
  account.hasPassword && /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword }),
2073
- /* @__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 })
2329
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.changeEmail }),
2330
+ done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2075
2331
  ] });
2076
2332
  }
2077
2333
  function LinkedPart({ props }) {
@@ -2084,27 +2340,32 @@ function LinkedPart({ props }) {
2084
2340
  void auth.account.identities().then(setLinked);
2085
2341
  void auth.methods().then((m) => setOffered(m.providers));
2086
2342
  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." });
2343
+ const provider = back?.get("linked");
2344
+ if (provider) {
2345
+ setCame(provider);
2346
+ props.onSaved?.({ kind: "identity.linked", provider });
2347
+ } else if (back?.get("auth_error")) f.setError({ code: back.get("auth_error"), message: "That account could not be linked. Try again." });
2089
2348
  }, []);
2090
2349
  if (linked === null) return null;
2091
2350
  const name = (id) => offered.find((p) => p.id === id)?.name ?? id;
2092
2351
  const unlink = async (provider) => {
2093
- const r = await run(() => auth.account.unlink(provider));
2094
- if (r.ok) setLinked(linked.filter((x) => x.provider !== provider));
2352
+ const r = await run(() => auth.account.unlink(provider), `unlink:${provider}`);
2353
+ if (!r.ok) return;
2354
+ setLinked(linked.filter((x) => x.provider !== provider));
2355
+ props.onSaved?.({ kind: "identity.unlinked", provider });
2095
2356
  };
2096
2357
  const here = typeof window === "undefined" ? void 0 : window.location.pathname;
2097
- 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: [
2358
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "section", slot: "section", section: "linked", children: [
2359
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.linkedAccounts }),
2360
+ came && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: f.t.linked }),
2361
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2362
+ 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
2363
  name(x.provider),
2103
2364
  x.email ? ` \xB7 ${x.email}` : "",
2104
2365
  " ",
2105
- /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void unlink(x.provider), children: f.t.unlink })
2366
+ /* @__PURE__ */ 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 })
2106
2367
  ] }, x.provider)) }),
2107
- offered.filter((p) => !linked.some((x) => x.provider === p.id)).map((p) => /* @__PURE__ */ jsxs(f.Button, { type: "button", className: f.cx("providerButton"), onClick: () => auth.account.link(p.id, { returnTo: here }), children: [
2368
+ offered.filter((p) => !linked.some((x) => x.provider === p.id)).map((p) => /* @__PURE__ */ jsxs(f.Button, { type: "button", variant: "secondary", className: f.cx("providerButton"), onClick: () => auth.account.link(p.id, { returnTo: here }), children: [
2108
2369
  f.t.linkWith,
2109
2370
  " ",
2110
2371
  p.name
@@ -2119,19 +2380,21 @@ function PasswordPart({ props }) {
2119
2380
  usePasswordPolicy(f, useMethods(auth));
2120
2381
  const submit = async (e) => {
2121
2382
  e.preventDefault();
2383
+ if (!f.check(checkPassword(password), newPasswordProblem(newPassword, f.policy, "newPassword"))) return;
2122
2384
  const r = await run(() => auth.account.changePassword({ password, newPassword }));
2123
2385
  if (!r.ok) return;
2124
2386
  setPassword("");
2125
2387
  setNewPassword("");
2126
2388
  setDone(f.t.passwordChangedHere);
2389
+ props.onSaved?.({ kind: "password.changed" });
2127
2390
  };
2128
- 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) }),
2391
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "password", onSubmit: submit, noValidate: true, children: [
2392
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.changePassword }),
2393
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2131
2394
  /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword }),
2132
2395
  /* @__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
- /* @__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 })
2396
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.changePassword }),
2397
+ done && /* @__PURE__ */ jsx(Say, { f, tone: "success", children: done })
2135
2398
  ] });
2136
2399
  }
2137
2400
  function SecondStepPart({ props, account, onChanged }) {
@@ -2155,6 +2418,7 @@ function SecondStepPart({ props, account, onChanged }) {
2155
2418
  setPassword("");
2156
2419
  setCodes(r.recoveryCodes);
2157
2420
  onChanged(true);
2421
+ props.onSaved?.({ kind: "mfa.enabled" });
2158
2422
  };
2159
2423
  const turnOff = async (e) => {
2160
2424
  e.preventDefault();
@@ -2163,35 +2427,38 @@ function SecondStepPart({ props, account, onChanged }) {
2163
2427
  setPassword("");
2164
2428
  setCodes(null);
2165
2429
  onChanged(false);
2430
+ props.onSaved?.({ kind: "mfa.disabled" });
2166
2431
  };
2167
2432
  const renew = async () => {
2168
- const r = await run(() => auth.account.mfa.recoveryCodes({ password }));
2169
- if (r.ok) setCodes(r.recoveryCodes);
2433
+ const r = await run(() => auth.account.mfa.recoveryCodes({ password }), "codes");
2434
+ if (!r.ok) return;
2435
+ setCodes(r.recoveryCodes);
2436
+ props.onSaved?.({ kind: "recovery_codes.regenerated" });
2170
2437
  };
2171
2438
  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 }),
2439
+ const shown = codes && /* @__PURE__ */ jsxs(Fragment2, { children: [
2440
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.keepCodes }),
2174
2441
  /* @__PURE__ */ jsx("ul", { className: f.cx("codes"), children: codes.map((c) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("code", { children: c }) }, c)) })
2175
2442
  ] });
2176
2443
  if (setup) {
2177
- 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 }),
2444
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "secondStep", onSubmit: confirm, noValidate: true, children: [
2445
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.secondStep }),
2446
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.addToApp }),
2180
2447
  /* @__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) }),
2448
+ /* @__PURE__ */ jsx(A, { f, href: setup.otpauthUri, children: f.t.openInApp }),
2449
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2183
2450
  /* @__PURE__ */ jsx(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
2184
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
2451
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.verify })
2185
2452
  ] });
2186
2453
  }
2187
- 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 }),
2454
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "secondStep", onSubmit: account.mfaEnabled ? turnOff : start, noValidate: true, children: [
2455
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.secondStep }),
2456
+ /* @__PURE__ */ jsx(Note, { f, children: account.mfaEnabled ? f.t.secondStepOn : f.t.secondStepOff }),
2190
2457
  shown,
2191
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2458
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2192
2459
  passwordField,
2193
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: account.mfaEnabled ? f.t.turnOff : f.t.turnOn }),
2194
- account.mfaEnabled && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void renew(), children: f.t.newRecoveryCodes })
2460
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: account.mfaEnabled ? "danger" : "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: account.mfaEnabled ? f.t.turnOff : f.t.turnOn }),
2461
+ account.mfaEnabled && /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "secondary", loading: f.loading("codes"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void renew(), children: f.t.newRecoveryCodes })
2195
2462
  ] });
2196
2463
  }
2197
2464
  function PhonePart({ props, account, onChanged }) {
@@ -2226,6 +2493,7 @@ function PhonePart({ props, account, onChanged }) {
2226
2493
  setPassword("");
2227
2494
  if (r.recoveryCodes.length > 0) setCodes(r.recoveryCodes);
2228
2495
  onChanged(true);
2496
+ props.onSaved?.({ kind: "phone.added", channel });
2229
2497
  await load();
2230
2498
  };
2231
2499
  const remove = async (e) => {
@@ -2236,33 +2504,35 @@ function PhonePart({ props, account, onChanged }) {
2236
2504
  await load();
2237
2505
  const left = await auth.account.mfa.factors();
2238
2506
  onChanged(Boolean(left?.totp));
2507
+ props.onSaved?.({ kind: "phone.removed" });
2239
2508
  };
2240
2509
  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
2510
  if (sentTo) {
2242
- 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) }),
2511
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "phone", onSubmit: confirm, noValidate: true, children: [
2512
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.phone }),
2513
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.codeSent.replace("{to}", sentTo) }),
2514
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2246
2515
  /* @__PURE__ */ jsx(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
2247
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
2516
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.verify })
2248
2517
  ] });
2249
2518
  }
2250
- 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 }),
2519
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "form", slot: "section", section: "phone", onSubmit: ways.phone ? remove : add, noValidate: true, children: [
2520
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.phone }),
2521
+ /* @__PURE__ */ jsx(Note, { f, children: ways.phone ? f.t.phoneOn.replace("{to}", ways.phone.number) : `${f.t.phoneOff} ${f.t.phoneHelp}` }),
2522
+ codes && /* @__PURE__ */ jsxs(Fragment2, { children: [
2523
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.keepCodes }),
2255
2524
  /* @__PURE__ */ jsx("ul", { className: f.cx("codes"), children: codes.map((c) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("code", { children: c }) }, c)) })
2256
2525
  ] }),
2257
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2526
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2258
2527
  !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: [
2528
+ !ways.phone && ways.phoneChannels.length > 1 && f.C.Select && /* @__PURE__ */ jsx(ChannelPick, { f, value: channel, channels: ways.phoneChannels, onChange: setChannel }),
2529
+ !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
2530
  /* @__PURE__ */ jsx("input", { type: "radio", name: "channel", value: c, checked: channel === c, onChange: () => setChannel(c) }),
2261
2531
  " ",
2262
2532
  c === "whatsapp" ? f.t.byWhatsapp : f.t.bySms
2263
2533
  ] }, c)) }),
2264
2534
  passwordField,
2265
- /* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: ways.phone ? f.t.removePhone : f.t.addPhone })
2535
+ /* @__PURE__ */ jsx(f.Button, { type: "submit", variant: ways.phone ? "danger" : "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: ways.phone ? f.t.removePhone : f.t.addPhone })
2266
2536
  ] });
2267
2537
  }
2268
2538
  function AgentsPart({ props, when }) {
@@ -2274,13 +2544,15 @@ function AgentsPart({ props, when }) {
2274
2544
  }, []);
2275
2545
  if (list.length === 0) return null;
2276
2546
  const disconnect = async (id) => {
2277
- const r = await run(() => auth.account.disconnectAgent(id));
2278
- if (r.ok) setList(list.filter((a) => a.id !== id));
2547
+ const r = await run(() => auth.account.disconnectAgent(id), `disconnect:${id}`);
2548
+ if (!r.ok) return;
2549
+ setList(list.filter((a) => a.id !== id));
2550
+ props.onSaved?.({ kind: "agent.disconnected", id });
2279
2551
  };
2280
- 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) }),
2552
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "section", slot: "section", section: "agents", children: [
2553
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.agents }),
2554
+ /* @__PURE__ */ jsx(Note, { f, children: f.t.agentsHelp }),
2555
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2284
2556
  /* @__PURE__ */ jsx("ul", { className: f.cx("sessions"), children: list.map((a) => /* @__PURE__ */ jsxs("li", { className: f.cx("session"), children: [
2285
2557
  a.name || f.t.anAgent,
2286
2558
  a.redirectHost ? ` (${a.redirectHost})` : "",
@@ -2290,37 +2562,71 @@ function AgentsPart({ props, when }) {
2290
2562
  " \xB7 ",
2291
2563
  when(a.lastUsedAt),
2292
2564
  " ",
2293
- /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void disconnect(a.id), children: f.t.disconnect })
2565
+ /* @__PURE__ */ 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 })
2294
2566
  ] }, a.id)) })
2295
2567
  ] });
2296
2568
  }
2297
- function SignOutEverywhere({ props }) {
2569
+ function SessionsPart({ props, when }) {
2298
2570
  const auth = useAuth();
2299
2571
  const { f, run, general } = useSection(props);
2572
+ const [sessions, setSessions] = useState([]);
2573
+ useEffect(() => {
2574
+ void auth.account.sessions().then(setSessions);
2575
+ }, []);
2300
2576
  const end = async () => {
2301
2577
  const r = await run(() => auth.account.signOutEverywhere());
2302
2578
  if (!r.ok) return;
2579
+ props.onSaved?.({ kind: "sessions.ended" });
2303
2580
  if (props.onSignedOut) props.onSignedOut();
2304
2581
  else window.location.assign("/");
2305
2582
  };
2306
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2307
- general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
2308
- /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("button"), onClick: () => void end(), children: f.t.signOutEverywhere })
2583
+ return /* @__PURE__ */ jsxs(Wrap, { f, as: "section", slot: "section", section: "sessions", children: [
2584
+ /* @__PURE__ */ jsx(Heading, { f, children: f.t.sessions }),
2585
+ /* @__PURE__ */ jsx("ul", { className: f.cx("sessions"), children: sessions.map((s, i) => /* @__PURE__ */ jsxs("li", { className: f.cx("session"), children: [
2586
+ s.device || f.t.unknownDevice,
2587
+ " \xB7 ",
2588
+ s.ip,
2589
+ " \xB7 ",
2590
+ when(s.createdAt)
2591
+ ] }, i)) }),
2592
+ general && /* @__PURE__ */ jsx(Say, { f, tone: "danger", children: f.say(general) }),
2593
+ /* @__PURE__ */ jsx(f.Button, { type: "button", variant: "danger", loading: f.loading(), disabled: f.busy, className: f.cx("button"), onClick: () => void end(), children: f.t.signOutEverywhere })
2309
2594
  ] });
2310
2595
  }
2596
+ function ChannelPick({ f, value, channels, onChange }) {
2597
+ const id = useId();
2598
+ return /* @__PURE__ */ jsx(Labelled, { f, id, label: f.t.channel, children: /* @__PURE__ */ jsx(
2599
+ Pick,
2600
+ {
2601
+ f,
2602
+ id,
2603
+ name: "channel",
2604
+ value,
2605
+ options: channels.map((c) => ({ value: c, label: c === "whatsapp" ? f.t.byWhatsapp : f.t.bySms })),
2606
+ onChange: (c) => onChange(c)
2607
+ }
2608
+ ) });
2609
+ }
2311
2610
 
2312
2611
  // src/react.tsx
2313
2612
  import { jsx as jsx2 } from "react/jsx-runtime";
2314
2613
  var Ctx = createContext(null);
2614
+ var LocaleCtx = createContext(() => void 0);
2315
2615
  var AHEAD_MS = 6e4;
2316
- function CauthyProvider({ children, basePath = "/api/auth", initialSession }) {
2616
+ function CauthyProvider({ children, basePath = "/api/auth", initialSession, locale }) {
2317
2617
  const base = withoutTrailingSlashes(basePath);
2618
+ const said = useRef2(locale);
2619
+ said.current = locale;
2620
+ const readLocale = useCallback2(() => {
2621
+ const l = said.current;
2622
+ return typeof l === "function" ? l() : l;
2623
+ }, []);
2318
2624
  const [session, setSession] = useState2(initialSession ?? null);
2319
2625
  const [status, setStatus] = useState2(
2320
2626
  initialSession === void 0 ? "loading" : initialSession === null ? "unauthenticated" : "authenticated"
2321
2627
  );
2322
2628
  const asking = useRef2(null);
2323
- const refresh = useCallback(() => {
2629
+ const refresh = useCallback2(() => {
2324
2630
  asking.current ??= (async () => {
2325
2631
  try {
2326
2632
  const res = await fetch(`${base}/session`, { credentials: "same-origin", headers: { accept: "application/json" } });
@@ -2378,7 +2684,7 @@ function CauthyProvider({ children, basePath = "/api/auth", initialSession }) {
2378
2684
  }),
2379
2685
  [session, status, refresh, base]
2380
2686
  );
2381
- return /* @__PURE__ */ jsx2(Ctx.Provider, { value, children });
2687
+ return /* @__PURE__ */ jsx2(Ctx.Provider, { value, children: /* @__PURE__ */ jsx2(LocaleCtx.Provider, { value: readLocale, children }) });
2382
2688
  }
2383
2689
  function useSession() {
2384
2690
  const ctx = useContext(Ctx);
@@ -2387,8 +2693,9 @@ function useSession() {
2387
2693
  }
2388
2694
  function useAuth() {
2389
2695
  const { session, status, refresh, basePath } = useSession();
2696
+ const locale = useContext(LocaleCtx);
2390
2697
  return useMemo(() => {
2391
- const client = createAuthClient({ basePath });
2698
+ const client = createAuthClient({ basePath, locale });
2392
2699
  const then = async (running) => {
2393
2700
  const r = await running;
2394
2701
  await refresh();
@@ -2425,15 +2732,25 @@ function useAuth() {
2425
2732
  status,
2426
2733
  refresh
2427
2734
  };
2428
- }, [basePath, refresh, session, status]);
2735
+ }, [basePath, locale, refresh, session, status]);
2429
2736
  }
2430
2737
  export {
2431
2738
  AcceptInvite,
2432
2739
  Account,
2740
+ AccountAgents,
2741
+ AccountEmail,
2742
+ AccountLinked,
2743
+ AccountPasskeys,
2744
+ AccountPassword,
2745
+ AccountPhone,
2746
+ AccountProfile,
2747
+ AccountSecondStep,
2748
+ AccountSessions,
2433
2749
  CauthyProvider,
2434
2750
  ForgotPassword,
2435
2751
  GoogleOneTap,
2436
2752
  OrganizationMembers,
2753
+ PendingTerms,
2437
2754
  ResetPassword,
2438
2755
  SignIn,
2439
2756
  SignUp,