@redacto.io/consent-sdk-react 3.5.0 → 3.6.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 (152) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/LICENSE +176 -0
  3. package/dist/index.d.mts +51 -4
  4. package/dist/index.d.ts +51 -4
  5. package/dist/index.js +1891 -243
  6. package/dist/index.mjs +1890 -243
  7. package/dist/privacy-center.d.mts +566 -0
  8. package/dist/privacy-center.d.ts +566 -0
  9. package/dist/privacy-center.js +17174 -0
  10. package/dist/privacy-center.mjs +17136 -0
  11. package/package.json +47 -3
  12. package/src/RedactoNoticeConsent/RedactoNoticeConsent.test.tsx +201 -118
  13. package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +1194 -93
  14. package/src/RedactoNoticeConsent/api/index.ts +335 -206
  15. package/src/RedactoNoticeConsent/api/types.ts +140 -16
  16. package/src/RedactoNoticeConsent/components/DigiLockerCallback.tsx +602 -0
  17. package/src/RedactoNoticeConsent/components/index.ts +7 -0
  18. package/src/RedactoNoticeConsent/injectStyles.ts +11 -0
  19. package/src/RedactoNoticeConsent/language-codes.ts +61 -0
  20. package/src/RedactoNoticeConsent/services/storage.ts +122 -0
  21. package/src/RedactoNoticeConsent/types.ts +2 -0
  22. package/src/RedactoPrivacyCenter/PrivacyCenterActivityPage.tsx +19 -0
  23. package/src/RedactoPrivacyCenter/PrivacyCenterConsentManagerPage.tsx +17 -0
  24. package/src/RedactoPrivacyCenter/PrivacyCenterFormPage.tsx +79 -0
  25. package/src/RedactoPrivacyCenter/PrivacyCenterHomePage.tsx +27 -0
  26. package/src/RedactoPrivacyCenter/PrivacyCenterLayout.tsx +142 -0
  27. package/src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx +51 -0
  28. package/src/RedactoPrivacyCenter/api/actions.ts +444 -0
  29. package/src/RedactoPrivacyCenter/api/client.ts +413 -0
  30. package/src/RedactoPrivacyCenter/api/config.ts +33 -0
  31. package/src/RedactoPrivacyCenter/api/fetcher.ts +181 -0
  32. package/src/RedactoPrivacyCenter/api/upload.ts +93 -0
  33. package/src/RedactoPrivacyCenter/assets/character.svg +31 -0
  34. package/src/RedactoPrivacyCenter/assets/fileAttach.svg +9 -0
  35. package/src/RedactoPrivacyCenter/assets/greenTick.svg +4 -0
  36. package/src/RedactoPrivacyCenter/assets/mail.svg +3 -0
  37. package/src/RedactoPrivacyCenter/assets/poweredByRedacto.svg +9 -0
  38. package/src/RedactoPrivacyCenter/assets/sentImg.svg +9 -0
  39. package/src/RedactoPrivacyCenter/assets/uploadFileIcon.svg +9 -0
  40. package/src/RedactoPrivacyCenter/components/ActivitiesList.tsx +272 -0
  41. package/src/RedactoPrivacyCenter/components/CaseDetailsModal.tsx +468 -0
  42. package/src/RedactoPrivacyCenter/components/CaseHistory.tsx +433 -0
  43. package/src/RedactoPrivacyCenter/components/ConsentManager.tsx +726 -0
  44. package/src/RedactoPrivacyCenter/components/ConsentManagerColumns.tsx +239 -0
  45. package/src/RedactoPrivacyCenter/components/ConsentManagerConstants.ts +7 -0
  46. package/src/RedactoPrivacyCenter/components/ConsentManagerTableSkeleton.tsx +155 -0
  47. package/src/RedactoPrivacyCenter/components/DataRequest/CorrectionDataSection.tsx +189 -0
  48. package/src/RedactoPrivacyCenter/components/DataRequest/GrievanceDataSection.tsx +118 -0
  49. package/src/RedactoPrivacyCenter/components/DataRequest/NominationDataSection.tsx +66 -0
  50. package/src/RedactoPrivacyCenter/components/DataRequest/PurposeSelectionSection.tsx +305 -0
  51. package/src/RedactoPrivacyCenter/components/Form.tsx +1455 -0
  52. package/src/RedactoPrivacyCenter/components/GrievanceTable.tsx +176 -0
  53. package/src/RedactoPrivacyCenter/components/GrievanceTablePagination.tsx +330 -0
  54. package/src/RedactoPrivacyCenter/components/ModifyConsentModal.tsx +329 -0
  55. package/src/RedactoPrivacyCenter/components/Pagination.tsx +140 -0
  56. package/src/RedactoPrivacyCenter/components/PrivacyCenterLayout.tsx +302 -0
  57. package/src/RedactoPrivacyCenter/components/RequestSubmit.tsx +218 -0
  58. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideBarPC.tsx +475 -0
  59. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideMenuPC.tsx +412 -0
  60. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/constants.tsx +42 -0
  61. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/menuItemsPC.tsx +1 -0
  62. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/types.ts +40 -0
  63. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/utils.ts +38 -0
  64. package/src/RedactoPrivacyCenter/components/ThemeTransitionOverlay.tsx +85 -0
  65. package/src/RedactoPrivacyCenter/components/activitiesColumns.tsx +141 -0
  66. package/src/RedactoPrivacyCenter/components/caseHistoryColumns.tsx +179 -0
  67. package/src/RedactoPrivacyCenter/components/constant.ts +20 -0
  68. package/src/RedactoPrivacyCenter/components/types.ts +68 -0
  69. package/src/RedactoPrivacyCenter/components/utils.tsx +10 -0
  70. package/src/RedactoPrivacyCenter/config/routes.ts +9 -0
  71. package/src/RedactoPrivacyCenter/config/table.ts +25 -0
  72. package/src/RedactoPrivacyCenter/context/AuthContext.tsx +123 -0
  73. package/src/RedactoPrivacyCenter/context/BaseUrlContext.tsx +44 -0
  74. package/src/RedactoPrivacyCenter/context/LocaleContext.tsx +11 -0
  75. package/src/RedactoPrivacyCenter/context/NavigationContext.tsx +64 -0
  76. package/src/RedactoPrivacyCenter/context/OrganizationContext.tsx +30 -0
  77. package/src/RedactoPrivacyCenter/context/PageChromeContext.tsx +60 -0
  78. package/src/RedactoPrivacyCenter/context/theme.tsx +127 -0
  79. package/src/RedactoPrivacyCenter/lib/api.ts +468 -0
  80. package/src/RedactoPrivacyCenter/lib/consent-manager/utils.tsx +63 -0
  81. package/src/RedactoPrivacyCenter/lib/constants.ts +116 -0
  82. package/src/RedactoPrivacyCenter/lib/i18n.ts +81 -0
  83. package/src/RedactoPrivacyCenter/lib/locales.ts +105 -0
  84. package/src/RedactoPrivacyCenter/lib/queries.ts +30 -0
  85. package/src/RedactoPrivacyCenter/lib/types.ts +346 -0
  86. package/src/RedactoPrivacyCenter/lib/useCurrentLanguage.ts +37 -0
  87. package/src/RedactoPrivacyCenter/lib/utils.ts +625 -0
  88. package/src/RedactoPrivacyCenter/locales/as/translation.json +188 -0
  89. package/src/RedactoPrivacyCenter/locales/bho/translation.json +188 -0
  90. package/src/RedactoPrivacyCenter/locales/bn/translation.json +188 -0
  91. package/src/RedactoPrivacyCenter/locales/brx/translation.json +188 -0
  92. package/src/RedactoPrivacyCenter/locales/doi/translation.json +188 -0
  93. package/src/RedactoPrivacyCenter/locales/en/translation.json +188 -0
  94. package/src/RedactoPrivacyCenter/locales/gom/translation.json +188 -0
  95. package/src/RedactoPrivacyCenter/locales/gu/translation.json +188 -0
  96. package/src/RedactoPrivacyCenter/locales/hi/translation.json +188 -0
  97. package/src/RedactoPrivacyCenter/locales/kn/translation.json +188 -0
  98. package/src/RedactoPrivacyCenter/locales/ks/translation.json +188 -0
  99. package/src/RedactoPrivacyCenter/locales/mai/translation.json +188 -0
  100. package/src/RedactoPrivacyCenter/locales/ml/translation.json +188 -0
  101. package/src/RedactoPrivacyCenter/locales/mni-Mtei/translation.json +188 -0
  102. package/src/RedactoPrivacyCenter/locales/mr/translation.json +188 -0
  103. package/src/RedactoPrivacyCenter/locales/ne/translation.json +188 -0
  104. package/src/RedactoPrivacyCenter/locales/or/translation.json +188 -0
  105. package/src/RedactoPrivacyCenter/locales/pa/translation.json +188 -0
  106. package/src/RedactoPrivacyCenter/locales/sa/translation.json +188 -0
  107. package/src/RedactoPrivacyCenter/locales/sat/translation.json +188 -0
  108. package/src/RedactoPrivacyCenter/locales/sd/translation.json +188 -0
  109. package/src/RedactoPrivacyCenter/locales/ta/translation.json +188 -0
  110. package/src/RedactoPrivacyCenter/locales/te/translation.json +188 -0
  111. package/src/RedactoPrivacyCenter/locales/ur/translation.json +188 -0
  112. package/src/RedactoPrivacyCenter/styles/base.css +117 -0
  113. package/src/RedactoPrivacyCenter/styles/injectStyles.ts +1000 -0
  114. package/src/RedactoPrivacyCenter/styles/pcStyles.ts +819 -0
  115. package/src/RedactoPrivacyCenter/styles/responsive.ts +138 -0
  116. package/src/RedactoPrivacyCenter/styles/variables.css +133 -0
  117. package/src/RedactoPrivacyCenter/table/AdvancedTable.tsx +365 -0
  118. package/src/RedactoPrivacyCenter/table/AdvancedTablePagination.tsx +479 -0
  119. package/src/RedactoPrivacyCenter/table/index.ts +2 -0
  120. package/src/RedactoPrivacyCenter/types/images.d.ts +29 -0
  121. package/src/RedactoPrivacyCenter/ui/Footer.tsx +73 -0
  122. package/src/RedactoPrivacyCenter/ui/FormBlockWrapper.tsx +54 -0
  123. package/src/RedactoPrivacyCenter/ui/PCAccordion.tsx +87 -0
  124. package/src/RedactoPrivacyCenter/ui/PCActionIcon.tsx +47 -0
  125. package/src/RedactoPrivacyCenter/ui/PCAvatar.tsx +69 -0
  126. package/src/RedactoPrivacyCenter/ui/PCBadge.tsx +48 -0
  127. package/src/RedactoPrivacyCenter/ui/PCButton.tsx +77 -0
  128. package/src/RedactoPrivacyCenter/ui/PCCheckbox.tsx +55 -0
  129. package/src/RedactoPrivacyCenter/ui/PCCollapse.tsx +97 -0
  130. package/src/RedactoPrivacyCenter/ui/PCEmpty.tsx +35 -0
  131. package/src/RedactoPrivacyCenter/ui/PCInput.tsx +128 -0
  132. package/src/RedactoPrivacyCenter/ui/PCLoader.tsx +30 -0
  133. package/src/RedactoPrivacyCenter/ui/PCModal.tsx +104 -0
  134. package/src/RedactoPrivacyCenter/ui/PCPopover.tsx +175 -0
  135. package/src/RedactoPrivacyCenter/ui/PCSelect.tsx +381 -0
  136. package/src/RedactoPrivacyCenter/ui/PCTable.tsx +137 -0
  137. package/src/RedactoPrivacyCenter/ui/PCText.tsx +39 -0
  138. package/src/RedactoPrivacyCenter/ui/PCTextarea.tsx +43 -0
  139. package/src/RedactoPrivacyCenter/ui/PCTitle.tsx +37 -0
  140. package/src/RedactoPrivacyCenter/ui/PCTooltip.tsx +119 -0
  141. package/src/RedactoPrivacyCenter/ui/SimpleBar.tsx +25 -0
  142. package/src/RedactoPrivacyCenter/ui/StatusBadge.tsx +55 -0
  143. package/src/RedactoPrivacyCenter/ui/ThemeToggle.tsx +70 -0
  144. package/src/RedactoPrivacyCenter/ui/index.ts +18 -0
  145. package/src/RedactoPrivacyCenter/utils/cn.ts +28 -0
  146. package/src/RedactoPrivacyCenter/utils/date.ts +48 -0
  147. package/src/RedactoPrivacyCenter/utils/status.ts +111 -0
  148. package/src/RedactoPrivacyCenter/utils/toast.ts +130 -0
  149. package/src/index.ts +10 -0
  150. package/src/privacy-center.ts +107 -0
  151. package/tsconfig.json +2 -0
  152. package/tsup.config.ts +16 -1
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Session Storage Service
3
+ *
4
+ * Handles state persistence for consent flow during DigiLocker redirects.
5
+ * Uses sessionStorage to maintain state across page navigations.
6
+ */
7
+
8
+ import type { PersistedConsentState } from "../api/types";
9
+
10
+ const STORAGE_KEY = "redacto_consent_sdk_state";
11
+ const STATE_EXPIRY_MS = 30 * 60 * 1000; // 30 minutes
12
+
13
+ /**
14
+ * Check if code is running in browser environment
15
+ */
16
+ const isBrowser = (): boolean => {
17
+ return typeof window !== "undefined" && typeof window.sessionStorage !== "undefined";
18
+ };
19
+
20
+ /**
21
+ * Save consent flow state to sessionStorage
22
+ */
23
+ export const saveConsentState = (state: PersistedConsentState): void => {
24
+ if (!isBrowser()) {
25
+ console.warn("SessionStorage not available - state not persisted");
26
+ return;
27
+ }
28
+
29
+ try {
30
+ const stateWithTimestamp = {
31
+ ...state,
32
+ timestamp: new Date().toISOString(),
33
+ };
34
+ sessionStorage.setItem(STORAGE_KEY, JSON.stringify(stateWithTimestamp));
35
+ } catch (error) {
36
+ console.error("Failed to save consent state:", error);
37
+ }
38
+ };
39
+
40
+ /**
41
+ * Retrieve consent flow state from sessionStorage
42
+ * Returns null if state doesn't exist or is expired
43
+ */
44
+ export const getConsentState = (): PersistedConsentState | null => {
45
+ if (!isBrowser()) {
46
+ return null;
47
+ }
48
+
49
+ try {
50
+ const savedState = sessionStorage.getItem(STORAGE_KEY);
51
+ if (!savedState) {
52
+ return null;
53
+ }
54
+
55
+ const parsed: PersistedConsentState = JSON.parse(savedState);
56
+
57
+ // Check if state is expired
58
+ const stateAge = Date.now() - new Date(parsed.timestamp).getTime();
59
+ if (stateAge > STATE_EXPIRY_MS) {
60
+ clearConsentState();
61
+ return null;
62
+ }
63
+
64
+ return parsed;
65
+ } catch (error) {
66
+ console.error("Failed to get consent state:", error);
67
+ return null;
68
+ }
69
+ };
70
+
71
+ /**
72
+ * Clear consent flow state from sessionStorage
73
+ */
74
+ export const clearConsentState = (): void => {
75
+ if (!isBrowser()) {
76
+ return;
77
+ }
78
+
79
+ try {
80
+ sessionStorage.removeItem(STORAGE_KEY);
81
+ } catch (error) {
82
+ console.error("Failed to clear consent state:", error);
83
+ }
84
+ };
85
+
86
+ /**
87
+ * Update specific fields in the persisted consent state
88
+ */
89
+ export const updateConsentState = (updates: Partial<PersistedConsentState>): void => {
90
+ const currentState = getConsentState();
91
+ if (currentState) {
92
+ saveConsentState({
93
+ ...currentState,
94
+ ...updates,
95
+ timestamp: new Date().toISOString(),
96
+ });
97
+ }
98
+ };
99
+
100
+ /**
101
+ * Check if there's a pending verification to resume
102
+ */
103
+ export const hasPendingVerification = (): boolean => {
104
+ const state = getConsentState();
105
+ return state !== null && state.flowState === "digilocker-pending";
106
+ };
107
+
108
+ /**
109
+ * Get verification result from persisted state
110
+ */
111
+ export const getVerificationResult = (): "pending" | "verified" | "failed" | "in_progress" | "expired" | null => {
112
+ const state = getConsentState();
113
+ return state?.verificationResult ?? null;
114
+ };
115
+
116
+ /**
117
+ * Get verification reference from persisted state (after successful verification)
118
+ */
119
+ export const getVerificationReference = (): string | null => {
120
+ const state = getConsentState();
121
+ return state?.verificationReference ?? null;
122
+ };
@@ -13,6 +13,8 @@ export type Props = Readonly<{
13
13
  onError?: (error: Error) => void;
14
14
  applicationId?: string;
15
15
  validateAgainst?: "all" | "required";
16
+ digilockerMode?: "popup" | "redirect"; // Default: "redirect" (full page redirect - required for DigiLocker)
17
+ digilockerCallbackUrl?: string; // Default: "/digilocker-callback"
16
18
  includeFullyConsentedData?: boolean;
17
19
  reviewModeButtonText?: string;
18
20
  }>;
