@redacto.io/consent-sdk-react 5.3.0 → 7.0.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 (68) hide show
  1. package/.turbo/turbo-build.log +24 -0
  2. package/CHANGELOG.md +23 -0
  3. package/dist/privacy-center.d.mts +100 -42
  4. package/dist/privacy-center.d.ts +100 -42
  5. package/dist/privacy-center.js +4232 -2247
  6. package/dist/privacy-center.mjs +4266 -2284
  7. package/package.json +1 -1
  8. package/src/RedactoPrivacyCenter/PrivacyCenterLayout.tsx +6 -10
  9. package/src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx +18 -17
  10. package/src/RedactoPrivacyCenter/api/actions.ts +52 -8
  11. package/src/RedactoPrivacyCenter/api/client.ts +58 -15
  12. package/src/RedactoPrivacyCenter/api/orgBranding.ts +9 -0
  13. package/src/RedactoPrivacyCenter/components/ConsentManager.tsx +729 -278
  14. package/src/RedactoPrivacyCenter/components/ConsentManagerCells.tsx +187 -0
  15. package/src/RedactoPrivacyCenter/components/ConsentManagerColumns.tsx +65 -165
  16. package/src/RedactoPrivacyCenter/components/ConsentManagerExpandedRow.tsx +179 -0
  17. package/src/RedactoPrivacyCenter/components/ConsentManagerGroupedColumns.tsx +318 -0
  18. package/src/RedactoPrivacyCenter/components/ConsentManagerProductCards.tsx +333 -0
  19. package/src/RedactoPrivacyCenter/components/ConsentManagerPurposeCards.tsx +102 -0
  20. package/src/RedactoPrivacyCenter/components/ConsentManagerTableSkeleton.tsx +6 -2
  21. package/src/RedactoPrivacyCenter/components/Form.tsx +3 -3
  22. package/src/RedactoPrivacyCenter/components/ModifyConsentModal.tsx +9 -6
  23. package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationBell.tsx +1 -1
  24. package/src/RedactoPrivacyCenter/components/PrivacyCenterLayout.tsx +101 -56
  25. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideBarPC.tsx +16 -2
  26. package/src/RedactoPrivacyCenter/components/types.ts +2 -5
  27. package/src/RedactoPrivacyCenter/context/BrandingContext.tsx +44 -0
  28. package/src/RedactoPrivacyCenter/context/PageChromeContext.tsx +21 -1
  29. package/src/RedactoPrivacyCenter/lib/api.ts +70 -28
  30. package/src/RedactoPrivacyCenter/lib/consent-manager/utils.tsx +4 -2
  31. package/src/RedactoPrivacyCenter/lib/types.ts +79 -36
  32. package/src/RedactoPrivacyCenter/locales/as/translation.json +12 -1
  33. package/src/RedactoPrivacyCenter/locales/bho/translation.json +12 -1
  34. package/src/RedactoPrivacyCenter/locales/bn/translation.json +12 -1
  35. package/src/RedactoPrivacyCenter/locales/brx/translation.json +12 -1
  36. package/src/RedactoPrivacyCenter/locales/doi/translation.json +12 -1
  37. package/src/RedactoPrivacyCenter/locales/en/translation.json +16 -1
  38. package/src/RedactoPrivacyCenter/locales/gom/translation.json +12 -1
  39. package/src/RedactoPrivacyCenter/locales/gu/translation.json +12 -1
  40. package/src/RedactoPrivacyCenter/locales/hi/translation.json +15 -1
  41. package/src/RedactoPrivacyCenter/locales/kn/translation.json +12 -1
  42. package/src/RedactoPrivacyCenter/locales/ks/translation.json +12 -1
  43. package/src/RedactoPrivacyCenter/locales/mai/translation.json +12 -1
  44. package/src/RedactoPrivacyCenter/locales/ml/translation.json +12 -1
  45. package/src/RedactoPrivacyCenter/locales/mni-Mtei/translation.json +12 -1
  46. package/src/RedactoPrivacyCenter/locales/mr/translation.json +12 -1
  47. package/src/RedactoPrivacyCenter/locales/ne/translation.json +12 -1
  48. package/src/RedactoPrivacyCenter/locales/or/translation.json +12 -1
  49. package/src/RedactoPrivacyCenter/locales/pa/translation.json +12 -1
  50. package/src/RedactoPrivacyCenter/locales/sa/translation.json +12 -1
  51. package/src/RedactoPrivacyCenter/locales/sat/translation.json +12 -1
  52. package/src/RedactoPrivacyCenter/locales/sd/translation.json +12 -1
  53. package/src/RedactoPrivacyCenter/locales/ta/translation.json +12 -1
  54. package/src/RedactoPrivacyCenter/locales/te/translation.json +12 -1
  55. package/src/RedactoPrivacyCenter/locales/ur/translation.json +12 -1
  56. package/src/RedactoPrivacyCenter/styles/applyBrandingTheme.ts +66 -0
  57. package/src/RedactoPrivacyCenter/styles/injectStyles.ts +528 -114
  58. package/src/RedactoPrivacyCenter/styles/pcStyles.ts +3 -2
  59. package/src/RedactoPrivacyCenter/styles/variables.css +5 -42
  60. package/src/RedactoPrivacyCenter/table/AdvancedTable.tsx +36 -16
  61. package/src/RedactoPrivacyCenter/table/AdvancedTablePagination.tsx +4 -12
  62. package/src/RedactoPrivacyCenter/ui/Footer.tsx +4 -10
  63. package/src/RedactoPrivacyCenter/ui/PCInput.tsx +1 -1
  64. package/src/RedactoPrivacyCenter/ui/PCSelect.tsx +51 -5
  65. package/src/privacy-center.ts +7 -3
  66. package/src/RedactoPrivacyCenter/components/ThemeTransitionOverlay.tsx +0 -85
  67. package/src/RedactoPrivacyCenter/context/theme.tsx +0 -127
  68. package/src/RedactoPrivacyCenter/ui/ThemeToggle.tsx +0 -72
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redacto.io/consent-sdk-react",
3
- "version": "5.3.0",
3
+ "version": "7.0.0",
4
4
  "main": "dist/index.cjs",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",
