@redacto.io/consent-sdk-react 5.2.0 → 6.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 (84) hide show
  1. package/.turbo/turbo-build.log +24 -0
  2. package/CHANGELOG.md +12 -0
  3. package/dist/index.d.mts +24 -1
  4. package/dist/index.d.ts +24 -1
  5. package/dist/index.js +149 -206
  6. package/dist/index.mjs +148 -206
  7. package/dist/privacy-center.d.mts +178 -39
  8. package/dist/privacy-center.d.ts +178 -39
  9. package/dist/privacy-center.js +10534 -6795
  10. package/dist/privacy-center.mjs +7922 -4181
  11. package/package.json +1 -1
  12. package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +16 -20
  13. package/src/RedactoNoticeConsent/api/index.ts +21 -103
  14. package/src/RedactoNoticeConsentInline/RedactoNoticeConsentInline.tsx +5 -4
  15. package/src/RedactoNoticeConsentInline/api/index.ts +64 -117
  16. package/src/RedactoPrivacyCenter/NotificationsPage.tsx +174 -0
  17. package/src/RedactoPrivacyCenter/PrivacyCenterHomePage.tsx +3 -0
  18. package/src/RedactoPrivacyCenter/api/actions.ts +136 -8
  19. package/src/RedactoPrivacyCenter/api/client.ts +176 -15
  20. package/src/RedactoPrivacyCenter/components/ConsentManager.tsx +722 -273
  21. package/src/RedactoPrivacyCenter/components/ConsentManagerCells.tsx +187 -0
  22. package/src/RedactoPrivacyCenter/components/ConsentManagerColumns.tsx +65 -165
  23. package/src/RedactoPrivacyCenter/components/ConsentManagerExpandedRow.tsx +179 -0
  24. package/src/RedactoPrivacyCenter/components/ConsentManagerGroupedColumns.tsx +318 -0
  25. package/src/RedactoPrivacyCenter/components/ConsentManagerProductCards.tsx +333 -0
  26. package/src/RedactoPrivacyCenter/components/ConsentManagerPurposeCards.tsx +102 -0
  27. package/src/RedactoPrivacyCenter/components/ConsentManagerTableSkeleton.tsx +6 -2
  28. package/src/RedactoPrivacyCenter/components/DataRequest/PurposeSelectionSection.tsx +15 -0
  29. package/src/RedactoPrivacyCenter/components/ModifyConsentModal.tsx +8 -5
  30. package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationBell.tsx +160 -0
  31. package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationCTAButton.tsx +18 -0
  32. package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationDropdown.tsx +54 -0
  33. package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationDropdownItem.tsx +57 -0
  34. package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationIcon.tsx +19 -0
  35. package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationListPanel.tsx +92 -0
  36. package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationRefreshButton.tsx +20 -0
  37. package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationRow.tsx +58 -0
  38. package/src/RedactoPrivacyCenter/components/NotificationHub/StatCard.tsx +10 -0
  39. package/src/RedactoPrivacyCenter/components/NotificationHub/useNotificationActions.ts +112 -0
  40. package/src/RedactoPrivacyCenter/components/NotificationHub/utils.ts +27 -0
  41. package/src/RedactoPrivacyCenter/components/PrivacyCenterLayout.tsx +105 -55
  42. package/src/RedactoPrivacyCenter/components/types.ts +2 -5
  43. package/src/RedactoPrivacyCenter/context/NavigationContext.tsx +2 -2
  44. package/src/RedactoPrivacyCenter/context/PageChromeContext.tsx +21 -1
  45. package/src/RedactoPrivacyCenter/lib/api.ts +147 -28
  46. package/src/RedactoPrivacyCenter/lib/consent-manager/utils.tsx +4 -2
  47. package/src/RedactoPrivacyCenter/lib/constants.ts +78 -1
  48. package/src/RedactoPrivacyCenter/lib/types.ts +205 -36
  49. package/src/RedactoPrivacyCenter/lib/utils.ts +3 -0
  50. package/src/RedactoPrivacyCenter/locales/as/translation.json +12 -1
  51. package/src/RedactoPrivacyCenter/locales/bho/translation.json +12 -1
  52. package/src/RedactoPrivacyCenter/locales/bn/translation.json +12 -1
  53. package/src/RedactoPrivacyCenter/locales/brx/translation.json +12 -1
  54. package/src/RedactoPrivacyCenter/locales/doi/translation.json +12 -1
  55. package/src/RedactoPrivacyCenter/locales/en/translation.json +42 -1
  56. package/src/RedactoPrivacyCenter/locales/gom/translation.json +12 -1
  57. package/src/RedactoPrivacyCenter/locales/gu/translation.json +12 -1
  58. package/src/RedactoPrivacyCenter/locales/hi/translation.json +15 -1
  59. package/src/RedactoPrivacyCenter/locales/kn/translation.json +12 -1
  60. package/src/RedactoPrivacyCenter/locales/ks/translation.json +12 -1
  61. package/src/RedactoPrivacyCenter/locales/mai/translation.json +12 -1
  62. package/src/RedactoPrivacyCenter/locales/ml/translation.json +12 -1
  63. package/src/RedactoPrivacyCenter/locales/mni-Mtei/translation.json +12 -1
  64. package/src/RedactoPrivacyCenter/locales/mr/translation.json +12 -1
  65. package/src/RedactoPrivacyCenter/locales/ne/translation.json +12 -1
  66. package/src/RedactoPrivacyCenter/locales/or/translation.json +12 -1
  67. package/src/RedactoPrivacyCenter/locales/pa/translation.json +12 -1
  68. package/src/RedactoPrivacyCenter/locales/sa/translation.json +12 -1
  69. package/src/RedactoPrivacyCenter/locales/sat/translation.json +12 -1
  70. package/src/RedactoPrivacyCenter/locales/sd/translation.json +12 -1
  71. package/src/RedactoPrivacyCenter/locales/ta/translation.json +12 -1
  72. package/src/RedactoPrivacyCenter/locales/te/translation.json +12 -1
  73. package/src/RedactoPrivacyCenter/locales/ur/translation.json +12 -1
  74. package/src/RedactoPrivacyCenter/styles/injectStyles.ts +1027 -0
  75. package/src/RedactoPrivacyCenter/styles/variables.css +5 -0
  76. package/src/RedactoPrivacyCenter/table/AdvancedTable.tsx +36 -16
  77. package/src/RedactoPrivacyCenter/table/AdvancedTablePagination.tsx +4 -12
  78. package/src/RedactoPrivacyCenter/ui/PCInput.tsx +1 -1
  79. package/src/RedactoPrivacyCenter/ui/PCPopover.tsx +23 -1
  80. package/src/RedactoPrivacyCenter/ui/PCSelect.tsx +51 -5
  81. package/src/RedactoPrivacyCenter/ui/ThemeToggle.tsx +2 -4
  82. package/src/index.ts +5 -0
  83. package/src/privacy-center.ts +8 -3
  84. package/src/shared/api-errors.ts +171 -0
