@atlasauth/react 0.13.0 → 0.15.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/AtlasProvider.d.ts +8 -1
  3. package/dist/AtlasProvider.js +54 -4
  4. package/dist/AtlasProvider.js.map +1 -1
  5. package/dist/account.d.ts +19 -1
  6. package/dist/account.js +121 -16
  7. package/dist/account.js.map +1 -1
  8. package/dist/active-atlas.d.ts +29 -0
  9. package/dist/active-atlas.js +40 -0
  10. package/dist/active-atlas.js.map +1 -0
  11. package/dist/billing.d.ts +55 -1
  12. package/dist/billing.js +115 -8
  13. package/dist/billing.js.map +1 -1
  14. package/dist/captcha.js +1 -1
  15. package/dist/captcha.js.map +1 -1
  16. package/dist/compat.d.ts +307 -0
  17. package/dist/compat.js +387 -0
  18. package/dist/compat.js.map +1 -0
  19. package/dist/components.d.ts +10 -4
  20. package/dist/components.js +15 -1
  21. package/dist/components.js.map +1 -1
  22. package/dist/device-authorization.d.ts +42 -0
  23. package/dist/device-authorization.js +137 -0
  24. package/dist/device-authorization.js.map +1 -0
  25. package/dist/flow.d.ts +33 -9
  26. package/dist/flow.js +130 -11
  27. package/dist/flow.js.map +1 -1
  28. package/dist/i18n.js +68 -0
  29. package/dist/i18n.js.map +1 -1
  30. package/dist/impersonation.d.ts +25 -0
  31. package/dist/impersonation.js +52 -0
  32. package/dist/impersonation.js.map +1 -0
  33. package/dist/index.js +3 -0
  34. package/dist/index.js.map +1 -1
  35. package/dist/mfa-steps.d.ts +53 -0
  36. package/dist/mfa-steps.js +148 -0
  37. package/dist/mfa-steps.js.map +1 -0
  38. package/dist/organization.d.ts +82 -1
  39. package/dist/organization.js +222 -4
  40. package/dist/organization.js.map +1 -1
  41. package/dist/reverification.d.ts +19 -0
  42. package/dist/reverification.js +49 -1
  43. package/dist/reverification.js.map +1 -1
  44. package/dist/sign-in.js +2 -2
  45. package/dist/sign-in.js.map +1 -1
  46. package/dist/sign-up.d.ts +6 -0
  47. package/dist/sign-up.js +69 -13
  48. package/dist/sign-up.js.map +1 -1
  49. package/dist/styles.js +8 -0
  50. package/dist/styles.js.map +1 -1
  51. package/dist/types.d.ts +52 -0
  52. package/dist/version.d.ts +1 -1
  53. package/dist/version.js +1 -1
  54. package/package.json +15 -4
package/dist/index.js CHANGED
@@ -72,4 +72,7 @@ var localizations_1 = require("@atlasauth/localizations");
72
72
  Object.defineProperty(exports, "LOCALIZATION_KEYS", { enumerable: true, get: function () { return localizations_1.LOCALIZATION_KEYS; } });
73
73
  Object.defineProperty(exports, "missingKeys", { enumerable: true, get: function () { return localizations_1.missingKeys; } });
74
74
  Object.defineProperty(exports, "resolveLocale", { enumerable: true, get: function () { return localizations_1.resolveLocale; } });
