@redacto.io/consent-sdk-react 4.0.0 → 5.1.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 (161) hide show
  1. package/CHANGELOG.md +19 -1
  2. package/LICENSE +176 -0
  3. package/README.md +98 -1
  4. package/dist/index.js +231 -96
  5. package/dist/index.mjs +231 -96
  6. package/dist/privacy-center.d.mts +649 -0
  7. package/dist/privacy-center.d.ts +649 -0
  8. package/dist/privacy-center.js +20824 -0
  9. package/dist/privacy-center.mjs +20789 -0
  10. package/package.json +47 -3
  11. package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +6 -4
  12. package/src/RedactoNoticeConsent/api/index.ts +2 -0
  13. package/src/RedactoNoticeConsent/api/types.ts +2 -0
  14. package/src/RedactoNoticeConsent/components/DigiLockerCallback.tsx +128 -62
  15. package/src/RedactoNoticeConsent/language-codes.ts +97 -0
  16. package/src/RedactoNoticeConsentInline/RedactoNoticeConsentInline.tsx +0 -3
  17. package/src/RedactoNoticeConsentInline/api/index.ts +0 -1
  18. package/src/RedactoPrivacyCenter/PrivacyCenterActivityPage.tsx +19 -0
  19. package/src/RedactoPrivacyCenter/PrivacyCenterCaseDetailsPage.tsx +154 -0
  20. package/src/RedactoPrivacyCenter/PrivacyCenterConsentManagerPage.tsx +17 -0
  21. package/src/RedactoPrivacyCenter/PrivacyCenterFormPage.tsx +79 -0
  22. package/src/RedactoPrivacyCenter/PrivacyCenterHomePage.tsx +30 -0
  23. package/src/RedactoPrivacyCenter/PrivacyCenterLayout.tsx +173 -0
  24. package/src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx +55 -0
  25. package/src/RedactoPrivacyCenter/api/actions.ts +476 -0
  26. package/src/RedactoPrivacyCenter/api/client.ts +524 -0
  27. package/src/RedactoPrivacyCenter/api/config.ts +33 -0
  28. package/src/RedactoPrivacyCenter/api/fetcher.ts +181 -0
  29. package/src/RedactoPrivacyCenter/api/upload.ts +93 -0
  30. package/src/RedactoPrivacyCenter/assets/character.svg +31 -0
  31. package/src/RedactoPrivacyCenter/assets/fileAttach.svg +9 -0
  32. package/src/RedactoPrivacyCenter/assets/greenTick.svg +4 -0
  33. package/src/RedactoPrivacyCenter/assets/mail.svg +3 -0
  34. package/src/RedactoPrivacyCenter/assets/poweredByRedacto.svg +9 -0
  35. package/src/RedactoPrivacyCenter/assets/sentImg.svg +9 -0
  36. package/src/RedactoPrivacyCenter/assets/uploadFileIcon.svg +9 -0
  37. package/src/RedactoPrivacyCenter/components/ActivitiesList.tsx +272 -0
  38. package/src/RedactoPrivacyCenter/components/CaseDetails/AttachmentCard.tsx +38 -0
  39. package/src/RedactoPrivacyCenter/components/CaseDetails/ChatBubble.tsx +31 -0
  40. package/src/RedactoPrivacyCenter/components/CaseDetails/DocumentRequestCard.tsx +73 -0
  41. package/src/RedactoPrivacyCenter/components/CaseDetails/MessageComposer.tsx +87 -0
  42. package/src/RedactoPrivacyCenter/components/CaseDetails/MessageItem.tsx +95 -0
  43. package/src/RedactoPrivacyCenter/components/CaseDetails/MessagesTab.tsx +147 -0
  44. package/src/RedactoPrivacyCenter/components/CaseDetails/RequestDetailsTab.tsx +171 -0
  45. package/src/RedactoPrivacyCenter/components/CaseDetails/StatusEvent.tsx +23 -0
  46. package/src/RedactoPrivacyCenter/components/CaseDetails/useFileUpload.ts +116 -0
  47. package/src/RedactoPrivacyCenter/components/CaseDetails/useMessagePolling.ts +36 -0
  48. package/src/RedactoPrivacyCenter/components/CaseDetails/utils.ts +56 -0
  49. package/src/RedactoPrivacyCenter/components/CaseDetailsModal.tsx +469 -0
  50. package/src/RedactoPrivacyCenter/components/CaseHistory.tsx +426 -0
  51. package/src/RedactoPrivacyCenter/components/ConsentManager.tsx +732 -0
  52. package/src/RedactoPrivacyCenter/components/ConsentManagerColumns.tsx +238 -0
  53. package/src/RedactoPrivacyCenter/components/ConsentManagerConstants.ts +7 -0
  54. package/src/RedactoPrivacyCenter/components/ConsentManagerTableSkeleton.tsx +155 -0
  55. package/src/RedactoPrivacyCenter/components/DataRequest/CorrectionDataSection.tsx +189 -0
  56. package/src/RedactoPrivacyCenter/components/DataRequest/GrievanceDataSection.tsx +118 -0
  57. package/src/RedactoPrivacyCenter/components/DataRequest/NominationDataSection.tsx +66 -0
  58. package/src/RedactoPrivacyCenter/components/DataRequest/PurposeSelectionSection.tsx +304 -0
  59. package/src/RedactoPrivacyCenter/components/Form.tsx +1596 -0
  60. package/src/RedactoPrivacyCenter/components/GrievanceTable.tsx +174 -0
  61. package/src/RedactoPrivacyCenter/components/GrievanceTablePagination.tsx +337 -0
  62. package/src/RedactoPrivacyCenter/components/ModifyConsentModal.tsx +330 -0
  63. package/src/RedactoPrivacyCenter/components/Pagination.tsx +140 -0
  64. package/src/RedactoPrivacyCenter/components/PrivacyCenterLayout.tsx +304 -0
  65. package/src/RedactoPrivacyCenter/components/RequestSubmit.tsx +232 -0
  66. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideBarPC.tsx +475 -0
  67. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideMenuPC.tsx +408 -0
  68. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/constants.tsx +42 -0
  69. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/menuItemsPC.tsx +1 -0
  70. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/types.ts +40 -0
  71. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/utils.ts +38 -0
  72. package/src/RedactoPrivacyCenter/components/ThemeTransitionOverlay.tsx +85 -0
  73. package/src/RedactoPrivacyCenter/components/activitiesColumns.tsx +141 -0
  74. package/src/RedactoPrivacyCenter/components/caseHistoryColumns.tsx +159 -0
  75. package/src/RedactoPrivacyCenter/components/constant.ts +16 -0
  76. package/src/RedactoPrivacyCenter/components/types.ts +76 -0
  77. package/src/RedactoPrivacyCenter/components/utils.tsx +10 -0
  78. package/src/RedactoPrivacyCenter/config/routes.ts +9 -0
  79. package/src/RedactoPrivacyCenter/config/table.ts +25 -0
  80. package/src/RedactoPrivacyCenter/context/AuthContext.tsx +137 -0
  81. package/src/RedactoPrivacyCenter/context/BaseUrlContext.tsx +41 -0
  82. package/src/RedactoPrivacyCenter/context/LocaleContext.tsx +11 -0
  83. package/src/RedactoPrivacyCenter/context/NavigationContext.tsx +96 -0
  84. package/src/RedactoPrivacyCenter/context/OrganizationContext.tsx +30 -0
  85. package/src/RedactoPrivacyCenter/context/PageChromeContext.tsx +60 -0
  86. package/src/RedactoPrivacyCenter/context/theme.tsx +127 -0
  87. package/src/RedactoPrivacyCenter/context/types.ts +14 -0
  88. package/src/RedactoPrivacyCenter/lib/api.ts +566 -0
  89. package/src/RedactoPrivacyCenter/lib/consent-manager/utils.tsx +63 -0
  90. package/src/RedactoPrivacyCenter/lib/constants.ts +161 -0
  91. package/src/RedactoPrivacyCenter/lib/i18n.ts +81 -0
  92. package/src/RedactoPrivacyCenter/lib/locales.ts +105 -0
  93. package/src/RedactoPrivacyCenter/lib/queries.ts +134 -0
  94. package/src/RedactoPrivacyCenter/lib/types.ts +531 -0
  95. package/src/RedactoPrivacyCenter/lib/useCurrentLanguage.ts +37 -0
  96. package/src/RedactoPrivacyCenter/lib/utils.ts +626 -0
  97. package/src/RedactoPrivacyCenter/locales/as/translation.json +280 -0
  98. package/src/RedactoPrivacyCenter/locales/bho/translation.json +280 -0
  99. package/src/RedactoPrivacyCenter/locales/bn/translation.json +280 -0
  100. package/src/RedactoPrivacyCenter/locales/brx/translation.json +280 -0
  101. package/src/RedactoPrivacyCenter/locales/doi/translation.json +280 -0
  102. package/src/RedactoPrivacyCenter/locales/en/translation.json +255 -0
  103. package/src/RedactoPrivacyCenter/locales/gom/translation.json +280 -0
  104. package/src/RedactoPrivacyCenter/locales/gu/translation.json +280 -0
  105. package/src/RedactoPrivacyCenter/locales/hi/translation.json +280 -0
  106. package/src/RedactoPrivacyCenter/locales/kn/translation.json +280 -0
  107. package/src/RedactoPrivacyCenter/locales/ks/translation.json +280 -0
  108. package/src/RedactoPrivacyCenter/locales/mai/translation.json +280 -0
  109. package/src/RedactoPrivacyCenter/locales/ml/translation.json +280 -0
  110. package/src/RedactoPrivacyCenter/locales/mni-Mtei/translation.json +280 -0
  111. package/src/RedactoPrivacyCenter/locales/mr/translation.json +280 -0
  112. package/src/RedactoPrivacyCenter/locales/ne/translation.json +280 -0
  113. package/src/RedactoPrivacyCenter/locales/or/translation.json +280 -0
  114. package/src/RedactoPrivacyCenter/locales/pa/translation.json +280 -0
  115. package/src/RedactoPrivacyCenter/locales/sa/translation.json +280 -0
  116. package/src/RedactoPrivacyCenter/locales/sat/translation.json +280 -0
  117. package/src/RedactoPrivacyCenter/locales/sd/translation.json +280 -0
  118. package/src/RedactoPrivacyCenter/locales/ta/translation.json +280 -0
  119. package/src/RedactoPrivacyCenter/locales/te/translation.json +280 -0
  120. package/src/RedactoPrivacyCenter/locales/ur/translation.json +280 -0
  121. package/src/RedactoPrivacyCenter/styles/base.css +117 -0
  122. package/src/RedactoPrivacyCenter/styles/injectStyles.ts +1725 -0
  123. package/src/RedactoPrivacyCenter/styles/pcStyles.ts +813 -0
  124. package/src/RedactoPrivacyCenter/styles/responsive.ts +138 -0
  125. package/src/RedactoPrivacyCenter/styles/variables.css +133 -0
  126. package/src/RedactoPrivacyCenter/table/AdvancedTable.tsx +367 -0
  127. package/src/RedactoPrivacyCenter/table/AdvancedTablePagination.tsx +476 -0
  128. package/src/RedactoPrivacyCenter/table/index.ts +2 -0
  129. package/src/RedactoPrivacyCenter/types/images.d.ts +29 -0
  130. package/src/RedactoPrivacyCenter/ui/Footer.tsx +75 -0
  131. package/src/RedactoPrivacyCenter/ui/FormBlockWrapper.tsx +54 -0
  132. package/src/RedactoPrivacyCenter/ui/PCAccordion.tsx +93 -0
  133. package/src/RedactoPrivacyCenter/ui/PCActionIcon.tsx +47 -0
  134. package/src/RedactoPrivacyCenter/ui/PCAvatar.tsx +69 -0
  135. package/src/RedactoPrivacyCenter/ui/PCBadge.tsx +48 -0
  136. package/src/RedactoPrivacyCenter/ui/PCButton.tsx +77 -0
  137. package/src/RedactoPrivacyCenter/ui/PCCheckbox.tsx +55 -0
  138. package/src/RedactoPrivacyCenter/ui/PCCollapse.tsx +107 -0
  139. package/src/RedactoPrivacyCenter/ui/PCEmpty.tsx +35 -0
  140. package/src/RedactoPrivacyCenter/ui/PCInput.tsx +132 -0
  141. package/src/RedactoPrivacyCenter/ui/PCLoader.tsx +31 -0
  142. package/src/RedactoPrivacyCenter/ui/PCModal.tsx +106 -0
  143. package/src/RedactoPrivacyCenter/ui/PCPopover.tsx +173 -0
  144. package/src/RedactoPrivacyCenter/ui/PCSelect.tsx +387 -0
  145. package/src/RedactoPrivacyCenter/ui/PCTable.tsx +149 -0
  146. package/src/RedactoPrivacyCenter/ui/PCText.tsx +41 -0
  147. package/src/RedactoPrivacyCenter/ui/PCTextarea.tsx +43 -0
  148. package/src/RedactoPrivacyCenter/ui/PCTitle.tsx +37 -0
  149. package/src/RedactoPrivacyCenter/ui/PCTooltip.tsx +119 -0
  150. package/src/RedactoPrivacyCenter/ui/SimpleBar.tsx +25 -0
  151. package/src/RedactoPrivacyCenter/ui/StatusBadge.tsx +53 -0
  152. package/src/RedactoPrivacyCenter/ui/ThemeToggle.tsx +72 -0
  153. package/src/RedactoPrivacyCenter/ui/index.ts +18 -0
  154. package/src/RedactoPrivacyCenter/utils/cn.ts +28 -0
  155. package/src/RedactoPrivacyCenter/utils/date.ts +48 -0
  156. package/src/RedactoPrivacyCenter/utils/status.ts +111 -0
  157. package/src/RedactoPrivacyCenter/utils/toast.ts +130 -0
  158. package/src/privacy-center.ts +108 -0
  159. package/tsconfig.json +2 -0
  160. package/tsup.config.ts +16 -1
  161. package/.turbo/turbo-build.log +0 -21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redacto.io/consent-sdk-react",