@@ -22,15 +22,24 @@ import type {
22
22
  GetCaseDocumentParams,
23
23
  GetCaseMessagesParams,
24
24
  MessageDocument,
25
+ GetNotificationsServerParams,
26
+ NotificationActionBaseParams,
27
+ NotificationActionResponse,
28
+ NotificationByUuidServerParams,
29
+ NotificationClient,
30
+ NotificationListResponse,
31
+ NotificationSummary,
25
32
  OtpResponse,
26
33
  OtpVerificationResponse,
27
34
  Pagination,
28
35
  SendCaseMessageParams,
29
36
  SubmitDocumentForRequestParams,
30
37
  TokenExchangeResponse,
38
+ UnreadCountResponse,
31
39
  UploadCaseDocumentParams,
32
40
  UploadDocumentResponse,
33
41
  UserConsentDetail,
42
+ GroupPurposesOut,
34
43
  } from '../lib/types';
35
44
 
36
45
  function buildBaseRequest(organizationId: string, workspaceId: string, authToken?: string): BaseRequest {
@@ -258,9 +267,10 @@ export async function fetchProductsServer(params: {
258
267
  export async function fetchUserConsentsServer(params: {
259
268
  organizationId: string;
260
269
  workspaceId: string;
261
- contact: string;
262
270
  offset?: number;
263
271
  limit?: number;
272
+ nominatedOffset?: number;
273
+ nominatedLimit?: number;
264
274
  search?: string;
265
275
  status?: string;
266
276
  product_uuid?: string;
@@ -277,13 +287,16 @@ export async function fetchUserConsentsServer(params: {
277
287
 
278
288
  const result = await params.consentClient.getUserConsents(
279
289
  baseRequest,
280
- params.contact,
281
- params.offset,
282
- params.limit,
283
- params.search,
284
- params.status,
285
- params.product_uuid,
286
- 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
+ },
287
300
  params.authToken
288
301
  );
289
302
 
@@ -293,6 +306,45 @@ export async function fetchUserConsentsServer(params: {
293
306
  }
294
307
  }
295
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
+
296
348
  export async function manageConsentServer(params: {
297
349
  organizationId: string;
298
350
  workspaceId: string;
@@ -474,3 +526,79 @@ export async function getCaseDocumentServer(params: GetCaseDocumentParams): Prom
474
526
  client.getCaseDocument(base, params.caseUuid, params.documentUuid, params.authToken)
475
527
  );
476
528
  }
529
+
530
+ // ─── Notification Hub ───────────────────────────────────────────────────────
531
+
532
+
533
+
534
+ async function runNotificationAction<T>(
535
+ params: NotificationActionBaseParams,
536
+ label: string,
537
+ fn: (client: NotificationClient, baseRequest: BaseRequest) => Promise<T>
538
+ ): Promise<T | null> {
539
+ try {
540
+ if (!params.consentClient) {
541
+ throw new Error('Consent client is required')
542
+ }
543
+ const baseRequest = buildBaseRequest(params.organizationId, params.workspaceId, params.authToken);
544
+ return await fn(params.consentClient, baseRequest);
545
+ } catch (error) {
546
+ return handleServerActionError(error, label);
547
+ }
548
+ }
549
+
550
+ export async function getNotificationsServer(
551
+ params: GetNotificationsServerParams
552
+ ): Promise<NotificationListResponse | null> {
553
+ return runNotificationAction(params, 'fetching notifications', (client, base) =>
554
+ client.getNotifications(base, {
555
+ category: params.category,
556
+ notification_type: params.notification_type,
557
+ is_read: params.is_read,
558
+ skip: params.skip,
559
+ limit: params.limit,
560
+ include_summary: params.include_summary,
561
+ language: params.language,
562
+ }, params.authToken)
563
+ );
564
+ }
565
+
566
+ export async function getNotificationSummaryServer(
567
+ params: NotificationActionBaseParams
568
+ ): Promise<NotificationSummary | null> {
569
+ return runNotificationAction(params, 'fetching notification summary', (client, base) =>
570
+ client.getNotificationSummary(base, params.authToken)
571
+ );
572
+ }
573
+
574
+ export async function getUnreadCountServer(
575
+ params: NotificationActionBaseParams
576
+ ): Promise<UnreadCountResponse | null> {
577
+ return runNotificationAction(params, 'fetching unread count', (client, base) =>
578
+ client.getUnreadCount(base, params.authToken)
579
+ );
580
+ }
581
+
582
+ export async function markNotificationReadServer(
583
+ params: NotificationByUuidServerParams
584
+ ): Promise<NotificationActionResponse | null> {
585
+ return runNotificationAction(params, 'marking notification as read', (client, base) =>
586
+ client.markNotificationRead(base, params.notificationUuid, params.authToken)
587
+ );
588
+ }
589
+
590
+ export async function markAllNotificationsReadServer(
591
+ params: NotificationActionBaseParams
592
+ ): Promise<NotificationActionResponse | null> {
593
+ return runNotificationAction(params, 'marking all notifications as read', (client, base) =>
594
+ client.markAllNotificationsRead(base, params.authToken)
595
+ );
596
+ }
597
+
598
+ export async function acknowledgeNotificationServer(
599
+ params: NotificationByUuidServerParams
600
+ ): Promise<NotificationActionResponse | null> {
601
+ return runNotificationAction(params, 'acknowledging notification', (client, base) =>
602
+ client.acknowledgeNotification(base, params.notificationUuid, params.authToken)
603
+ );
604
+ }
@@ -3,6 +3,11 @@ import type { ApiFetchOptions, ApiResponse } from "./fetcher";
3
3
  import type {
4
4
  CaseMessage,
5
5
  MessageDocument,
6
+ NotificationActionResponse,
7
+ NotificationListResponse,
8
+ NotificationQueryParams,
9
+ NotificationSummary,
10
+ UnreadCountResponse,
6
11
  UploadDocumentResponse,
7
12
  } from "../lib/types";
8
13
 
@@ -157,6 +162,10 @@ export function createConsentClient(baseUrl: string) {
157
162
  return privacyCenterUrl(baseRequest, `/cases/${caseUuid}${path}`);
158
163
  }
159
164
 
165
+ function notificationUrl(baseRequest: BaseRequest, path = ''): string {
166
+ return privacyCenterUrl(baseRequest, `/notifications${path}`);
167
+ }
168
+
160
169
  function resolveAuthToken(authToken: string | undefined, baseRequest: BaseRequest): string {
161
170
  return authToken || baseRequest.authToken;
162
171
  }
@@ -329,29 +338,72 @@ export function createConsentClient(baseUrl: string) {
329
338
  */
330
339
  async getUserConsents(
331
340
  baseRequest: BaseRequest,
332
- contact: string,
333
- offset?: number,
334
- limit?: number,
335
- search?: string,
336
- status?: string,
337
- product_uuid?: string,
338
- 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
+ },
339
351
  authToken?: string,
340
352
  options?: Omit<ApiFetchOptions, "getAuthToken">
341
353
  ): Promise<unknown> {
342
354
  const queryParams = new URLSearchParams();
343
- if (offset !== undefined) queryParams.append("offset", offset.toString());
344
- if (limit !== undefined) queryParams.append("limit", limit.toString());
345
- if (search) queryParams.append("search", search);
346
- if (status) queryParams.append("status", status);
347
- if (product_uuid) queryParams.append("product_uuid", product_uuid);
348
- 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);
349
363
  const queryString = queryParams.toString();
350
364
  const url = privacyCenterUrl(baseRequest, `/user-consents${queryString ? `?${queryString}` : ""}`);
351
365
  return privacyCenterApiFetch(url, {
352
366
  ...options,
353
- method: "POST",
354
- 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",
355
407
  authToken: resolveAuthToken(authToken, baseRequest),
356
408
  });
357
409
  },
@@ -520,5 +572,114 @@ export function createConsentClient(baseUrl: string) {
520
572
  authToken: resolveAuthToken(authToken, baseRequest),
521
573
  });
522
574
  },
575
+
576
+ // ─── Notification Hub ─────────────────────────────────────────────────
577
+
578
+ /**
579
+ * List notifications (paginated, with filters)
580
+ */
581
+ async getNotifications(
582
+ baseRequest: BaseRequest,
583
+ params?: NotificationQueryParams,
584
+ authToken?: string
585
+ ): Promise<NotificationListResponse> {
586
+ const queryParams = new URLSearchParams();
587
+ if (params?.category) {
588
+ queryParams.append('category', params.category);
589
+ }
590
+ if (params?.notification_type) {
591
+ queryParams.append('notification_type', params.notification_type);
592
+ }
593
+ if (params?.is_read !== undefined) {
594
+ queryParams.append('is_read', String(params.is_read));
595
+ }
596
+ if (params?.skip !== undefined) {
597
+ queryParams.append('skip', params.skip.toString());
598
+ }
599
+ if (params?.limit !== undefined) {
600
+ queryParams.append('limit', params.limit.toString());
601
+ }
602
+ if (params?.include_summary) {
603
+ queryParams.append('include_summary', 'true');
604
+ }
605
+ if (params?.language) {
606
+ queryParams.append('language', params.language);
607
+ }
608
+ const queryString = queryParams.toString();
609
+ const url = notificationUrl(baseRequest, queryString ? `?${queryString}` : '');
610
+ return privacyCenterApiFetch<NotificationListResponse>(url, {
611
+ authToken: resolveAuthToken(authToken, baseRequest),
612
+ });
613
+ },
614
+
615
+ /**
616
+ * Get notification summary (aggregated counts only)
617
+ */
618
+ async getNotificationSummary(
619
+ baseRequest: BaseRequest,
620
+ authToken?: string
621
+ ): Promise<NotificationSummary> {
622
+ const url = notificationUrl(baseRequest, '/summary');
623
+ return privacyCenterApiFetch<NotificationSummary>(url, {
624
+ authToken: resolveAuthToken(authToken, baseRequest),
625
+ });
626
+ },
627
+
628
+ /**
629
+ * Get unread notification count (lightweight, for badge)
630
+ */
631
+ async getUnreadCount(
632
+ baseRequest: BaseRequest,
633
+ authToken?: string
634
+ ): Promise<UnreadCountResponse> {
635
+ const url = notificationUrl(baseRequest, '/unread-count');
636
+ return privacyCenterApiFetch<UnreadCountResponse>(url, {
637
+ authToken: resolveAuthToken(authToken, baseRequest),
638
+ });
639
+ },
640
+
641
+ /**
642
+ * Mark a single notification as read
643
+ */
644
+ async markNotificationRead(
645
+ baseRequest: BaseRequest,
646
+ notificationUuid: string,
647
+ authToken?: string
648
+ ): Promise<NotificationActionResponse> {
649
+ const url = notificationUrl(baseRequest, `/${notificationUuid}/read`);
650
+ return privacyCenterApiFetch<NotificationActionResponse>(url, {
651
+ method: 'PATCH',
652
+ authToken: resolveAuthToken(authToken, baseRequest),
653
+ });
654
+ },
655
+
656
+ /**
657
+ * Mark all notifications as read
658
+ */
659
+ async markAllNotificationsRead(
660
+ baseRequest: BaseRequest,
661
+ authToken?: string
662
+ ): Promise<NotificationActionResponse> {
663
+ const url = notificationUrl(baseRequest, '/mark-all-read');
664
+ return privacyCenterApiFetch<NotificationActionResponse>(url, {
665
+ method: 'POST',
666
+ authToken: resolveAuthToken(authToken, baseRequest),
667
+ });
668
+ },
669
+
670
+ /**
671
+ * Acknowledge a notification (sets is_read and is_acknowledged to true)
672
+ */
673
+ async acknowledgeNotification(
674
+ baseRequest: BaseRequest,
675
+ notificationUuid: string,
676
+ authToken?: string
677
+ ): Promise<NotificationActionResponse> {
678
+ const url = notificationUrl(baseRequest, `/${notificationUuid}/acknowledge`);
679
+ return privacyCenterApiFetch<NotificationActionResponse>(url, {
680
+ method: 'POST',
681
+ authToken: resolveAuthToken(authToken, baseRequest),
682
+ });
683
+ },
523
684
  };
524
685
  }