75
+ // Drop-in compatibility helpers for apps migrated from other auth providers
76
+ // live on their own entry point, `@atlasauth/react/compat`, so the main API
77
+ // stays Atlas-named.
75
78
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,+CAA6B;AAC7B,2CAAyB;AACzB,4CAA0B;AAC1B,yCAAuB;AACvB,0CAAwB;AAKxB,iDAA0D;AAAjD,8GAAA,aAAa,OAAA;AAAE,yGAAA,QAAQ,OAAA;AAEhC,iCASiB;AARf,wGAAA,eAAe,OAAA;AACf,gGAAA,OAAO,OAAA;AACP,wGAAA,eAAe,OAAA;AACf,mGAAA,UAAU,OAAA;AACV,uGAAA,cAAc,OAAA;AACd,2GAAA,kBAAkB,OAAA;AAClB,sGAAA,aAAa,OAAA;AACb,gGAAA,OAAO,OAAA;AAET,qCAAiE;AAAxD,0GAAA,eAAe,OAAA;AAAE,8GAAA,mBAAmB,OAAA;AAC7C,oFAAoF;AACpF,kCAAkC;AAClC,oCAA+D;AAAtD,oGAAA,cAAc,OAAA;AAAE,oGAAA,cAAc,OAAA;AAEvC,yCAAuB;AACvB,+CAA6B;AAE7B,2DAK8B;AAJ5B,4HAAA,sBAAsB,OAAA;AACtB,6HAAA,uBAAuB,OAAA;AACvB,0HAAA,oBAAoB,OAAA;AACpB,2HAAA,qBAAqB,OAAA;AAEvB,iFAAiF;AACjF,gDAAgD;AAChD,oCAAmE;AAA1D,kGAAA,YAAY,OAAA;AAAE,0GAAA,oBAAoB,OAAA;AAE3C,qFAAqF;AACrF,gFAAgF;AAChF,gFAAgF;AAChF,oCAAiD;AAAxC,sGAAA,gBAAgB,OAAA;AACzB,0EAA0E;AAC1E,4DAA4D;AAC5D,oCAAyF;AAAhF,sGAAA,gBAAgB,OAAA;AAAE,8GAAA,wBAAwB,OAAA;AAAE,kGAAA,YAAY,OAAA;AAEjE,qFAAqF;AACrF,+CAA+C;AAC/C,oCAAuD;AAA9C,4GAAA,sBAAsB,OAAA;AAE/B,4EAA4E;AAC5E,0DAAyF;AAAhF,kHAAA,iBAAiB,OAAA;AAAE,4GAAA,WAAW,OAAA;AAAE,8GAAA,aAAa,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,+CAA6B;AAC7B,2CAAyB;AACzB,4CAA0B;AAC1B,yCAAuB;AACvB,0CAAwB;AAKxB,iDAA0D;AAAjD,8GAAA,aAAa,OAAA;AAAE,yGAAA,QAAQ,OAAA;AAEhC,iCASiB;AARf,wGAAA,eAAe,OAAA;AACf,gGAAA,OAAO,OAAA;AACP,wGAAA,eAAe,OAAA;AACf,mGAAA,UAAU,OAAA;AACV,uGAAA,cAAc,OAAA;AACd,2GAAA,kBAAkB,OAAA;AAClB,sGAAA,aAAa,OAAA;AACb,gGAAA,OAAO,OAAA;AAET,qCAAiE;AAAxD,0GAAA,eAAe,OAAA;AAAE,8GAAA,mBAAmB,OAAA;AAC7C,oFAAoF;AACpF,kCAAkC;AAClC,oCAA+D;AAAtD,oGAAA,cAAc,OAAA;AAAE,oGAAA,cAAc,OAAA;AAEvC,yCAAuB;AACvB,+CAA6B;AAE7B,2DAK8B;AAJ5B,4HAAA,sBAAsB,OAAA;AACtB,6HAAA,uBAAuB,OAAA;AACvB,0HAAA,oBAAoB,OAAA;AACpB,2HAAA,qBAAqB,OAAA;AAEvB,iFAAiF;AACjF,gDAAgD;AAChD,oCAAmE;AAA1D,kGAAA,YAAY,OAAA;AAAE,0GAAA,oBAAoB,OAAA;AAE3C,qFAAqF;AACrF,gFAAgF;AAChF,gFAAgF;AAChF,oCAAiD;AAAxC,sGAAA,gBAAgB,OAAA;AACzB,0EAA0E;AAC1E,4DAA4D;AAC5D,oCAAyF;AAAhF,sGAAA,gBAAgB,OAAA;AAAE,8GAAA,wBAAwB,OAAA;AAAE,kGAAA,YAAY,OAAA;AAEjE,qFAAqF;AACrF,+CAA+C;AAC/C,oCAAuD;AAA9C,4GAAA,sBAAsB,OAAA;AAE/B,4EAA4E;AAC5E,0DAAyF;AAAhF,kHAAA,iBAAiB,OAAA;AAAE,4GAAA,WAAW,OAAA;AAAE,8GAAA,aAAa,OAAA;AAQtD,4EAA4E;AAC5E,4EAA4E;AAC5E,qBAAqB"}
@@ -0,0 +1,53 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type MfaChoices, type MfaEnrollmentController, type MfaEnrollmentState } from '@atlasauth/js';
3
+ /**
4
+ * The sign-in's MFA steps for `<SignIn>`: setting up a second factor when the
5
+ * app requires one (`needs_mfa_enrollment`) and the texted / emailed code at the
6
+ * second-factor step. The requests and the step's state live in
7
+ * `@atlasauth/js` (`createMfaEnrollment`), shared with every framework SDK.
8
+ */
9
+ /** What the instance lets the MFA steps offer (`account.mfa`, `account.emailSecondFactor`). */
10
+ export declare function useMfaChoices(): MfaChoices;
11
+ /**
12
+ * The enrolment step's controller and state, started for `attemptId` while
13
+ * `active` (the flow is at `needs_mfa_enrollment`) and reset when it leaves.
14
+ */
15
+ export declare function useMfaEnrollment(attemptId: string | null, active: boolean): {
16
+ controller: MfaEnrollmentController;
17
+ state: MfaEnrollmentState;
18
+ };
19
+ /**
20
+ * The enrolment step's body: the method choice, the authenticator's QR code and
21
+ * setup key with its code field(s), or the phone number then the texted code.
22
+ * `field` draws an input the way the rest of the form does.
23
+ */
24
+ export declare function MfaEnrollmentFields({ controller, state, busy, field, clearValues, }: {
25
+ controller: MfaEnrollmentController;
26
+ state: MfaEnrollmentState;
27
+ busy: boolean;
28
+ field: (name: string, labelKey: string, type?: string, extra?: {
29
+ autoComplete?: string;
30
+ inputMode?: 'numeric' | 'tel';
31
+ }) => ReactNode;
32
+ clearValues: () => void;
33
+ }): import("react").JSX.Element;
34
+ /**
35
+ * "Text me a code instead" / "Email me a code instead" at the second factor:
36
+ * sends the code for this attempt, then says where it went. The code is typed
37
+ * into the step's code field like any second-factor code.
38
+ */
39
+ export declare function SecondFactorCodeOption({ attemptId, strategy }: {
40
+ attemptId: string;
41
+ strategy: 'sms' | 'email_code';
42
+ }): import("react").JSX.Element;
43
+ /**
44
+ * The recovery codes a mid-sign-in enrolment came with, shown once before the
45
+ * sign-in finishes: copy them, download them as a .txt file, then "I've saved
46
+ * my codes" (`onSaved`) exchanges the ticket. The codes live only in the flow's
47
+ * memory and are forgotten once saved.
48
+ */
49
+ export declare function RecoveryCodes({ codes, busy, onSaved }: {
50
+ codes: string[];
51
+ busy: boolean;
52
+ onSaved(): void;
53
+ }): import("react").JSX.Element;
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ 'use client';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.useMfaChoices = useMfaChoices;
5
+ exports.useMfaEnrollment = useMfaEnrollment;
6
+ exports.MfaEnrollmentFields = MfaEnrollmentFields;
7
+ exports.SecondFactorCodeOption = SecondFactorCodeOption;
8
+ exports.RecoveryCodes = RecoveryCodes;
9
+ const jsx_runtime_1 = require("react/jsx-runtime");
10
+ const react_1 = require("react");
11
+ const js_1 = require("@atlasauth/js");
12
+ const AtlasProvider_1 = require("./AtlasProvider");
13
+ const components_shared_1 = require("./components-shared");
14
+ /**
15
+ * The sign-in's MFA steps for `<SignIn>`: setting up a second factor when the
16
+ * app requires one (`needs_mfa_enrollment`) and the texted / emailed code at the
17
+ * second-factor step. The requests and the step's state live in
18
+ * `@atlasauth/js` (`createMfaEnrollment`), shared with every framework SDK.
19
+ */
20
+ /** What the instance lets the MFA steps offer (`account.mfa`, `account.emailSecondFactor`). */
21
+ function useMfaChoices() {
22
+ const { instanceSettings } = (0, AtlasProvider_1.useAtlas)();
23
+ return (0, js_1.mfaChoicesFrom)(instanceSettings?.account);
24
+ }
25
+ /**
26
+ * The enrolment step's controller and state, started for `attemptId` while
27
+ * `active` (the flow is at `needs_mfa_enrollment`) and reset when it leaves.
28
+ */
29
+ function useMfaEnrollment(attemptId, active) {
30
+ const client = (0, components_shared_1.useFapiClient)();
31
+ const choices = useMfaChoices();
32
+ const choicesRef = (0, react_1.useRef)(choices);
33
+ choicesRef.current = choices;
34
+ const controller = (0, react_1.useMemo)(() => (0, js_1.createMfaEnrollment)(client, () => choicesRef.current), [client]);
35
+ const [state, setState] = (0, react_1.useState)(controller.state);
36
+ (0, react_1.useEffect)(() => controller.subscribe(setState), [controller]);
37
+ (0, react_1.useEffect)(() => {
38
+ if (active && attemptId)
39
+ controller.start(attemptId);
40
+ else
41
+ controller.reset();
42
+ }, [controller, active, attemptId]);
43
+ return { controller, state };
44
+ }
45
+ /**
46
+ * The enrolment step's body: the method choice, the authenticator's QR code and
47
+ * setup key with its code field(s), or the phone number then the texted code.
48
+ * `field` draws an input the way the rest of the form does.
49
+ */
50
+ function MfaEnrollmentFields({ controller, state, busy, field, clearValues, }) {
51
+ const t = (0, components_shared_1.useText)();
52
+ const cls = (0, components_shared_1.useClasses)();
53
+ const errorClass = (0, components_shared_1.useClass)('formFieldError', 'atlas-error');
54
+ const secondaryClass = (0, components_shared_1.useClass)('formButtonSecondary', 'atlas-button-secondary');
55
+ const { method, enrollment, options } = state;
56
+ const out = [];
57
+ if (method === null) {
58
+ out.push((0, jsx_runtime_1.jsx)("p", { className: cls('text', 'atlas-text'), children: t('mfa.enrollChoose') }, "choose"), ...options.map((m) => ((0, jsx_runtime_1.jsx)("button", { type: "button", className: secondaryClass, "data-atlas-mfa-method": m, disabled: busy || state.busy, onClick: () => {
59
+ clearValues();
60
+ void controller.choose(m);
61
+ }, children: t(m === 'totp' ? 'mfa.enrollTotpOption' : 'mfa.enrollSmsOption') }, m))));
62
+ }
63
+ else if (method === 'totp') {
64
+ if (!enrollment) {
65
+ out.push((0, jsx_runtime_1.jsx)("p", { className: cls('text', 'atlas-text'), "aria-live": "polite", children: state.busy ? t('mfa.enrollPreparing') : null }, "preparing"));
66
+ }
67
+ else {
68
+ out.push((0, jsx_runtime_1.jsx)("p", { className: cls('text', 'atlas-text'), children: t('mfa.enrollScan') }, "scan"));
69
+ if (enrollment.qrCode) {
70
+ out.push((0, jsx_runtime_1.jsx)("img", { className: "atlas-mfa-qr", "data-atlas": "mfa-qr", src: enrollment.qrCode, alt: t('mfa.enrollQrAlt'), width: 180, height: 180 }, "qr"));
71
+ }
72
+ if (enrollment.secret) {
73
+ out.push((0, jsx_runtime_1.jsxs)("p", { className: cls('text', 'atlas-text'), "data-atlas": "mfa-setup-key", children: [t('mfa.enrollSetupKey'), ": ", (0, jsx_runtime_1.jsx)("code", { children: (0, js_1.groupSetupKey)(enrollment.secret) })] }, "secret"));
74
+ }
75
+ out.push(field('code', 'mfa.enrollFirstCodeLabel', 'text', { autoComplete: 'one-time-code', inputMode: 'numeric' }));
76
+ if (state.totpCodes >= 2) {
77
+ out.push(field('secondCode', 'mfa.enrollSecondCodeLabel', 'text', { autoComplete: 'one-time-code', inputMode: 'numeric' }));
78
+ }
79
+ }
80
+ }
81
+ else if (!enrollment) {
82
+ out.push(field('phone', 'mfa.phoneLabel', 'tel', { autoComplete: 'tel', inputMode: 'tel' }), (0, jsx_runtime_1.jsx)("p", { className: cls('text', 'atlas-text'), children: t('mfa.phoneHint') }, "phone-hint"));
83
+ }
84
+ else {
85
+ out.push((0, jsx_runtime_1.jsx)("p", { className: cls('text', 'atlas-text'), "data-atlas": "mfa-code-texted", "aria-live": "polite", children: t('mfa.codeTextedTo', { phone: enrollment.phone ?? '' }) }, "texted"), field('code', 'mfa.codeLabel', 'text', { autoComplete: 'one-time-code', inputMode: 'numeric' }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: cls('link', 'atlas-link'), "data-atlas-resend": "", disabled: busy || state.busy, onClick: () => void controller.resend(), children: t('signIn.resendCode') }, "resend"));
86
+ }
87
+ for (const error of state.errors) {
88
+ out.push((0, jsx_runtime_1.jsx)("p", { className: errorClass, role: "alert", children: error.message }, `enrol-error-${error.code}-${error.param ?? ''}`));
89
+ }
90
+ if (method !== null && options.length > 1) {
91
+ const other = options.find((m) => m !== method);
92
+ out.push((0, jsx_runtime_1.jsx)("button", { type: "button", className: cls('link', 'atlas-link'), "data-atlas-mfa-method": other, disabled: busy || state.busy, onClick: () => {
93
+ clearValues();
94
+ void controller.choose(other);
95
+ }, children: t(other === 'totp' ? 'mfa.enrollTotpOption' : 'mfa.enrollSmsOption') }, "switch"));
96
+ }
97
+ return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: out });
98
+ }
99
+ /**
100
+ * "Text me a code instead" / "Email me a code instead" at the second factor:
101
+ * sends the code for this attempt, then says where it went. The code is typed
102
+ * into the step's code field like any second-factor code.
103
+ */
104
+ function SecondFactorCodeOption({ attemptId, strategy }) {
105
+ const client = (0, components_shared_1.useFapiClient)();
106
+ const t = (0, components_shared_1.useText)();
107
+ const cls = (0, components_shared_1.useClasses)();
108
+ const [state, setState] = (0, react_1.useState)({});
109
+ const name = strategy === 'sms' ? 'sms-second-factor' : 'email-second-factor';
110
+ const send = () => {
111
+ setState({ busy: true });
112
+ (0, js_1.prepareSecondFactorCode)(client, attemptId, strategy)
113
+ .then(({ sentTo }) => setState({ sentTo }))
114
+ .catch((e) => setState({ error: e instanceof Error ? e.message : String(e) }));
115
+ };
116
+ if (state.sentTo !== undefined) {
117
+ return ((0, jsx_runtime_1.jsx)("p", { className: cls('text', 'atlas-text'), "data-atlas": `${name}-sent`, "aria-live": "polite", children: strategy === 'sms' ? t('mfa.codeTextedTo', { phone: state.sentTo }) : t('signIn.codeSentTo', { email: state.sentTo }) }));
118
+ }
119
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: cls('link', 'atlas-link'), "data-atlas": name, disabled: state.busy, onClick: send, children: state.busy ? t('signIn.sending') : t(strategy === 'sms' ? 'mfa.smsCodeOption' : 'signIn.emailCodeOption') }), state.error ? ((0, jsx_runtime_1.jsx)("div", { className: cls('alert', 'atlas-alert'), role: "alert", children: state.error })) : null] }));
120
+ }
121
+ const RECOVERY_LIST_STYLE = {
122
+ listStyle: 'none',
123
+ padding: 0,
124
+ margin: '8px 0 12px',
125
+ display: 'grid',
126
+ gridTemplateColumns: 'repeat(2, minmax(0, 1fr))',
127
+ gap: 6,
128
+ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',
129
+ textAlign: 'center',
130
+ };
131
+ /**
132
+ * The recovery codes a mid-sign-in enrolment came with, shown once before the
133
+ * sign-in finishes: copy them, download them as a .txt file, then "I've saved
134
+ * my codes" (`onSaved`) exchanges the ticket. The codes live only in the flow's
135
+ * memory and are forgotten once saved.
136
+ */
137
+ function RecoveryCodes({ codes, busy, onSaved }) {
138
+ const t = (0, components_shared_1.useText)();
139
+ const cls = (0, components_shared_1.useClasses)();
140
+ const cardClass = (0, components_shared_1.useClass)('card', 'atlas-card');
141
+ const titleClass = (0, components_shared_1.useClass)('headerTitle', 'atlas-title');
142
+ const buttonClass = (0, components_shared_1.useClass)('formButtonPrimary', 'atlas-button');
143
+ const secondaryClass = (0, components_shared_1.useClass)('formButtonSecondary', 'atlas-button-secondary');
144
+ const [copied, setCopied] = (0, react_1.useState)(false);
145
+ const canCopy = typeof navigator !== 'undefined' && !!navigator.clipboard?.writeText;
146
+ return ((0, jsx_runtime_1.jsxs)("div", { className: cardClass, "data-atlas": "recovery-codes", children: [(0, jsx_runtime_1.jsx)("h1", { className: titleClass, children: t('mfa.recoveryCodesTitle') }), (0, jsx_runtime_1.jsx)("p", { className: cls('text', 'atlas-text'), children: t('mfa.recoveryCodesHelp') }), (0, jsx_runtime_1.jsx)("ul", { "data-atlas": "recovery-codes-list", style: RECOVERY_LIST_STYLE, children: codes.map((code) => ((0, jsx_runtime_1.jsx)("li", { children: (0, jsx_runtime_1.jsx)("code", { children: code }) }, code))) }), (0, jsx_runtime_1.jsxs)("div", { className: "atlas-alternatives", children: [canCopy ? ((0, jsx_runtime_1.jsx)("button", { type: "button", className: secondaryClass, "data-atlas": "recovery-codes-copy", onClick: () => void (0, js_1.copyRecoveryCodes)(codes).then(setCopied), children: t('mfa.recoveryCodesCopy') })) : null, (0, js_1.canDownloadRecoveryCodes)() ? ((0, jsx_runtime_1.jsx)("button", { type: "button", className: secondaryClass, "data-atlas": "recovery-codes-download", onClick: () => (0, js_1.downloadRecoveryCodes)(codes), children: t('mfa.recoveryCodesDownload') })) : null] }), copied ? ((0, jsx_runtime_1.jsx)("p", { className: cls('text', 'atlas-text'), "aria-live": "polite", "data-atlas": "recovery-codes-copied", children: t('mfa.recoveryCodesCopied') })) : null, (0, jsx_runtime_1.jsx)("button", { type: "button", className: buttonClass, "data-atlas": "recovery-codes-saved", disabled: busy, onClick: onSaved, children: t('mfa.recoveryCodesSaved') })] }));
147
+ }
148
+ //# sourceMappingURL=mfa-steps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mfa-steps.js","sourceRoot":"","sources":["../src/mfa-steps.tsx"],"names":[],"mappings":"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"}
@@ -1,4 +1,6 @@
1
+ import { OrganizationProfileController, type OrganizationProfileState } from '@atlasauth/js';
1
2
  import { useOrganization } from './hooks';