@@ -1,6 +1,5 @@
1
1
  import { useEffect, useState, useRef, type ReactNode } from 'react';
2
2
  import { OrganizationProvider } from './context/OrganizationContext';
3
- import { ThemeProvider } from './context/theme';
4
3
  import type { OrganizationDetail } from './lib/types';
5
4
  import {
6
5
  checkAndRefreshAuth,
@@ -14,12 +13,11 @@ import PrivacyCenterLayoutComponent from './components/PrivacyCenterLayout';
14
13
 
15
14
  interface PrivacyCenterLayoutProps {
16
15
  children: ReactNode;
17
- defaultTheme?: 'light' | 'dark';
18
16
  slug: string;
19
17
  onBack?: 'back' | 'signout';
20
18
  }
21
19
 
22
- export function PrivacyCenterLayout({ children, defaultTheme, slug, onBack }: PrivacyCenterLayoutProps) {
20
+ export function PrivacyCenterLayout({ children, slug, onBack }: PrivacyCenterLayoutProps) {
23
21
  const { consentClient } = useBaseUrl();
24
22
  // Read accessToken from AuthContext so this effect re-runs after AuthProvider
25
23
  // syncs JWT data to sessionStorage (fixes race condition where child effects
@@ -162,12 +160,10 @@ export function PrivacyCenterLayout({ children, defaultTheme, slug, onBack }: Pr
162
160
  }
163
161
 
164
162
  return (
165
- <ThemeProvider defaultTheme={defaultTheme}>
166
- <OrganizationProvider organization={organization}>
167
- <PrivacyCenterLayoutComponent onBack={onBack}>
168
- {children}
169
- </PrivacyCenterLayoutComponent>
170
- </OrganizationProvider>
171
- </ThemeProvider>
163
+ <OrganizationProvider organization={organization}>
164
+ <PrivacyCenterLayoutComponent onBack={onBack}>
165
+ {children}
166
+ </PrivacyCenterLayoutComponent>
167
+ </OrganizationProvider>
172
168
  );
173
169
  }
@@ -7,6 +7,7 @@ import { AuthProvider } from './context/AuthContext';
7
7
  import { NavigationProvider } from './context/NavigationContext';
8
8
  import type { InternalPage } from './context/NavigationContext';
9
9
  import { PageChromeProvider } from './context/PageChromeContext';
10
+ import { BrandingProvider } from './context/BrandingContext';
10
11
  import { injectPCStyles } from './styles/injectStyles';
11
12
 
12
13
  // Inject Privacy Center CSS styles on module load
@@ -15,7 +16,6 @@ injectPCStyles();
15
16
  interface RedactoPrivacyCenterProps {
16
17
  baseUrl: string; // Consent server base URL
17
18
  slug: string;
18
- theme?: 'light' | 'dark';
19
19
  accessToken: string;
20
20
  refreshToken: string;
21
21
  onError: (error: Error) => boolean;
@@ -26,7 +26,6 @@ interface RedactoPrivacyCenterProps {
26
26
  export function RedactoPrivacyCenter({
27
27
  baseUrl,
28
28
  slug,
29
- theme = 'light',
30
29
  accessToken,
31
30
  refreshToken,
32
31
  onError,
@@ -35,21 +34,23 @@ export function RedactoPrivacyCenter({
35
34
  }: RedactoPrivacyCenterProps) {
36
35
  return (
37
36
  <I18nextProvider i18n={i18n}>
38
- <BaseUrlProvider baseUrl={baseUrl}>
39
- <AuthProvider
40
- accessToken={accessToken}
41
- refreshToken={refreshToken}
42
- onError={onError}
43
- >
44
- <NavigationProvider initialPage={initialPage}>
45
- <PageChromeProvider>
46
- <PrivacyCenterLayout defaultTheme={theme} slug={slug} onBack={onBack}>
47
- <PrivacyCenterHomePage />
48
- </PrivacyCenterLayout>
49
- </PageChromeProvider>
50
- </NavigationProvider>
51
- </AuthProvider>
52
- </BaseUrlProvider>
37
+ <BrandingProvider slug={slug}>
38
+ <BaseUrlProvider baseUrl={baseUrl}>
39
+ <AuthProvider
40
+ accessToken={accessToken}
41
+ refreshToken={refreshToken}
42
+ onError={onError}
43
+ >
44
+ <NavigationProvider initialPage={initialPage}>
45
+ <PageChromeProvider>
46
+ <PrivacyCenterLayout slug={slug} onBack={onBack}>
47
+ <PrivacyCenterHomePage />
48
+ </PrivacyCenterLayout>
49
+ </PageChromeProvider>
50
+ </NavigationProvider>
51
+ </AuthProvider>
52
+ </BaseUrlProvider>
53
+ </BrandingProvider>
53
54
  </I18nextProvider>
54
55
  );
55
56
  }
@@ -39,6 +39,7 @@ import type {
39
39
  UploadCaseDocumentParams,
40
40
  UploadDocumentResponse,
41
41
  UserConsentDetail,
42
+ GroupPurposesOut,
42
43
  } from '../lib/types';
43
44
 
44
45
  function buildBaseRequest(organizationId: string, workspaceId: string, authToken?: string): BaseRequest {
@@ -266,9 +267,10 @@ export async function fetchProductsServer(params: {
266
267
  export async function fetchUserConsentsServer(params: {
267
268
  organizationId: string;
268
269
  workspaceId: string;
269
- contact: string;
270
270
  offset?: number;
271
271
  limit?: number;
272
+ nominatedOffset?: number;
273
+ nominatedLimit?: number;
272
274
  search?: string;
273
275
  status?: string;
274
276
  product_uuid?: string;
@@ -285,13 +287,16 @@ export async function fetchUserConsentsServer(params: {
285
287
 
286
288
  const result = await params.consentClient.getUserConsents(
287
289
  baseRequest,
288
- params.contact,
289
- params.offset,
290
- params.limit,
291
- params.search,
292
- params.status,
293
- params.product_uuid,
294
- params.language,
290
+ {
291
+ offset: params.offset,
292
+ limit: params.limit,
293
+ nominated_offset: params.nominatedOffset,
294
+ nominated_limit: params.nominatedLimit,
295
+ status: params.status,
296
+ product_uuid: params.product_uuid,
297
+ search: params.search,
298
+ language: params.language,
299
+ },
295
300
  params.authToken
296
301
  );
297
302
 
@@ -301,6 +306,45 @@ export async function fetchUserConsentsServer(params: {
301
306
  }
302
307
  }
303
308
 
309
+ export async function fetchGroupPurposesServer(params: {
310
+ organizationId: string;
311
+ workspaceId: string;
312
+ productUuid: string;
313
+ offset?: number;
314
+ limit?: number;
315
+ status?: string;
316
+ search?: string;
317
+ language?: string;
318
+ nominatorUuid?: string;
319
+ authToken?: string;
320
+ consentClient?: ReturnType<typeof import('./client').createConsentClient>;
321
+ }): Promise<GroupPurposesOut | null> {
322
+ try {
323
+ if (!params.consentClient) {
324
+ throw new Error('Consent client is required');
325
+ }
326
+ const baseRequest = buildBaseRequest(params.organizationId, params.workspaceId, params.authToken);
327
+
328
+ const result = await params.consentClient.getGroupPurposes(
329
+ baseRequest,
330
+ params.productUuid,
331
+ {
332
+ offset: params.offset,
333
+ limit: params.limit,
334
+ status: params.status,
335
+ search: params.search,
336
+ language: params.language,
337
+ nominator_uuid: params.nominatorUuid,
338
+ },
339
+ params.authToken
340
+ );
341
+
342
+ return result as GroupPurposesOut;
343
+ } catch (error: any) {
344
+ return handleServerActionError(error, 'fetching group purposes');
345
+ }
346
+ }
347
+
304
348
  export async function manageConsentServer(params: {
305
349
  organizationId: string;
306
350
  workspaceId: string;
@@ -338,29 +338,72 @@ export function createConsentClient(baseUrl: string) {
338
338
  */
339
339
  async getUserConsents(
340
340
  baseRequest: BaseRequest,
341
- contact: string,
342
- offset?: number,
343
- limit?: number,
344
- search?: string,
345
- status?: string,
346
- product_uuid?: string,
347
- language?: string,
341
+ params: {
342
+ offset?: number;
343
+ limit?: number;
344
+ nominated_offset?: number;
345
+ nominated_limit?: number;
346
+ status?: string;
347
+ product_uuid?: string;
348
+ search?: string;
349
+ language?: string;
350
+ },
348
351
  authToken?: string,
349
352
  options?: Omit<ApiFetchOptions, "getAuthToken">
350
353
  ): Promise<unknown> {
351
354
  const queryParams = new URLSearchParams();
352
- if (offset !== undefined) queryParams.append("offset", offset.toString());
353
- if (limit !== undefined) queryParams.append("limit", limit.toString());
354
- if (search) queryParams.append("search", search);
355
- if (status) queryParams.append("status", status);
356
- if (product_uuid) queryParams.append("product_uuid", product_uuid);
357
- if (language) queryParams.append("language", language);
355
+ if (params.offset !== undefined) queryParams.append("offset", params.offset.toString());
356
+ if (params.limit !== undefined) queryParams.append("limit", params.limit.toString());
357
+ if (params.nominated_offset !== undefined) queryParams.append("nominated_offset", params.nominated_offset.toString());
358
+ if (params.nominated_limit !== undefined) queryParams.append("nominated_limit", params.nominated_limit.toString());
359
+ if (params.search) queryParams.append("search", params.search);
360
+ if (params.status) queryParams.append("status", params.status);
361
+ if (params.product_uuid) queryParams.append("product_uuid", params.product_uuid);
362
+ if (params.language) queryParams.append("language", params.language);
358
363
  const queryString = queryParams.toString();
359
364
  const url = privacyCenterUrl(baseRequest, `/user-consents${queryString ? `?${queryString}` : ""}`);
360
365
  return privacyCenterApiFetch(url, {
361
366
  ...options,
362
- method: "POST",
363
- body: JSON.stringify({ contact }),
367
+ method: "GET",
368
+ cache: "no-store",
369
+ authToken: resolveAuthToken(authToken, baseRequest),
370
+ });
371
+ },
372
+
373
+ /**
374
+ * Get purposes for a single product card (privacy center) — sister endpoint
375
+ * for lazy-loading purposes when a card returns has_more_purposes: true.
376
+ */
377
+ async getGroupPurposes(
378
+ baseRequest: BaseRequest,
379
+ productUuid: string,
380
+ params: {
381
+ offset?: number;
382
+ limit?: number;
383
+ status?: string;
384
+ search?: string;
385
+ language?: string;
386
+ nominator_uuid?: string;
387
+ },
388
+ authToken?: string,
389
+ options?: Omit<ApiFetchOptions, "getAuthToken">
390
+ ): Promise<unknown> {
391
+ const queryParams = new URLSearchParams();
392
+ if (params.offset !== undefined) queryParams.append("offset", params.offset.toString());
393
+ if (params.limit !== undefined) queryParams.append("limit", params.limit.toString());
394
+ if (params.status) queryParams.append("status", params.status);
395
+ if (params.search) queryParams.append("search", params.search);
396
+ if (params.language) queryParams.append("language", params.language);
397
+ if (params.nominator_uuid) queryParams.append("nominator_uuid", params.nominator_uuid);
398
+ const queryString = queryParams.toString();
399
+ const url = privacyCenterUrl(
400
+ baseRequest,
401
+ `/user-consents/${productUuid}/purposes${queryString ? `?${queryString}` : ""}`
402
+ );
403
+ return privacyCenterApiFetch(url, {
404
+ ...options,
405
+ method: "GET",
406
+ cache: "no-store",
364
407
  authToken: resolveAuthToken(authToken, baseRequest),
365
408
  });
366
409
  },
@@ -0,0 +1,9 @@
1
+ import { apiFetch } from './fetcher';
2
+ import type { OrgBrandingDetail } from '../lib/types';
3
+
4
+ const USER_SERVER_BASE_URL = 'https://api.redacto.tech/user/api/0';
5
+
6
+ export async function fetchOrgBranding(slug: string): Promise<OrgBrandingDetail> {
7
+ const url = `${USER_SERVER_BASE_URL}/public/org-branding/by-slug/${encodeURIComponent(slug)}`;
8
+ return apiFetch<OrgBrandingDetail>(url, { method: 'GET' });
9
+ }