@authowl/react 0.23.0 → 0.24.1
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 +11 -2
- package/dist/OrganizationListContent-BSGDHYAC.js +185 -0
- package/dist/OrganizationProfileContent-HJT33DMJ.js +451 -0
- package/dist/OrganizationSwitcherContent-2TJ76HZM.js +176 -0
- package/dist/PrivacyCenterContent-EQEXXSLA.js +204 -0
- package/dist/{TeamsSection-NV64O5I4.js → TeamsSection-VJTKS4KP.js} +8 -4
- package/dist/chunk-2HNQYORD.js +139 -0
- package/dist/chunk-7WIBXVGK.js +19 -0
- package/dist/chunk-IDMSHQ6V.js +106 -0
- package/dist/chunk-SAVEQ6RC.js +83 -0
- package/dist/{chunk-E5ZOQHXY.js → chunk-TOVDZ2ZE.js} +6 -119
- package/dist/chunk-ZYCPC5DH.js +46 -0
- package/dist/index.cjs +5213 -4748
- package/dist/index.d.cts +26 -5
- package/dist/index.d.ts +26 -5
- package/dist/index.js +489 -1305
- package/dist/styles.css +58 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ function Page() {
|
|
|
31
31
|
}
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
**Components**: `<SignIn />`, `<SignUp />`, `<PhoneOTP />`, `<SocialButtons />`, `<MagicLinkForm />`,
|
|
34
|
+
**Components**: `<SignIn />`, `<SignUp />`, `<PrivacyCenter />`, `<PhoneOTP />`, `<SocialButtons />`, `<MagicLinkForm />`,
|
|
35
35
|
`<EmailOtpForm />`, `<PasskeyButton />`, `<PasskeyManager />`, `<ForgotPassword />`,
|
|
36
36
|
`<ResetPassword />`, `<VerifyEmail />`, `<VerificationPending />`, `<MFAEnrollment />`,
|
|
37
37
|
`<MFAChallenge />`, `<Waitlist />`, `<ConsentGate />`, `<UserButton />`, `<SignOutButton />`,
|
|
@@ -39,7 +39,7 @@ function Page() {
|
|
|
39
39
|
`<OrganizationSwitcher />`, `<OrganizationList />`, `<CreateOrganization />`,
|
|
40
40
|
`<OrganizationProfile />`, `<GoogleOneTap />`.
|
|
41
41
|
|
|
42
|
-
**Hooks**: `useUser`, `useSession`, `useSignIn`, `useSignUp`, `useSignOut`,
|
|
42
|
+
**Hooks**: `useUser`, `useSession`, `useSignIn`, `useSignUp`, `useSignOut`, `usePrivacy`,
|
|
43
43
|
`usePasskeys`, `usePasswordReset`, `useEmailVerification`, `useMFA`, `useConsent`,
|
|
44
44
|
`usePublicConfig`, `useWaitlist`.
|
|
45
45
|
|
|
@@ -48,6 +48,15 @@ renders email enrollment instead of account-creation methods. `<Waitlist />` is
|
|
|
48
48
|
also available as a standalone surface. Both use the same public endpoint,
|
|
49
49
|
privacy-safe accepted state, and action-bound Turnstile challenge.
|
|
50
50
|
|
|
51
|
+
When the environment publishes privacy notices, `<SignUp />` renders their exact
|
|
52
|
+
English or Arabic versions and submits immutable delivery evidence with the
|
|
53
|
+
account-creation request. Optional consent purposes are off by default and are
|
|
54
|
+
recorded separately from terms acceptance. Signed-in users can use
|
|
55
|
+
`<PrivacyCenter />` to change those choices, withdraw a grant immediately, read
|
|
56
|
+
current notices, submit data-rights requests, and track request status. The same
|
|
57
|
+
operations are available headlessly through `usePrivacy()` or
|
|
58
|
+
`client.privacy` from `@authowl/core`.
|
|
59
|
+
|
|
51
60
|
The sign-up, sign-in, and account-management components also follow the
|
|
52
61
|
project's identity lifecycle policy. This includes separate sign-up and sign-in
|
|
53
62
|
permissions, username sign-in, optional username and first/last-name collection,
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
organizationRoleLabel
|
|
4
|
+
} from "./chunk-7WIBXVGK.js";
|
|
5
|
+
import {
|
|
6
|
+
OrganizationModal,
|
|
7
|
+
useOrganizationsResource
|
|
8
|
+
} from "./chunk-IDMSHQ6V.js";
|
|
9
|
+
import {
|
|
10
|
+
CreateOrganization,
|
|
11
|
+
OrganizationProfile
|
|
12
|
+
} from "./chunk-2HNQYORD.js";
|
|
13
|
+
import {
|
|
14
|
+
useSubmitAction
|
|
15
|
+
} from "./chunk-SAVEQ6RC.js";
|
|
16
|
+
import {
|
|
17
|
+
Bidi,
|
|
18
|
+
FormError,
|
|
19
|
+
useAuthClient,
|
|
20
|
+
usePublicConfig,
|
|
21
|
+
useServerError,
|
|
22
|
+
useSession,
|
|
23
|
+
useT,
|
|
24
|
+
useUser
|
|
25
|
+
} from "./chunk-TOVDZ2ZE.js";
|
|
26
|
+
|
|
27
|
+
// src/components/OrganizationListContent.tsx
|
|
28
|
+
import * as React from "react";
|
|
29
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
30
|
+
function OrganizationListContent({ onOrganizationChange } = {}) {
|
|
31
|
+
const t = useT();
|
|
32
|
+
const toServerError = useServerError();
|
|
33
|
+
const { config, isLoading: configLoading } = usePublicConfig();
|
|
34
|
+
const { user, isLoaded, isSignedIn } = useUser();
|
|
35
|
+
const session = useSession();
|
|
36
|
+
const api = useAuthClient().organization;
|
|
37
|
+
const apiRef = React.useRef(api);
|
|
38
|
+
apiRef.current = api;
|
|
39
|
+
const enabled = config?.organizations === true && isSignedIn;
|
|
40
|
+
const { organizations, isLoading, error: organizationError, refresh } = useOrganizationsResource(enabled);
|
|
41
|
+
const { pending, error, run } = useSubmitAction();
|
|
42
|
+
const [invitations, setInvitations] = React.useState(null);
|
|
43
|
+
const [invitationError, setInvitationError] = React.useState(null);
|
|
44
|
+
const [dialog, setDialog] = React.useState(null);
|
|
45
|
+
const [profileId, setProfileId] = React.useState(null);
|
|
46
|
+
const dialogReturnFocusRef = React.useRef(null);
|
|
47
|
+
const invitationRequestRef = React.useRef(0);
|
|
48
|
+
const activeId = session.data?.session.activeOrganizationId ?? null;
|
|
49
|
+
const loadInvitations = React.useCallback(async () => {
|
|
50
|
+
const token = ++invitationRequestRef.current;
|
|
51
|
+
if (!enabled) {
|
|
52
|
+
setInvitations([]);
|
|
53
|
+
setInvitationError(null);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const result = await apiRef.current.listUserInvitations();
|
|
58
|
+
if (token !== invitationRequestRef.current) return;
|
|
59
|
+
if (result.error) {
|
|
60
|
+
setInvitationError(toServerError(result.error, t("organization.list.invitationsError")));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
setInvitationError(null);
|
|
64
|
+
setInvitations((result.data ?? []).filter((invitation) => invitation.status === "pending"));
|
|
65
|
+
} catch {
|
|
66
|
+
if (token === invitationRequestRef.current) setInvitationError(t("organization.list.invitationsError"));
|
|
67
|
+
}
|
|
68
|
+
}, [enabled, t, toServerError]);
|
|
69
|
+
React.useEffect(() => {
|
|
70
|
+
setInvitations(null);
|
|
71
|
+
setInvitationError(null);
|
|
72
|
+
if (!isLoaded) return;
|
|
73
|
+
void loadInvitations();
|
|
74
|
+
return () => {
|
|
75
|
+
invitationRequestRef.current += 1;
|
|
76
|
+
};
|
|
77
|
+
}, [isLoaded, loadInvitations, user?.id]);
|
|
78
|
+
if (configLoading || !isLoaded) return /* @__PURE__ */ jsx("div", { className: "ba-skeleton", "aria-label": t("organization.loading") });
|
|
79
|
+
if (config?.organizations !== true) return null;
|
|
80
|
+
if (!isSignedIn) return /* @__PURE__ */ jsx("p", { className: "ba-muted", children: t("organization.signedOut") });
|
|
81
|
+
const setActive = (organization) => {
|
|
82
|
+
void run(
|
|
83
|
+
() => api.setActive({ organizationId: organization.id }),
|
|
84
|
+
{
|
|
85
|
+
failure: t("organization.switcher.error"),
|
|
86
|
+
onSuccess: async () => {
|
|
87
|
+
await session.refetch({ query: { disableCookieCache: true } });
|
|
88
|
+
onOrganizationChange?.(organization);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
const invitationAction = (invitation, action) => {
|
|
94
|
+
const onSuccess = async () => {
|
|
95
|
+
await Promise.all([refresh(), loadInvitations()]);
|
|
96
|
+
if (action === "accept") await session.refetch({ query: { disableCookieCache: true } });
|
|
97
|
+
};
|
|
98
|
+
if (action === "accept") {
|
|
99
|
+
void run(
|
|
100
|
+
() => api.acceptInvitation({ invitationId: invitation.id }),
|
|
101
|
+
{ failure: t("organization.list.acceptError"), onSuccess }
|
|
102
|
+
);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
void run(
|
|
106
|
+
() => api.rejectInvitation({ invitationId: invitation.id }),
|
|
107
|
+
{ failure: t("organization.list.rejectError"), onSuccess }
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
const openProfile = (organization, trigger) => {
|
|
111
|
+
dialogReturnFocusRef.current = trigger;
|
|
112
|
+
setProfileId(organization.id);
|
|
113
|
+
setDialog("profile");
|
|
114
|
+
};
|
|
115
|
+
const afterProfileRemoval = async () => {
|
|
116
|
+
await refresh();
|
|
117
|
+
await session.refetch({ query: { disableCookieCache: true } });
|
|
118
|
+
setDialog(null);
|
|
119
|
+
setProfileId(null);
|
|
120
|
+
};
|
|
121
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
122
|
+
/* @__PURE__ */ jsxs("section", { className: "ba-organization-directory", children: [
|
|
123
|
+
/* @__PURE__ */ jsxs("header", { className: "ba-organization-directory-header", children: [
|
|
124
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
125
|
+
/* @__PURE__ */ jsx("h2", { className: "ba-title", children: t("organization.list.title") }),
|
|
126
|
+
/* @__PURE__ */ jsx("p", { className: "ba-muted", children: t("organization.list.description") })
|
|
127
|
+
] }),
|
|
128
|
+
/* @__PURE__ */ jsx("button", { className: "ba-button", type: "button", onClick: (event) => {
|
|
129
|
+
dialogReturnFocusRef.current = event.currentTarget;
|
|
130
|
+
setDialog("create");
|
|
131
|
+
}, children: t("organization.switcher.create") })
|
|
132
|
+
] }),
|
|
133
|
+
(organizationError || error) && /* @__PURE__ */ jsxs("div", { className: "ba-inline-error", children: [
|
|
134
|
+
/* @__PURE__ */ jsx(FormError, { children: organizationError ?? error }),
|
|
135
|
+
organizationError && /* @__PURE__ */ jsx("button", { className: "ba-link-button", type: "button", onClick: () => void refresh(), children: t("organization.retry") })
|
|
136
|
+
] }),
|
|
137
|
+
isLoading ? /* @__PURE__ */ jsxs("div", { className: "ba-organization-card-grid", children: [
|
|
138
|
+
/* @__PURE__ */ jsx("div", { className: "ba-skeleton" }),
|
|
139
|
+
/* @__PURE__ */ jsx("div", { className: "ba-skeleton" })
|
|
140
|
+
] }) : organizations?.length ? /* @__PURE__ */ jsx("ul", { className: "ba-organization-card-grid", children: organizations.map((organization) => /* @__PURE__ */ jsxs("li", { className: "ba-organization-card", children: [
|
|
141
|
+
/* @__PURE__ */ jsx("span", { className: "ba-organization-avatar ba-organization-avatar-large", "aria-hidden": "true", children: organization.name.trim().charAt(0).toUpperCase() || "?" }),
|
|
142
|
+
/* @__PURE__ */ jsxs("span", { className: "ba-organization-card-copy", children: [
|
|
143
|
+
/* @__PURE__ */ jsx("strong", { children: organization.name }),
|
|
144
|
+
/* @__PURE__ */ jsx("small", { children: /* @__PURE__ */ jsx(Bidi, { children: organization.slug }) })
|
|
145
|
+
] }),
|
|
146
|
+
activeId === organization.id && /* @__PURE__ */ jsx("span", { className: "ba-organization-active", children: t("organization.list.active") }),
|
|
147
|
+
/* @__PURE__ */ jsxs("span", { className: "ba-organization-card-actions", children: [
|
|
148
|
+
activeId !== organization.id && /* @__PURE__ */ jsx("button", { className: "ba-button ba-button-secondary", type: "button", disabled: pending, onClick: () => setActive(organization), children: t("organization.list.switch") }),
|
|
149
|
+
/* @__PURE__ */ jsx("button", { className: "ba-link-button", type: "button", onClick: (event) => openProfile(organization, event.currentTarget), children: t("organization.list.manage") })
|
|
150
|
+
] })
|
|
151
|
+
] }, organization.id)) }) : organizationError ? null : /* @__PURE__ */ jsx("p", { className: "ba-muted", children: t("organization.list.empty") }),
|
|
152
|
+
/* @__PURE__ */ jsxs("section", { className: "ba-organization-user-invitations", children: [
|
|
153
|
+
/* @__PURE__ */ jsxs("header", { className: "ba-organization-section-header", children: [
|
|
154
|
+
/* @__PURE__ */ jsx("h3", { className: "ba-title", children: t("organization.list.invitationsTitle") }),
|
|
155
|
+
/* @__PURE__ */ jsx("p", { className: "ba-muted", children: t("organization.list.invitationsDescription") })
|
|
156
|
+
] }),
|
|
157
|
+
invitationError && /* @__PURE__ */ jsxs("div", { className: "ba-inline-error", children: [
|
|
158
|
+
/* @__PURE__ */ jsx(FormError, { children: invitationError }),
|
|
159
|
+
/* @__PURE__ */ jsx("button", { className: "ba-link-button", type: "button", onClick: () => void loadInvitations(), children: t("organization.retry") })
|
|
160
|
+
] }),
|
|
161
|
+
invitations?.length ? /* @__PURE__ */ jsx("ul", { className: "ba-organization-list", children: invitations.map((invitation) => /* @__PURE__ */ jsxs("li", { className: "ba-organization-invitation", children: [
|
|
162
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
163
|
+
/* @__PURE__ */ jsx("strong", { children: invitation.organizationName }),
|
|
164
|
+
/* @__PURE__ */ jsx("small", { children: organizationRoleLabel(invitation.role, t) })
|
|
165
|
+
] }),
|
|
166
|
+
/* @__PURE__ */ jsxs("span", { className: "ba-organization-card-actions", children: [
|
|
167
|
+
/* @__PURE__ */ jsx("button", { className: "ba-button", type: "button", disabled: pending, onClick: () => invitationAction(invitation, "accept"), children: t("organization.list.accept") }),
|
|
168
|
+
/* @__PURE__ */ jsx("button", { className: "ba-link-button", type: "button", disabled: pending, onClick: () => invitationAction(invitation, "reject"), children: t("organization.list.reject") })
|
|
169
|
+
] })
|
|
170
|
+
] }, invitation.id)) }) : invitationError ? null : invitations === null ? /* @__PURE__ */ jsx("div", { className: "ba-skeleton" }) : /* @__PURE__ */ jsx("p", { className: "ba-muted", children: t("organization.list.noInvitations") })
|
|
171
|
+
] })
|
|
172
|
+
] }),
|
|
173
|
+
dialog === "create" && /* @__PURE__ */ jsx(OrganizationModal, { title: t("organization.create.title"), returnFocusRef: dialogReturnFocusRef, onClose: () => setDialog(null), children: /* @__PURE__ */ jsx(CreateOrganization, { title: null, onCreated: () => {
|
|
174
|
+
void (async () => {
|
|
175
|
+
await refresh();
|
|
176
|
+
await session.refetch({ query: { disableCookieCache: true } });
|
|
177
|
+
setDialog(null);
|
|
178
|
+
})();
|
|
179
|
+
} }) }),
|
|
180
|
+
dialog === "profile" && profileId && /* @__PURE__ */ jsx(OrganizationModal, { title: t("organization.profile.title"), returnFocusRef: dialogReturnFocusRef, onClose: () => setDialog(null), children: /* @__PURE__ */ jsx(OrganizationProfile, { organizationId: profileId, onDeleted: () => void afterProfileRemoval(), onLeft: () => void afterProfileRemoval() }) })
|
|
181
|
+
] });
|
|
182
|
+
}
|
|
183
|
+
export {
|
|
184
|
+
OrganizationListContent
|
|
185
|
+
};
|