3
+ import { useReverification } from './reverification';
2
4
  export declare function OrganizationSwitcher(): import("react").JSX.Element;
3
5
  /**
4
6
  * Clerk-parity `useOrganizationList()`. The memberships the signed-in user
@@ -34,4 +36,83 @@ export declare function CreateOrganization({ onCreate, }?: {
34
36
  * can make one without leaving the list.
35
37
  */
36
38
  export declare function OrganizationList(): import("react").JSX.Element;
37
- export declare function OrganizationProfile(): import("react").JSX.Element | null;
39
+ /**
40
+ * Headless state for the active organization's profile: members, pending
41
+ * invitations and roles, with the admin actions (change role, remove, invite,
42
+ * revoke, resend) and the member actions (leave; delete for an admin, which
43
+ * parks for a password re-authentication on a stale session). The same
44
+ * `OrganizationProfileController` backs every SDK's `<OrganizationProfile>`.
45
+ */
46
+ export declare function useOrganizationProfile(): {
47
+ organizationId: string | null;
48
+ state: OrganizationProfileState;
49
+ canManage: boolean;
50
+ isAdmin: boolean;
51
+ controller: OrganizationProfileController | null;
52
+ deleteOrganization(): Promise<boolean>;
53
+ reverification: ReturnType<typeof useReverification>;
54
+ };
55
+ export interface OrganizationProfileProps {
56
+ /** Called after the user leaves or deletes the organization (e.g. to navigate away). */
57
+ onExit?(reason: 'left' | 'deleted'): void;
58
+ }
59
+ /**
60
+ * Clerk-parity `<OrganizationProfile>`: the active organization's members
61
+ * (admins change roles and remove), invitations (admins invite, revoke and
62
+ * resend) and settings (leave; an admin deletes, after re-authenticating).
63
+ * Every control is gated on the ACTIVE org role, matching the server's rule.
64
+ */
65
+ export declare function OrganizationProfile({ onExit }?: OrganizationProfileProps): import("react").JSX.Element | null;
66
+ /** One entry of an organization's audit trail, as the Frontend API returns it. */
67
+ export interface OrganizationAuditLogEntry {
68
+ object: 'audit_log';
69
+ id: string;
70
+ action: string;
71
+ actor: {
72
+ type: string;
73
+ id: string | null;
74
+ };
75
+ target: {
76
+ type: string | null;
77
+ id: string | null;
78
+ };
79
+ organization_id: string;
80
+ ip: string | null;
81
+ user_agent: string | null;
82
+ metadata: Record<string, unknown>;
83
+ created_at: number;
84
+ }
85
+ /**
86
+ * The active organization's audit trail, for org admins: one page at a time,
87
+ * newest first, with a "load more" cursor. `action` narrows it (exact, or a
88
+ * prefix ending in `.`). Returns `forbidden: true` when the signed-in member is
89
+ * not allowed to read it, so the caller can hide the surface.
90
+ */
91
+ export declare function useOrganizationAuditLog(options?: {
92
+ pageSize?: number;
93
+ action?: string;
94
+ }): {
95
+ entries: OrganizationAuditLogEntry[];
96
+ loading: boolean;
97
+ forbidden: boolean;
98
+ hasMore: boolean;
99
+ loadMore(): Promise<void>;
100
+ reload(): Promise<void>;
101
+ };
102
+ /**
103
+ * `<OrganizationAuditLog/>` — the active organization's audit trail for its
104
+ * admins, next to `<OrganizationProfile/>`. Renders nothing for a member who may
105
+ * not read it (the server is the authority; the component just follows it).
106
+ * Labels are props so an app can localise them.
107
+ */
108
+ export declare function OrganizationAuditLog({ pageSize, action, labels, formatDate, }?: {
109
+ pageSize?: number;
110
+ action?: string;
111
+ labels?: {
112
+ title?: string;
113
+ empty?: string;
114
+ loadMore?: string;
115
+ actor?: string;
116
+ };
117
+ formatDate?(ms: number): string;
118
+ }): import("react").JSX.Element | null;
@@ -5,13 +5,17 @@ exports.OrganizationSwitcher = OrganizationSwitcher;
5
5
  exports.useOrganizationList = useOrganizationList;