3
- "version": "4.0.0",
3
+ "version": "5.1.0",
4
4
  "main": "dist/index.cjs",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",
@@ -9,12 +9,17 @@
9
9
  "types": "./dist/index.d.ts",
10
10
  "require": "./dist/index.cjs",
11
11
  "import": "./dist/index.mjs"
12
+ },
13
+ "./privacy-center": {
14
+ "types": "./dist/privacy-center.d.ts",
15
+ "require": "./dist/privacy-center.cjs",
16
+ "import": "./dist/privacy-center.mjs"
12
17
  }
13
18
  },
14
19
  "keywords": [],
15
20
  "author": "",
16
21
  "license": "Apache-2.0",
17
- "description": "",
22
+ "description": "React SDK for Redacto consent notices and privacy center",
18
23
  "publishConfig": {
19
24
  "access": "public"
20
25
  },
@@ -24,6 +29,7 @@
24
29
  "devDependencies": {
25
30
  "@testing-library/jest-dom": "^6.6.3",
26
31
  "@testing-library/react": "^16.3.0",
32
+ "@types/lodash": "^4.17.0",
27
33
  "@types/react": "^19.1.3",
28
34
  "@types/react-dom": "^19.1.3",
29
35
  "@vitest/coverage-v8": "3.1.3",
@@ -34,7 +40,45 @@
34
40
  },