@@ -0,0 +1,19 @@
1
+ import { useEffect } from 'react';
2
+ import i18n from './lib/i18n';
3
+ import ActivitiesList from './components/ActivitiesList';
4
+
5
+ export function PrivacyCenterActivityPage() {
6
+ useEffect(() => {
7
+ if (!i18n.language) {
8
+ i18n.changeLanguage('en');
9
+ }
10
+ }, []);
11
+
12
+ return (
13
+ <div style={{ width: '100%' }}>
14
+ <div style={{ paddingTop: '0.5rem' }}>
15
+ <ActivitiesList />
16
+ </div>
17
+ </div>
18
+ );
19
+ }
@@ -0,0 +1,17 @@
1
+ import { useEffect } from 'react';
2
+ import i18n from './lib/i18n';
3
+ import ConsentManager from './components/ConsentManager';
4
+
5
+ export function PrivacyCenterConsentManagerPage() {
6
+ useEffect(() => {
7
+ if (!i18n.language) {
8
+ i18n.changeLanguage('en');
9
+ }
10
+ }, []);
11
+
12
+ return (
13
+ <div style={{ width: '100%' }}>
14
+ <ConsentManager />
15
+ </div>
16
+ );
17
+ }
@@ -0,0 +1,79 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { PCButton } from './ui';
4
+ import { pcStyles } from './styles/pcStyles';
5
+ import { IoArrowBack } from 'react-icons/io5';
6
+ import i18n from './lib/i18n';
7
+ import Form from './components/Form';
8
+ import CaseHistory from './components/CaseHistory';
9
+ import { usePageChrome } from './context/PageChromeContext';
10
+
11
+ const getVar = (varName: string): string => `var(${varName})`;
12
+
13
+ export function PrivacyCenterFormPage() {
14
+ const { t } = useTranslation();
15
+ const [showForm, setShowForm] = useState(() => {
16
+ try {
17
+ return sessionStorage.getItem('pc_grievance_view') === 'form';
18
+ } catch {
19
+ return false;
20
+ }
21
+ });
22
+ const { setPageChromeOverride, clearPageChromeOverride } = usePageChrome();
23
+
24
+ useEffect(() => {
25
+ if (!i18n.language) {
26
+ i18n.changeLanguage('en');
27
+ }
28
+ }, []);
29
+
30
+ useEffect(() => {
31
+ try {
32
+ sessionStorage.setItem('pc_grievance_view', showForm ? 'form' : 'list');
33
+ } catch {
34
+ // sessionStorage unavailable
35
+ }
36
+ }, [showForm]);
37
+
38
+ useEffect(() => {
39
+ if (showForm) {
40
+ setPageChromeOverride({
41
+ titleKey: 'yourPrivacyCenter',
42
+ descriptionKey: 'yourRightsOverDataSimplified',
43
+ });
44
+ } else {
45
+ clearPageChromeOverride();
46
+ }
47
+
48
+ return () => {
49
+ clearPageChromeOverride();
50
+ };
51
+ }, [showForm, setPageChromeOverride, clearPageChromeOverride]);
52
+
53
+ return (
54
+ <div style={{ ...pcStyles.wFull, ...pcStyles.flex, ...pcStyles.flexCol, gap: pcStyles.gapLg.gap }}>
55
+ {showForm ? (
56
+ <>
57
+ <div style={pcStyles.mbMd}>
58
+ <PCButton
59
+ variant="text"
60
+ size="sm"
61
+ onClick={() => setShowForm(false)}
62
+ style={{ paddingLeft: 0, color: getVar('--pc-text-secondary') }}
63
+ >
64
+ <IoArrowBack size={16} style={{ marginRight: '0.5rem' }} />
65
+ {t('backToHome')}
66
+ </PCButton>
67
+ </div>
68
+ <div style={{ width: '100%' }}>
69
+ <Form onCreateRequest={() => setShowForm(false)} />
70
+ </div>
71
+ </>
72
+ ) : (
73
+ <div style={{ width: '100%' }}>
74
+ <CaseHistory onCreateRequest={() => setShowForm(true)} />
75
+ </div>
76
+ )}
77
+ </div>
78
+ );
79
+ }
@@ -0,0 +1,27 @@
1
+ import { useEffect } from 'react';
2
+ import i18n from './lib/i18n';
3
+ import { useInternalNavigation } from './context/NavigationContext';
4
+ import { PrivacyCenterConsentManagerPage } from './PrivacyCenterConsentManagerPage';
5
+ import { PrivacyCenterFormPage } from './PrivacyCenterFormPage';
6
+ import { PrivacyCenterActivityPage } from './PrivacyCenterActivityPage';
7
+
8
+ export function PrivacyCenterHomePage() {
9
+ const { currentPage } = useInternalNavigation();
10
+
11
+ useEffect(() => {
12
+ if (!i18n.language) {
13
+ i18n.changeLanguage('en');
14
+ }
15
+ }, []);
16
+
17
+ switch (currentPage) {
18
+ case 'consent-manager':
19
+ return <PrivacyCenterConsentManagerPage />;
20
+ case 'form':
21
+ return <PrivacyCenterFormPage />;
22
+ case 'activity':
23
+ return <PrivacyCenterActivityPage />;
24
+ default:
25
+ return <PrivacyCenterConsentManagerPage />;
26
+ }
27
+ }
@@ -0,0 +1,142 @@
1
+ import { useEffect, useState, useRef, ReactNode } from 'react';
2
+ import { OrganizationProvider } from './context/OrganizationContext';
3
+ import { ThemeProvider } from './context/theme';
4
+ import type { OrganizationDetail } from './lib/types';
5
+ import {
6
+ checkAndRefreshAuth,
7
+ decodeJwtToken,
8
+ } from './lib/utils';
9
+ import { useBaseUrl } from './context/BaseUrlContext';
10
+ import { PCLoader } from './ui';
11
+ import { pcStyles } from './styles/pcStyles';
12
+ import PrivacyCenterLayoutComponent from './components/PrivacyCenterLayout';
13
+
14
+ interface PrivacyCenterLayoutProps {
15
+ children: ReactNode;
16
+ defaultTheme?: 'light' | 'dark';
17
+ slug: string;
18
+ onBack?: 'back' | 'signout';
19
+ }
20
+
21
+ export function PrivacyCenterLayout({ children, defaultTheme, slug, onBack }: PrivacyCenterLayoutProps) {
22
+ const { consentClient } = useBaseUrl();
23
+ const [organization, setOrganization] = useState<OrganizationDetail | null>(null);
24
+ const [isCheckingAuth, setIsCheckingAuth] = useState(true);
25
+ const [isLoadingOrg, setIsLoadingOrg] = useState(true);
26
+ const hasRedirectedRef = useRef(false);
27
+ const authCheckInProgressRef = useRef(false);
28
+
29
+ useEffect(() => {
30
+ // Load organization info from sessionStorage
31
+ if (typeof window !== 'undefined' && slug) {
32
+ const accessToken = sessionStorage.getItem('access_token');
33
+ const orgId = sessionStorage.getItem('pc_org_id');
34
+ const workspaceId = sessionStorage.getItem('pc_workspace_id');
35
+ const orgSlug = sessionStorage.getItem('pc_org_slug');
36
+
37
+ // Create organization object from sessionStorage
38
+ if (accessToken && orgId && workspaceId && orgSlug === slug) {
39
+ // Prefer organisation_name from JWT; fall back to slug-derived name
40
+ const decoded = decodeJwtToken(accessToken);
41
+ const orgNameFromJwt = decoded?.organisation_name;
42
+ const orgName =
43
+ typeof orgNameFromJwt === 'string' && orgNameFromJwt.trim() !== ''
44
+ ? orgNameFromJwt
45
+ : slug
46
+ .split('-')
47
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
48
+ .join(' ');
49
+
50
+ setOrganization({
51
+ uuid: orgId,
52
+ suid: '',
53
+ name: orgName,
54
+ slug: slug,
55
+ description: '',
56
+ domain: '',
57
+ workspaces: [{
58
+ uuid: workspaceId,
59
+ suid: '',
60
+ name: '',
61
+ description: '',
62
+ workspace_type: '',
63
+ status: '',
64
+ }],
65
+ ai_summary: {},
66
+ icon_url: null,
67
+ activated: true,
68
+ allowed_domains: [],
69
+ });
70
+ setIsLoadingOrg(false);
71
+ } else {
72
+ // If organization info is not available, set loading to false
73
+ // User will need to authenticate first
74
+ setIsLoadingOrg(false);
75
+ }
76
+ }
77
+ }, [slug]);
78
+
79
+ useEffect(() => {
80
+ // Don't check auth while still loading organization
81
+ if (isLoadingOrg || !slug || !organization) {
82
+ return;
83
+ }
84
+
85
+ // Prevent multiple simultaneous auth checks
86
+ if (authCheckInProgressRef.current) {
87
+ return;
88
+ }
89
+
90
+ // Check authentication status with token refresh capability
91
+ const checkAuth = async () => {
92
+ authCheckInProgressRef.current = true;
93
+
94
+ try {
95
+ // Check if user is authenticated, attempt refresh if needed
96
+ const isAuthenticated = await checkAndRefreshAuth(
97
+ slug,
98
+ organization?.uuid,
99
+ organization?.workspaces[0]?.uuid,
100
+ consentClient
101
+ );
102
+
103
+ // User is authenticated, allow access
104
+ hasRedirectedRef.current = false;
105
+ setIsCheckingAuth(false);
106
+ } catch (error) {
107
+ // Auth check failed - user will need to provide tokens via props
108
+ setIsCheckingAuth(false);
109
+ } finally {
110
+ authCheckInProgressRef.current = false;
111
+ }
112
+ };
113
+
114
+ // Check auth after organization is loaded
115
+ checkAuth();
116
+ }, [slug, organization, isLoadingOrg, consentClient]);
117
+
118
+ // Show loading state while checking authentication or loading organization
119
+ if (isLoadingOrg || !organization || isCheckingAuth) {
120
+ return (
121
+ <div style={{
122
+ ...pcStyles.flex,
123
+ ...pcStyles.hScreen,
124
+ ...pcStyles.wScreen,
125
+ ...pcStyles.itemsCenter,
126
+ ...pcStyles.justifyCenter,
127
+ }}>
128
+ <PCLoader variant="spinner" color="info" size="lg" />
129
+ </div>
130
+ );
131
+ }
132
+
133
+ return (
134
+ <ThemeProvider defaultTheme={defaultTheme}>
135
+ <OrganizationProvider organization={organization}>
136
+ <PrivacyCenterLayoutComponent onBack={onBack}>
137
+ {children}
138
+ </PrivacyCenterLayoutComponent>
139
+ </OrganizationProvider>
140
+ </ThemeProvider>
141
+ );
142
+ }
@@ -0,0 +1,51 @@
1
+ import { I18nextProvider } from 'react-i18next';
2
+ import i18n from './lib/i18n';
3
+ import { PrivacyCenterLayout } from './PrivacyCenterLayout';
4
+ import { PrivacyCenterHomePage } from './PrivacyCenterHomePage';
5
+ import { BaseUrlProvider } from './context/BaseUrlContext';
6
+ import { AuthProvider } from './context/AuthContext';
7
+ import { NavigationProvider } from './context/NavigationContext';
8
+ import type { InternalPage } from './context/NavigationContext';
9
+ import { PageChromeProvider } from './context/PageChromeContext';
10
+
11
+ interface RedactoPrivacyCenterProps {
12
+ baseUrl: string; // Consent server base URL
13
+ slug: string;
14
+ theme?: 'light' | 'dark';
15
+ accessToken: string;
16
+ refreshToken: string;
17
+ onError: (error: Error) => boolean;
18
+ initialPage?: InternalPage;
19
+ onBack?: 'back' | 'signout';
20
+ }
21
+
22
+ export function RedactoPrivacyCenter({
23
+ baseUrl,
24
+ slug,
25
+ theme = 'light',
26
+ accessToken,
27
+ refreshToken,
28
+ onError,
29
+ initialPage = 'consent-manager',
30
+ onBack,
31
+ }: RedactoPrivacyCenterProps) {
32
+ return (
33
+ <I18nextProvider i18n={i18n}>
34
+ <BaseUrlProvider baseUrl={baseUrl}>
35
+ <AuthProvider
36
+ accessToken={accessToken}
37
+ refreshToken={refreshToken}
38
+ onError={onError}
39
+ >
40
+ <NavigationProvider initialPage={initialPage}>
41
+ <PageChromeProvider>
42
+ <PrivacyCenterLayout defaultTheme={theme} slug={slug} onBack={onBack}>
43
+ <PrivacyCenterHomePage />
44
+ </PrivacyCenterLayout>
45
+ </PageChromeProvider>
46
+ </NavigationProvider>
47
+ </AuthProvider>
48
+ </BaseUrlProvider>
49
+ </I18nextProvider>
50
+ );
51
+ }