@carlonicora/nextjs-jsonapi 6.5.0 → 6.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{BlockNoteEditor-CT4XUXPC.js → BlockNoteEditor-BSFR2GPI.js} +19 -19
- package/dist/{BlockNoteEditor-CT4XUXPC.js.map → BlockNoteEditor-BSFR2GPI.js.map} +1 -1
- package/dist/{BlockNoteEditor-M6UQRVRK.mjs → BlockNoteEditor-HWJBQAKG.mjs} +4 -4
- package/dist/{BlockNoteViewer-XZJ5Y7WN.mjs → BlockNoteViewer-IIBUU7J7.mjs} +2 -2
- package/dist/{BlockNoteViewer-MFLWF3Y2.js → BlockNoteViewer-NHS6CCMS.js} +3 -3
- package/dist/{BlockNoteViewer-MFLWF3Y2.js.map → BlockNoteViewer-NHS6CCMS.js.map} +1 -1
- package/dist/{auth.interface-D5606g19.d.ts → auth.interface-B4tRE0k0.d.ts} +2 -0
- package/dist/{auth.interface-tsOuc0Fi.d.mts → auth.interface-DuX87BZ3.d.mts} +2 -0
- package/dist/billing/index.js +492 -492
- package/dist/billing/index.mjs +3 -3
- package/dist/{chunk-DSGP6GC3.js → chunk-3HOJNAZB.js} +8 -8
- package/dist/{chunk-DSGP6GC3.js.map → chunk-3HOJNAZB.js.map} +1 -1
- package/dist/{chunk-XZCCIKX3.js → chunk-6KE7UBYY.js} +504 -233
- package/dist/chunk-6KE7UBYY.js.map +1 -0
- package/dist/{chunk-QJIDORHM.js → chunk-73XWQA6U.js} +4 -4
- package/dist/{chunk-QJIDORHM.js.map → chunk-73XWQA6U.js.map} +1 -1
- package/dist/{chunk-DYOSXIEL.mjs → chunk-EC4PQ44E.mjs} +2 -2
- package/dist/{chunk-RUJCWOJE.mjs → chunk-OYRJ32KR.mjs} +2 -2
- package/dist/{chunk-Y7JIQNM6.js → chunk-PCCUPEBU.js} +1066 -873
- package/dist/chunk-PCCUPEBU.js.map +1 -0
- package/dist/{chunk-ELQHE2DK.js → chunk-PYDDFFQM.js} +3 -3
- package/dist/{chunk-ELQHE2DK.js.map → chunk-PYDDFFQM.js.map} +1 -1
- package/dist/{chunk-3FD6Y734.mjs → chunk-QNIWATQD.mjs} +272 -1
- package/dist/chunk-QNIWATQD.mjs.map +1 -0
- package/dist/{chunk-TIT4CJYQ.mjs → chunk-S4NKWMX5.mjs} +3729 -3536
- package/dist/chunk-S4NKWMX5.mjs.map +1 -0
- package/dist/{chunk-O2TEUH5S.mjs → chunk-XYKDBOCE.mjs} +2 -2
- package/dist/client/index.js +4 -4
- package/dist/client/index.mjs +3 -3
- package/dist/components/index.d.mts +39 -4
- package/dist/components/index.d.ts +39 -4
- package/dist/components/index.js +12 -4
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +11 -3
- package/dist/contexts/index.js +4 -4
- package/dist/contexts/index.mjs +3 -3
- package/dist/core/index.d.mts +144 -5
- package/dist/core/index.d.ts +144 -5
- package/dist/core/index.js +18 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +17 -1
- package/dist/email-automations/index.js +92 -92
- package/dist/email-automations/index.mjs +3 -3
- package/dist/features/analytics/index.js +121 -121
- package/dist/features/analytics/index.mjs +4 -4
- package/dist/features/analytics/server.js +4 -4
- package/dist/features/analytics/server.mjs +2 -2
- package/dist/features/blog/index.js +150 -150
- package/dist/features/blog/index.mjs +3 -3
- package/dist/features/blog/server.js +16 -16
- package/dist/features/blog/server.mjs +1 -1
- package/dist/features/help/index.js +37 -37
- package/dist/features/help/index.mjs +3 -3
- package/dist/features/tokenusage/index.js +127 -127
- package/dist/features/tokenusage/index.mjs +4 -4
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +19 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +18 -2
- package/dist/{s3.service-CzFBDEuQ.d.mts → s3.service-CRGJbO-6.d.mts} +18 -2
- package/dist/{s3.service-BTCpmp5g.d.ts → s3.service-DZG-VyLY.d.ts} +18 -2
- package/dist/server/index.d.mts +2 -2
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +3 -3
- package/dist/server/index.mjs +2 -2
- package/messages/en.json +24 -0
- package/messages/it.json +24 -0
- package/package.json +1 -1
- package/src/components/index.ts +1 -0
- package/src/core/index.ts +6 -0
- package/src/core/registry/ModuleRegistry.ts +15 -0
- package/src/features/account-deletion/account-deletion.module.ts +13 -0
- package/src/features/account-deletion/components/AccountDeletionAdminTrigger.tsx +183 -0
- package/src/features/account-deletion/components/AccountDeletionCancelledNotice.tsx +51 -0
- package/src/features/account-deletion/components/AccountDeletionKeepContainer.tsx +74 -0
- package/src/features/account-deletion/components/index.ts +3 -0
- package/src/features/account-deletion/data/__tests__/AccountDeletion.spec.ts +89 -0
- package/src/features/account-deletion/data/account-deletion.interface.ts +22 -0
- package/src/features/account-deletion/data/account-deletion.service.ts +69 -0
- package/src/features/account-deletion/data/account-deletion.ts +90 -0
- package/src/features/account-deletion/data/index.ts +3 -0
- package/src/features/account-deletion/index.ts +4 -0
- package/src/features/account-deletion/utils/__tests__/account-deletion-notice.spec.ts +40 -0
- package/src/features/account-deletion/utils/account-deletion-notice.ts +27 -0
- package/src/features/account-deletion/utils/index.ts +1 -0
- package/src/features/auth/components/forms/Cookies.tsx +3 -0
- package/src/features/auth/components/forms/TwoFactorChallenge.tsx +2 -0
- package/src/features/auth/data/__tests__/auth.spec.ts +36 -0
- package/src/features/auth/data/__tests__/pending-registration.spec.ts +37 -0
- package/src/features/auth/data/auth.interface.ts +2 -0
- package/src/features/auth/data/auth.service.ts +23 -0
- package/src/features/auth/data/auth.ts +7 -0
- package/src/features/auth/data/index.ts +2 -0
- package/src/features/auth/data/pending-registration.interface.ts +10 -0
- package/src/features/auth/data/pending-registration.ts +57 -0
- package/src/features/auth/pending-registration.module.ts +9 -0
- package/src/features/user/contexts/UserContext.tsx +8 -0
- package/dist/chunk-3FD6Y734.mjs.map +0 -1
- package/dist/chunk-TIT4CJYQ.mjs.map +0 -1
- package/dist/chunk-XZCCIKX3.js.map +0 -1
- package/dist/chunk-Y7JIQNM6.js.map +0 -1
- /package/dist/{BlockNoteEditor-M6UQRVRK.mjs.map → BlockNoteEditor-HWJBQAKG.mjs.map} +0 -0
- /package/dist/{BlockNoteViewer-XZJ5Y7WN.mjs.map → BlockNoteViewer-IIBUU7J7.mjs.map} +0 -0
- /package/dist/{chunk-DYOSXIEL.mjs.map → chunk-EC4PQ44E.mjs.map} +0 -0
- /package/dist/{chunk-RUJCWOJE.mjs.map → chunk-OYRJ32KR.mjs.map} +0 -0
- /package/dist/{chunk-O2TEUH5S.mjs.map → chunk-XYKDBOCE.mjs.map} +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { consumeAccountDeletionCancelled, markAccountDeletionCancelled } from "../account-deletion-notice";
|
|
3
|
+
|
|
4
|
+
describe("account-deletion notice flag", () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
window.sessionStorage.clear();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
vi.unstubAllGlobals();
|
|
11
|
+
window.sessionStorage.clear();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("consume returns true once after mark, then false", () => {
|
|
15
|
+
expect(consumeAccountDeletionCancelled()).toBe(false);
|
|
16
|
+
|
|
17
|
+
markAccountDeletionCancelled();
|
|
18
|
+
|
|
19
|
+
expect(consumeAccountDeletionCancelled()).toBe(true);
|
|
20
|
+
expect(consumeAccountDeletionCancelled()).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("survives a throwing sessionStorage", () => {
|
|
24
|
+
const throwing = {
|
|
25
|
+
getItem: () => {
|
|
26
|
+
throw new Error("blocked");
|
|
27
|
+
},
|
|
28
|
+
setItem: () => {
|
|
29
|
+
throw new Error("blocked");
|
|
30
|
+
},
|
|
31
|
+
removeItem: () => {
|
|
32
|
+
throw new Error("blocked");
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
vi.stubGlobal("sessionStorage", throwing);
|
|
36
|
+
|
|
37
|
+
expect(() => markAccountDeletionCancelled()).not.toThrow();
|
|
38
|
+
expect(consumeAccountDeletionCancelled()).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-shot flag between the login that cancelled a pending account deletion and
|
|
3
|
+
* the first page after it, which shows AccountDeletionCancelledNotice. Kept in
|
|
4
|
+
* sessionStorage so it survives the redirect (and the full reload of the
|
|
5
|
+
* Discord / Google path) but not the tab. Storage can be missing or throw
|
|
6
|
+
* (private mode, blocked site data): the notice is then simply not shown.
|
|
7
|
+
*/
|
|
8
|
+
export const ACCOUNT_DELETION_CANCELLED_KEY = "nja:accountDeletionCancelled";
|
|
9
|
+
|
|
10
|
+
export function markAccountDeletionCancelled(): void {
|
|
11
|
+
try {
|
|
12
|
+
sessionStorage.setItem(ACCOUNT_DELETION_CANCELLED_KEY, "1");
|
|
13
|
+
} catch {
|
|
14
|
+
// Storage unavailable: no notice.
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function consumeAccountDeletionCancelled(): boolean {
|
|
19
|
+
try {
|
|
20
|
+
const value = sessionStorage.getItem(ACCOUNT_DELETION_CANCELLED_KEY);
|
|
21
|
+
if (value === null) return false;
|
|
22
|
+
sessionStorage.removeItem(ACCOUNT_DELETION_CANCELLED_KEY);
|
|
23
|
+
return true;
|
|
24
|
+
} catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./account-deletion-notice";
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { useEffect, useState } from "react";
|
|
4
4
|
import { JsonApiHydratedDataInterface, Modules, rehydrate } from "../../../../core";
|
|
5
5
|
import { useI18nRouter } from "../../../../i18n";
|
|
6
|
+
import { markAccountDeletionCancelled } from "../../../account-deletion/utils/account-deletion-notice";
|
|
6
7
|
import { UserInterface } from "../../../user";
|
|
7
8
|
import { useCurrentUserContext } from "../../../user/contexts";
|
|
8
9
|
import { AuthInterface } from "../../data";
|
|
@@ -18,6 +19,8 @@ export function Cookies({ dehydratedAuth, page }: { dehydratedAuth: JsonApiHydra
|
|
|
18
19
|
async function saveTokenOnServer() {
|
|
19
20
|
await AuthService.saveToken({ dehydratedAuth });
|
|
20
21
|
const auth: AuthInterface = rehydrate(Modules.Auth, dehydratedAuth) as AuthInterface;
|
|
22
|
+
// Discord / Google path: the flag is on the Auth document from the code exchange.
|
|
23
|
+
if (auth.deletionCancelled) markAccountDeletionCancelled();
|
|
21
24
|
setUser(auth.user as UserInterface);
|
|
22
25
|
setHasSaved(true);
|
|
23
26
|
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
TabsList,
|
|
19
19
|
TabsTrigger,
|
|
20
20
|
} from "../../../../shadcnui";
|
|
21
|
+
import { markAccountDeletionCancelled } from "../../../account-deletion/utils/account-deletion-notice";
|
|
21
22
|
import { UserInterface } from "../../../user";
|
|
22
23
|
import { useCurrentUserContext } from "../../../user/contexts";
|
|
23
24
|
import { useAuthContext } from "../../contexts";
|
|
@@ -46,6 +47,7 @@ export function TwoFactorChallenge() {
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
const handleSuccess = (auth: AuthInterface) => {
|
|
50
|
+
if (auth.deletionCancelled) markAccountDeletionCancelled();
|
|
49
51
|
setPendingTwoFactor(undefined);
|
|
50
52
|
setUser(auth.user);
|
|
51
53
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { beforeAll, describe, expect, it } from "vitest";
|
|
2
|
+
import { ApiRequestDataTypeInterface } from "../../../../core/interfaces/ApiRequestDataTypeInterface";
|
|
3
|
+
import { ModuleRegistry } from "../../../../core/registry/ModuleRegistry";
|
|
4
|
+
import { Auth } from "../auth";
|
|
5
|
+
|
|
6
|
+
// Auth.rehydrate() resolves Modules.User through the real ModuleRegistry (the
|
|
7
|
+
// argument of `_readIncluded`), so it must be registered even though these
|
|
8
|
+
// fixtures carry no included user.
|
|
9
|
+
const registerIfAbsent = (key: string, module: ApiRequestDataTypeInterface) => {
|
|
10
|
+
try {
|
|
11
|
+
ModuleRegistry.get(key as any);
|
|
12
|
+
} catch {
|
|
13
|
+
ModuleRegistry.register(key, module);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
beforeAll(() => {
|
|
18
|
+
registerIfAbsent("Auth", { name: "auth", model: Auth } as any);
|
|
19
|
+
registerIfAbsent("User", { name: "users", model: class {} } as any);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const rehydrateWith = (attributes: Record<string, any>) =>
|
|
23
|
+
new Auth().rehydrate({
|
|
24
|
+
jsonApi: { type: "auth", id: "auth-1", attributes: { token: "t", refreshToken: "r", ...attributes } },
|
|
25
|
+
included: [],
|
|
26
|
+
} as any);
|
|
27
|
+
|
|
28
|
+
describe("Auth", () => {
|
|
29
|
+
describe("rehydrate", () => {
|
|
30
|
+
it("deletionCancelled true when the attribute is true, false otherwise", () => {
|
|
31
|
+
expect(rehydrateWith({ deletionCancelled: true }).deletionCancelled).toBe(true);
|
|
32
|
+
expect(rehydrateWith({ deletionCancelled: false }).deletionCancelled).toBe(false);
|
|
33
|
+
expect(rehydrateWith({}).deletionCancelled).toBe(false);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { PendingRegistration } from "../pending-registration";
|
|
3
|
+
|
|
4
|
+
const hydrate = (attributes: Record<string, any>) =>
|
|
5
|
+
new PendingRegistration().rehydrate({
|
|
6
|
+
jsonApi: { type: "pending-registrations", id: "pending-1", attributes },
|
|
7
|
+
included: [],
|
|
8
|
+
} as any);
|
|
9
|
+
|
|
10
|
+
describe("PendingRegistration.rehydrate()", () => {
|
|
11
|
+
it("rehydrates every attribute", () => {
|
|
12
|
+
const pending = hydrate({
|
|
13
|
+
name: "Aria",
|
|
14
|
+
email: "aria@example.com",
|
|
15
|
+
avatar: "https://cdn.example.com/aria.png",
|
|
16
|
+
provider: "discord",
|
|
17
|
+
hasPass: true,
|
|
18
|
+
emailInUse: true,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
expect(pending.id).toBe("pending-1");
|
|
22
|
+
expect(pending.name).toBe("Aria");
|
|
23
|
+
expect(pending.email).toBe("aria@example.com");
|
|
24
|
+
expect(pending.avatar).toBe("https://cdn.example.com/aria.png");
|
|
25
|
+
expect(pending.provider).toBe("discord");
|
|
26
|
+
expect(pending.hasPass).toBe(true);
|
|
27
|
+
expect(pending.emailInUse).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("leaves avatar undefined and defaults hasPass/emailInUse to false when absent", () => {
|
|
31
|
+
const pending = hydrate({ name: "Aria", email: "aria@example.com", provider: "google" });
|
|
32
|
+
|
|
33
|
+
expect(pending.avatar).toBeUndefined();
|
|
34
|
+
expect(pending.hasPass).toBe(false);
|
|
35
|
+
expect(pending.emailInUse).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -35,4 +35,6 @@ export interface AuthInterface extends ApiDataInterface {
|
|
|
35
35
|
get token(): string;
|
|
36
36
|
get refreshToken(): string;
|
|
37
37
|
get user(): UserInterface;
|
|
38
|
+
/** True on the session created by the login that cancelled a pending account deletion. */
|
|
39
|
+
get deletionCancelled(): boolean;
|
|
38
40
|
}
|
|
@@ -9,8 +9,10 @@ import {
|
|
|
9
9
|
rehydrate,
|
|
10
10
|
} from "../../../core";
|
|
11
11
|
import { JsonApiDelete, JsonApiGet, JsonApiPost } from "../../../unified";
|
|
12
|
+
import { markAccountDeletionCancelled } from "../../account-deletion/utils/account-deletion-notice";
|
|
12
13
|
import { UserInterface } from "../../user";
|
|
13
14
|
import { getTokenHandler } from "../config";
|
|
15
|
+
import { PendingRegistrationInterface } from "./pending-registration.interface";
|
|
14
16
|
import { TwoFactorChallengeInterface } from "./two-factor-challenge.interface";
|
|
15
17
|
|
|
16
18
|
export class AuthService extends AbstractService {
|
|
@@ -44,6 +46,9 @@ export class AuthService extends AbstractService {
|
|
|
44
46
|
// Normal auth response
|
|
45
47
|
const auth = apiResponse.data as AuthInterface;
|
|
46
48
|
|
|
49
|
+
// Login returns only the user to Login.tsx, so the flag is read here.
|
|
50
|
+
if (auth.deletionCancelled) markAccountDeletionCancelled();
|
|
51
|
+
|
|
47
52
|
// Use injected token handler if configured
|
|
48
53
|
const handler = getTokenHandler();
|
|
49
54
|
if (handler) {
|
|
@@ -172,6 +177,24 @@ export class AuthService extends AbstractService {
|
|
|
172
177
|
}
|
|
173
178
|
}
|
|
174
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Reads an OAuth sign-up waiting for consent. Throws an Error carrying
|
|
182
|
+
* `status` (404 when the pending registration expired); callers catch it.
|
|
183
|
+
*/
|
|
184
|
+
static async findPendingRegistration(params: { pendingId: string }): Promise<PendingRegistrationInterface> {
|
|
185
|
+
return this.callApi<PendingRegistrationInterface>({
|
|
186
|
+
type: Modules.PendingRegistration,
|
|
187
|
+
method: HttpMethod.GET,
|
|
188
|
+
endpoint: new EndpointCreator({
|
|
189
|
+
endpoint: Modules.Auth,
|
|
190
|
+
id: "oauth",
|
|
191
|
+
childEndpoint: Modules.PendingRegistration,
|
|
192
|
+
childId: params.pendingId,
|
|
193
|
+
}).generate(),
|
|
194
|
+
suppressGlobalError: true,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
175
198
|
static async completeOAuthRegistration(params: {
|
|
176
199
|
pendingId: string;
|
|
177
200
|
termsAcceptedAt: string;
|
|
@@ -6,6 +6,12 @@ export class Auth extends AbstractApiData implements AuthInterface {
|
|
|
6
6
|
private _token?: string;
|
|
7
7
|
private _refreshToken?: string;
|
|
8
8
|
private _user?: UserInterface;
|
|
9
|
+
private _deletionCancelled: boolean = false;
|
|
10
|
+
|
|
11
|
+
/** True on the session created by the login that cancelled a pending account deletion. */
|
|
12
|
+
get deletionCancelled(): boolean {
|
|
13
|
+
return this._deletionCancelled;
|
|
14
|
+
}
|
|
9
15
|
|
|
10
16
|
get token(): string {
|
|
11
17
|
if (!this._token) throw new Error("Token is not defined");
|
|
@@ -27,6 +33,7 @@ export class Auth extends AbstractApiData implements AuthInterface {
|
|
|
27
33
|
|
|
28
34
|
this._token = data.jsonApi.attributes.token ?? undefined;
|
|
29
35
|
this._refreshToken = data.jsonApi.attributes.refreshToken ?? undefined;
|
|
36
|
+
this._deletionCancelled = data.jsonApi.attributes.deletionCancelled === true;
|
|
30
37
|
|
|
31
38
|
this._user = this._readIncluded(data, "user", Modules.User) as UserInterface;
|
|
32
39
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ApiDataInterface } from "../../../core";
|
|
2
|
+
|
|
3
|
+
export interface PendingRegistrationInterface extends ApiDataInterface {
|
|
4
|
+
get name(): string;
|
|
5
|
+
get email(): string;
|
|
6
|
+
get avatar(): string | undefined;
|
|
7
|
+
get provider(): string;
|
|
8
|
+
get hasPass(): boolean;
|
|
9
|
+
get emailInUse(): boolean;
|
|
10
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { AbstractApiData, JsonApiHydratedDataInterface, Modules } from "../../../core";
|
|
2
|
+
import { PendingRegistrationInterface } from "./pending-registration.interface";
|
|
3
|
+
|
|
4
|
+
export class PendingRegistration extends AbstractApiData implements PendingRegistrationInterface {
|
|
5
|
+
private _name?: string;
|
|
6
|
+
private _email?: string;
|
|
7
|
+
private _avatar?: string;
|
|
8
|
+
private _provider?: string;
|
|
9
|
+
private _hasPass?: boolean;
|
|
10
|
+
private _emailInUse?: boolean;
|
|
11
|
+
|
|
12
|
+
get name(): string {
|
|
13
|
+
return this._name ?? "";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get email(): string {
|
|
17
|
+
return this._email ?? "";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get avatar(): string | undefined {
|
|
21
|
+
return this._avatar;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get provider(): string {
|
|
25
|
+
return this._provider ?? "";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get hasPass(): boolean {
|
|
29
|
+
return this._hasPass ?? false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get emailInUse(): boolean {
|
|
33
|
+
return this._emailInUse ?? false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
rehydrate(data: JsonApiHydratedDataInterface): this {
|
|
37
|
+
super.rehydrate(data);
|
|
38
|
+
this._name = data.jsonApi.attributes.name;
|
|
39
|
+
this._email = data.jsonApi.attributes.email;
|
|
40
|
+
this._avatar = data.jsonApi.attributes.avatar ?? undefined;
|
|
41
|
+
this._provider = data.jsonApi.attributes.provider;
|
|
42
|
+
this._hasPass = data.jsonApi.attributes.hasPass ?? false;
|
|
43
|
+
this._emailInUse = data.jsonApi.attributes.emailInUse ?? false;
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Read-only resource: the payload only identifies it. */
|
|
48
|
+
createJsonApi(data: { id: string }) {
|
|
49
|
+
return {
|
|
50
|
+
data: {
|
|
51
|
+
type: Modules.PendingRegistration.name,
|
|
52
|
+
id: data.id,
|
|
53
|
+
attributes: {},
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ModuleFactory } from "../../permissions";
|
|
2
|
+
import { PendingRegistration } from "./data/pending-registration";
|
|
3
|
+
|
|
4
|
+
export const PendingRegistrationModule = (factory: ModuleFactory) =>
|
|
5
|
+
factory({
|
|
6
|
+
name: "pending-registrations",
|
|
7
|
+
pageUrl: "/auth/consent",
|
|
8
|
+
model: PendingRegistration,
|
|
9
|
+
});
|
|
@@ -8,6 +8,10 @@ import { usePageUrlGenerator } from "../../../hooks";
|
|
|
8
8
|
import { BreadcrumbItemData } from "../../../interfaces";
|
|
9
9
|
import { Action } from "../../../permissions";
|
|
10
10
|
import { getRoleId } from "../../../roles";
|
|
11
|
+
import {
|
|
12
|
+
AccountDeletionAdminTrigger,
|
|
13
|
+
isAccountDeletionEnabled,
|
|
14
|
+
} from "../../account-deletion/components/AccountDeletionAdminTrigger";
|
|
11
15
|
import { UserDeleter, UserEditor, UserReactivator, UserResentInvitationEmail } from "../components/forms";
|
|
12
16
|
import { UserInterface } from "../data";
|
|
13
17
|
import { useCurrentUserContext } from "./CurrentUserContext";
|
|
@@ -83,6 +87,10 @@ export const UserProvider = ({ children, dehydratedUser }: UserProviderProps) =>
|
|
|
83
87
|
functions.push(<UserResentInvitationEmail key={`UserResentInvitationEmail`} user={user} />);
|
|
84
88
|
|
|
85
89
|
functions.push(<UserDeleter key={`UserDeleter`} user={user} />);
|
|
90
|
+
|
|
91
|
+
// Opt-in: only apps that registered AccountDeletionModule, platform administrators only.
|
|
92
|
+
if (hasRole(getRoleId().Administrator) && isAccountDeletionEnabled())
|
|
93
|
+
functions.push(<AccountDeletionAdminTrigger key={`AccountDeletionAdminTrigger`} user={user} />);
|
|
86
94
|
}
|
|
87
95
|
|
|
88
96
|
functions.push(<UserEditor key={`UserEditor`} user={user} propagateChanges={setUser} />);
|