@redacto.io/consent-sdk-react 10.0.0-beta.8 → 10.0.0-beta.9

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 (30) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/{chunk-6A665OPR.mjs → chunk-OGL6OC3A.mjs} +4 -0
  3. package/dist/index.d.mts +29 -3
  4. package/dist/index.d.ts +29 -3
  5. package/dist/index.js +162 -56
  6. package/dist/index.mjs +161 -57
  7. package/dist/privacy-center.d.mts +51 -10
  8. package/dist/privacy-center.d.ts +51 -10
  9. package/dist/privacy-center.js +250 -70
  10. package/dist/privacy-center.mjs +249 -71
  11. package/package.json +1 -1
  12. package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +55 -5
  13. package/src/RedactoNoticeConsent/api/index.ts +78 -48
  14. package/src/RedactoNoticeConsent/api/sandbox.ts +150 -0
  15. package/src/RedactoNoticeConsent/api/types.ts +57 -11
  16. package/src/RedactoNoticeConsent/types.ts +28 -2
  17. package/src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx +37 -2
  18. package/src/RedactoPrivacyCenter/api/client.ts +116 -24
  19. package/src/RedactoPrivacyCenter/api/fetcher.ts +24 -9
  20. package/src/RedactoPrivacyCenter/components/Form.tsx +55 -30
  21. package/src/RedactoPrivacyCenter/components/SelectUserGate.tsx +8 -0
  22. package/src/RedactoPrivacyCenter/components/groupHelpers.ts +42 -6
  23. package/src/RedactoPrivacyCenter/context/AuthContext.tsx +131 -39
  24. package/src/RedactoPrivacyCenter/context/types.ts +38 -0
  25. package/src/RedactoPrivacyCenter/lib/constants.ts +12 -5
  26. package/src/RedactoPrivacyCenter/lib/sandbox.ts +104 -0
  27. package/src/RedactoPrivacyCenter/lib/types.ts +20 -0
  28. package/src/RedactoPrivacyCenter/lib/utils.ts +15 -0
  29. package/src/shared/sandbox.ts +6 -0
  30. package/tests/Form.test.tsx +142 -5
package/dist/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import {
2
+ SANDBOX_TOKEN_HEADER,
2
3
  useMediaQuery
3
- } from "./chunk-6A665OPR.mjs";
4
+ } from "./chunk-OGL6OC3A.mjs";
4
5
 
5
6
  // src/RedactoNoticeConsent/RedactoNoticeConsent.tsx
6
7
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
@@ -66,6 +67,64 @@ async function parseApiError(response, fallbackMessage) {
66
67
  });
67
68
  }
68
69
 
70
+ // src/RedactoNoticeConsent/api/sandbox.ts
71
+ var resolveSandboxAuth = (params) => {
72
+ var _a, _b, _c, _d, _e;
73
+ const token = (_a = params.token) == null ? void 0 : _a.trim();
74
+ if (!token) {
75
+ return null;
76
+ }
77
+ const organisationUuid = (_b = params.organisationUuid) == null ? void 0 : _b.trim();
78
+ const workspaceUuid = (_c = params.workspaceUuid) == null ? void 0 : _c.trim();
79
+ const ucic = (_d = params.sandboxUcic) == null ? void 0 : _d.trim();
80
+ const subject = (_e = params.sandboxSubject) == null ? void 0 : _e.trim();
81
+ if (!organisationUuid || !workspaceUuid) {
82
+ throw new Error(
83
+ "Sandbox mode requires organisationUuid and workspaceUuid props"
84
+ );
85
+ }
86
+ if (!ucic && !subject) {
87
+ throw new Error("Sandbox mode requires a UCIC, email, or mobile");
88
+ }
89
+ return { token, ucic, subject: subject != null ? subject : "", organisationUuid, workspaceUuid };
90
+ };
91
+ var resolveRequestAuth = (accessToken, sandboxParams, decodeTokenSafely2) => {
92
+ const sandbox = resolveSandboxAuth(sandboxParams);
93
+ if (sandbox) {
94
+ return {
95
+ organisationUuid: sandbox.organisationUuid,
96
+ workspaceUuid: sandbox.workspaceUuid,
97
+ sandbox
98
+ };
99
+ }
100
+ const decodedToken = accessToken ? decodeTokenSafely2(accessToken) : null;
101
+ if (!decodedToken) {
102
+ throw new Error("Invalid access token");
103
+ }
104
+ const { organisation_uuid, workspace_uuid } = decodedToken;
105
+ if (!organisation_uuid || !workspace_uuid) {
106
+ throw new Error("Invalid token: missing organization or workspace UUID");
107
+ }
108
+ return {
109
+ organisationUuid: organisation_uuid,
110
+ workspaceUuid: workspace_uuid,
111
+ sandbox: null
112
+ };
113
+ };
114
+ var buildAuthHeaders = (accessToken, sandbox) => {
115
+ if (sandbox) {
116
+ return { [SANDBOX_TOKEN_HEADER]: sandbox.token };
117
+ }
118
+ return { Authorization: `Bearer ${accessToken}` };
119
+ };
120
+ var sandboxBodyIdentity = (sandbox) => {
121
+ if (sandbox.ucic) {
122
+ return { org_user_id: sandbox.ucic };
123
+ }
124
+ return sandbox.subject.includes("@") ? { primary_email: sandbox.subject } : { primary_mobile: sandbox.subject };
125
+ };
126
+ var sandboxQueryIdentity = (sandbox) => sandboxBodyIdentity(sandbox);
127
+
69
128
  // src/shared/notice-consent-status.ts