35
41
  "peerDependencies": {
36
42
  "react": "^16.8.0 || ^17 || ^18 || ^19",
37
- "react-dom": "^16.8.0 || ^17 || ^18 || ^19"
43
+ "react-dom": "^16.8.0 || ^17 || ^18 || ^19",
44
+ "@tanstack/react-query": "^5.0.0",
45
+ "@tanstack/react-table": "^8.0.0",
46
+ "i18next": "^23.0.0",
47
+ "i18next-browser-languagedetector": "^7.0.0",
48
+ "react-i18next": "^13.0.0",
49
+ "lucide-react": ">=0.300.0",
50
+ "react-icons": "^5.0.0",
51
+ "dayjs": "^1.11.0",
52
+ "lodash": "^4.17.0"
53
+ },
54
+ "peerDependenciesMeta": {
55
+ "@tanstack/react-query": {
56
+ "optional": true
57
+ },
58
+ "@tanstack/react-table": {
59
+ "optional": true
60
+ },
61
+ "i18next": {
62
+ "optional": true
63
+ },
64
+ "i18next-browser-languagedetector": {
65
+ "optional": true
66
+ },
67
+ "react-i18next": {
68
+ "optional": true
69
+ },
70
+ "lucide-react": {
71
+ "optional": true
72
+ },
73
+ "react-icons": {
74
+ "optional": true
75
+ },
76
+ "dayjs": {
77
+ "optional": true
78
+ },
79
+ "lodash": {
80
+ "optional": true
81
+ }
38
82
  },