6
6
  exports.CreateOrganization = CreateOrganization;
7
7
  exports.OrganizationList = OrganizationList;
8
+ exports.useOrganizationProfile = useOrganizationProfile;
8
9
  exports.OrganizationProfile = OrganizationProfile;
10
+ exports.useOrganizationAuditLog = useOrganizationAuditLog;
11
+ exports.OrganizationAuditLog = OrganizationAuditLog;
9
12
  const jsx_runtime_1 = require("react/jsx-runtime");
10
13
  const react_1 = require("react");
14
+ const js_1 = require("@atlasauth/js");
11
15
  const AtlasProvider_1 = require("./AtlasProvider");
12
16
  const hooks_1 = require("./hooks");
13
17
  const components_shared_1 = require("./components-shared");
14
- const control_components_1 = require("./control-components");
18
+ const reverification_1 = require("./reverification");
15
19
  function OrganizationSwitcher() {
16
20
  const { memberships, organization, setActive } = (0, hooks_1.useOrganization)();
17
21
  const t = (0, components_shared_1.useText)();
@@ -102,13 +106,227 @@ function OrganizationList() {
102
106
  const itemClass = (0, components_shared_1.useClass)('menuItem', 'atlas-menu-item');
103
107
  return ((0, jsx_runtime_1.jsxs)("div", { className: menuClass, "data-atlas": "organization-list", children: [memberships.map((membership) => ((0, jsx_runtime_1.jsx)("button", { className: itemClass, disabled: membership.organization.id === organization?.id, onClick: () => void setActive(membership.organization.id), children: membership.organization.name }, membership.organization.id))), (0, jsx_runtime_1.jsx)("span", { className: itemClass, "data-atlas": "organization-list-divider", children: t('organization.orCreate') }), (0, jsx_runtime_1.jsx)(CreateOrganization, {})] }));
104
108
  }
105
- function OrganizationProfile() {
109
+ /**
110
+ * Headless state for the active organization's profile: members, pending
111
+ * invitations and roles, with the admin actions (change role, remove, invite,
112
+ * revoke, resend) and the member actions (leave; delete for an admin, which
113
+ * parks for a password re-authentication on a stale session). The same
114
+ * `OrganizationProfileController` backs every SDK's `<OrganizationProfile>`.
115
+ */
116
+ function useOrganizationProfile() {
117
+ const { organization, membership } = (0, hooks_1.useOrganization)();
118
+ const { has } = (0, hooks_1.useAuth)();
119
+ const { reload } = (0, AtlasProvider_1.useAtlas)();
120
+ const client = (0, components_shared_1.useMeClient)();
121
+ const reverification = (0, reverification_1.useReverification)();
122
+ const canManage = has({ permission: 'org:sys_memberships:manage' });
123
+ const isAdmin = membership?.role === 'org:admin';
124
+ const organizationId = organization?.id ?? null;
125
+ const controller = (0, react_1.useMemo)(() => organizationId
126
+ ? new js_1.OrganizationProfileController(client, organizationId, {
127
+ canManage,
128
+ onChange: async () => {
129
+ await reload();
130
+ },
131
+ })
132
+ : null, [client, organizationId, canManage, reload]);
133
+ (0, react_1.useEffect)(() => {
134
+ void controller?.load();
135
+ }, [controller]);
136
+ const empty = (0, react_1.useMemo)(() => ({ status: 'idle', members: [], membersTotal: 0, invitations: [], roles: [], permissions: [], error: null, busy: null }), []);
137
+ const state = (0, react_1.useSyncExternalStore)((0, react_1.useCallback)((cb) => (controller ? controller.subscribe(cb) : () => undefined), [controller]), () => controller?.getState() ?? empty, () => controller?.getState() ?? empty);
138
+ const { withReverification } = reverification;
139
+ const remove = (0, react_1.useCallback)(async () => {
140
+ if (!organizationId)
141
+ return false;
142
+ await withReverification(() => (0, js_1.deleteOrganization)(client, organizationId));
143
+ await reload();
144
+ return true;
145
+ }, [client, organizationId, reload, withReverification]);
146
+ return { organizationId, state, canManage, isAdmin, controller, deleteOrganization: remove, reverification };
147
+ }
148
+ function memberName(member, fallback) {
149
+ const name = [member.user?.first_name, member.user?.last_name].filter(Boolean).join(' ');
150
+ return name || fallback;
151
+ }
152
+ /**
153
+ * Clerk-parity `<OrganizationProfile>`: the active organization's members
154
+ * (admins change roles and remove), invitations (admins invite, revoke and
155
+ * resend) and settings (leave; an admin deletes, after re-authenticating).
156
+ * Every control is gated on the ACTIVE org role, matching the server's rule.
157
+ */
158
+ function OrganizationProfile({ onExit } = {}) {
106
159
  const { organization, membership } = (0, hooks_1.useOrganization)();
160
+ const { userId } = (0, hooks_1.useAuth)();
107
161
  const t = (0, components_shared_1.useText)();
162
+ const errorText = (0, components_shared_1.useErrorText)();
163
+ const cardClass = (0, components_shared_1.useClass)('card', 'atlas-card');
164
+ const cls = (0, components_shared_1.useClasses)();
165
+ const profile = useOrganizationProfile();
166
+ const { state, controller, canManage, isAdmin, reverification } = profile;
167
+ const [tab, setTab] = (0, react_1.useState)('members');
168
+ // The custom-role editor: null = creating a new role, else the id being edited.
169
+ const [editingRole, setEditingRole] = (0, react_1.useState)(null);
170
+ const [roleKey, setRoleKey] = (0, react_1.useState)('');
171
+ const [roleName, setRoleName] = (0, react_1.useState)('');
172
+ const [rolePerms, setRolePerms] = (0, react_1.useState)([]);
173
+ const [email, setEmail] = (0, react_1.useState)('');
174
+ const [inviteRole, setInviteRole] = (0, react_1.useState)('org:member');
175
+ const [confirm, setConfirm] = (0, react_1.useState)(null);
176
+ const [password, setPassword] = (0, react_1.useState)('');
177
+ const [localError, setLocalError] = (0, react_1.useState)(null);
178
+ if (!organization || !controller)
179
+ return null;
180
+ const buttonSecondary = cls('formButtonSecondary', 'atlas-button-secondary');
181
+ const buttonPrimary = cls('formButtonPrimary', 'atlas-button');
182
+ const busy = (key) => state.busy === key;
183
+ const error = localError ?? (state.error ? errorText(state.error) : null);
184
+ const sendInvite = async () => {
185
+ const address = email.trim();
186
+ if (!address)
187
+ return;
188
+ const out = await controller.invite(address, inviteRole);
189
+ if (out)
190
+ setEmail('');
191
+ };
192
+ const doLeave = async () => {
193
+ setLocalError(null);
194
+ if (await controller.leave())
195
+ onExit?.('left');
196
+ setConfirm(null);
197
+ };
198
+ const doDelete = async () => {
199
+ setLocalError(null);
200
+ try {
201
+ if (await profile.deleteOrganization())
202
+ onExit?.('deleted');
203
+ }
204
+ catch (err) {
205
+ if (!(err instanceof Error && err.message === 'Reverification cancelled.')) {
206
+ setLocalError(err instanceof Error ? errorText(err) : t('error.generic'));
207
+ }
208
+ }
209
+ setConfirm(null);
210
+ };
211
+ const roleOptions = state.roles.length > 0 ? state.roles : [{ key: 'org:member', name: 'org:member' }];
212
+ const customRoles = state.roles.filter((r) => Boolean(r.organization_id));
213
+ const resetRoleForm = () => {
214
+ setEditingRole(null);
215
+ setRoleKey('');
216
+ setRoleName('');
217
+ setRolePerms([]);
218
+ };
219
+ const saveRole = async () => {
220
+ const out = editingRole
221
+ ? await controller.updateRole(editingRole, { name: roleName.trim(), permissions: rolePerms })
222
+ : await controller.createRole({ key: roleKey.trim(), name: roleName.trim(), permissions: rolePerms });
223
+ if (out)
224
+ resetRoleForm();
225
+ };
226
+ return ((0, jsx_runtime_1.jsxs)("div", { className: cardClass, "data-atlas": "organization-profile", children: [(0, jsx_runtime_1.jsx)("h1", { className: cls('headerTitle', 'atlas-heading'), children: organization.name }), (0, jsx_runtime_1.jsxs)("p", { className: cls('headerSubtitle', 'atlas-subtitle'), children: [t('organization.roleLabel'), ": ", membership?.role] }), (0, jsx_runtime_1.jsx)("div", { className: cls('menu', 'atlas-menu'), role: "tablist", children: ['members', ...(canManage ? ['invitations', 'roles'] : []), 'settings'].map((key) => ((0, jsx_runtime_1.jsx)("button", { type: "button", role: "tab", "aria-selected": tab === key, className: cls('menuItem', 'atlas-menu-item'), "data-atlas": `organization-tab-${key}`, onClick: () => setTab(key), children: t(`organization.${key}`) }, key))) }), error ? ((0, jsx_runtime_1.jsx)("p", { className: cls('alert', 'atlas-alert'), role: "alert", children: error })) : null, tab === 'members' ? ((0, jsx_runtime_1.jsx)("ul", { className: cls('list', 'atlas-list'), "data-atlas": "organization-members", children: state.members.map((member) => {
227
+ const self = member.user?.id === userId;
228
+ const id = member.user?.id ?? member.id;
229
+ return ((0, jsx_runtime_1.jsxs)("li", { "data-atlas": "organization-member", children: [(0, jsx_runtime_1.jsxs)("span", { children: [memberName(member, t('organization.unnamedMember')), self ? ` ${t('organization.you')}` : ''] }), canManage && !self && member.user ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("select", { "aria-label": t('organization.roleLabel'), className: cls('formFieldInput', 'atlas-input'), value: member.role, disabled: busy(`role:${id}`), onChange: (event) => void controller.changeRole(id, event.target.value), children: roleOptions.map((role) => ((0, jsx_runtime_1.jsx)("option", { value: role.key, children: role.name }, role.key))) }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: buttonSecondary, disabled: busy(`remove:${id}`), onClick: () => void controller.removeMember(id), children: t('organization.remove') })] })) : ((0, jsx_runtime_1.jsxs)("span", { children: [" \u00B7 ", member.role] }))] }, member.id));
230
+ }) })) : null, tab === 'invitations' && canManage ? ((0, jsx_runtime_1.jsxs)("div", { "data-atlas": "organization-invitations", children: [(0, jsx_runtime_1.jsxs)("form", { onSubmit: (event) => {
231
+ event.preventDefault();
232
+ void sendInvite();
233
+ }, children: [(0, jsx_runtime_1.jsxs)("label", { className: cls('formField', 'atlas-field'), children: [(0, jsx_runtime_1.jsx)("span", { className: cls('formFieldLabel', 'atlas-field-label'), children: t('organization.emailLabel') }), (0, jsx_runtime_1.jsx)("input", { className: cls('formFieldInput', 'atlas-input'), type: "email", value: email, onChange: (event) => setEmail(event.target.value) })] }), (0, jsx_runtime_1.jsxs)("label", { className: cls('formField', 'atlas-field'), children: [(0, jsx_runtime_1.jsx)("span", { className: cls('formFieldLabel', 'atlas-field-label'), children: t('organization.roleLabel') }), (0, jsx_runtime_1.jsx)("select", { className: cls('formFieldInput', 'atlas-input'), value: inviteRole, onChange: (event) => setInviteRole(event.target.value), children: roleOptions.map((role) => ((0, jsx_runtime_1.jsx)("option", { value: role.key, children: role.name }, role.key))) })] }), (0, jsx_runtime_1.jsx)("button", { type: "submit", className: buttonPrimary, disabled: busy('invite') || !email.trim(), children: t('organization.sendInvite') })] }), state.invitations.length === 0 ? ((0, jsx_runtime_1.jsx)("p", { className: cls('text', 'atlas-text'), children: t('organization.noInvitations') })) : ((0, jsx_runtime_1.jsx)("ul", { className: cls('list', 'atlas-list'), children: state.invitations.map((inv) => ((0, jsx_runtime_1.jsxs)("li", { "data-atlas": "organization-invitation", children: [(0, jsx_runtime_1.jsxs)("span", { children: [inv.email_address, " \u00B7 ", inv.role] }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: buttonSecondary, disabled: busy(`resend:${inv.id}`), onClick: () => void controller.resendInvitation(inv.id), children: t('organization.resend') }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: buttonSecondary, disabled: busy(`revoke:${inv.id}`), onClick: () => void controller.revokeInvitation(inv.id), children: t('organization.revoke') })] }, inv.id))) }))] })) : null, tab === 'roles' && canManage ? ((0, jsx_runtime_1.jsxs)("div", { "data-atlas": "organization-roles", children: [customRoles.length === 0 ? ((0, jsx_runtime_1.jsx)("p", { className: cls('text', 'atlas-text'), children: t('organization.noCustomRoles') })) : ((0, jsx_runtime_1.jsx)("ul", { className: cls('list', 'atlas-list'), children: customRoles.map((role) => ((0, jsx_runtime_1.jsxs)("li", { "data-atlas": "organization-role", children: [(0, jsx_runtime_1.jsxs)("span", { children: [role.name, " \u00B7 ", role.key] }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: buttonSecondary, onClick: () => {
234
+ setEditingRole(role.id ?? null);
235
+ setRoleKey(role.key);
236
+ setRoleName(role.name);
237
+ setRolePerms(role.permissions ?? []);
238
+ }, children: t('organization.editRole') }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: buttonSecondary, disabled: !role.id || busy(`role:delete:${role.id}`), onClick: () => role.id && void controller.deleteRole(role.id), children: t('organization.deleteRole') })] }, role.id ?? role.key))) })), (0, jsx_runtime_1.jsxs)("form", { "data-atlas": "organization-role-form", onSubmit: (event) => {
239
+ event.preventDefault();
240
+ void saveRole();
241
+ }, children: [editingRole ? null : ((0, jsx_runtime_1.jsxs)("label", { className: cls('formField', 'atlas-field'), children: [(0, jsx_runtime_1.jsx)("span", { className: cls('formFieldLabel', 'atlas-field-label'), children: t('organization.roleKey') }), (0, jsx_runtime_1.jsx)("input", { className: cls('formFieldInput', 'atlas-input'), name: "role_key", value: roleKey, onChange: (event) => setRoleKey(event.target.value) })] })), (0, jsx_runtime_1.jsxs)("label", { className: cls('formField', 'atlas-field'), children: [(0, jsx_runtime_1.jsx)("span", { className: cls('formFieldLabel', 'atlas-field-label'), children: t('organization.roleName') }), (0, jsx_runtime_1.jsx)("input", { className: cls('formFieldInput', 'atlas-input'), name: "role_name", value: roleName, onChange: (event) => setRoleName(event.target.value) })] }), (0, jsx_runtime_1.jsxs)("fieldset", { className: cls('formField', 'atlas-field'), children: [(0, jsx_runtime_1.jsx)("legend", { className: cls('formFieldLabel', 'atlas-field-label'), children: t('organization.rolePermissions') }), state.permissions.map((permission) => ((0, jsx_runtime_1.jsxs)("label", { "data-atlas": "organization-role-permission", children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", value: permission.key, checked: rolePerms.includes(permission.key), onChange: (event) => setRolePerms((current) => event.target.checked
242
+ ? [...current, permission.key]
243
+ : current.filter((key) => key !== permission.key)) }), ' ', permission.name] }, permission.key)))] }), (0, jsx_runtime_1.jsx)("button", { type: "submit", className: buttonPrimary, disabled: !roleName.trim() || (!editingRole && !roleKey.trim()) || state.busy?.startsWith('role:') === true, children: editingRole ? t('organization.saveRole') : t('organization.createRole') }), editingRole ? ((0, jsx_runtime_1.jsx)("button", { type: "button", className: buttonSecondary, onClick: resetRoleForm, children: t('organization.cancel') })) : null] })] })) : null, tab === 'settings' ? ((0, jsx_runtime_1.jsx)("div", { "data-atlas": "organization-settings", children: confirm ? ((0, jsx_runtime_1.jsxs)("div", { role: "dialog", "data-atlas": `organization-confirm-${confirm}`, children: [(0, jsx_runtime_1.jsx)("p", { className: cls('text', 'atlas-text'), children: t(confirm === 'leave' ? 'organization.leaveConfirm' : 'organization.deleteConfirm', {
244
+ name: organization.name,
245
+ }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: buttonPrimary, disabled: busy('leave') || busy('delete'), onClick: () => void (confirm === 'leave' ? doLeave() : doDelete()), children: t('organization.confirm') }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: buttonSecondary, onClick: () => setConfirm(null), children: t('organization.cancel') })] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: buttonSecondary, onClick: () => setConfirm('leave'), children: t('organization.leave') }), isAdmin ? ((0, jsx_runtime_1.jsx)("button", { type: "button", className: buttonSecondary, onClick: () => setConfirm('delete'), children: t('organization.delete') })) : null] })) })) : null, reverification.needsReverification ? ((0, jsx_runtime_1.jsxs)("div", { "data-atlas": "organization-reverification", role: "dialog", "aria-label": t('reverification.title'), children: [(0, jsx_runtime_1.jsx)("p", { className: cls('text', 'atlas-text'), children: (0, jsx_runtime_1.jsx)("strong", { children: t('reverification.title') }) }), (0, jsx_runtime_1.jsx)("p", { className: cls('text', 'atlas-text'), children: t('reverification.subtitle') }), (0, jsx_runtime_1.jsxs)("label", { className: cls('formField', 'atlas-field'), children: [(0, jsx_runtime_1.jsx)("span", { className: cls('formFieldLabel', 'atlas-field-label'), children: t('reverification.passwordLabel') }), (0, jsx_runtime_1.jsx)("input", { className: cls('formFieldInput', 'atlas-account-input'), type: "password", autoComplete: "current-password", value: password, onChange: (event) => setPassword(event.target.value) })] }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: buttonPrimary, onClick: () => void reverification
246
+ .completeReverification({ strategy: 'password', password })
247
+ .then(() => setPassword(''))
248
+ .catch((err) => setLocalError(err instanceof Error ? errorText(err) : t('error.generic'))), children: t('reverification.submit') }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: buttonSecondary, onClick: () => {
249
+ setPassword('');
250
+ reverification.cancelReverification();
251
+ }, children: t('reverification.cancel') })] })) : null] }));
252
+ }
253
+ /**
254
+ * The active organization's audit trail, for org admins: one page at a time,
255
+ * newest first, with a "load more" cursor. `action` narrows it (exact, or a
256
+ * prefix ending in `.`). Returns `forbidden: true` when the signed-in member is
257
+ * not allowed to read it, so the caller can hide the surface.
258
+ */
259
+ function useOrganizationAuditLog(options = {}) {
260
+ const { organization } = (0, hooks_1.useOrganization)();
261
+ const client = (0, components_shared_1.useMeClient)();
262
+ const [entries, setEntries] = (0, react_1.useState)([]);
263
+ const [cursor, setCursor] = (0, react_1.useState)(null);
264
+ const [hasMore, setHasMore] = (0, react_1.useState)(false);
265
+ const [loading, setLoading] = (0, react_1.useState)(false);
266
+ const [forbidden, setForbidden] = (0, react_1.useState)(false);
267
+ const orgId = organization?.id;
268
+ const pageSize = options.pageSize ?? 20;
269
+ const action = options.action;
270
+ const fetchPage = (0, react_1.useCallback)(async (after) => {
271
+ if (!orgId)
272
+ return;
273
+ setLoading(true);
274
+ try {
275
+ const params = new URLSearchParams({ limit: String(pageSize) });
276
+ if (after)
277
+ params.set('starting_after', after);
278
+ if (action)
279
+ params.set('action', action);
280
+ const res = await client.get(`/v1/client/organizations/${encodeURIComponent(orgId)}/audit_logs?${params.toString()}`);
281
+ if (!res.ok || !res.data) {
282
+ setForbidden(res.status === 403 || res.status === 404);
283
+ return;
284
+ }
285
+ const page = res.data;
286
+ setForbidden(false);
287
+ setEntries((prev) => (after ? [...prev, ...page.data] : page.data));
288
+ setCursor(page.next_cursor);
289
+ setHasMore(page.has_more);
290
+ }
291
+ finally {
292
+ setLoading(false);
293
+ }
294
+ }, [client, orgId, pageSize, action]);
295
+ (0, react_1.useEffect)(() => {
296
+ setEntries([]);
297
+ setCursor(null);
298
+ void fetchPage(null);
299
+ }, [fetchPage]);
300
+ return {
301
+ entries,
302
+ loading,
303
+ forbidden,
304
+ hasMore,
305
+ loadMore: async () => {
306
+ if (cursor)
307
+ await fetchPage(cursor);
308
+ },
309
+ reload: () => fetchPage(null),
310
+ };
311
+ }
312
+ /** `organization_invitation.created` → `Organization invitation created`. */
313
+ function describeAction(action) {
314
+ const text = action.replace(/[._]+/g, ' ').trim();
315
+ return text.charAt(0).toUpperCase() + text.slice(1);
316
+ }
317
+ /**
318
+ * `<OrganizationAuditLog/>` — the active organization's audit trail for its
319
+ * admins, next to `<OrganizationProfile/>`. Renders nothing for a member who may
320
+ * not read it (the server is the authority; the component just follows it).
321
+ * Labels are props so an app can localise them.
322
+ */
323
+ function OrganizationAuditLog({ pageSize, action, labels = {}, formatDate = (ms) => new Date(ms).toLocaleString(), } = {}) {
324
+ const { organization } = (0, hooks_1.useOrganization)();
325
+ const log = useOrganizationAuditLog({ pageSize, action });
108
326
  const cardClass = (0, components_shared_1.useClass)('card', 'atlas-card');
109
327
  const cls = (0, components_shared_1.useClasses)();
110
- if (!organization)
328
+ if (!organization || log.forbidden)
111
329
  return null;
112
- return ((0, jsx_runtime_1.jsxs)("div", { className: cardClass, children: [(0, jsx_runtime_1.jsx)("h1", { className: cls('headerTitle', 'atlas-heading'), children: organization.name }), (0, jsx_runtime_1.jsxs)("p", { className: cls('headerSubtitle', 'atlas-subtitle'), children: [t('organization.roleLabel'), ": ", membership?.role] }), (0, jsx_runtime_1.jsx)(control_components_1.Protect, { permission: "org:sys_memberships:manage", children: (0, jsx_runtime_1.jsx)("button", { className: cls('formButtonSecondary', 'atlas-button-secondary'), children: t('organization.invite') }) })] }));
330
+ return ((0, jsx_runtime_1.jsxs)("div", { className: cardClass, "data-atlas": "organization-audit-log", children: [(0, jsx_runtime_1.jsx)("h2", { className: cls('headerTitle', 'atlas-title'), children: labels.title ?? 'Audit log' }), log.entries.length === 0 && !log.loading ? ((0, jsx_runtime_1.jsx)("p", { className: cls('headerSubtitle', 'atlas-subtitle'), children: labels.empty ?? 'No activity recorded yet.' })) : ((0, jsx_runtime_1.jsx)("ul", { className: cls('list', 'atlas-audit-list'), children: log.entries.map((entry) => ((0, jsx_runtime_1.jsxs)("li", { className: cls('listItem', 'atlas-audit-item'), "data-action": entry.action, children: [(0, jsx_runtime_1.jsx)("strong", { children: describeAction(entry.action) }), (0, jsx_runtime_1.jsxs)("span", { className: "atlas-audit-meta", children: [' · ', formatDate(entry.created_at), entry.actor.id ? ` · ${labels.actor ?? 'by'} ${entry.actor.type} ${entry.actor.id}` : ''] })] }, entry.id))) })), log.hasMore ? ((0, jsx_runtime_1.jsx)("button", { className: cls('formButtonSecondary', 'atlas-button-secondary'), disabled: log.loading, onClick: () => void log.loadMore(), children: labels.loadMore ?? 'Load more' })) : null] }));
113
331
  }
114
332
  //# sourceMappingURL=organization.js.map