70
129
  import { jwtDecode } from "jwt-decode";
71
130
  var decodeNoticeConsentToken = (token) => {
@@ -179,21 +238,19 @@ var fetchConsentContent = async ({
179
238
  specific_uuid,
180
239
  validate_against = "all",
181
240
  include_fully_consented_data = false,
182
- signal
241
+ signal,
242
+ ...sandboxParams
183
243
  }) => {
184
- if (!noticeId || !accessToken || !validate_against) {
185
- throw new Error("noticeId and accessToken are required");
244
+ if (!noticeId || !validate_against) {
245
+ throw new Error("noticeId is required");
186
246
  }
187
- const decodedToken = decodeTokenSafely(accessToken);
188
- if (!decodedToken) {
189
- throw new Error("Invalid access token");
190
- }
191
- const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
192
- if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
193
- throw new Error("Invalid token: missing organization or workspace UUID");
247
+ if (!accessToken && !sandboxParams.token) {
248
+ throw new Error("accessToken or token is required");
194
249
  }
250
+ const { organisationUuid: ORGANISATION_UUID, workspaceUuid: WORKSPACE_UUID, sandbox } = resolveRequestAuth(accessToken, sandboxParams, decodeTokenSafely);
195
251
  const apiBaseUrl = baseUrl || BASE_URL;
196
- const cacheKey = `${accessToken}-${noticeId}-${validate_against}-${language}-${specific_uuid || ""}-${include_fully_consented_data}`;
252
+ const cacheIdentity = sandbox ? `sandbox:${sandbox.organisationUuid}:${sandbox.workspaceUuid}:${sandbox.ucic || sandbox.subject}` : accessToken;
253
+ const cacheKey = `${cacheIdentity}-${noticeId}-${validate_against}-${language}-${specific_uuid || ""}-${include_fully_consented_data}`;
197
254
  if (validate_against === "all" && !specific_uuid) {
198
255
  const cachedData = getCachedData(cacheKey);
199
256
  if (cachedData) {
@@ -210,10 +267,15 @@ var fetchConsentContent = async ({
210
267
  if (include_fully_consented_data) {
211
268
  url.searchParams.append("include_fully_consented_data", "true");
212
269
  }
270
+ if (sandbox) {
271
+ for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
272
+ url.searchParams.append(key, value);
273
+ }
274
+ }
213
275
  const response = await fetch(url.toString(), {
214
276
  method: "GET",
215
277
  headers: {
216
- Authorization: `Bearer ${accessToken}`,
278
+ ...buildAuthHeaders(accessToken, sandbox),
217
279
  "Accept-Language": language
218
280
  },
219
281
  signal
@@ -241,20 +303,17 @@ var submitConsentEvent = async ({
241
303
  meta_data,
242
304
  guardianVerificationReference,
243
305
  selfDeclaredAdult,
244
- signal
306
+ signal,
307
+ ...sandboxParams
245
308
  }) => {
246
- if (!noticeUuid || !accessToken || !Array.isArray(purposes)) {
247
- throw new Error("noticeUuid, accessToken, and purposes array are required");
248
- }
249
- const decodedToken = decodeTokenSafely(accessToken);
250
- if (!decodedToken) {
251
- throw new Error("Invalid access token");
309
+ if (!noticeUuid || !Array.isArray(purposes)) {
310
+ throw new Error("noticeUuid and purposes array are required");
252
311
  }
253
- const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
254
- if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
255
- throw new Error("Invalid token: missing organization or workspace UUID");
312
+ if (!accessToken && !sandboxParams.token) {
313
+ throw new Error("accessToken or token is required");
256
314
  }
257
- const apiBaseUrl = ledgerBaseUrl || baseUrl || BASE_URL;
315
+ const { organisationUuid: ORGANISATION_UUID, workspaceUuid: WORKSPACE_UUID, sandbox } = resolveRequestAuth(accessToken, sandboxParams, decodeTokenSafely);
316
+ const apiBaseUrl = (sandbox ? null : ledgerBaseUrl) || baseUrl || BASE_URL;
258
317
  const validatedPurposes = purposes.map((purpose) => {
259
318
  if (!purpose.uuid) {
260
319
  throw new Error(`Invalid purpose data: missing uuid`);
@@ -280,15 +339,16 @@ var submitConsentEvent = async ({
280
339
  guardian_verification_reference: guardianVerificationReference,
281
340
  self_declared_adult: selfDeclaredAdult
282
341
  };
342
+ const requestBody = sandbox ? { ...payload, ...sandboxBodyIdentity(sandbox) } : payload;
283
343
  const response = await fetch(
284
344
  `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/submit-consent`,
285
345
  {
286
346
  method: "POST",
287
347
  headers: {
288
- Authorization: `Bearer ${accessToken}`,
348
+ ...buildAuthHeaders(accessToken, sandbox),
289
349
  "Content-Type": "application/json"
290
350
  },
291
- body: JSON.stringify(payload),
351
+ body: JSON.stringify(requestBody),
292
352
  signal
293
353
  }
294
354
  );
@@ -344,25 +404,26 @@ var fetchTTSAudioUrls = async ({
344
404
  baseUrl,
345
405
  noticeUuid,
346
406
  language,
347
- signal
407
+ signal,
408
+ ...sandboxParams
348
409
  }) => {
349
- if (!accessToken || !noticeUuid || !language) {
350
- throw new Error("accessToken, noticeUuid, and language are required");
351
- }
352
- const decodedToken = decodeTokenSafely(accessToken);
353
- if (!decodedToken) {
354
- throw new Error("Invalid access token");
355
- }
356
- const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
357
- if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
358
- throw new Error("Invalid token: missing organization or workspace UUID");
410
+ if (!accessToken && !sandboxParams.token || !noticeUuid || !language) {
411
+ throw new Error("accessToken (or token), noticeUuid, and language are required");
359
412
  }
413
+ const { organisationUuid: ORGANISATION_UUID, workspaceUuid: WORKSPACE_UUID, sandbox } = resolveRequestAuth(accessToken, sandboxParams, decodeTokenSafely);
360
414
  const apiBaseUrl = baseUrl || BASE_URL;
361
- const url = `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/notices/${noticeUuid}/audio/${language}`;
362
- const response = await fetch(url, {
415
+ const url = new URL(
416
+ `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/notices/${noticeUuid}/audio/${language}`
417
+ );
418
+ if (sandbox) {
419
+ for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
420
+ url.searchParams.append(key, value);
421
+ }
422
+ }
423
+ const response = await fetch(url.toString(), {
363
424
  method: "GET",
364
425
  headers: {
365
- Authorization: `Bearer ${accessToken}`,
426
+ ...buildAuthHeaders(accessToken, sandbox),
366
427
  Accept: "application/json"
367
428
  },
368
429
  signal
@@ -404,7 +465,10 @@ var initiateGuardianVerification = async ({
404
465
  if (!decodedToken) {
405
466
  throw new Error("Invalid access token");
406
467
  }
407
- const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
468
+ const {
469
+ organisation_uuid: ORGANISATION_UUID,
470
+ workspace_uuid: WORKSPACE_UUID
471
+ } = decodedToken;
408
472
  if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
409
473
  throw new Error("Invalid token: missing organization or workspace UUID");
410
474
  }
@@ -478,7 +542,10 @@ var verifyGuardianStatus = async ({
478
542
  if (!decodedToken) {
479
543
  throw new Error("Invalid access token");
480
544
  }
481
- const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
545
+ const {
546
+ organisation_uuid: ORGANISATION_UUID,
547
+ workspace_uuid: WORKSPACE_UUID
548
+ } = decodedToken;
482
549
  if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
483
550
  throw new Error("Invalid token: missing organization or workspace UUID");
484
551
  }
@@ -491,9 +558,7 @@ var verifyGuardianStatus = async ({
491
558
  Authorization: `Bearer ${accessToken}`,
492
559
  "Content-Type": "application/json"
493
560
  },
494
- body: JSON.stringify({
495
- session_token: sessionToken
496
- }),
561
+ body: JSON.stringify({ session_token: sessionToken }),
497
562
  signal
498
563
  }
499
564
  );
@@ -2355,6 +2420,12 @@ var RedactoNoticeConsent = ({
2355
2420
  noticeId,
2356
2421
  accessToken,
2357
2422
  refreshToken,
2423
+ token,
2424
+ email,
2425
+ mobile,
2426
+ ucic,
2427
+ organisationUuid,
2428
+ workspaceUuid,
2358
2429
  baseUrl,
2359
2430
  ledgerBaseUrl,
2360
2431
  language = "en",
@@ -2373,15 +2444,43 @@ var RedactoNoticeConsent = ({
2373
2444
  reviewModeButtonText = "Continue"
2374
2445
  }) => {
2375
2446
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
2447
+ const sandboxParams = useMemo(
2448
+ () => ({
2449
+ token,
2450
+ sandboxUcic: ucic,
2451
+ sandboxSubject: email || mobile,
2452
+ organisationUuid,
2453
+ workspaceUuid
2454
+ }),
2455
+ [token, ucic, email, mobile, organisationUuid, workspaceUuid]
2456
+ );
2376
2457
  const propValidationError = useMemo(() => {
2377
2458
  if (!(noticeId == null ? void 0 : noticeId.trim())) {
2378
2459
  return "RedactoNoticeConsent: 'noticeId' prop is required and cannot be empty";
2379
2460
  }
2461
+ if (token == null ? void 0 : token.trim()) {
2462
+ if (!(organisationUuid == null ? void 0 : organisationUuid.trim()) || !(workspaceUuid == null ? void 0 : workspaceUuid.trim())) {
2463
+ return "RedactoNoticeConsent: sandbox mode requires 'organisationUuid' and 'workspaceUuid'";
2464
+ }
2465
+ if (!(email == null ? void 0 : email.trim()) && !(mobile == null ? void 0 : mobile.trim()) && !(ucic == null ? void 0 : ucic.trim())) {
2466
+ return "RedactoNoticeConsent: sandbox mode requires 'email', 'mobile', or 'ucic'";
2467
+ }
2468
+ return null;
2469
+ }
2380
2470
  if (!(accessToken == null ? void 0 : accessToken.trim())) {
2381
2471
  return "RedactoNoticeConsent: 'accessToken' prop is required and cannot be empty";
2382
2472
  }
2383
2473
  return null;
2384
- }, [noticeId, accessToken]);
2474
+ }, [
2475
+ noticeId,
2476
+ accessToken,
2477
+ token,
2478
+ organisationUuid,
2479
+ workspaceUuid,
2480
+ email,
2481
+ mobile,
2482
+ ucic
2483
+ ]);
2385
2484
  if (propValidationError) {
2386
2485
  return /* @__PURE__ */ jsxs(
2387
2486
  "div",
@@ -2734,7 +2833,8 @@ var RedactoNoticeConsent = ({
2734
2833
  specific_uuid: applicationId,
2735
2834
  validate_against: validateAgainst,
2736
2835
  include_fully_consented_data: includeFullyConsentedData,
2737
- signal: (_a2 = abortControllerRef.current) == null ? void 0 : _a2.signal
2836
+ signal: (_a2 = abortControllerRef.current) == null ? void 0 : _a2.signal,
2837
+ ...sandboxParams
2738
2838
  });
2739
2839
  const activeConfig = consentContentData.detail.active_config;
2740
2840
  setContent(activeConfig);
@@ -2759,7 +2859,8 @@ var RedactoNoticeConsent = ({
2759
2859
  accessToken,
2760
2860
  ledgerBaseUrl,
2761
2861
  noticeUuid: noticeId,
2762
- signal: (_b2 = abortControllerRef.current) == null ? void 0 : _b2.signal
2862
+ signal: (_b2 = abortControllerRef.current) == null ? void 0 : _b2.signal,
2863
+ ...sandboxParams
2763
2864
  });
2764
2865
  if (ledgerStatus) {
2765
2866
  const overlay = buildLedgerConsentOverlay(
@@ -2891,6 +2992,7 @@ var RedactoNoticeConsent = ({
2891
2992
  noticeId,
2892
2993
  accessToken,
2893
2994
  refreshToken,
2995
+ sandboxParams,
2894
2996
  language,
2895
2997
  applicationId,
2896
2998
  validateAgainst,
@@ -3007,7 +3109,8 @@ var RedactoNoticeConsent = ({
3007
3109
  meta_data: applicationId ? { specific_uuid: applicationId } : void 0,
3008
3110
  guardianVerificationReference: verificationReference || void 0,
3009
3111
  selfDeclaredAdult: selfDeclaredAdult || void 0,
3010
- signal: submitController.signal
3112
+ signal: submitController.signal,
3113
+ ...sandboxParams
3011
3114
  });
3012
3115
  }
3013
3116
  onAccept == null ? void 0 : onAccept();
@@ -3190,7 +3293,7 @@ var RedactoNoticeConsent = ({
3190
3293
  } finally {
3191
3294
  setIsSubmittingGuardian(false);
3192
3295
  }
3193
- }, [accessToken, baseUrl, guardianFormData, digilockerMode, digilockerCallbackUrl, selectedPurposes, selectedDataElements, noticeId, onError]);
3296
+ }, [accessToken, sandboxParams, baseUrl, guardianFormData, digilockerMode, digilockerCallbackUrl, selectedPurposes, selectedDataElements, noticeId, onError]);
3194
3297
  const handleAgeVerificationYes = useCallback(() => {
3195
3298
  setShowAgeVerification(false);
3196
3299
  setIsMinorFlow(false);
@@ -3247,7 +3350,7 @@ var RedactoNoticeConsent = ({
3247
3350
  setIsPollingStatus(false);
3248
3351
  isPollingStatusRef.current = false;
3249
3352
  setIsInitiatingVerification(false);
3250
- }, [accessToken, baseUrl]);
3353
+ }, [accessToken, sandboxParams, baseUrl]);
3251
3354
  const startPopupMonitoring = useCallback((popup) => {
3252
3355
  popupCheckIntervalRef.current = window.setInterval(() => {
3253
3356
  if (popup.closed) {
@@ -3275,8 +3378,8 @@ var RedactoNoticeConsent = ({
3275
3378
  []
3276
3379
  );
3277
3380
  const startStatusPolling = useCallback(
3278
- (token) => {
3279
- verificationSessionTokenRef.current = token;
3381
+ (token2) => {
3382
+ verificationSessionTokenRef.current = token2;
3280
3383
  setIsPollingStatus(true);
3281
3384
  isPollingStatusRef.current = true;
3282
3385
  setVerificationError(null);
@@ -3328,7 +3431,7 @@ var RedactoNoticeConsent = ({
3328
3431
  const response = await verifyGuardianStatus({
3329
3432
  accessToken,
3330
3433
  baseUrl,
3331
- sessionToken: token
3434
+ sessionToken: token2
3332
3435
  });
3333
3436
  consecutiveErrors = 0;
3334
3437
  if (response.status === "verified") {
@@ -3398,7 +3501,7 @@ var RedactoNoticeConsent = ({
3398
3501
  };
3399
3502
  statusPollIntervalRef.current = window.setTimeout(poll, POLL_INTERVAL);
3400
3503
  },
3401
- [accessToken, baseUrl, getVerificationErrorMessage]
3504
+ [accessToken, sandboxParams, baseUrl, getVerificationErrorMessage]
3402
3505
  );
3403
3506
  const handleBackToGuardianForm = useCallback((clearName = false) => {
3404
3507
  var _a2, _b2;
@@ -3740,7 +3843,8 @@ var RedactoNoticeConsent = ({
3740
3843
  baseUrl,
3741
3844
  noticeUuid: content.notice_uuid,
3742
3845
  language: languageCode,
3743
- signal: ttsAbortController.signal
3846
+ signal: ttsAbortController.signal,
3847
+ ...sandboxParams
3744
3848
  }).then((response) => {
3745
3849
  setTtsAudioUrls(response.detail);
3746
3850
  setIsTTSAvailable(true);
@@ -3753,7 +3857,7 @@ var RedactoNoticeConsent = ({
3753
3857
  return () => {
3754
3858
  ttsAbortController.abort();
3755
3859
  };
3756
- }, [content, selectedLanguage, accessToken, baseUrl, getLanguageCode]);
3860
+ }, [content, selectedLanguage, accessToken, sandboxParams, baseUrl, getLanguageCode]);
3757
3861
  const modalStyle = {
3758
3862
  borderRadius: (settings == null ? void 0 : settings.borderRadius) || "8px",
3759
3863
  backgroundColor: (settings == null ? void 0 : settings.backgroundColor) || "#ffffff"
@@ -382,6 +382,7 @@ type NominatorInfo = {
382
382
  };
383
383
  type PurposeItem = {
384
384
  purpose_uuid: string;
385
+ uuid?: string;
385
386
  name: string;
386
387
  description: string | null;
387
388
  status: ConsentStatusEnum | string;
@@ -661,13 +662,36 @@ interface RedactoPrivacyCenterProps {
661
662
  baseUrl: string;
662
663
  ledgerBaseUrl?: string;
663
664
  slug: string;
664
- accessToken: string;
665
- refreshToken: string;
665
+ /** JWT access token. Optional so sandbox mode (see `token`) can omit it. */
666
+ accessToken?: string;
667
+ /** JWT refresh token. Optional; used by the OTP-refresh loop in the JWT flow. */
668
+ refreshToken?: string;
669
+ /**
670
+ * Sandbox PoC: a pasted static token. When set, the Privacy Center runs
671
+ * against the sandbox with no JWT — it sends the single `X-Consent-Token`
672
+ * header, carries the acting identity (`email` || `mobile`) in each request's
673
+ * own payload, takes org/workspace from `organisationUuid`/`workspaceUuid`,
674
+ * and never calls `/otp/refresh`.
675
+ */
676
+ token?: string;
677
+ /** Sandbox test subject email (carried in the request payload when set). */
678
+ email?: string;
679
+ /** Sandbox test subject mobile (fallback subject when `email` is absent). */
680
+ mobile?: string;
681
+ /**
682
+ * Sandbox UCIC: the client's own user id / org_user_id, e.g. a UUID or
683
+ * customer code; optional third identifier alongside email/mobile.
684
+ */
685
+ ucic?: string;
686
+ /** Sandbox org UUID (replaces the JWT-decoded org in sandbox mode). */
687
+ organisationUuid?: string;
688
+ /** Sandbox workspace UUID (replaces the JWT-decoded workspace in sandbox mode). */
689
+ workspaceUuid?: string;
666
690
  onError: (error: Error) => boolean;
667
691
  initialPage?: InternalPage;
668
692
  onBack?: 'back' | 'signout';
669
693
  }
670
- declare function RedactoPrivacyCenter({ baseUrl, ledgerBaseUrl, slug, accessToken, refreshToken, onError, initialPage, onBack, }: RedactoPrivacyCenterProps): react.JSX.Element;
694
+ declare function RedactoPrivacyCenter({ baseUrl, ledgerBaseUrl, slug, accessToken, refreshToken, token, email, mobile, ucic, organisationUuid, workspaceUuid, onError, initialPage, onBack, }: RedactoPrivacyCenterProps): react.JSX.Element;
671
695
 
672
696
  interface PrivacyCenterLayoutProps {
673
697
  children: ReactNode;
@@ -688,6 +712,29 @@ declare const PrivacyCenterCaseDetailsPage: () => react.JSX.Element | null;
688
712
 
689
713
  declare const NotificationsPage: () => react.JSX.Element;
690
714
 
715
+ type AuthProviderProps = Readonly<{
716
+ children: ReactNode;
717
+ accessToken?: string;
718
+ refreshToken?: string;
719
+ /**
720
+ * Sandbox PoC: when set, the Privacy Center skips the JWT entirely — it seeds
721
+ * org/workspace/subject from the props below, sends the single
722
+ * `X-Consent-Token` header, carries the acting identity in each request's own
723
+ * payload, and never calls `/otp/refresh`.
724
+ */
725
+ token?: string;
726
+ /** Sandbox UCIC — the client's own user id (`org_user_id`); takes precedence over the subject. */
727
+ sandboxUcic?: string;
728
+ /** Sandbox subject (email or mobile) — carried in the request payload. */
729
+ sandboxSubject?: string;
730
+ /** Sandbox org UUID (replaces the JWT-decoded org). */
731
+ organisationUuid?: string;
732
+ /** Sandbox workspace UUID (replaces the JWT-decoded workspace). */
733
+ workspaceUuid?: string;
734
+ /** Organisation slug — used to seed sandbox sessionStorage. */
735
+ slug?: string;
736
+ onError: (error: Error) => boolean;
737
+ }>;
691
738
  type AuthContextValue = Readonly<{
692
739
  accessToken: string;
693
740
  refreshToken: string;
@@ -878,13 +925,7 @@ declare const fetchCaseHistory: (organizationId: string, workspaceId: string, co
878
925
 
879
926
  declare const useCaseHistory: (organizationId: string | undefined, workspaceId: string | undefined, contact: string | undefined, authContext?: AuthContext) => _tanstack_react_query.UseQueryResult<NoInfer<CaseRequest[]>, Error>;
880
927
 
881
- interface AuthProviderProps {
882
- children: ReactNode;
883
- accessToken: string;
884
- refreshToken: string;
885
- onError: (error: Error) => boolean;
886
- }
887
- declare const AuthProvider: ({ children, accessToken: initialAccessToken, refreshToken: initialRefreshToken, onError, }: AuthProviderProps) => react.JSX.Element;
928
+ declare const AuthProvider: (props: AuthProviderProps) => react.JSX.Element;
888
929
  declare const useAuth: () => AuthContextValue;
889
930
 
890
931
  /**
@@ -382,6 +382,7 @@ type NominatorInfo = {
382
382
  };
383
383
  type PurposeItem = {
384
384
  purpose_uuid: string;
385
+ uuid?: string;
385
386
  name: string;
386
387
  description: string | null;
387
388
  status: ConsentStatusEnum | string;
@@ -661,13 +662,36 @@ interface RedactoPrivacyCenterProps {
661
662
  baseUrl: string;
662
663
  ledgerBaseUrl?: string;
663
664
  slug: string;
664
- accessToken: string;
665
- refreshToken: string;
665
+ /** JWT access token. Optional so sandbox mode (see `token`) can omit it. */
666
+ accessToken?: string;
667
+ /** JWT refresh token. Optional; used by the OTP-refresh loop in the JWT flow. */
668
+ refreshToken?: string;
669
+ /**
670
+ * Sandbox PoC: a pasted static token. When set, the Privacy Center runs
671
+ * against the sandbox with no JWT — it sends the single `X-Consent-Token`
672
+ * header, carries the acting identity (`email` || `mobile`) in each request's
673
+ * own payload, takes org/workspace from `organisationUuid`/`workspaceUuid`,
674
+ * and never calls `/otp/refresh`.
675
+ */
676
+ token?: string;
677
+ /** Sandbox test subject email (carried in the request payload when set). */
678
+ email?: string;
679
+ /** Sandbox test subject mobile (fallback subject when `email` is absent). */
680
+ mobile?: string;
681
+ /**
682
+ * Sandbox UCIC: the client's own user id / org_user_id, e.g. a UUID or
683
+ * customer code; optional third identifier alongside email/mobile.
684
+ */
685
+ ucic?: string;
686
+ /** Sandbox org UUID (replaces the JWT-decoded org in sandbox mode). */
687
+ organisationUuid?: string;
688
+ /** Sandbox workspace UUID (replaces the JWT-decoded workspace in sandbox mode). */
689
+ workspaceUuid?: string;
666
690
  onError: (error: Error) => boolean;
667
691
  initialPage?: InternalPage;
668
692
  onBack?: 'back' | 'signout';
669
693
  }
670
- declare function RedactoPrivacyCenter({ baseUrl, ledgerBaseUrl, slug, accessToken, refreshToken, onError, initialPage, onBack, }: RedactoPrivacyCenterProps): react.JSX.Element;
694
+ declare function RedactoPrivacyCenter({ baseUrl, ledgerBaseUrl, slug, accessToken, refreshToken, token, email, mobile, ucic, organisationUuid, workspaceUuid, onError, initialPage, onBack, }: RedactoPrivacyCenterProps): react.JSX.Element;
671
695
 
672
696
  interface PrivacyCenterLayoutProps {
673
697
  children: ReactNode;
@@ -688,6 +712,29 @@ declare const PrivacyCenterCaseDetailsPage: () => react.JSX.Element | null;
688
712
 
689
713
  declare const NotificationsPage: () => react.JSX.Element;
690
714
 
715
+ type AuthProviderProps = Readonly<{
716
+ children: ReactNode;
717
+ accessToken?: string;
718
+ refreshToken?: string;
719
+ /**
720
+ * Sandbox PoC: when set, the Privacy Center skips the JWT entirely — it seeds
721
+ * org/workspace/subject from the props below, sends the single
722
+ * `X-Consent-Token` header, carries the acting identity in each request's own
723
+ * payload, and never calls `/otp/refresh`.
724
+ */
725
+ token?: string;
726
+ /** Sandbox UCIC — the client's own user id (`org_user_id`); takes precedence over the subject. */
727
+ sandboxUcic?: string;
728
+ /** Sandbox subject (email or mobile) — carried in the request payload. */
729
+ sandboxSubject?: string;
730
+ /** Sandbox org UUID (replaces the JWT-decoded org). */
731
+ organisationUuid?: string;
732
+ /** Sandbox workspace UUID (replaces the JWT-decoded workspace). */
733
+ workspaceUuid?: string;
734
+ /** Organisation slug — used to seed sandbox sessionStorage. */
735
+ slug?: string;
736
+ onError: (error: Error) => boolean;
737
+ }>;
691
738
  type AuthContextValue = Readonly<{
692
739
  accessToken: string;
693
740
  refreshToken: string;
@@ -878,13 +925,7 @@ declare const fetchCaseHistory: (organizationId: string, workspaceId: string, co
878
925
 
879
926
  declare const useCaseHistory: (organizationId: string | undefined, workspaceId: string | undefined, contact: string | undefined, authContext?: AuthContext) => _tanstack_react_query.UseQueryResult<NoInfer<CaseRequest[]>, Error>;
880
927
 
881
- interface AuthProviderProps {
882
- children: ReactNode;
883
- accessToken: string;
884
- refreshToken: string;
885
- onError: (error: Error) => boolean;
886
- }
887
- declare const AuthProvider: ({ children, accessToken: initialAccessToken, refreshToken: initialRefreshToken, onError, }: AuthProviderProps) => react.JSX.Element;
928
+ declare const AuthProvider: (props: AuthProviderProps) => react.JSX.Element;
888
929
  declare const useAuth: () => AuthContextValue;
889
930
 
890
931
  /**