@redacto.io/consent-sdk-react 10.0.0-beta.1 → 10.0.0-beta.11
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/.turbo/turbo-build.log +11 -11
- package/CHANGELOG.md +109 -0
- package/README.md +78 -0
- package/dist/{chunk-6A665OPR.mjs → chunk-OGL6OC3A.mjs} +4 -0
- package/dist/index.d.mts +32 -4
- package/dist/index.d.ts +32 -4
- package/dist/index.js +340 -230
- package/dist/index.mjs +339 -231
- package/dist/privacy-center.d.mts +55 -12
- package/dist/privacy-center.d.ts +55 -12
- package/dist/privacy-center.js +848 -950
- package/dist/privacy-center.mjs +698 -802
- package/package.json +1 -1
- package/src/RedactoNoticeAssisted/RedactoNoticeAssisted.test.tsx +87 -0
- package/src/RedactoNoticeAssisted/RedactoNoticeAssisted.tsx +27 -0
- package/src/RedactoNoticeAssisted/api/index.ts +7 -1
- package/src/RedactoNoticeAssisted/api/types.ts +2 -0
- package/src/RedactoNoticeAssisted/i18n.ts +0 -55
- package/src/RedactoNoticeAssisted/types.ts +2 -0
- package/src/RedactoNoticeConsent/RedactoNoticeConsent.test.tsx +42 -8
- package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +74 -33
- package/src/RedactoNoticeConsent/api/index.ts +88 -175
- package/src/RedactoNoticeConsent/api/sandbox.ts +150 -0
- package/src/RedactoNoticeConsent/api/types.ts +58 -43
- package/src/RedactoNoticeConsent/language-codes.ts +0 -2
- package/src/RedactoNoticeConsent/types.ts +28 -2
- package/src/RedactoNoticeConsentInline/RedactoNoticeConsentInline.tsx +85 -5
- package/src/RedactoNoticeConsentInline/api/index.ts +10 -1
- package/src/RedactoNoticeConsentInline/api/notice-read-routing.test.ts +72 -0
- package/src/RedactoNoticeConsentInline/api/types.ts +2 -0
- package/src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx +37 -2
- package/src/RedactoPrivacyCenter/api/actions.ts +3 -1
- package/src/RedactoPrivacyCenter/api/client.ts +122 -25
- package/src/RedactoPrivacyCenter/api/fetcher.ts +24 -9
- package/src/RedactoPrivacyCenter/components/AppShell/AppShell.tsx +28 -0
- package/src/RedactoPrivacyCenter/components/AppShell/UserMenu.tsx +47 -45
- package/src/RedactoPrivacyCenter/components/ConsentManager.tsx +2 -1
- package/src/RedactoPrivacyCenter/components/Form.tsx +55 -30
- package/src/RedactoPrivacyCenter/components/ModifyConsentModal.tsx +1 -1
- package/src/RedactoPrivacyCenter/components/SelectUser.tsx +25 -13
- package/src/RedactoPrivacyCenter/components/SelectUserGate.tsx +8 -6
- package/src/RedactoPrivacyCenter/components/constant.ts +3 -3
- package/src/RedactoPrivacyCenter/components/groupHelpers.ts +42 -6
- package/src/RedactoPrivacyCenter/context/AuthContext.tsx +131 -39
- package/src/RedactoPrivacyCenter/context/types.ts +38 -0
- package/src/RedactoPrivacyCenter/lib/api.ts +3 -0
- package/src/RedactoPrivacyCenter/lib/constants.ts +12 -6
- package/src/RedactoPrivacyCenter/lib/locales.ts +0 -3
- package/src/RedactoPrivacyCenter/lib/sandbox.ts +104 -0
- package/src/RedactoPrivacyCenter/lib/types.ts +25 -1
- package/src/RedactoPrivacyCenter/lib/utils.ts +15 -0
- package/src/RedactoPrivacyCenter/locales/as/translation.json +3 -3
- package/src/RedactoPrivacyCenter/locales/bn/translation.json +3 -3
- package/src/RedactoPrivacyCenter/locales/brx/translation.json +3 -3
- package/src/RedactoPrivacyCenter/locales/doi/translation.json +3 -3
- package/src/RedactoPrivacyCenter/locales/en/translation.json +4 -3
- package/src/RedactoPrivacyCenter/locales/gom/translation.json +3 -3
- package/src/RedactoPrivacyCenter/locales/gu/translation.json +3 -3
- package/src/RedactoPrivacyCenter/locales/hi/translation.json +3 -3
- package/src/RedactoPrivacyCenter/locales/kn/translation.json +3 -3
- package/src/RedactoPrivacyCenter/locales/ks/translation.json +3 -3
- package/src/RedactoPrivacyCenter/locales/mai/translation.json +3 -3
- package/src/RedactoPrivacyCenter/locales/ml/translation.json +3 -3
- package/src/RedactoPrivacyCenter/locales/mni-Mtei/translation.json +3 -3
- package/src/RedactoPrivacyCenter/locales/mr/translation.json +3 -3
- package/src/RedactoPrivacyCenter/locales/ne/translation.json +3 -3
- package/src/RedactoPrivacyCenter/locales/or/translation.json +3 -3
- package/src/RedactoPrivacyCenter/locales/pa/translation.json +3 -3
- package/src/RedactoPrivacyCenter/locales/sa/translation.json +3 -3
- package/src/RedactoPrivacyCenter/locales/sat/translation.json +3 -3
- package/src/RedactoPrivacyCenter/locales/sd/translation.json +3 -3
- package/src/RedactoPrivacyCenter/locales/ta/translation.json +3 -3
- package/src/RedactoPrivacyCenter/locales/te/translation.json +3 -3
- package/src/RedactoPrivacyCenter/locales/ur/translation.json +3 -3
- package/src/RedactoPrivacyCenter/styles/injectStyles.ts +7 -2
- package/src/RedactoPrivacyCenter/styles/pcStyles.ts +17 -0
- package/src/RedactoPrivacyCenter/ui/PCAvatar.test.tsx +83 -0
- package/src/RedactoPrivacyCenter/ui/PCAvatar.tsx +33 -9
- package/src/shared/notice-consent-status.ts +190 -0
- package/src/shared/sandbox.ts +6 -0
- package/tests/Form.test.tsx +142 -5
- package/tests/RevokeWarningMessage.test.tsx +151 -0
- package/tests/consentClient.test.ts +46 -0
- package/src/RedactoPrivacyCenter/locales/bho/translation.json +0 -340
|
@@ -6,7 +6,6 @@ export const LANGUAGE_NATIVE_LABELS: Record<string, string> = {
|
|
|
6
6
|
English: "English",
|
|
7
7
|
Assamese: "অসমীয়া (Assamese)",
|
|
8
8
|
Bengali: "বাংলা (Bengali)",
|
|
9
|
-
Bhojpuri: "भोजपुरी (Bhojpuri)",
|
|
10
9
|
Bodo: "बड़ो (Bodo)",
|
|
11
10
|
Dogri: "डोगरी (Dogri)",
|
|
12
11
|
Gujarati: "ગુજરાતી (Gujarati)",
|
|
@@ -44,7 +43,6 @@ const LANGUAGE_NAME_TO_BCP47: Record<string, string> = {
|
|
|
44
43
|
English: "en",
|
|
45
44
|
Assamese: "as",
|
|
46
45
|
Bengali: "bn",
|
|
47
|
-
Bhojpuri: "bho",
|
|
48
46
|
Bodo: "brx",
|
|
49
47
|
Dogri: "doi",
|
|
50
48
|
Gujarati: "gu",
|
|
@@ -2,8 +2,34 @@ import type { Settings, DpoInfoTranslation } from "./api/types";
|
|
|
2
2
|
|
|
3
3
|
export type Props = Readonly<{
|
|
4
4
|
noticeId: string;
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* JWT access token. Optional so sandbox integrations (see `token`) can
|
|
7
|
+
* omit it. Required in the normal JWT flow.
|
|
8
|
+
*/
|
|
9
|
+
accessToken?: string;
|
|
10
|
+
/** JWT refresh token. Optional; accepted for source-compat, currently unused. */
|
|
11
|
+
refreshToken?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Sandbox PoC: a pasted static token. When set, the SDK runs against the
|
|
14
|
+
* sandbox with no JWT — it sends the single `X-Consent-Token` header, carries
|
|
15
|
+
* the acting identity (`email` || `mobile`) in each request's own payload,
|
|
16
|
+
* takes org/workspace from `organisationUuid`/`workspaceUuid`, and does no
|
|
17
|
+
* token refresh.
|
|
18
|
+
*/
|
|
19
|
+
token?: string;
|
|
20
|
+
/** Sandbox test subject email (carried in the request payload when set). */
|
|
21
|
+
email?: string;
|
|
22
|
+
/** Sandbox test subject mobile (fallback subject when `email` is absent). */
|
|
23
|
+
mobile?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Sandbox UCIC: the client's own user id / org_user_id, e.g. a UUID or
|
|
26
|
+
* customer code; optional third identifier alongside email/mobile.
|
|
27
|
+
*/
|
|
28
|
+
ucic?: string;
|
|
29
|
+
/** Sandbox org UUID (replaces the JWT-decoded org in sandbox mode). */
|
|
30
|
+
organisationUuid?: string;
|
|
31
|
+
/** Sandbox workspace UUID (replaces the JWT-decoded workspace in sandbox mode). */
|
|
32
|
+
workspaceUuid?: string;
|
|
7
33
|
baseUrl?: string;
|
|
8
34
|
ledgerBaseUrl?: string;
|
|
9
35
|
settings?: Partial<Settings>;
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { useEffect, useMemo, useState, useRef, useCallback, memo } from "react";
|
|
2
2
|
import { fetchConsentContent, submitConsentEvent } from "./api";
|
|
3
|
+
import {
|
|
4
|
+
fetchNoticeConsentStatus,
|
|
5
|
+
isNoticeFullyConsented,
|
|
6
|
+
} from "../shared/notice-consent-status";
|
|
3
7
|
import type { ConsentContent } from "./api/types";
|
|
4
8
|
import type { Props, TranslationObject } from "./types";
|
|
5
9
|
import { styles } from "./styles";
|
|
@@ -36,6 +40,14 @@ const RedactoConsentInlineComponent = ({
|
|
|
36
40
|
Record<string, boolean>
|
|
37
41
|
>({});
|
|
38
42
|
const [hasAlreadyConsented, setHasAlreadyConsented] = useState(false);
|
|
43
|
+
// When a ledger is configured, block the auto-submit effect until the ledger
|
|
44
|
+
// check-consent read settles, so an already-consented principal is not
|
|
45
|
+
// re-submitted. Starts true only when a ledger check will actually run
|
|
46
|
+
// (skipped for the magic-link specific_uuid flow); stays false otherwise so
|
|
47
|
+
// the legacy Python-409 path is unaffected.
|
|
48
|
+
const [isCheckingConsent, setIsCheckingConsent] = useState<boolean>(
|
|
49
|
+
Boolean(ledgerBaseUrl) && !applicationId
|
|
50
|
+
);
|
|
39
51
|
const [fetchError, setFetchError] = useState<Error | null>(null);
|
|
40
52
|
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
|
41
53
|
|
|
@@ -149,18 +161,26 @@ const RedactoConsentInlineComponent = ({
|
|
|
149
161
|
return defaultText;
|
|
150
162
|
};
|
|
151
163
|
|
|
152
|
-
const fetchNotice = async () => {
|
|
164
|
+
const fetchNotice = async (signal?: AbortSignal) => {
|
|
153
165
|
setIsLoading(true);
|
|
154
166
|
setFetchError(null);
|
|
167
|
+
setHasAlreadyConsented(false);
|
|
155
168
|
try {
|
|
156
169
|
const consentContentData = await fetchConsentContent({
|
|
157
170
|
org_uuid,
|
|
158
171
|
workspace_uuid,
|
|
159
172
|
notice_uuid,
|
|
173
|
+
accessToken,
|
|
160
174
|
baseUrl,
|
|
161
175
|
language,
|
|
162
176
|
specific_uuid: applicationId,
|
|
177
|
+
signal,
|
|
163
178
|
});
|
|
179
|
+
|
|
180
|
+
if (signal?.aborted) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
164
184
|
const activeConfig = consentContentData.detail.active_config;
|
|
165
185
|
setContent(activeConfig);
|
|
166
186
|
if (activeConfig.default_language) {
|
|
@@ -203,6 +223,13 @@ const RedactoConsentInlineComponent = ({
|
|
|
203
223
|
);
|
|
204
224
|
setSelectedPurposes(initialPurposeState);
|
|
205
225
|
} catch (err: unknown) {
|
|
226
|
+
if (
|
|
227
|
+
signal?.aborted ||
|
|
228
|
+
(err instanceof Error && err.name === "AbortError")
|
|
229
|
+
) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
206
233
|
const error = err as Error & { status?: number };
|
|
207
234
|
setFetchError(error);
|
|
208
235
|
if (error.status === 409) {
|
|
@@ -211,14 +238,60 @@ const RedactoConsentInlineComponent = ({
|
|
|
211
238
|
onError?.(error);
|
|
212
239
|
}
|
|
213
240
|
} finally {
|
|
214
|
-
|
|
241
|
+
if (!signal?.aborted) {
|
|
242
|
+
setIsLoading(false);
|
|
243
|
+
}
|
|
215
244
|
}
|
|
216
245
|
};
|
|
217
246
|
|
|
218
247
|
useEffect(() => {
|
|
219
|
-
|
|
248
|
+
const controller = new AbortController();
|
|
249
|
+
void fetchNotice(controller.signal);
|
|
250
|
+
|
|
251
|
+
return () => {
|
|
252
|
+
controller.abort();
|
|
253
|
+
};
|
|
220
254
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
221
|
-
}, [org_uuid, workspace_uuid, notice_uuid, language, applicationId]);
|
|
255
|
+
}, [org_uuid, workspace_uuid, notice_uuid, language, applicationId, accessToken]);
|
|
256
|
+
|
|
257
|
+
// Post-cutover, consent records live in the Go ledger, so Python's
|
|
258
|
+
// get-notice no longer returns 409 CONSENT_ALREADY_PROVIDED. When a ledger
|
|
259
|
+
// is configured, ask it directly whether this principal has already fully
|
|
260
|
+
// consented — the same check-consent read the banner uses. Skipped for the
|
|
261
|
+
// magic-link specific_uuid flow (which always re-submits against a specific
|
|
262
|
+
// application) and when no ledger is set (the legacy 409 path governs).
|
|
263
|
+
useEffect(() => {
|
|
264
|
+
if (!ledgerBaseUrl || applicationId) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
if (!accessToken || !content) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
let active = true;
|
|
271
|
+
const controller = new AbortController();
|
|
272
|
+
const checkLedgerConsent = async () => {
|
|
273
|
+
try {
|
|
274
|
+
const ledgerStatus = await fetchNoticeConsentStatus({
|
|
275
|
+
accessToken,
|
|
276
|
+
ledgerBaseUrl,
|
|
277
|
+
noticeUuid: notice_uuid,
|
|
278
|
+
signal: controller.signal,
|
|
279
|
+
});
|
|
280
|
+
if (active && ledgerStatus && isNoticeFullyConsented(ledgerStatus)) {
|
|
281
|
+
setHasAlreadyConsented(true);
|
|
282
|
+
}
|
|
283
|
+
} finally {
|
|
284
|
+
if (active) {
|
|
285
|
+
setIsCheckingConsent(false);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
checkLedgerConsent();
|
|
290
|
+
return () => {
|
|
291
|
+
active = false;
|
|
292
|
+
controller.abort();
|
|
293
|
+
};
|
|
294
|
+
}, [ledgerBaseUrl, applicationId, accessToken, content, notice_uuid]);
|
|
222
295
|
|
|
223
296
|
const togglePurposeCollapse = (purposeUuid: string) => {
|
|
224
297
|
setCollapsedPurposes((prev) => ({
|
|
@@ -392,13 +465,18 @@ const RedactoConsentInlineComponent = ({
|
|
|
392
465
|
]
|
|
393
466
|
);
|
|
394
467
|
|
|
395
|
-
// Auto-submit consent when all conditions are met
|
|
468
|
+
// Auto-submit consent when all conditions are met. Waits for the ledger
|
|
469
|
+
// check-consent read to settle (isCheckingConsent) and never fires for an
|
|
470
|
+
// already-consented principal, so the cutover cannot trigger a duplicate
|
|
471
|
+
// submission.
|
|
396
472
|
useEffect(() => {
|
|
397
473
|
if (
|
|
398
474
|
accessToken &&
|
|
399
475
|
content &&
|
|
400
476
|
areAllRequiredElementsChecked &&
|
|
401
477
|
!isSubmitting &&
|
|
478
|
+
!isCheckingConsent &&
|
|
479
|
+
!hasAlreadyConsented &&
|
|
402
480
|
!hasSubmittedRef.current
|
|
403
481
|
) {
|
|
404
482
|
hasSubmittedRef.current = true;
|
|
@@ -417,6 +495,8 @@ const RedactoConsentInlineComponent = ({
|
|
|
417
495
|
content,
|
|
418
496
|
areAllRequiredElementsChecked,
|
|
419
497
|
isSubmitting,
|
|
498
|
+
isCheckingConsent,
|
|
499
|
+
hasAlreadyConsented,
|
|
420
500
|
handleAcceptWithToken,
|
|
421
501
|
]);
|
|
422
502
|
|
|
@@ -12,13 +12,19 @@ export const fetchConsentContent = async ({
|
|
|
12
12
|
org_uuid,
|
|
13
13
|
workspace_uuid,
|
|
14
14
|
notice_uuid,
|
|
15
|
+
accessToken,
|
|
15
16
|
baseUrl,
|
|
17
|
+
language,
|
|
16
18
|
specific_uuid,
|
|
19
|
+
signal,
|
|
17
20
|
}: FetchConsentContentParams): Promise<ConsentContent> => {
|
|
18
21
|
const apiBaseUrl = baseUrl || BASE_URL;
|
|
22
|
+
const noticesUrl = `${apiBaseUrl}/public/organisations/${org_uuid}/workspaces/${workspace_uuid}/notices`;
|
|
19
23
|
|
|
20
24
|
const url = new URL(
|
|
21
|
-
|
|
25
|
+
accessToken
|
|
26
|
+
? `${noticesUrl}/${notice_uuid}`
|
|
27
|
+
: `${noticesUrl}/get-notice/${notice_uuid}`
|
|
22
28
|
);
|
|
23
29
|
|
|
24
30
|
if (specific_uuid) {
|
|
@@ -29,7 +35,10 @@ export const fetchConsentContent = async ({
|
|
|
29
35
|
method: "GET",
|
|
30
36
|
headers: {
|
|
31
37
|
"Content-Type": "application/json",
|
|
38
|
+
"Accept-Language": language || "en",
|
|
39
|
+
...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}),
|
|
32
40
|
},
|
|
41
|
+
signal,
|
|
33
42
|
});
|
|
34
43
|
|
|
35
44
|
if (!response.ok) {
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { fetchConsentContent } from "./index";
|
|
3
|
+
|
|
4
|
+
const LEDGER = "https://ledger.example.test/consent-ledger";
|
|
5
|
+
const PYTHON = "https://api.example.test/consent";
|
|
6
|
+
const TOKEN = "opaque-token";
|
|
7
|
+
|
|
8
|
+
const scope = (base: string) =>
|
|
9
|
+
`${base}/public/organisations/org-1/workspaces/ws-1/notices`;
|
|
10
|
+
|
|
11
|
+
const requestedUrl = (fetchMock: ReturnType<typeof vi.fn>): string =>
|
|
12
|
+
String(fetchMock.mock.calls[0][0]);
|
|
13
|
+
|
|
14
|
+
const requestedHeaders = (
|
|
15
|
+
fetchMock: ReturnType<typeof vi.fn>
|
|
16
|
+
): Record<string, string> =>
|
|
17
|
+
(fetchMock.mock.calls[0][1] as RequestInit).headers as Record<string, string>;
|
|
18
|
+
|
|
19
|
+
describe("inline notice read routing", () => {
|
|
20
|
+
const fetchMock = vi.fn();
|
|
21
|
+
const base = { org_uuid: "org-1", workspace_uuid: "ws-1", notice_uuid: "ntc-1" };
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
fetchMock.mockReset();
|
|
25
|
+
fetchMock.mockResolvedValue({
|
|
26
|
+
ok: true,
|
|
27
|
+
status: 200,
|
|
28
|
+
json: async () => ({}),
|
|
29
|
+
} as Response);
|
|
30
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
vi.unstubAllGlobals();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("reads the authenticated notice endpoint when a token is available", async () => {
|
|
38
|
+
await fetchConsentContent({ ...base, accessToken: TOKEN, baseUrl: LEDGER });
|
|
39
|
+
expect(requestedUrl(fetchMock)).toBe(`${scope(LEDGER)}/ntc-1`);
|
|
40
|
+
expect(requestedHeaders(fetchMock).Authorization).toBe(`Bearer ${TOKEN}`);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("falls back to the tokenless get-notice endpoint without a token", async () => {
|
|
44
|
+
await fetchConsentContent({ ...base, baseUrl: PYTHON });
|
|
45
|
+
expect(requestedUrl(fetchMock)).toBe(`${scope(PYTHON)}/get-notice/ntc-1`);
|
|
46
|
+
expect(requestedHeaders(fetchMock).Authorization).toBeUndefined();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("keeps specific_uuid on the authenticated read", async () => {
|
|
50
|
+
await fetchConsentContent({
|
|
51
|
+
...base,
|
|
52
|
+
accessToken: TOKEN,
|
|
53
|
+
baseUrl: LEDGER,
|
|
54
|
+
specific_uuid: "app-1",
|
|
55
|
+
});
|
|
56
|
+
expect(requestedUrl(fetchMock)).toBe(`${scope(LEDGER)}/ntc-1?specific_uuid=app-1`);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("sends the requested language on both paths", async () => {
|
|
60
|
+
await fetchConsentContent({ ...base, baseUrl: PYTHON, language: "hi" });
|
|
61
|
+
expect(requestedHeaders(fetchMock)["Accept-Language"]).toBe("hi");
|
|
62
|
+
|
|
63
|
+
fetchMock.mockClear();
|
|
64
|
+
await fetchConsentContent({
|
|
65
|
+
...base,
|
|
66
|
+
accessToken: TOKEN,
|
|
67
|
+
baseUrl: LEDGER,
|
|
68
|
+
language: "hi",
|
|
69
|
+
});
|
|
70
|
+
expect(requestedHeaders(fetchMock)["Accept-Language"]).toBe("hi");
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -128,9 +128,11 @@ export type FetchConsentContentParams = {
|
|
|
128
128
|
org_uuid: string;
|
|
129
129
|
workspace_uuid: string;
|
|
130
130
|
notice_uuid: string;
|
|
131
|
+
accessToken?: string;
|
|
131
132
|
baseUrl?: string;
|
|
132
133
|
language?: string;
|
|
133
134
|
specific_uuid?: string;
|
|
135
|
+
signal?: AbortSignal;
|
|
134
136
|
};
|
|
135
137
|
|
|
136
138
|
export type ConsentEventPayload = {
|
|
@@ -17,8 +17,31 @@ interface RedactoPrivacyCenterProps {
|
|
|
17
17
|
baseUrl: string; // Consent server base URL
|
|
18
18
|
ledgerBaseUrl?: string; // Go ledger base URL — serves user-consents; omit to use the legacy Python privacy-center consent endpoints
|
|
19
19
|
slug: string;
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
/** JWT access token. Optional so sandbox mode (see `token`) can omit it. */
|
|
21
|
+
accessToken?: string;
|
|
22
|
+
/** JWT refresh token. Optional; used by the OTP-refresh loop in the JWT flow. */
|
|
23
|
+
refreshToken?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Sandbox PoC: a pasted static token. When set, the Privacy Center runs
|
|
26
|
+
* against the sandbox with no JWT — it sends the single `X-Consent-Token`
|
|
27
|
+
* header, carries the acting identity (`email` || `mobile`) in each request's
|
|
28
|
+
* own payload, takes org/workspace from `organisationUuid`/`workspaceUuid`,
|
|
29
|
+
* and never calls `/otp/refresh`.
|
|
30
|
+
*/
|
|
31
|
+
token?: string;
|
|
32
|
+
/** Sandbox test subject email (carried in the request payload when set). */
|
|
33
|
+
email?: string;
|
|
34
|
+
/** Sandbox test subject mobile (fallback subject when `email` is absent). */
|
|
35
|
+
mobile?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Sandbox UCIC: the client's own user id / org_user_id, e.g. a UUID or
|
|
38
|
+
* customer code; optional third identifier alongside email/mobile.
|
|
39
|
+
*/
|
|
40
|
+
ucic?: string;
|
|
41
|
+
/** Sandbox org UUID (replaces the JWT-decoded org in sandbox mode). */
|
|
42
|
+
organisationUuid?: string;
|
|
43
|
+
/** Sandbox workspace UUID (replaces the JWT-decoded workspace in sandbox mode). */
|
|
44
|
+
workspaceUuid?: string;
|
|
22
45
|
onError: (error: Error) => boolean;
|
|
23
46
|
initialPage?: InternalPage;
|
|
24
47
|
onBack?: 'back' | 'signout';
|
|
@@ -30,6 +53,12 @@ export function RedactoPrivacyCenter({
|
|
|
30
53
|
slug,
|
|
31
54
|
accessToken,
|
|
32
55
|
refreshToken,
|
|
56
|
+
token,
|
|
57
|
+
email,
|
|
58
|
+
mobile,
|
|
59
|
+
ucic,
|
|
60
|
+
organisationUuid,
|
|
61
|
+
workspaceUuid,
|
|
33
62
|
onError,
|
|
34
63
|
initialPage = 'consent-manager',
|
|
35
64
|
onBack,
|
|
@@ -41,6 +70,12 @@ export function RedactoPrivacyCenter({
|
|
|
41
70
|
<AuthProvider
|
|
42
71
|
accessToken={accessToken}
|
|
43
72
|
refreshToken={refreshToken}
|
|
73
|
+
token={token}
|
|
74
|
+
sandboxUcic={ucic}
|
|
75
|
+
sandboxSubject={email || mobile}
|
|
76
|
+
organisationUuid={organisationUuid}
|
|
77
|
+
workspaceUuid={workspaceUuid}
|
|
78
|
+
slug={slug}
|
|
44
79
|
onError={onError}
|
|
45
80
|
>
|
|
46
81
|
<NavigationProvider initialPage={initialPage}>
|
|
@@ -354,6 +354,7 @@ export async function manageConsentServer(params: {
|
|
|
354
354
|
productUuid?: string;
|
|
355
355
|
noticeUuid?: string;
|
|
356
356
|
dataElements?: Array<{ uuid: string; selected: boolean }>;
|
|
357
|
+
language?: string;
|
|
357
358
|
}): Promise<boolean | null> {
|
|
358
359
|
try {
|
|
359
360
|
if (!params.consentClient) {
|
|
@@ -378,7 +379,8 @@ export async function manageConsentServer(params: {
|
|
|
378
379
|
params.dataElementUuids,
|
|
379
380
|
params.productUuid,
|
|
380
381
|
params.noticeUuid,
|
|
381
|
-
params.dataElements
|
|
382
|
+
params.dataElements,
|
|
383
|
+
params.language
|
|
382
384
|
);
|
|
383
385
|
if (result && typeof result === 'object' && 'success' in result) {
|
|
384
386
|
return Boolean((result as LegacyManageConsentResponse).success);
|