39
83
  "scripts": {
40
84
  "build": "tsup",
@@ -32,6 +32,7 @@ import logo from "./assets/redacto-logo.png";
32
32
 
33
33
  // Constants
34
34
  import { DPO_AUDIO_ELEMENT_MAP } from "./constants";
35
+ import { toBcp47Code, toNativeLabel } from "./language-codes";
35
36
 
36
37
  // Utility imports
37
38
  import { injectCheckboxStyles } from "./injectStyles";
@@ -2151,6 +2152,7 @@ export const RedactoNoticeConsent = ({
2151
2152
  };
2152
2153
  }),
2153
2154
  declined: false,
2155
+ language: toBcp47Code(selectedLanguage),
2154
2156
  meta_data: applicationId
2155
2157
  ? { specific_uuid: applicationId }
2156
2158
  : undefined,
@@ -3778,9 +3780,9 @@ export const RedactoNoticeConsent = ({
3778
3780
  aria-expanded={isLanguageDropdownOpen}
3779
3781
  aria-haspopup="listbox"
3780
3782
  aria-controls="language-listbox"
3781
- aria-label={`Select language. Current language: ${selectedLanguage}`}
3783
+ aria-label={`Select language. Current language: ${toNativeLabel(selectedLanguage)}`}
3782
3784
  >
3783
- {selectedLanguage}
3785
+ {toNativeLabel(selectedLanguage)}
3784
3786
  <svg
3785
3787
  xmlns="http://www.w3.org/2000/svg"
3786
3788
  width="16"
@@ -3827,9 +3829,9 @@ export const RedactoNoticeConsent = ({
3827
3829
  role="option"
3828
3830
  aria-selected={selectedLanguage === lang}
3829
3831
  tabIndex={0}
3830
- aria-label={`Select ${lang} language`}
3832
+ aria-label={`Select ${toNativeLabel(lang)} language`}
3831
3833
  >
3832
- {lang}
3834
+ {toNativeLabel(lang)}
3833
3835
  </li>
3834
3836
  ))}
3835
3837
  </ul>
@@ -143,6 +143,7 @@ export const submitConsentEvent = async ({
143
143
  noticeUuid,
144
144
  purposes,
145
145
  declined,
146
+ language,
146
147
  meta_data,
147
148
  guardianVerificationReference,
148
149
  selfDeclaredAdult,
@@ -190,6 +191,7 @@ export const submitConsentEvent = async ({
190
191
  select_all_mandatory: false,
191
192
  source: "WEB",
192
193
  declined: Boolean(declined),
194
+ language: language || undefined,
193
195
  meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid, minor_age: meta_data.minor_age } : undefined,
194
196
  guardian_verification_reference: guardianVerificationReference,
195
197
  self_declared_adult: selfDeclaredAdult,
@@ -170,6 +170,7 @@ export type ConsentEventPayload = {
170
170
  select_all_mandatory: boolean;
171
171
  source?: string;
172
172
  declined?: boolean;
173
+ language?: string;
173
174
  meta_data?: {
174
175
  specific_uuid?: string;
175
176
  minor_age?: number;
@@ -201,6 +202,7 @@ export type SubmitConsentEventParams = {
201
202
  noticeUuid: string;
202
203
  purposes: Array<Purpose>;
203
204
  declined: boolean;
205
+ language?: string;
204
206
  meta_data?: {
205
207
  specific_uuid?: string;
206
208
  minor_age?: number;
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * DigiLockerCallback Component
3
- *
3
+ *
4
4
  * Standalone component for handling DigiLocker redirect callbacks.
5
5
  * Clients should add this to their routing (e.g., /digilocker-callback page).
6
- *
6
+ *
7
7
  * Usage in Next.js:
8
8
  * ```tsx
9
9
  * // pages/digilocker-callback.tsx
@@ -18,10 +18,10 @@
18
18
  * ```tsx
19
19
  * <Route path="/digilocker-callback" element={<DigiLockerCallback accessToken={"<token>"} />} />
20
20
  * ```
21
- *
21
+ *
22
22
  * URL Parameters expected from redirect:
23
23
  * - session_token: The verification session token
24
- * - status: "verified" | "failed" | "expired"
24
+ * - status: "verified" | "failed" | "expired"
25
25
  * - verification_reference: (if verified) Reference for consent submission
26
26
  * - error_code: (if failed) Error code like GUARDIAN_UNDER_18, SESSION_EXPIRED, etc.
27
27
  */
@@ -131,7 +131,7 @@ export const DigiLockerCallback: React.FC<DigiLockerCallbackProps> = ({
131
131
  setStatus("success");
132
132
  setMessage("Guardian verified successfully! Redirecting...");
133
133
  onSuccess?.(verificationReference);
134
-
134
+
135
135
  // If in iframe, postMessage to parent instead of redirecting
136
136
  if (isInIframe && window.parent) {
137
137
  window.parent.postMessage(
@@ -144,7 +144,7 @@ export const DigiLockerCallback: React.FC<DigiLockerCallbackProps> = ({
144
144
  );
145
145
  return;
146
146
  }
147
-
147
+
148
148
  redirectToOriginalPage(originalUrl, "verified", verificationReference);
149
149
  return;
150
150
  }
@@ -170,16 +170,16 @@ export const DigiLockerCallback: React.FC<DigiLockerCallbackProps> = ({
170
170
  );
171
171
  return;
172
172
  }
173
-
173
+
174
174
  redirectToOriginalPage(originalUrl, urlStatus);
175
175
  return;
176
176
  }
177
177
 
178
178
  // 4. If no status in URL, poll the backend for verification status
179
179
  setMessage("Processing verification...");
180
-
180
+
181
181
  const apiBaseUrl = baseUrl || storedBaseUrl;
182
-
182
+
183
183
  const response = await verifyGuardianStatus({
184
184
  accessToken,
185
185
  baseUrl: apiBaseUrl,
@@ -336,11 +336,11 @@ export const DigiLockerCallback: React.FC<DigiLockerCallbackProps> = ({
336
336
  } catch (error) {
337
337
  console.error("DigiLocker callback error:", error);
338
338
  setStatus("error");
339
-
339
+
340
340
  const err = error as Error;
341
341
  setMessage("Verification failed");
342
342
  setErrorDetail(err.message || "An error occurred during verification");
343
-
343
+
344
344
  onError?.(err);
345
345
 
346
346
  // Try to redirect back even on error
@@ -373,106 +373,157 @@ export const DigiLockerCallback: React.FC<DigiLockerCallbackProps> = ({
373
373
  }
374
374
  }, []);
375
375
 
376
- // Inline styles for the callback page
376
+ // --- Styles ---
377
+
377
378
  const containerStyle: React.CSSProperties = {
378
379
  display: "flex",
379
380
  alignItems: "center",
380
381
  justifyContent: "center",
381
382
  minHeight: "100vh",
382
- backgroundColor: "#f9fafb",
383
+ backgroundColor: "#f8fafc",
383
384
  fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
384
- padding: "20px",
385
+ padding: "24px",
385
386
  boxSizing: "border-box",
386
387
  };
387
388
 
388
389
  const cardStyle: React.CSSProperties = {
389
390
  backgroundColor: "#ffffff",
390
- borderRadius: "12px",
391
- boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
392
- padding: "40px",
391
+ borderRadius: "16px",
392
+ boxShadow: "0 1px 3px rgba(0, 0, 0, 0.08), 0 8px 30px rgba(0, 0, 0, 0.06)",
393
+ padding: "48px 40px 40px",
393
394
  textAlign: "center",
394
- maxWidth: "400px",
395
+ maxWidth: "420px",
395
396
  width: "100%",
396
397
  };
397
398
 
399
+ const spinnerContainerStyle: React.CSSProperties = {
400
+ width: "72px",
401
+ height: "72px",
402
+ margin: "0 auto 28px",
403
+ position: "relative",
404
+ };
405
+
398
406
  const spinnerStyle: React.CSSProperties = {
399
- width: "48px",
400
- height: "48px",
401
- border: "4px solid #e5e7eb",
402
- borderTopColor: "#3b82f6",
407
+ width: "72px",
408
+ height: "72px",
409
+ border: "3px solid #e5e7eb",
410
+ borderTopColor: "#4f87ff",
403
411
  borderRadius: "50%",
404
- animation: "spin 1s linear infinite",
405
- margin: "0 auto 24px",
412
+ animation: "redacto-dl-spin 0.8s linear infinite",
413
+ boxSizing: "border-box",
406
414
  };
407
415
 
408
- const iconStyle: React.CSSProperties = {
409
- width: "64px",
410
- height: "64px",
416
+ const iconContainerStyle: React.CSSProperties = {
417
+ width: "72px",
418
+ height: "72px",
411
419
  borderRadius: "50%",
412
420
  display: "flex",
413
421
  alignItems: "center",
414
422
  justifyContent: "center",
415
- margin: "0 auto 24px",
423
+ margin: "0 auto 28px",
424
+ animation: "redacto-dl-pop 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards",
425
+ position: "relative",
416
426
  };
417
427
 
418
428
  const successIconStyle: React.CSSProperties = {
419
- ...iconStyle,
429
+ ...iconContainerStyle,
420
430
  backgroundColor: "#d1fae5",
421
431
  color: "#059669",
422
432
  };
423
433
 
424
434
  const errorIconStyle: React.CSSProperties = {
425
- ...iconStyle,
435
+ ...iconContainerStyle,
426
436
  backgroundColor: "#fee2e2",
427
437
  color: "#dc2626",
428
438
  };
429
439
 
430
440
  const titleStyle: React.CSSProperties = {
431
- fontSize: "20px",
432
- fontWeight: 600,
441
+ fontSize: "22px",
442
+ fontWeight: 700,
433
443
  color: "#111827",
434
- margin: "0 0 12px 0",
444
+ margin: "0 0 8px 0",
445
+ letterSpacing: "-0.01em",
435
446
  };
436
447
 
437
448
  const messageStyle: React.CSSProperties = {
438
449
  fontSize: "14px",
439
450
  color: "#6b7280",
440
- marginBottom: "8px",
451
+ marginBottom: "0",
452
+ lineHeight: 1.6,
453
+ };
454
+
455
+ const subtextStyle: React.CSSProperties = {
456
+ fontSize: "13px",
457
+ color: "#9ca3af",
458
+ marginTop: "16px",
441
459
  lineHeight: 1.5,
442
460
  };
443
461
 
444
462
  const errorDetailStyle: React.CSSProperties = {
445
463
  fontSize: "13px",
446
464
  color: "#dc2626",
447
- marginTop: "12px",
448
- padding: "12px",
465
+ marginTop: "16px",
466
+ padding: "12px 16px",
449
467
  backgroundColor: "#fef2f2",
450
- borderRadius: "6px",
468
+ borderRadius: "10px",
451
469
  border: "1px solid #fecaca",
470
+ lineHeight: 1.5,
471
+ textAlign: "left",
452
472
  };
453
473
 
454
474
  const buttonStyle: React.CSSProperties = {
455
- marginTop: "20px",
456
- padding: "10px 24px",
457
- backgroundColor: "#3b82f6",
475
+ marginTop: "24px",
476
+ padding: "12px 28px",
477
+ backgroundColor: "#4f87ff",
458
478
  color: "#ffffff",
459
479
  border: "none",
460
- borderRadius: "6px",
461
- fontSize: "14px",
462
- fontWeight: 500,
480
+ borderRadius: "10px",
481
+ fontSize: "15px",
482
+ fontWeight: 600,
463
483
  cursor: "pointer",
464
- transition: "background-color 0.2s",
484
+ transition: "background-color 0.2s, transform 0.15s",
485
+ width: "100%",
465
486
  };
466
487
 
488
+ const keyframesStyle = `
489
+ @keyframes redacto-dl-spin {
490
+ to { transform: rotate(360deg); }
491
+ }
492
+ @keyframes redacto-dl-pop {
493
+ 0% { transform: scale(0.3); opacity: 0; }
494
+ 60% { transform: scale(1.08); opacity: 1; }
495
+ 100% { transform: scale(1); opacity: 1; }
496
+ }
497
+ @keyframes redacto-dl-check {
498
+ 0% { stroke-dashoffset: 24; }
499
+ 100% { stroke-dashoffset: 0; }
500
+ }
501
+ @keyframes redacto-dl-pulse {
502
+ 0%, 100% { transform: scale(1); opacity: 0.6; }
503
+ 50% { transform: scale(1.15); opacity: 0; }
504
+ }
505
+ .redacto-dl-retry-btn:hover {
506
+ background-color: #3d6fdb !important;
507
+ transform: translateY(-1px);
508
+ }
509
+ .redacto-dl-retry-btn:active {
510
+ transform: translateY(0);
511
+ }
512
+ `;
513
+
467
514
  return (
515
+ <>
516
+ <style dangerouslySetInnerHTML={{ __html: keyframesStyle }} />
468
517
  <div style={containerStyle}>
469
518
  <div style={cardStyle}>
470
519
  {status === "processing" && (
471
520
  <>
472
- <div style={spinnerStyle} />
521
+ <div style={spinnerContainerStyle}>
522
+ <div style={spinnerStyle} />
523
+ </div>
473
524
  <h2 style={titleStyle}>Processing Verification</h2>
474
525
  <p style={messageStyle}>{message}</p>
475
- <p style={{ ...messageStyle, fontSize: "12px", color: "#9ca3af" }}>
526
+ <p style={subtextStyle}>
476
527
  Please wait while we verify your guardian's identity...
477
528
  </p>
478
529
  </>
@@ -480,22 +531,36 @@ export const DigiLockerCallback: React.FC<DigiLockerCallbackProps> = ({
480
531
 
481
532
  {status === "success" && (
482
533
  <>
483
- <div style={successIconStyle}>
484
- <svg
485
- width="32"
486
- height="32"
487
- viewBox="0 0 24 24"
488
- fill="none"
489
- stroke="currentColor"
490
- strokeWidth="2"
491
- strokeLinecap="round"
492
- strokeLinejoin="round"
493
- >
494
- <polyline points="20 6 9 17 4 12" />
495
- </svg>
534
+ <div style={{ position: "relative", width: "72px", height: "72px", margin: "0 auto 28px" }}>
535
+ {/* Pulse ring */}
536
+ <div style={{
537
+ position: "absolute",
538
+ inset: "-8px",
539
+ borderRadius: "50%",
540
+ border: "2px solid #d1fae5",
541
+ animation: "redacto-dl-pulse 1.5s ease-out infinite",
542
+ }} />
543
+ <div style={successIconStyle}>
544
+ <svg
545
+ width="30"
546
+ height="30"
547
+ viewBox="0 0 24 24"
548
+ fill="none"
549
+ stroke="currentColor"
550
+ strokeWidth="2.5"
551
+ strokeLinecap="round"
552
+ strokeLinejoin="round"
553
+ style={{ strokeDasharray: 24, animation: "redacto-dl-check 0.4s ease-out 0.3s forwards", strokeDashoffset: 24 }}
554
+ >
555
+ <polyline points="20 6 9 17 4 12" />
556
+ </svg>
557
+ </div>
496
558
  </div>
497
559
  <h2 style={titleStyle}>Verification Successful</h2>
498
560
  <p style={messageStyle}>{message}</p>
561
+ <p style={subtextStyle}>
562
+ You will be redirected automatically.
563
+ </p>
499
564
  </>
500
565
  )}
501
566
 
@@ -503,12 +568,12 @@ export const DigiLockerCallback: React.FC<DigiLockerCallbackProps> = ({
503
568
  <>
504
569
  <div style={errorIconStyle}>
505
570
  <svg
506
- width="32"
507
- height="32"
571
+ width="30"
572
+ height="30"
508
573
  viewBox="0 0 24 24"
509
574
  fill="none"
510
575
  stroke="currentColor"
511
- strokeWidth="2"
576
+ strokeWidth="2.5"
512
577
  strokeLinecap="round"
513
578
  strokeLinejoin="round"
514
579
  >
@@ -530,6 +595,7 @@ export const DigiLockerCallback: React.FC<DigiLockerCallbackProps> = ({
530
595
  )}
531
596
  </div>
532
597
  </div>
598
+ </>
533
599
  );
534
600
  };
535
601
 
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Maps English language names to "Native script (English)" display labels.
3
+ * English itself is kept as-is; all others show the native script first.
4
+ */
5
+ export const LANGUAGE_NATIVE_LABELS: Record<string, string> = {
6
+ English: "English",
7
+ Assamese: "অসমীয়া (Assamese)",
8
+ Bengali: "বাংলা (Bengali)",
9
+ Bhojpuri: "भोजपुरी (Bhojpuri)",
10
+ Bodo: "बड़ो (Bodo)",
11
+ Dogri: "डोगरी (Dogri)",
12
+ Gujarati: "ગુજરાતી (Gujarati)",
13
+ Hindi: "हिन्दी (Hindi)",
14
+ Kannada: "ಕನ್ನಡ (Kannada)",
15
+ Kashmiri: "كٲشُر (Kashmiri)",
16
+ "Goan Konkani": "कोंकणी (Goan Konkani)",
17
+ Maithili: "मैथिली (Maithili)",
18
+ Malayalam: "മലയാളം (Malayalam)",
19
+ Manipuri: "ꯃꯩꯇꯩꯂꯣꯟ (Manipuri)",
20
+ Marathi: "मराठी (Marathi)",
21
+ Nepali: "नेपाली (Nepali)",
22
+ Odia: "ଓଡ଼ିଆ (Odia)",
23
+ Punjabi: "ਪੰਜਾਬੀ (Punjabi)",
24
+ Sanskrit: "संस्कृतम् (Sanskrit)",
25
+ Santali: "ᱥᱟᱱᱛᱟᱲᱤ (Santali)",
26
+ Sindhi: "سنڌي (Sindhi)",
27
+ Tamil: "தமிழ் (Tamil)",
28
+ Telugu: "తెలుగు (Telugu)",
29
+ Urdu: "اردو (Urdu)",
30
+ };
31
+
32
+ /** Returns the "Native (English)" display label for a language name, falling back to the name itself. */
33
+ export const toNativeLabel = (name: string): string =>
34
+ LANGUAGE_NATIVE_LABELS[name] ?? name;
35
+
36
+ /**
37
+ * Maps display language names (as used in supported_languages_and_translations
38
+ * keys and the language dropdown) to BCP-47 codes for the submit-consent payload.
39
+ *
40
+ * Covers English + the 22 languages in the 8th Schedule of the Indian Constitution
41
+ * (DPDP Act 2023, Sections 5 & 6).
42
+ */
43
+ const LANGUAGE_NAME_TO_BCP47: Record<string, string> = {
44
+ English: "en",
45
+ Assamese: "as",
46
+ Bengali: "bn",
47
+ Bhojpuri: "bho",
48
+ Bodo: "brx",
49
+ Dogri: "doi",
50
+ Gujarati: "gu",
51
+ Hindi: "hi",
52
+ Kannada: "kn",
53
+ Kashmiri: "ks",
54
+ "Goan Konkani": "gom",
55
+ Maithili: "mai",
56
+ Malayalam: "ml",
57
+ Manipuri: "mni-Mtei",
58
+ Marathi: "mr",
59
+ Nepali: "ne",
60
+ Odia: "or",
61
+ Punjabi: "pa",
62
+ Sanskrit: "sa",
63
+ Santali: "sat",
64
+ Sindhi: "sd",
65
+ Tamil: "ta",
66
+ Telugu: "te",
67
+ Urdu: "ur",
68
+ };
69
+
70
+ const KNOWN_BCP47_CODES = new Set(Object.values(LANGUAGE_NAME_TO_BCP47));
71
+
72
+ /**
73
+ * Convert a language display name or code to a BCP-47 code.
74
+ * Handles these cases:
75
+ * 1. Display name ("Punjabi") → BCP-47 ("pa")
76
+ * 2. Already a BCP-47 tag, including region/script subtags ("pa", "en-US", "zh-Hans") → returned as-is
77
+ * 3. Unknown value → falls back to "en"
78
+ */
79
+ export const toBcp47Code = (language: string): string => {
80
+ if (LANGUAGE_NAME_TO_BCP47[language]) {
81
+ return LANGUAGE_NAME_TO_BCP47[language];
82
+ }
83
+ if (KNOWN_BCP47_CODES.has(language)) {
84
+ return language;
85
+ }
86
+ const lower = language.toLowerCase();
87
+ for (const [name, code] of Object.entries(LANGUAGE_NAME_TO_BCP47)) {
88
+ if (name.toLowerCase() === lower) {
89
+ return code;
90
+ }
91
+ }
92
+ // Pass through well-formed BCP-47 tags with subtags (e.g. en-US, pa-IN, zh-Hans).
93
+ if (/^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/.test(language)) {
94
+ return language;
95
+ }
96
+ return "en";
97
+ };
@@ -87,9 +87,6 @@ const RedactoConsentInlineComponent = ({
87
87
  onValidationChange?.(areAllRequiredElementsChecked);
88
88
  }, [areAllRequiredElementsChecked, onValidationChange]);
89
89
 
90
- const acceptDisabled =
91
- isSubmitting || !areAllRequiredElementsChecked || !accessToken;
92
-
93
90
  useEffect(() => {
94
91
  if (hasAlreadyConsented) {
95
92
  onAccept?.();
@@ -12,7 +12,6 @@ export const fetchConsentContent = async ({
12
12
  workspace_uuid,
13
13
  notice_uuid,
14
14
  baseUrl,
15
- language = "en",
16
15
  specific_uuid,
17
16
  }: FetchConsentContentParams): Promise<ConsentContent> => {
18
17
  try {
@@ -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
+ }