cauthy-auth 0.28.1 → 0.31.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/README.md +61 -3
- package/dist/{cauthy-CTVg4kvo.d.mts → cauthy-2jtP2LVQ.d.mts} +19 -1
- package/dist/{cauthy-BAwAJChs.d.ts → cauthy-CKfvvv_i.d.ts} +19 -1
- package/dist/cli.js +163 -59
- package/dist/client.d.mts +91 -10
- package/dist/client.d.ts +91 -10
- package/dist/client.js +39 -3
- package/dist/client.js.map +1 -1
- package/dist/client.mjs +39 -3
- package/dist/client.mjs.map +1 -1
- package/dist/express.d.mts +2 -2
- package/dist/express.d.ts +2 -2
- package/dist/fastify.d.mts +2 -2
- package/dist/fastify.d.ts +2 -2
- package/dist/hono.d.mts +2 -2
- package/dist/hono.d.ts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +387 -148
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +385 -148
- package/dist/index.mjs.map +1 -1
- package/dist/next.d.mts +2 -2
- package/dist/next.d.ts +2 -2
- package/dist/react.d.mts +275 -23
- package/dist/react.d.ts +275 -23
- package/dist/react.js +312 -78
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +311 -78
- package/dist/react.mjs.map +1 -1
- package/dist/styles.css +8 -0
- package/dist/svelte.d.mts +2 -2
- package/dist/svelte.d.ts +2 -2
- package/dist/svelte.js +53 -4
- package/dist/svelte.js.map +1 -1
- package/dist/svelte.mjs +53 -4
- package/dist/svelte.mjs.map +1 -1
- package/dist/{system-CVBW_AV8.d.mts → system-BrrJsrFW.d.mts} +321 -109
- package/dist/{system-Bn8uckvQ.d.ts → system-BvvoNs2y.d.ts} +321 -109
- package/dist/vue.d.mts +2 -2
- package/dist/vue.d.ts +2 -2
- package/dist/vue.js +53 -4
- package/dist/vue.js.map +1 -1
- package/dist/vue.mjs +53 -4
- package/dist/vue.mjs.map +1 -1
- package/package.json +1 -1
package/dist/react.js
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
// src/react.tsx
|
|
22
22
|
var react_exports = {};
|
|
23
23
|
__export(react_exports, {
|
|
24
|
+
AcceptInvite: () => AcceptInvite,
|
|
24
25
|
Account: () => Account,
|
|
25
26
|
CauthyProvider: () => CauthyProvider,
|
|
26
27
|
ForgotPassword: () => ForgotPassword,
|
|
@@ -321,6 +322,12 @@ function checkOrganization(value, required = false) {
|
|
|
321
322
|
if (v.length > MAX_ORGANIZATION) return { field: "organization", code: "too_long", message: `Use at most ${MAX_ORGANIZATION} characters.` };
|
|
322
323
|
return null;
|
|
323
324
|
}
|
|
325
|
+
function checkOrganizationName(value) {
|
|
326
|
+
const v = typeof value === "string" ? value.trim() : "";
|
|
327
|
+
if (v === "") return { field: "name", code: "required", message: "Enter the organization's name." };
|
|
328
|
+
if (v.length > MAX_ORGANIZATION) return { field: "name", code: "too_long", message: `Use at most ${MAX_ORGANIZATION} characters.` };
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
324
331
|
function checkCode(value) {
|
|
325
332
|
const v = typeof value === "string" ? value.trim() : "";
|
|
326
333
|
if (v === "") return { field: "code", code: "required", message: "Enter the code." };
|
|
@@ -405,11 +412,11 @@ function createAuthClient(options = {}) {
|
|
|
405
412
|
if (res.ok) {
|
|
406
413
|
const m = await res.json();
|
|
407
414
|
if (m.password) policy = passwordPolicyOf(m.password);
|
|
408
|
-
return { providers: [], signUp: true, magicLink: false, passkey: false, ...m, ...policy ? { password: policy } : {}, passkeyHere: m.passkeyHere === true, passkeyOnCauthy: m.passkeyOnCauthy ?? m.passkey === true, sso: m.sso === true };
|
|
415
|
+
return { providers: [], signUp: true, magicLink: false, passkey: false, ...m, ...policy ? { password: policy } : {}, passwordSignIn: m.passwordSignIn !== false, passkeyHere: m.passkeyHere === true, passkeyOnCauthy: m.passkeyOnCauthy ?? m.passkey === true, sso: m.sso === true };
|
|
409
416
|
}
|
|
410
417
|
} catch {
|
|
411
418
|
}
|
|
412
|
-
return { providers: [], signUp: true, magicLink: false, passkey: false, passkeyHere: false, passkeyOnCauthy: false, sso: false };
|
|
419
|
+
return { providers: [], signUp: true, passwordSignIn: true, magicLink: false, passkey: false, passkeyHere: false, passkeyOnCauthy: false, sso: false };
|
|
413
420
|
}
|
|
414
421
|
async function ceremony(begin, finish, form, run, more = {}) {
|
|
415
422
|
const begun = await post(begin, form, null);
|
|
@@ -556,6 +563,20 @@ function createAuthClient(options = {}) {
|
|
|
556
563
|
forgotPassword: (f) => post("/forgot-password", { email: f.email }, checkEmail(f.email), f.captchaToken),
|
|
557
564
|
resetPassword: (f) => post("/reset-password", { token: f.token, password: f.password }, checkNewPassword(f.password, policy)),
|
|
558
565
|
verifyEmail: (f) => post("/verify-email", { token: f.token }, null),
|
|
566
|
+
async invitation(token) {
|
|
567
|
+
const bad = { ok: false, error: { code: "invalid_token", message: "This invitation is not valid, or it has expired." } };
|
|
568
|
+
if (typeof token !== "string" || token.trim() === "") return bad;
|
|
569
|
+
try {
|
|
570
|
+
const res = await doFetch(`${base}/invitation?token=${encodeURIComponent(token.trim())}`, { credentials: "same-origin", headers: { accept: "application/json" } });
|
|
571
|
+
const body = await res.json().catch(() => null);
|
|
572
|
+
if (res.ok && body?.invitation) return { ok: true, status: "ok", invitation: body.invitation };
|
|
573
|
+
const e = body?.error;
|
|
574
|
+
return { ok: false, error: e && typeof e.code === "string" ? { code: e.code, message: String(e.message ?? "") } : UNAVAILABLE };
|
|
575
|
+
} catch {
|
|
576
|
+
return { ok: false, error: UNAVAILABLE };
|
|
577
|
+
}
|
|
578
|
+
},
|
|
579
|
+
acceptInvitation: (token, password) => post("/invitation/accept", { token, password }, checkNewPassword(password, policy)),
|
|
559
580
|
signOut: () => post("/sign-out", {}, null),
|
|
560
581
|
organizations: {
|
|
561
582
|
async list(system) {
|
|
@@ -573,7 +594,23 @@ function createAuthClient(options = {}) {
|
|
|
573
594
|
add: (id, f) => post("/me/organizations/members/add", { organizationId: id, email: f.email, role: f.role, name: f.name ?? "", locale: f.locale ?? "" }, checkEmail(f.email)),
|
|
574
595
|
setRole: (id, userId, role) => post("/me/organizations/members/role", { organizationId: id, userId, role }, null),
|
|
575
596
|
remove: (id, userId) => post("/me/organizations/members/remove", { organizationId: id, userId }, null),
|
|
576
|
-
inviteAgain: (id, userId, f = {}) => post("/me/organizations/members/invite", { organizationId: id, userId, locale: f.locale ?? "" }, null)
|
|
597
|
+
inviteAgain: (id, userId, f = {}) => post("/me/organizations/members/invite", { organizationId: id, userId, locale: f.locale ?? "" }, null),
|
|
598
|
+
rename: (id, name) => post("/me/organizations/rename", { organizationId: id, name }, checkOrganizationName(name)),
|
|
599
|
+
archive: (id) => post("/me/organizations/archive", { organizationId: id }, null),
|
|
600
|
+
delete: (id) => post("/me/organizations/delete", { organizationId: id }, null),
|
|
601
|
+
async check(q) {
|
|
602
|
+
const organizationId = typeof q.organization === "string" ? q.organization : q.organization?.id ?? "";
|
|
603
|
+
const r = await post("/me/organizations/check", {
|
|
604
|
+
organizationId,
|
|
605
|
+
resource: q.resource,
|
|
606
|
+
action: q.action,
|
|
607
|
+
...q.attributes !== void 0 ? { attributes: q.attributes } : {},
|
|
608
|
+
...q.resourceId !== void 0 ? { resourceId: q.resourceId } : {},
|
|
609
|
+
...q.ownerId !== void 0 ? { ownerId: q.ownerId } : {}
|
|
610
|
+
}, null);
|
|
611
|
+
if (!r.ok) return { allowed: false, reason: r.error.message, error: r.error };
|
|
612
|
+
return { allowed: r.allowed === true, reason: String(r.reason ?? ""), ...r.rule ? { rule: r.rule } : {} };
|
|
613
|
+
}
|
|
577
614
|
},
|
|
578
615
|
account: {
|
|
579
616
|
async get() {
|
|
@@ -669,7 +706,9 @@ function can(organization, resource, action) {
|
|
|
669
706
|
if ((organization.denied ?? []).some((d) => matches(d, resource, action))) return false;
|
|
670
707
|
return organization.permissions.some((p) => matches(p, resource, action));
|
|
671
708
|
}
|
|
672
|
-
function definePolicy(
|
|
709
|
+
function definePolicy(given) {
|
|
710
|
+
const roles = Array.isArray(given) ? given : given.roles;
|
|
711
|
+
const resources = Array.isArray(given) ? [] : given.catalog ?? [];
|
|
673
712
|
const entry = (p) => `${p.resource}:${p.action}`;
|
|
674
713
|
const byName = new Map(roles.map((r) => [r.role, r]));
|
|
675
714
|
const split = (permission) => {
|
|
@@ -684,6 +723,12 @@ function definePolicy(roles) {
|
|
|
684
723
|
...r.inherits ? { inherits: [...r.inherits] } : {},
|
|
685
724
|
...r.permissions ? { permissions: r.permissions.map((p) => ({ ...p })) } : {}
|
|
686
725
|
})),
|
|
726
|
+
catalog: resources.map((c) => ({
|
|
727
|
+
resource: c.resource,
|
|
728
|
+
...c.description !== void 0 ? { description: c.description } : {},
|
|
729
|
+
actions: [...c.actions],
|
|
730
|
+
...c.attributes ? { attributes: { ...c.attributes } } : {}
|
|
731
|
+
})),
|
|
687
732
|
roleNames: roles.map((r) => r.role),
|
|
688
733
|
permissions: [...new Set(roles.flatMap((r) => (r.permissions ?? []).map(entry)))],
|
|
689
734
|
can(organization, permission) {
|
|
@@ -705,6 +750,11 @@ function definePolicy(roles) {
|
|
|
705
750
|
};
|
|
706
751
|
walk(role);
|
|
707
752
|
return [...out];
|
|
753
|
+
},
|
|
754
|
+
// The types did the work; this only hands the question on, as a method of
|
|
755
|
+
// the asker so it keeps its `this`.
|
|
756
|
+
check(asker, ...args) {
|
|
757
|
+
return asker.check(...args);
|
|
708
758
|
}
|
|
709
759
|
};
|
|
710
760
|
}
|
|
@@ -763,6 +813,13 @@ var TEXTS = {
|
|
|
763
813
|
passwordChanged: "Your password was changed. You can sign in with it now.",
|
|
764
814
|
confirming: "Confirming your address\u2026",
|
|
765
815
|
emailConfirmed: "Your address is confirmed. You can sign in now.",
|
|
816
|
+
invitedTo: "You've been invited to {workspace}",
|
|
817
|
+
invitedToOrganization: "You've been invited to {organization} on {workspace}",
|
|
818
|
+
invitedAs: "You will sign in as {email}.",
|
|
819
|
+
choosePassword: "Choose a password",
|
|
820
|
+
acceptInvite: "Accept and sign in",
|
|
821
|
+
openingInvitation: "Opening your invitation\u2026",
|
|
822
|
+
invitationInvalid: "This invitation is not valid: it was already used, or it has expired. Ask whoever invited you to send it again.",
|
|
766
823
|
profile: "Profile",
|
|
767
824
|
save: "Save",
|
|
768
825
|
saved: "Saved.",
|
|
@@ -823,11 +880,22 @@ var TEXTS = {
|
|
|
823
880
|
memberLeaveConfirm: "Leave {org}? You will need an invitation to come back.",
|
|
824
881
|
memberInviteAgain: "Send the invitation again",
|
|
825
882
|
memberInvitationSent: "The invitation is on its way.",
|
|
883
|
+
organization: "Organization",
|
|
884
|
+
organizationRename: "Rename",
|
|
885
|
+
organizationRenamed: "The organization is now called {name}.",
|
|
886
|
+
organizationArchive: "Archive",
|
|
887
|
+
organizationArchiveConfirm: "Archive {org}? Its members will no longer reach it, and only the workspace can restore it.",
|
|
888
|
+
organizationArchived: "{org} was archived.",
|
|
889
|
+
organizationDelete: "Delete organization",
|
|
890
|
+
organizationDeleteConfirm: "Delete {org} for good? Its members lose it and this cannot be undone.",
|
|
891
|
+
organizationDeleted: "{org} was deleted.",
|
|
826
892
|
yes: "Yes",
|
|
827
893
|
cancel: "Cancel",
|
|
828
894
|
errors: {
|
|
829
895
|
not_allowed: "Your role in this organization does not allow that.",
|
|
830
896
|
last_manager: "Somebody else must manage the members first: give that role to another member.",
|
|
897
|
+
last_owner: "An organization keeps at least one owner: make another member owner first.",
|
|
898
|
+
organization_not_found: "That organization no longer exists.",
|
|
831
899
|
already_member: "That person already belongs to the organization. Change their role instead.",
|
|
832
900
|
unknown_role: "Choose one of the roles offered.",
|
|
833
901
|
user_not_active: "That account is disabled.",
|
|
@@ -866,7 +934,9 @@ var TEXTS = {
|
|
|
866
934
|
invalid_assertion: "Your company's answer could not be checked. Try again.",
|
|
867
935
|
provisioning_disabled: "Your company's sign-in worked, but this workspace has no account for you. Ask its admin to add you.",
|
|
868
936
|
connection_inactive: "Your company's sign-in is turned off for this workspace.",
|
|
869
|
-
invalid_grant: "That sign-in expired. Try again."
|
|
937
|
+
invalid_grant: "That sign-in expired. Try again.",
|
|
938
|
+
password_disabled: "This workspace does not sign in with a password. Use one of the other ways.",
|
|
939
|
+
passkeys_disabled: "Passkeys are turned off for this workspace."
|
|
870
940
|
}
|
|
871
941
|
},
|
|
872
942
|
es: {
|
|
@@ -919,6 +989,13 @@ var TEXTS = {
|
|
|
919
989
|
passwordChanged: "Tu contrase\xF1a se cambi\xF3. Ya puedes entrar con ella.",
|
|
920
990
|
confirming: "Confirmando tu correo\u2026",
|
|
921
991
|
emailConfirmed: "Tu correo qued\xF3 confirmado. Ya puedes entrar.",
|
|
992
|
+
invitedTo: "Te invitaron a {workspace}",
|
|
993
|
+
invitedToOrganization: "Te invitaron a {organization} en {workspace}",
|
|
994
|
+
invitedAs: "Vas a entrar como {email}.",
|
|
995
|
+
choosePassword: "Elige una contrase\xF1a",
|
|
996
|
+
acceptInvite: "Aceptar y entrar",
|
|
997
|
+
openingInvitation: "Abriendo tu invitaci\xF3n\u2026",
|
|
998
|
+
invitationInvalid: "Esta invitaci\xF3n no es v\xE1lida: ya se us\xF3 o venci\xF3. P\xEDdele a quien te invit\xF3 que te la env\xEDe de nuevo.",
|
|
922
999
|
profile: "Perfil",
|
|
923
1000
|
save: "Guardar",
|
|
924
1001
|
saved: "Guardado.",
|
|
@@ -979,11 +1056,22 @@ var TEXTS = {
|
|
|
979
1056
|
memberLeaveConfirm: "\xBFSalir de {org}? Necesitar\xE1s una invitaci\xF3n para volver.",
|
|
980
1057
|
memberInviteAgain: "Enviar la invitaci\xF3n de nuevo",
|
|
981
1058
|
memberInvitationSent: "La invitaci\xF3n va en camino.",
|
|
1059
|
+
organization: "Organizaci\xF3n",
|
|
1060
|
+
organizationRename: "Cambiar nombre",
|
|
1061
|
+
organizationRenamed: "La organizaci\xF3n ahora se llama {name}.",
|
|
1062
|
+
organizationArchive: "Archivar",
|
|
1063
|
+
organizationArchiveConfirm: "\xBFArchivar {org}? Sus miembros ya no podr\xE1n entrar y solo el espacio de trabajo puede restaurarla.",
|
|
1064
|
+
organizationArchived: "{org} qued\xF3 archivada.",
|
|
1065
|
+
organizationDelete: "Eliminar organizaci\xF3n",
|
|
1066
|
+
organizationDeleteConfirm: "\xBFEliminar {org} para siempre? Sus miembros la pierden y no se puede deshacer.",
|
|
1067
|
+
organizationDeleted: "{org} qued\xF3 eliminada.",
|
|
982
1068
|
yes: "S\xED",
|
|
983
1069
|
cancel: "Cancelar",
|
|
984
1070
|
errors: {
|
|
985
1071
|
not_allowed: "Tu rol en esta organizaci\xF3n no permite eso.",
|
|
986
1072
|
last_manager: "Primero alguien m\xE1s debe administrar los miembros: dale ese rol a otro miembro.",
|
|
1073
|
+
last_owner: "Una organizaci\xF3n conserva al menos un propietario: primero haz propietario a otro miembro.",
|
|
1074
|
+
organization_not_found: "Esa organizaci\xF3n ya no existe.",
|
|
987
1075
|
already_member: "Esa persona ya pertenece a la organizaci\xF3n. Cambia su rol.",
|
|
988
1076
|
unknown_role: "Escoge uno de los roles ofrecidos.",
|
|
989
1077
|
user_not_active: "Esa cuenta est\xE1 desactivada.",
|
|
@@ -1022,7 +1110,9 @@ var TEXTS = {
|
|
|
1022
1110
|
invalid_assertion: "No se pudo verificar la respuesta de tu empresa. Intenta de nuevo.",
|
|
1023
1111
|
provisioning_disabled: "El ingreso con tu empresa funcion\xF3, pero este workspace no tiene una cuenta para ti. P\xEDdele a su admin que te agregue.",
|
|
1024
1112
|
connection_inactive: "El inicio de sesi\xF3n de tu empresa est\xE1 apagado en este workspace.",
|
|
1025
|
-
invalid_grant: "Ese ingreso venci\xF3. Intenta de nuevo."
|
|
1113
|
+
invalid_grant: "Ese ingreso venci\xF3. Intenta de nuevo.",
|
|
1114
|
+
password_disabled: "En este workspace no se entra con contrase\xF1a. Usa una de las otras formas.",
|
|
1115
|
+
passkeys_disabled: "Las llaves de acceso est\xE1n desactivadas en este workspace."
|
|
1026
1116
|
}
|
|
1027
1117
|
}
|
|
1028
1118
|
};
|
|
@@ -1217,23 +1307,16 @@ function useMailedToken(name, open) {
|
|
|
1217
1307
|
open(takeToken(name));
|
|
1218
1308
|
}, []);
|
|
1219
1309
|
}
|
|
1220
|
-
function
|
|
1221
|
-
const auth =
|
|
1222
|
-
const f = useForm(props);
|
|
1223
|
-
const [methods, setMethods] = (0, import_react.useState)(null);
|
|
1224
|
-
const [email, setEmail] = (0, import_react.useState)("");
|
|
1225
|
-
const [password, setPassword] = (0, import_react.useState)("");
|
|
1310
|
+
function SecondStepForm(p) {
|
|
1311
|
+
const { f, auth } = p;
|
|
1226
1312
|
const [code, setCode] = (0, import_react.useState)("");
|
|
1227
|
-
const [step, setStep] = (0, import_react.useState)("password");
|
|
1228
|
-
const challenge = useChallenge(methods?.captchaSiteKey, props.locale);
|
|
1229
1313
|
const [phoneWay, setPhoneWay] = (0, import_react.useState)(null);
|
|
1230
1314
|
const [phoneSent, setPhoneSent] = (0, import_react.useState)("");
|
|
1231
1315
|
(0, import_react.useEffect)(() => {
|
|
1232
|
-
if (step !== "code") return;
|
|
1233
1316
|
void auth.signIn.mfaMethods().then((r) => {
|
|
1234
1317
|
if (r.ok) setPhoneWay({ totp: r.totp, phone: r.phone, push: r.push, email: r.email });
|
|
1235
1318
|
});
|
|
1236
|
-
}, [
|
|
1319
|
+
}, []);
|
|
1237
1320
|
const sendEmail = async () => {
|
|
1238
1321
|
f.setBusy(true);
|
|
1239
1322
|
f.setError(null);
|
|
@@ -1269,6 +1352,55 @@ function SignIn(props) {
|
|
|
1269
1352
|
if (!r.ok) return f.setError(r.error);
|
|
1270
1353
|
setPhoneSent(r.sentTo);
|
|
1271
1354
|
};
|
|
1355
|
+
const submit = async (e) => {
|
|
1356
|
+
e.preventDefault();
|
|
1357
|
+
f.setBusy(true);
|
|
1358
|
+
f.setError(null);
|
|
1359
|
+
const r = await auth.signIn.mfa({ code });
|
|
1360
|
+
f.setBusy(false);
|
|
1361
|
+
if (!r.ok) return f.setError(r.error);
|
|
1362
|
+
f.done();
|
|
1363
|
+
};
|
|
1364
|
+
const general = f.error && !f.error.field ? f.error : null;
|
|
1365
|
+
const to = phoneWay?.phone?.number ?? "";
|
|
1366
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
|
|
1367
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: phoneSent ? f.t.codeSent.replace("{to}", phoneSent) : phoneWay?.email ? f.t.codeHelpEmail.replace("{to}", phoneWay.email.to) : phoneWay?.phone && !phoneWay.totp ? f.t.codeHelpPhone : f.t.codeHelp }),
|
|
1368
|
+
general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
|
|
1369
|
+
phoneWay?.email && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendEmail(), children: f.t.sendEmailAgain }),
|
|
1370
|
+
pushNumber > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("notice"), role: "status", "aria-live": "polite", children: [
|
|
1371
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: f.t.pushPick.replace("{n}", String(pushNumber)) }),
|
|
1372
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontSize: 48, fontWeight: 700, margin: "4px 0", textAlign: "center" }, children: pushNumber }),
|
|
1373
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: f.t.pushWaiting })
|
|
1374
|
+
] }) : phoneWay?.push && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("button"), onClick: () => void sendPush(), children: f.t.pushSend }),
|
|
1375
|
+
phoneWay?.phone && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendCode(), children: (phoneWay.phone.channel === "whatsapp" ? f.t.sendCodeWhatsapp : f.t.sendCodeSms).replace("{to}", to) }),
|
|
1376
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
|
|
1377
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
|
|
1378
|
+
] });
|
|
1379
|
+
}
|
|
1380
|
+
function ProviderButtons(p) {
|
|
1381
|
+
const { f } = p;
|
|
1382
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: p.providers.map((provider) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(f.Button, { type: "button", className: f.cx("providerButton"), onClick: () => p.onPick(provider.id), children: [
|
|
1383
|
+
provider.iconUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: provider.iconUrl, alt: "", width: 18, height: 18, className: f.cx("providerIcon") }),
|
|
1384
|
+
f.t.continueWith,
|
|
1385
|
+
" ",
|
|
1386
|
+
provider.name
|
|
1387
|
+
] }, provider.id)) });
|
|
1388
|
+
}
|
|
1389
|
+
function signInOffers(methods) {
|
|
1390
|
+
const password = methods?.passwordSignIn !== false;
|
|
1391
|
+
return { password, email: password || methods?.magicLink === true || methods?.sso === true, passkey: methods?.passkey === true };
|
|
1392
|
+
}
|
|
1393
|
+
function SignIn(props) {
|
|
1394
|
+
const auth = useAuth();
|
|
1395
|
+
const f = useForm(props);
|
|
1396
|
+
const [methods, setMethods] = (0, import_react.useState)(null);
|
|
1397
|
+
const [email, setEmail] = (0, import_react.useState)("");
|
|
1398
|
+
const [password, setPassword] = (0, import_react.useState)("");
|
|
1399
|
+
const [code, setCode] = (0, import_react.useState)("");
|
|
1400
|
+
const [step, setStep] = (0, import_react.useState)("password");
|
|
1401
|
+
const challenge = useChallenge(methods?.captchaSiteKey, props.locale);
|
|
1402
|
+
const offers = signInOffers(methods);
|
|
1403
|
+
const passwordOn = offers.password;
|
|
1272
1404
|
const autofill = (0, import_react.useRef)(null);
|
|
1273
1405
|
const [offer, setOffer] = (0, import_react.useState)(0);
|
|
1274
1406
|
const [company, setCompany] = (0, import_react.useState)(null);
|
|
@@ -1320,10 +1452,18 @@ function SignIn(props) {
|
|
|
1320
1452
|
const submit = async (e) => {
|
|
1321
1453
|
e.preventDefault();
|
|
1322
1454
|
autofill.current?.abort();
|
|
1455
|
+
if (step === "password" && company?.enforced) {
|
|
1456
|
+
f.setError(null);
|
|
1457
|
+
return toCompany();
|
|
1458
|
+
}
|
|
1459
|
+
if (step === "password" && !passwordOn) {
|
|
1460
|
+
if (company) return toCompany();
|
|
1461
|
+
if (methods?.magicLink) return sendLink();
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1323
1464
|
f.setBusy(true);
|
|
1324
1465
|
f.setError(null);
|
|
1325
|
-
|
|
1326
|
-
const r = step === "code" ? await auth.signIn.mfa({ code }) : step === "sent" ? await auth.signIn.emailCode({ code }) : await auth.signIn.email({ email, password, captchaToken: challenge.token });
|
|
1466
|
+
const r = step === "sent" ? await auth.signIn.emailCode({ code }) : await auth.signIn.email({ email, password, captchaToken: challenge.token });
|
|
1327
1467
|
f.setBusy(false);
|
|
1328
1468
|
if (step === "password") challenge.renew();
|
|
1329
1469
|
if (!r.ok && r.error.code === "sso_required") return toCompany();
|
|
@@ -1357,6 +1497,7 @@ function SignIn(props) {
|
|
|
1357
1497
|
setStep("sent");
|
|
1358
1498
|
};
|
|
1359
1499
|
const general = f.error && !f.error.field ? f.error : null;
|
|
1500
|
+
const asksEmail = offers.email;
|
|
1360
1501
|
if (step === "opening") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("form"), ...f.root, "aria-busy": "true" });
|
|
1361
1502
|
if (step === "sent") {
|
|
1362
1503
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
|
|
@@ -1367,39 +1508,20 @@ function SignIn(props) {
|
|
|
1367
1508
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
|
|
1368
1509
|
] });
|
|
1369
1510
|
}
|
|
1370
|
-
if (step === "code") {
|
|
1371
|
-
const to = phoneWay?.phone?.number ?? "";
|
|
1372
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
|
|
1373
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: phoneSent ? f.t.codeSent.replace("{to}", phoneSent) : phoneWay?.email ? f.t.codeHelpEmail.replace("{to}", phoneWay.email.to) : phoneWay?.phone && !phoneWay.totp ? f.t.codeHelpPhone : f.t.codeHelp }),
|
|
1374
|
-
general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
|
|
1375
|
-
phoneWay?.email && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendEmail(), children: f.t.sendEmailAgain }),
|
|
1376
|
-
pushNumber > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("notice"), role: "status", "aria-live": "polite", children: [
|
|
1377
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: f.t.pushPick.replace("{n}", String(pushNumber)) }),
|
|
1378
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontSize: 48, fontWeight: 700, margin: "4px 0", textAlign: "center" }, children: pushNumber }),
|
|
1379
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: f.t.pushWaiting })
|
|
1380
|
-
] }) : phoneWay?.push && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("button"), onClick: () => void sendPush(), children: f.t.pushSend }),
|
|
1381
|
-
phoneWay?.phone && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendCode(), children: (phoneWay.phone.channel === "whatsapp" ? f.t.sendCodeWhatsapp : f.t.sendCodeSms).replace("{to}", to) }),
|
|
1382
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
|
|
1383
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
|
|
1384
|
-
] });
|
|
1385
|
-
}
|
|
1511
|
+
if (step === "code") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SecondStepForm, { f, auth });
|
|
1386
1512
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
|
|
1387
1513
|
general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
] }, p.id)),
|
|
1393
|
-
methods?.passkey && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void usePasskey(), children: f.t.usePasskey }),
|
|
1394
|
-
((methods?.providers.length ?? 0) > 0 || methods?.passkey) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("separator"), children: f.t.orWithEmail }),
|
|
1395
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: methods?.passkeyHere ? "username webauthn" : "username", required: true, onChange: setEmail }),
|
|
1514
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProviderButtons, { f, providers: methods?.providers ?? [], onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
|
|
1515
|
+
offers.passkey && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void usePasskey(), children: f.t.usePasskey }),
|
|
1516
|
+
asksEmail && ((methods?.providers.length ?? 0) > 0 || offers.passkey) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("separator"), children: f.t.orWithEmail }),
|
|
1517
|
+
asksEmail && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: methods?.passkeyHere ? "username webauthn" : "username", required: true, onChange: setEmail }),
|
|
1396
1518
|
company && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(f.Button, { type: "button", disabled: f.busy, className: f.cx(company.enforced ? "button" : "providerButton"), onClick: toCompany, children: [
|
|
1397
1519
|
f.t.continueWith,
|
|
1398
1520
|
" ",
|
|
1399
1521
|
company.name
|
|
1400
1522
|
] }),
|
|
1401
1523
|
company?.enforced && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.ssoOnly }),
|
|
1402
|
-
!company?.enforced && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1524
|
+
!company?.enforced && passwordOn && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1403
1525
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1404
1526
|
Field,
|
|
1405
1527
|
{
|
|
@@ -1417,6 +1539,7 @@ function SignIn(props) {
|
|
|
1417
1539
|
challenge.node,
|
|
1418
1540
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signIn })
|
|
1419
1541
|
] }),
|
|
1542
|
+
!company?.enforced && !passwordOn && methods?.magicLink && challenge.node,
|
|
1420
1543
|
methods?.magicLink && !company?.enforced && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendLink(), children: f.t.emailMeALink }),
|
|
1421
1544
|
methods?.signUp !== false && props.links?.signUp && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: f.cx("footer"), children: [
|
|
1422
1545
|
f.t.noAccount,
|
|
@@ -1476,14 +1599,30 @@ function SignUp(props) {
|
|
|
1476
1599
|
f.done();
|
|
1477
1600
|
};
|
|
1478
1601
|
const general = f.error && !f.error.field ? f.error : null;
|
|
1602
|
+
const passwordOn = methods?.passwordSignIn !== false;
|
|
1603
|
+
const providers = auth.session?.user.isAnonymous === true || methods?.organization?.required ? [] : methods?.providers ?? [];
|
|
1479
1604
|
if (sent) {
|
|
1480
1605
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("form"), ...f.root, children: [
|
|
1481
1606
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.checkEmail }),
|
|
1482
1607
|
props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn })
|
|
1483
1608
|
] });
|
|
1484
1609
|
}
|
|
1610
|
+
const footer = props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: f.cx("footer"), children: [
|
|
1611
|
+
f.t.haveAccount,
|
|
1612
|
+
" ",
|
|
1613
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.signIn })
|
|
1614
|
+
] });
|
|
1615
|
+
if (!passwordOn) {
|
|
1616
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("form"), ...f.root, children: [
|
|
1617
|
+
general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
|
|
1618
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProviderButtons, { f, providers, onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
|
|
1619
|
+
footer
|
|
1620
|
+
] });
|
|
1621
|
+
}
|
|
1485
1622
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
|
|
1486
1623
|
general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
|
|
1624
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProviderButtons, { f, providers, onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
|
|
1625
|
+
providers.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("separator"), children: f.t.orWithEmail }),
|
|
1487
1626
|
methods?.organization?.ask && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "organization", type: "text", label: f.t.company, value: organization, autoComplete: "organization", required: methods.organization.required, onChange: setOrganization }),
|
|
1488
1627
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "name", onChange: setName }),
|
|
1489
1628
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "username", required: true, onChange: setEmail }),
|
|
@@ -1509,11 +1648,7 @@ function SignUp(props) {
|
|
|
1509
1648
|
] }, p.key)),
|
|
1510
1649
|
challenge.node,
|
|
1511
1650
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signUp }),
|
|
1512
|
-
|
|
1513
|
-
f.t.haveAccount,
|
|
1514
|
-
" ",
|
|
1515
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.signIn })
|
|
1516
|
-
] })
|
|
1651
|
+
footer
|
|
1517
1652
|
] });
|
|
1518
1653
|
}
|
|
1519
1654
|
function ForgotPassword(props) {
|
|
@@ -1610,6 +1745,62 @@ function VerifyEmail(props) {
|
|
|
1610
1745
|
state !== "confirming" && props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn })
|
|
1611
1746
|
] });
|
|
1612
1747
|
}
|
|
1748
|
+
function AcceptInvite(props) {
|
|
1749
|
+
const auth = useAuth();
|
|
1750
|
+
const f = useForm(props);
|
|
1751
|
+
const [token, setToken] = (0, import_react.useState)("");
|
|
1752
|
+
const [invitation, setInvitation] = (0, import_react.useState)(null);
|
|
1753
|
+
const [state, setState] = (0, import_react.useState)("opening");
|
|
1754
|
+
const [password, setPassword] = (0, import_react.useState)("");
|
|
1755
|
+
usePasswordPolicy(f, useMethods(auth));
|
|
1756
|
+
useMailedToken("token", (t) => {
|
|
1757
|
+
if (!t) return setState("invalid");
|
|
1758
|
+
setToken(t);
|
|
1759
|
+
void auth.invitation(t).then((r) => {
|
|
1760
|
+
if (r.ok) {
|
|
1761
|
+
setInvitation(r.invitation);
|
|
1762
|
+
return setState("ready");
|
|
1763
|
+
}
|
|
1764
|
+
if (r.error.code !== "invalid_token") f.setError(r.error);
|
|
1765
|
+
setState("invalid");
|
|
1766
|
+
});
|
|
1767
|
+
});
|
|
1768
|
+
const submit = async (e) => {
|
|
1769
|
+
e.preventDefault();
|
|
1770
|
+
f.setBusy(true);
|
|
1771
|
+
f.setError(null);
|
|
1772
|
+
const r = await auth.acceptInvitation(token, password);
|
|
1773
|
+
f.setBusy(false);
|
|
1774
|
+
if (!r.ok) {
|
|
1775
|
+
if (r.error.code === "invalid_token") return setState("invalid");
|
|
1776
|
+
return f.setError(r.error);
|
|
1777
|
+
}
|
|
1778
|
+
if (r.status === "mfa_required") return setState("code");
|
|
1779
|
+
f.done();
|
|
1780
|
+
};
|
|
1781
|
+
const general = f.error && !f.error.field ? f.error : null;
|
|
1782
|
+
const back = props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn });
|
|
1783
|
+
if (state === "opening") {
|
|
1784
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("form"), ...f.root, "aria-busy": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.openingInvitation }) });
|
|
1785
|
+
}
|
|
1786
|
+
if (state === "invalid") {
|
|
1787
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("form"), ...f.root, children: [
|
|
1788
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: general ? f.say(general) : f.t.invitationInvalid }),
|
|
1789
|
+
back
|
|
1790
|
+
] });
|
|
1791
|
+
}
|
|
1792
|
+
if (state === "code") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SecondStepForm, { f, auth });
|
|
1793
|
+
const workspace = invitation?.workspace ?? "";
|
|
1794
|
+
const heading = invitation?.organization ? f.t.invitedToOrganization.replace("{organization}", invitation.organization).replace("{workspace}", workspace) : f.t.invitedTo.replace("{workspace}", workspace);
|
|
1795
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
|
|
1796
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: heading }),
|
|
1797
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.invitedAs.replace("{email}", invitation?.email ?? "") }),
|
|
1798
|
+
general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
|
|
1799
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "email", name: "username", autoComplete: "username", value: invitation?.email ?? "", readOnly: true, hidden: true }),
|
|
1800
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "password", type: "password", label: f.t.choosePassword, value: password, autoComplete: "new-password", required: true, onChange: setPassword, hint: f.passwordRule }),
|
|
1801
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.acceptInvite })
|
|
1802
|
+
] });
|
|
1803
|
+
}
|
|
1613
1804
|
function useSection(props) {
|
|
1614
1805
|
const f = useForm({ ...props, unstyled: true });
|
|
1615
1806
|
const [done, setDone] = (0, import_react.useState)(null);
|
|
@@ -1660,32 +1851,52 @@ function Account(props) {
|
|
|
1660
1851
|
] })
|
|
1661
1852
|
] });
|
|
1662
1853
|
}
|
|
1854
|
+
var OWNER = "owner";
|
|
1855
|
+
function memberActions(seat, m) {
|
|
1856
|
+
const reachable = m.role !== OWNER || seat.role === OWNER;
|
|
1857
|
+
return {
|
|
1858
|
+
changeRole: seat.can.manageMembers && reachable,
|
|
1859
|
+
// Leaving is anybody's; removing somebody else is cauthy:members:remove.
|
|
1860
|
+
remove: m.you || seat.can.removeMembers && reachable,
|
|
1861
|
+
inviteAgain: seat.can.manageMembers && m.invited
|
|
1862
|
+
};
|
|
1863
|
+
}
|
|
1864
|
+
function rolesToGive(seat, roles) {
|
|
1865
|
+
return seat.role === OWNER ? roles : roles.filter((c) => c.role !== OWNER);
|
|
1866
|
+
}
|
|
1663
1867
|
function OrganizationMembers(props) {
|
|
1664
1868
|
const auth = useAuth();
|
|
1665
1869
|
const { root } = useForm(props);
|
|
1666
1870
|
const { f, done, setDone, run } = useSection(props);
|
|
1667
|
-
const problem = f.error && f.error.field !== "email" ? f.error : null;
|
|
1871
|
+
const problem = f.error && f.error.field !== "email" && f.error.field !== "name" ? f.error : null;
|
|
1668
1872
|
const [seat, setSeat] = (0, import_react.useState)(void 0);
|
|
1669
1873
|
const [members, setMembers] = (0, import_react.useState)(null);
|
|
1670
1874
|
const [roles, setRoles] = (0, import_react.useState)([]);
|
|
1671
1875
|
const [email, setEmail] = (0, import_react.useState)("");
|
|
1672
1876
|
const [role, setRole] = (0, import_react.useState)("");
|
|
1877
|
+
const [name, setName] = (0, import_react.useState)("");
|
|
1673
1878
|
const [asking, setAsking] = (0, import_react.useState)(null);
|
|
1879
|
+
const [gone, setGone] = (0, import_react.useState)(null);
|
|
1674
1880
|
const id = props.organizationId;
|
|
1675
1881
|
const when = props.formatDate ?? ((iso) => new Date(iso).toLocaleDateString());
|
|
1676
1882
|
const load = async () => {
|
|
1677
1883
|
const [list, people, choices] = await Promise.all([auth.organizations.list(), auth.organizations.members(id), auth.organizations.roles(id)]);
|
|
1678
|
-
|
|
1884
|
+
const mine = list.find((o) => o.id === id) ?? null;
|
|
1885
|
+
setSeat(mine);
|
|
1886
|
+
if (mine) setName(mine.name);
|
|
1679
1887
|
setMembers(people);
|
|
1680
1888
|
setRoles(choices ?? []);
|
|
1681
|
-
|
|
1889
|
+
const offered = mine ? rolesToGive(mine, choices ?? []) : [];
|
|
1890
|
+
if (offered.length > 0 && !offered.some((c) => c.role === role)) setRole(offered[0].role);
|
|
1682
1891
|
};
|
|
1683
1892
|
(0, import_react.useEffect)(() => {
|
|
1684
1893
|
void load();
|
|
1685
1894
|
}, [id]);
|
|
1895
|
+
if (gone !== null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("account"), ...root, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "status", className: f.cx("notice"), children: gone }) });
|
|
1686
1896
|
if (seat === void 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("account"), ...root, "aria-busy": "true" });
|
|
1687
1897
|
if (seat === null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("account"), ...root, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.organizationMissing }) });
|
|
1688
1898
|
const manage = seat.can.manageMembers;
|
|
1899
|
+
const giveable = rolesToGive(seat, roles);
|
|
1689
1900
|
const label = (r) => roles.find((c) => c.role === r)?.name || r;
|
|
1690
1901
|
const add = async (e) => {
|
|
1691
1902
|
e.preventDefault();
|
|
@@ -1710,6 +1921,25 @@ function OrganizationMembers(props) {
|
|
|
1710
1921
|
const r = await run(() => auth.organizations.inviteAgain(id, m.userId, { locale: props.locale }));
|
|
1711
1922
|
if (r.ok) setDone(f.t.memberInvitationSent);
|
|
1712
1923
|
};
|
|
1924
|
+
const rename = async (e) => {
|
|
1925
|
+
e.preventDefault();
|
|
1926
|
+
const r = await run(() => auth.organizations.rename(id, name));
|
|
1927
|
+
if (!r.ok) return;
|
|
1928
|
+
setSeat(r.organization);
|
|
1929
|
+
setName(r.organization.name);
|
|
1930
|
+
setDone(f.t.organizationRenamed.replace("{name}", r.organization.name));
|
|
1931
|
+
};
|
|
1932
|
+
const close = async (how) => {
|
|
1933
|
+
setAsking(null);
|
|
1934
|
+
const r = await run(() => how === "archive" ? auth.organizations.archive(id) : auth.organizations.delete(id));
|
|
1935
|
+
if (r.ok) setGone((how === "archive" ? f.t.organizationArchived : f.t.organizationDeleted).replace("{org}", seat.name));
|
|
1936
|
+
};
|
|
1937
|
+
const confirming = (text2, label2, yes) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { role: "group", "aria-label": label2, children: [
|
|
1938
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: text2 }),
|
|
1939
|
+
" ",
|
|
1940
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: yes, children: f.t.yes }),
|
|
1941
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(null), children: f.t.cancel })
|
|
1942
|
+
] });
|
|
1713
1943
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("account"), ...root, children: [
|
|
1714
1944
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: f.cx("section"), children: [
|
|
1715
1945
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h2", { className: f.cx("heading"), children: [
|
|
@@ -1720,23 +1950,14 @@ function OrganizationMembers(props) {
|
|
|
1720
1950
|
problem && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(problem) }),
|
|
1721
1951
|
members === null ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1722
1952
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.membersHidden }),
|
|
1723
|
-
auth.session && (asking === auth.session.user.id ?
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
{
|
|
1729
|
-
type: "button",
|
|
1730
|
-
disabled: f.busy,
|
|
1731
|
-
className: f.cx("providerButton"),
|
|
1732
|
-
onClick: () => void remove({ userId: auth.session.user.id, you: true }),
|
|
1733
|
-
children: f.t.yes
|
|
1734
|
-
}
|
|
1735
|
-
),
|
|
1736
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(null), children: f.t.cancel })
|
|
1737
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(auth.session.user.id), children: f.t.memberLeave }))
|
|
1953
|
+
auth.session && (asking === auth.session.user.id ? confirming(
|
|
1954
|
+
f.t.memberLeaveConfirm.replace("{org}", seat.name),
|
|
1955
|
+
f.t.memberLeave,
|
|
1956
|
+
() => void remove({ userId: auth.session.user.id, you: true })
|
|
1957
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(auth.session.user.id), children: f.t.memberLeave }))
|
|
1738
1958
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: f.cx("sessions"), children: members.map((m) => {
|
|
1739
1959
|
const who = m.name || m.email;
|
|
1960
|
+
const may = memberActions(seat, m);
|
|
1740
1961
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: f.cx("session"), children: [
|
|
1741
1962
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
1742
1963
|
who,
|
|
@@ -1748,7 +1969,7 @@ function OrganizationMembers(props) {
|
|
|
1748
1969
|
when(m.since)
|
|
1749
1970
|
] }),
|
|
1750
1971
|
" ",
|
|
1751
|
-
|
|
1972
|
+
may.changeRole && giveable.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1752
1973
|
"select",
|
|
1753
1974
|
{
|
|
1754
1975
|
"aria-label": `${f.t.memberRole}: ${who}`,
|
|
@@ -1756,31 +1977,41 @@ function OrganizationMembers(props) {
|
|
|
1756
1977
|
value: m.role,
|
|
1757
1978
|
disabled: f.busy,
|
|
1758
1979
|
onChange: (e) => void change(m, e.target.value),
|
|
1759
|
-
children: roles.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: c.role, children: c.name || c.role }, c.role))
|
|
1980
|
+
children: (giveable.some((c) => c.role === m.role) ? giveable : [...roles.filter((c) => c.role === m.role), ...giveable]).map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: c.role, children: c.name || c.role }, c.role))
|
|
1760
1981
|
}
|
|
1761
1982
|
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: label(m.role) }),
|
|
1762
1983
|
" ",
|
|
1763
|
-
asking === m.userId ?
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
(manage || m.you) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(m.userId), children: m.you ? f.t.memberLeave : f.t.memberRemove })
|
|
1984
|
+
asking === m.userId ? confirming(
|
|
1985
|
+
m.you ? f.t.memberLeaveConfirm.replace("{org}", seat.name) : f.t.memberRemoveConfirm.replace("{who}", who),
|
|
1986
|
+
m.you ? f.t.memberLeave : f.t.memberRemove,
|
|
1987
|
+
() => void remove(m)
|
|
1988
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1989
|
+
may.inviteAgain && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void inviteAgain(m), children: f.t.memberInviteAgain }),
|
|
1990
|
+
may.remove && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(m.userId), children: m.you ? f.t.memberLeave : f.t.memberRemove })
|
|
1771
1991
|
] })
|
|
1772
1992
|
] }, m.userId);
|
|
1773
1993
|
}) }),
|
|
1774
1994
|
done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "status", className: f.cx("notice"), children: done })
|
|
1775
1995
|
] }),
|
|
1776
|
-
manage &&
|
|
1996
|
+
manage && giveable.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: add, noValidate: true, children: [
|
|
1777
1997
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: f.t.memberAdd }),
|
|
1778
1998
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "off", required: true, onChange: setEmail }),
|
|
1779
1999
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: f.cx("field"), children: [
|
|
1780
2000
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: f.cx("label"), children: f.t.memberRole }),
|
|
1781
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { name: "role", className: f.cx("input"), value: role, onChange: (e) => setRole(e.target.value), children:
|
|
2001
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { name: "role", className: f.cx("input"), value: role, onChange: (e) => setRole(e.target.value), children: giveable.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: c.role, children: c.name || c.role }, c.role)) })
|
|
1782
2002
|
] }),
|
|
1783
2003
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.memberAdd })
|
|
2004
|
+
] }),
|
|
2005
|
+
(seat.can.update || seat.can.archive || seat.can.delete) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: f.cx("section"), children: [
|
|
2006
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: f.t.organization }),
|
|
2007
|
+
seat.can.update && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { onSubmit: rename, noValidate: true, children: [
|
|
2008
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "organization", required: true, onChange: setName }),
|
|
2009
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.organizationRename })
|
|
2010
|
+
] }),
|
|
2011
|
+
asking === "archive" ? confirming(f.t.organizationArchiveConfirm.replace("{org}", seat.name), f.t.organizationArchive, () => void close("archive")) : asking === "delete" ? confirming(f.t.organizationDeleteConfirm.replace("{org}", seat.name), f.t.organizationDelete, () => void close("delete")) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
2012
|
+
seat.can.archive && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking("archive"), children: f.t.organizationArchive }),
|
|
2013
|
+
seat.can.delete && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking("delete"), children: f.t.organizationDelete })
|
|
2014
|
+
] })
|
|
1784
2015
|
] })
|
|
1785
2016
|
] });
|
|
1786
2017
|
}
|
|
@@ -2217,6 +2448,8 @@ function useAuth() {
|
|
|
2217
2448
|
googleOneTap: (o) => then(client.signIn.googleOneTap(o))
|
|
2218
2449
|
},
|
|
2219
2450
|
signUp: { email: (f) => then(client.signUp.email(f)) },
|
|
2451
|
+
// The invited person is signed in once they accept.
|
|
2452
|
+
acceptInvitation: (token, password) => then(client.acceptInvitation(token, password)),
|
|
2220
2453
|
signOut: () => then(client.signOut()),
|
|
2221
2454
|
account: {
|
|
2222
2455
|
...client.account,
|
|
@@ -2235,6 +2468,7 @@ function useAuth() {
|
|
|
2235
2468
|
}
|
|
2236
2469
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2237
2470
|
0 && (module.exports = {
|
|
2471
|
+
AcceptInvite,
|
|
2238
2472
|
Account,
|
|
2239
2473
|
CauthyProvider,
|
|
2240
2474
|
ForgotPassword,
|