@redacto.io/consent-sdk-react 3.4.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 (155) hide show
  1. package/CHANGELOG.md +12 -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 +1912 -345
  6. package/dist/index.mjs +1911 -345
  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 +337 -246
  15. package/src/RedactoNoticeConsent/api/types.ts +140 -41
  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/RedactoNoticeConsentInline/api/index.ts +26 -64
  23. package/src/RedactoNoticeConsentInline/api/types.ts +0 -25
  24. package/src/RedactoPrivacyCenter/PrivacyCenterActivityPage.tsx +19 -0
  25. package/src/RedactoPrivacyCenter/PrivacyCenterConsentManagerPage.tsx +17 -0
  26. package/src/RedactoPrivacyCenter/PrivacyCenterFormPage.tsx +79 -0
  27. package/src/RedactoPrivacyCenter/PrivacyCenterHomePage.tsx +27 -0
  28. package/src/RedactoPrivacyCenter/PrivacyCenterLayout.tsx +142 -0
  29. package/src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx +51 -0
  30. package/src/RedactoPrivacyCenter/api/actions.ts +444 -0
  31. package/src/RedactoPrivacyCenter/api/client.ts +413 -0
  32. package/src/RedactoPrivacyCenter/api/config.ts +33 -0
  33. package/src/RedactoPrivacyCenter/api/fetcher.ts +181 -0
  34. package/src/RedactoPrivacyCenter/api/upload.ts +93 -0
  35. package/src/RedactoPrivacyCenter/assets/character.svg +31 -0
  36. package/src/RedactoPrivacyCenter/assets/fileAttach.svg +9 -0
  37. package/src/RedactoPrivacyCenter/assets/greenTick.svg +4 -0
  38. package/src/RedactoPrivacyCenter/assets/mail.svg +3 -0
  39. package/src/RedactoPrivacyCenter/assets/poweredByRedacto.svg +9 -0
  40. package/src/RedactoPrivacyCenter/assets/sentImg.svg +9 -0
  41. package/src/RedactoPrivacyCenter/assets/uploadFileIcon.svg +9 -0
  42. package/src/RedactoPrivacyCenter/components/ActivitiesList.tsx +272 -0
  43. package/src/RedactoPrivacyCenter/components/CaseDetailsModal.tsx +468 -0
  44. package/src/RedactoPrivacyCenter/components/CaseHistory.tsx +433 -0
  45. package/src/RedactoPrivacyCenter/components/ConsentManager.tsx +726 -0
  46. package/src/RedactoPrivacyCenter/components/ConsentManagerColumns.tsx +239 -0
  47. package/src/RedactoPrivacyCenter/components/ConsentManagerConstants.ts +7 -0
  48. package/src/RedactoPrivacyCenter/components/ConsentManagerTableSkeleton.tsx +155 -0
  49. package/src/RedactoPrivacyCenter/components/DataRequest/CorrectionDataSection.tsx +189 -0
  50. package/src/RedactoPrivacyCenter/components/DataRequest/GrievanceDataSection.tsx +118 -0
  51. package/src/RedactoPrivacyCenter/components/DataRequest/NominationDataSection.tsx +66 -0
  52. package/src/RedactoPrivacyCenter/components/DataRequest/PurposeSelectionSection.tsx +305 -0
  53. package/src/RedactoPrivacyCenter/components/Form.tsx +1455 -0
  54. package/src/RedactoPrivacyCenter/components/GrievanceTable.tsx +176 -0
  55. package/src/RedactoPrivacyCenter/components/GrievanceTablePagination.tsx +330 -0
  56. package/src/RedactoPrivacyCenter/components/ModifyConsentModal.tsx +329 -0
  57. package/src/RedactoPrivacyCenter/components/Pagination.tsx +140 -0
  58. package/src/RedactoPrivacyCenter/components/PrivacyCenterLayout.tsx +302 -0
  59. package/src/RedactoPrivacyCenter/components/RequestSubmit.tsx +218 -0
  60. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideBarPC.tsx +475 -0
  61. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideMenuPC.tsx +412 -0
  62. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/constants.tsx +42 -0
  63. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/menuItemsPC.tsx +1 -0
  64. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/types.ts +40 -0
  65. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/utils.ts +38 -0
  66. package/src/RedactoPrivacyCenter/components/ThemeTransitionOverlay.tsx +85 -0
  67. package/src/RedactoPrivacyCenter/components/activitiesColumns.tsx +141 -0
  68. package/src/RedactoPrivacyCenter/components/caseHistoryColumns.tsx +179 -0
  69. package/src/RedactoPrivacyCenter/components/constant.ts +20 -0
  70. package/src/RedactoPrivacyCenter/components/types.ts +68 -0
  71. package/src/RedactoPrivacyCenter/components/utils.tsx +10 -0
  72. package/src/RedactoPrivacyCenter/config/routes.ts +9 -0
  73. package/src/RedactoPrivacyCenter/config/table.ts +25 -0
  74. package/src/RedactoPrivacyCenter/context/AuthContext.tsx +123 -0
  75. package/src/RedactoPrivacyCenter/context/BaseUrlContext.tsx +44 -0
  76. package/src/RedactoPrivacyCenter/context/LocaleContext.tsx +11 -0
  77. package/src/RedactoPrivacyCenter/context/NavigationContext.tsx +64 -0
  78. package/src/RedactoPrivacyCenter/context/OrganizationContext.tsx +30 -0
  79. package/src/RedactoPrivacyCenter/context/PageChromeContext.tsx +60 -0
  80. package/src/RedactoPrivacyCenter/context/theme.tsx +127 -0
  81. package/src/RedactoPrivacyCenter/lib/api.ts +468 -0
  82. package/src/RedactoPrivacyCenter/lib/consent-manager/utils.tsx +63 -0
  83. package/src/RedactoPrivacyCenter/lib/constants.ts +116 -0
  84. package/src/RedactoPrivacyCenter/lib/i18n.ts +81 -0
  85. package/src/RedactoPrivacyCenter/lib/locales.ts +105 -0
  86. package/src/RedactoPrivacyCenter/lib/queries.ts +30 -0
  87. package/src/RedactoPrivacyCenter/lib/types.ts +346 -0
  88. package/src/RedactoPrivacyCenter/lib/useCurrentLanguage.ts +37 -0
  89. package/src/RedactoPrivacyCenter/lib/utils.ts +625 -0
  90. package/src/RedactoPrivacyCenter/locales/as/translation.json +188 -0
  91. package/src/RedactoPrivacyCenter/locales/bho/translation.json +188 -0
  92. package/src/RedactoPrivacyCenter/locales/bn/translation.json +188 -0
  93. package/src/RedactoPrivacyCenter/locales/brx/translation.json +188 -0
  94. package/src/RedactoPrivacyCenter/locales/doi/translation.json +188 -0
  95. package/src/RedactoPrivacyCenter/locales/en/translation.json +188 -0
  96. package/src/RedactoPrivacyCenter/locales/gom/translation.json +188 -0
  97. package/src/RedactoPrivacyCenter/locales/gu/translation.json +188 -0
  98. package/src/RedactoPrivacyCenter/locales/hi/translation.json +188 -0
  99. package/src/RedactoPrivacyCenter/locales/kn/translation.json +188 -0
  100. package/src/RedactoPrivacyCenter/locales/ks/translation.json +188 -0
  101. package/src/RedactoPrivacyCenter/locales/mai/translation.json +188 -0
  102. package/src/RedactoPrivacyCenter/locales/ml/translation.json +188 -0
  103. package/src/RedactoPrivacyCenter/locales/mni-Mtei/translation.json +188 -0
  104. package/src/RedactoPrivacyCenter/locales/mr/translation.json +188 -0
  105. package/src/RedactoPrivacyCenter/locales/ne/translation.json +188 -0
  106. package/src/RedactoPrivacyCenter/locales/or/translation.json +188 -0
  107. package/src/RedactoPrivacyCenter/locales/pa/translation.json +188 -0
  108. package/src/RedactoPrivacyCenter/locales/sa/translation.json +188 -0
  109. package/src/RedactoPrivacyCenter/locales/sat/translation.json +188 -0
  110. package/src/RedactoPrivacyCenter/locales/sd/translation.json +188 -0
  111. package/src/RedactoPrivacyCenter/locales/ta/translation.json +188 -0
  112. package/src/RedactoPrivacyCenter/locales/te/translation.json +188 -0
  113. package/src/RedactoPrivacyCenter/locales/ur/translation.json +188 -0
  114. package/src/RedactoPrivacyCenter/styles/base.css +117 -0
  115. package/src/RedactoPrivacyCenter/styles/injectStyles.ts +1000 -0
  116. package/src/RedactoPrivacyCenter/styles/pcStyles.ts +819 -0
  117. package/src/RedactoPrivacyCenter/styles/responsive.ts +138 -0
  118. package/src/RedactoPrivacyCenter/styles/variables.css +133 -0
  119. package/src/RedactoPrivacyCenter/table/AdvancedTable.tsx +365 -0
  120. package/src/RedactoPrivacyCenter/table/AdvancedTablePagination.tsx +479 -0
  121. package/src/RedactoPrivacyCenter/table/index.ts +2 -0
  122. package/src/RedactoPrivacyCenter/types/images.d.ts +29 -0
  123. package/src/RedactoPrivacyCenter/ui/Footer.tsx +73 -0
  124. package/src/RedactoPrivacyCenter/ui/FormBlockWrapper.tsx +54 -0
  125. package/src/RedactoPrivacyCenter/ui/PCAccordion.tsx +87 -0
  126. package/src/RedactoPrivacyCenter/ui/PCActionIcon.tsx +47 -0
  127. package/src/RedactoPrivacyCenter/ui/PCAvatar.tsx +69 -0
  128. package/src/RedactoPrivacyCenter/ui/PCBadge.tsx +48 -0
  129. package/src/RedactoPrivacyCenter/ui/PCButton.tsx +77 -0
  130. package/src/RedactoPrivacyCenter/ui/PCCheckbox.tsx +55 -0
  131. package/src/RedactoPrivacyCenter/ui/PCCollapse.tsx +97 -0
  132. package/src/RedactoPrivacyCenter/ui/PCEmpty.tsx +35 -0
  133. package/src/RedactoPrivacyCenter/ui/PCInput.tsx +128 -0
  134. package/src/RedactoPrivacyCenter/ui/PCLoader.tsx +30 -0
  135. package/src/RedactoPrivacyCenter/ui/PCModal.tsx +104 -0
  136. package/src/RedactoPrivacyCenter/ui/PCPopover.tsx +175 -0
  137. package/src/RedactoPrivacyCenter/ui/PCSelect.tsx +381 -0
  138. package/src/RedactoPrivacyCenter/ui/PCTable.tsx +137 -0
  139. package/src/RedactoPrivacyCenter/ui/PCText.tsx +39 -0
  140. package/src/RedactoPrivacyCenter/ui/PCTextarea.tsx +43 -0
  141. package/src/RedactoPrivacyCenter/ui/PCTitle.tsx +37 -0
  142. package/src/RedactoPrivacyCenter/ui/PCTooltip.tsx +119 -0
  143. package/src/RedactoPrivacyCenter/ui/SimpleBar.tsx +25 -0
  144. package/src/RedactoPrivacyCenter/ui/StatusBadge.tsx +55 -0
  145. package/src/RedactoPrivacyCenter/ui/ThemeToggle.tsx +70 -0
  146. package/src/RedactoPrivacyCenter/ui/index.ts +18 -0
  147. package/src/RedactoPrivacyCenter/utils/cn.ts +28 -0
  148. package/src/RedactoPrivacyCenter/utils/date.ts +48 -0
  149. package/src/RedactoPrivacyCenter/utils/status.ts +111 -0
  150. package/src/RedactoPrivacyCenter/utils/toast.ts +130 -0
  151. package/src/index.ts +10 -0
  152. package/src/privacy-center.ts +107 -0
  153. package/tsconfig.json +2 -0
  154. package/tsup.config.ts +16 -1
  155. package/src/utils/isLegacyApi.ts +0 -12
package/dist/index.js CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var src_exports = {};
22
22
  __export(src_exports, {
23
+ DigiLockerCallback: () => DigiLockerCallback,
23
24
  RedactoNoticeConsent: () => RedactoNoticeConsent,
24
25
  RedactoNoticeConsentInline: () => RedactoConsentInline
25
26
  });
@@ -30,21 +31,6 @@ var import_react2 = require("react");
30
31
 
31
32
  // src/RedactoNoticeConsent/api/index.ts
32
33
  var import_jwt_decode = require("jwt-decode");
33
-
34
- // src/utils/isLegacyApi.ts
35
- function isLegacyApi(baseUrl) {
36
- try {
37
- return new URL(baseUrl).hostname.endsWith(".redacto.io");
38
- } catch {
39
- try {
40
- return new URL(`https://${baseUrl}`).hostname.endsWith(".redacto.io");
41
- } catch {
42
- return false;
43
- }
44
- }
45
- }
46
-
47
- // src/RedactoNoticeConsent/api/index.ts
48
34
  var BASE_URL = "https://api.redacto.io/consent";
49
35
  var apiCache = /* @__PURE__ */ new Map();
50
36
  var CACHE_TTL = 5 * 60 * 1e3;
@@ -81,73 +67,66 @@ var fetchConsentContent = async ({
81
67
  validate_against = "all",
82
68
  include_fully_consented_data = false,
83
69
  signal
84
- // AbortSignal for request cancellation
85
70
  }) => {
86
- try {
87
- if (!noticeId || !accessToken || !validate_against) {
88
- throw new Error("noticeId and accessToken are required");
89
- }
90
- const decodedToken = decodeTokenSafely(accessToken);
91
- if (!decodedToken) {
92
- throw new Error("Invalid access token");
93
- }
94
- const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
95
- if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
96
- throw new Error("Invalid token: missing organization or workspace UUID");
97
- }
98
- const apiBaseUrl = baseUrl || BASE_URL;
99
- const cacheKey = `${accessToken}-${noticeId}-${validate_against}-${language}-${specific_uuid || ""}-${include_fully_consented_data}`;
100
- if (validate_against === "all" && !specific_uuid) {
101
- const cachedData = getCachedData(cacheKey);
102
- if (cachedData) {
103
- return cachedData;
104
- }
105
- }
106
- const url = new URL(
107
- `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/notices/${noticeId}`
108
- );
109
- if (specific_uuid) {
110
- url.searchParams.append("specific_uuid", specific_uuid);
111
- }
112
- url.searchParams.append("validate_against", validate_against);
113
- if (include_fully_consented_data) {
114
- url.searchParams.append("include_fully_consented_data", "true");
115
- }
116
- const response = await fetch(url.toString(), {
117
- method: "GET",
118
- headers: {
119
- Authorization: `Bearer ${accessToken}`,
120
- "Accept-Language": language,
121
- "Content-Type": "application/json"
122
- },
123
- signal
124
- // Add abort signal for request cancellation
125
- });
126
- if (!response.ok) {
127
- let errorMessage = `Failed to fetch consent content: ${response.status} ${response.statusText}`;
128
- if (response.status === 401) {
129
- errorMessage = "Unauthorized: Invalid or expired token";
130
- } else if (response.status === 403) {
131
- errorMessage = "Forbidden: Access denied";
132
- } else if (response.status === 404) {
133
- errorMessage = "Notice not found";
134
- } else if (response.status === 409) {
135
- errorMessage = "User has already provided consent";
136
- } else if (response.status >= 500) {
137
- errorMessage = "Server error: Please try again later";
138
- }
139
- const error = new Error(errorMessage);
140
- error.status = response.status;
141
- throw error;
71
+ if (!noticeId || !accessToken || !validate_against) {
72
+ throw new Error("noticeId and accessToken are required");
73
+ }
74
+ const decodedToken = decodeTokenSafely(accessToken);
75
+ if (!decodedToken) {
76
+ throw new Error("Invalid access token");
77
+ }
78
+ const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
79
+ if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
80
+ throw new Error("Invalid token: missing organization or workspace UUID");
81
+ }
82
+ const apiBaseUrl = baseUrl || BASE_URL;
83
+ const cacheKey = `${accessToken}-${noticeId}-${validate_against}-${language}-${specific_uuid || ""}-${include_fully_consented_data}`;
84
+ if (validate_against === "all" && !specific_uuid) {
85
+ const cachedData = getCachedData(cacheKey);
86
+ if (cachedData) {
87
+ return cachedData;
142
88
  }
143
- const data = await response.json();
144
- if (validate_against === "all" && !specific_uuid) {
145
- setCachedData(cacheKey, data);
89
+ }
90
+ const url = new URL(
91
+ `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/notices/${noticeId}`
92
+ );
93
+ if (specific_uuid) {
94
+ url.searchParams.append("specific_uuid", specific_uuid);
95
+ }
96
+ url.searchParams.append("validate_against", validate_against);
97
+ if (include_fully_consented_data) {
98
+ url.searchParams.append("include_fully_consented_data", "true");
99
+ }
100
+ const response = await fetch(url.toString(), {
101
+ method: "GET",
102
+ headers: {
103
+ Authorization: `Bearer ${accessToken}`,
104
+ "Accept-Language": language
105
+ },
106
+ signal
107
+ });
108
+ if (!response.ok) {
109
+ let errorMessage = `Failed to fetch consent content: ${response.status} ${response.statusText}`;
110
+ if (response.status === 401) {
111
+ errorMessage = "Unauthorized: Invalid or expired token";
112
+ } else if (response.status === 403) {
113
+ errorMessage = "Forbidden: Access denied";
114
+ } else if (response.status === 404) {
115
+ errorMessage = "Notice not found";
116
+ } else if (response.status === 409) {
117
+ errorMessage = "User has already provided consent";
118
+ } else if (response.status >= 500) {
119
+ errorMessage = "Server error: Please try again later";
146
120
  }
147
- return data;
148
- } catch (error) {
121
+ const error = new Error(errorMessage);
122
+ error.status = response.status;
149
123
  throw error;
150
124
  }
125
+ const data = await response.json();
126
+ if (validate_against === "all" && !specific_uuid) {
127
+ setCachedData(cacheKey, data);
128
+ }
129
+ return data;
151
130
  };
152
131
  var submitConsentEvent = async ({
153
132
  accessToken,
@@ -155,134 +134,154 @@ var submitConsentEvent = async ({
155
134
  noticeUuid,
156
135
  purposes,
157
136
  declined,
137
+ language,
158
138
  meta_data,
139
+ guardianVerificationReference,
140
+ selfDeclaredAdult,
159
141
  signal
160
- // AbortSignal for request cancellation
161
142
  }) => {
162
- try {
163
- if (!noticeUuid || !accessToken || !Array.isArray(purposes)) {
164
- throw new Error("noticeUuid, accessToken, and purposes array are required");
165
- }
166
- const decodedToken = decodeTokenSafely(accessToken);
167
- if (!decodedToken) {
168
- throw new Error("Invalid access token");
169
- }
170
- const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
171
- if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
172
- throw new Error("Invalid token: missing organization or workspace UUID");
143
+ if (!noticeUuid || !accessToken || !Array.isArray(purposes)) {
144
+ throw new Error("noticeUuid, accessToken, and purposes array are required");
145
+ }
146
+ const decodedToken = decodeTokenSafely(accessToken);
147
+ if (!decodedToken) {
148
+ throw new Error("Invalid access token");
149
+ }
150
+ const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
151
+ if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
152
+ throw new Error("Invalid token: missing organization or workspace UUID");
153
+ }
154
+ const apiBaseUrl = baseUrl || BASE_URL;
155
+ const validatedPurposes = purposes.map((purpose) => {
156
+ if (!purpose.uuid) {
157
+ throw new Error(`Invalid purpose data: missing uuid`);
173
158
  }
174
- const apiBaseUrl = baseUrl || BASE_URL;
175
- let payload;
176
- if (isLegacyApi(apiBaseUrl)) {
177
- const primaryEmail = typeof window !== "undefined" ? localStorage.getItem("userEmail") : null;
178
- payload = {
179
- primary_email: primaryEmail || void 0,
180
- notice_uuid: noticeUuid,
181
- source: "WEB",
182
- declined: Boolean(declined),
183
- consents: {
184
- purposes: purposes.map((purpose) => {
185
- if (!purpose.uuid) {
186
- throw new Error(`Invalid purpose data: missing uuid`);
187
- }
188
- return {
189
- uuid: purpose.uuid,
190
- name: purpose.name,
191
- description: purpose.description || "",
192
- industries: purpose.industries || "",
193
- selected: Boolean(purpose.selected),
194
- data_elements: (purpose.data_elements || []).map((element) => ({
195
- uuid: element.uuid,
196
- name: element.name,
197
- enabled: Boolean(element.enabled),
198
- required: Boolean(element.required),
199
- selected: element.required ? true : Boolean(element.selected)
200
- }))
201
- };
202
- })
203
- },
204
- meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid } : void 0
205
- };
206
- } else {
207
- const validatedPurposes = purposes.map((purpose) => {
208
- if (!purpose.uuid) {
209
- throw new Error(`Invalid purpose data: missing uuid`);
210
- }
211
- const dataElements = (purpose.data_elements || []).filter((element) => element.enabled).map((element) => ({
212
- uuid: element.uuid,
213
- selected: element.required ? true : Boolean(element.selected)
214
- }));
215
- return {
216
- purpose_uuid: purpose.uuid,
217
- selected: Boolean(purpose.selected),
218
- data_elements: dataElements.length > 0 ? dataElements : void 0
219
- };
220
- });
221
- payload = {
222
- notice_uuid: noticeUuid,
223
- purposes: validatedPurposes,
224
- select_all_mandatory: false,
225
- source: "WEB",
226
- declined: Boolean(declined),
227
- meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid } : void 0
228
- };
159
+ const dataElements = (purpose.data_elements || []).filter((element) => element.enabled).map((element) => ({
160
+ uuid: element.uuid,
161
+ selected: element.required ? true : Boolean(element.selected)
162
+ }));
163
+ return {
164
+ purpose_uuid: purpose.uuid,
165
+ selected: Boolean(purpose.selected),
166
+ data_elements: dataElements.length > 0 ? dataElements : void 0
167
+ };
168
+ });
169
+ const payload = {
170
+ notice_uuid: noticeUuid,
171
+ purposes: validatedPurposes,
172
+ select_all_mandatory: false,
173
+ source: "WEB",
174
+ declined: Boolean(declined),
175
+ language: language || void 0,
176
+ meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid, minor_age: meta_data.minor_age } : void 0,
177
+ guardian_verification_reference: guardianVerificationReference,
178
+ self_declared_adult: selfDeclaredAdult
179
+ };
180
+ const response = await fetch(
181
+ `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/submit-consent`,
182
+ {
183
+ method: "POST",
184
+ headers: {
185
+ Authorization: `Bearer ${accessToken}`,
186
+ "Content-Type": "application/json"
187
+ },
188
+ body: JSON.stringify(payload),
189
+ signal
229
190
  }
230
- const response = await fetch(
231
- `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/submit-consent`,
232
- {
233
- method: "POST",
234
- headers: {
235
- Authorization: `Bearer ${accessToken}`,
236
- "Content-Type": "application/json"
237
- },
238
- body: JSON.stringify(payload),
239
- signal
240
- // Add abort signal for request cancellation
241
- }
242
- );
243
- if (!response.ok) {
244
- let errorMessage = `Failed to submit consent: ${response.status} ${response.statusText}`;
245
- if (response.status === 400) {
191
+ );
192
+ if (!response.ok) {
193
+ let errorMessage = `Failed to submit consent: ${response.status} ${response.statusText}`;
194
+ if (response.status === 400) {
195
+ try {
196
+ const errorData = await response.json();
197
+ errorMessage = errorData.message || "Invalid request data";
198
+ } catch {
246
199
  errorMessage = "Invalid request data";
247
- } else if (response.status === 401) {
248
- errorMessage = "Unauthorized: Invalid or expired token";
249
- } else if (response.status === 403) {
250
- errorMessage = "Forbidden: Access denied";
251
- } else if (response.status === 404) {
252
- errorMessage = "Notice not found";
253
- } else if (response.status >= 500) {
254
- errorMessage = "Server error: Please try again later";
255
200
  }
256
- const error = new Error(errorMessage);
257
- error.status = response.status;
258
- throw error;
201
+ } else if (response.status === 401) {
202
+ errorMessage = "Unauthorized: Invalid or expired token";
203
+ } else if (response.status === 403) {
204
+ errorMessage = "Forbidden: Access denied";
205
+ } else if (response.status === 404) {
206
+ errorMessage = "Notice not found";
207
+ } else if (response.status >= 500) {
208
+ errorMessage = "Server error: Please try again later";
259
209
  }
260
- apiCache.clear();
261
- } catch (error) {
210
+ const error = new Error(errorMessage);
211
+ error.status = response.status;
212
+ throw error;
213
+ }
214
+ apiCache.clear();
215
+ };
216
+ var fetchTTSAudioUrls = async ({
217
+ accessToken,
218
+ baseUrl,
219
+ noticeUuid,
220
+ language,
221
+ signal
222
+ }) => {
223
+ if (!accessToken || !noticeUuid || !language) {
224
+ throw new Error("accessToken, noticeUuid, and language are required");
225
+ }
226
+ const decodedToken = decodeTokenSafely(accessToken);
227
+ if (!decodedToken) {
228
+ throw new Error("Invalid access token");
229
+ }
230
+ const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
231
+ if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
232
+ throw new Error("Invalid token: missing organization or workspace UUID");
233
+ }
234
+ const apiBaseUrl = baseUrl || BASE_URL;
235
+ const url = `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/notices/${noticeUuid}/audio/${language}`;
236
+ const response = await fetch(url, {
237
+ method: "GET",
238
+ headers: {
239
+ Authorization: `Bearer ${accessToken}`,
240
+ Accept: "application/json"
241
+ },
242
+ signal
243
+ });
244
+ if (!response.ok) {
245
+ let errorMessage = `Failed to fetch TTS audio URLs: ${response.status} ${response.statusText}`;
246
+ if (response.status === 401) {
247
+ errorMessage = "Unauthorized: Invalid or expired token";
248
+ } else if (response.status === 403) {
249
+ errorMessage = "Forbidden: Access denied";
250
+ } else if (response.status === 404) {
251
+ errorMessage = "Notice or audio not found";
252
+ } else if (response.status >= 500) {
253
+ errorMessage = "Server error: Please try again later";
254
+ }
255
+ const error = new Error(errorMessage);
256
+ error.status = response.status;
262
257
  throw error;
263
258
  }
259
+ const data = await response.json();
260
+ return data;
264
261
  };
265
- var submitGuardianInfo = async ({
262
+ var initiateGuardianVerification = async ({
266
263
  accessToken,
267
264
  baseUrl,
268
265
  guardianName,
269
266
  guardianContact,
270
267
  guardianRelationship,
268
+ frontendCallbackUrl,
269
+ metadata,
271
270
  signal
272
- // AbortSignal for request cancellation
273
271
  }) => {
272
+ var _a;
274
273
  try {
275
274
  if (!accessToken) {
276
275
  throw new Error("accessToken is required");
277
276
  }
278
277
  if (!(guardianName == null ? void 0 : guardianName.trim())) {
279
- throw new Error("guardianName is required and cannot be empty");
278
+ throw new Error("Guardian name is required");
280
279
  }
281
280
  if (!(guardianContact == null ? void 0 : guardianContact.trim())) {
282
- throw new Error("guardianContact is required and cannot be empty");
281
+ throw new Error("Guardian contact is required");
283
282
  }
284
283
  if (!(guardianRelationship == null ? void 0 : guardianRelationship.trim())) {
285
- throw new Error("guardianRelationship is required and cannot be empty");
284
+ throw new Error("Guardian relationship is required");
286
285
  }
287
286
  const decodedToken = decodeTokenSafely(accessToken);
288
287
  if (!decodedToken) {
@@ -292,14 +291,20 @@ var submitGuardianInfo = async ({
292
291
  if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
293
292
  throw new Error("Invalid token: missing organization or workspace UUID");
294
293
  }
294
+ const apiBaseUrl = baseUrl || BASE_URL;
295
295
  const payload = {
296
296
  guardian_name: guardianName.trim(),
297
297
  guardian_contact: guardianContact.trim(),
298
298
  guardian_relationship: guardianRelationship.trim()
299
299
  };
300
- const apiBaseUrl = baseUrl || BASE_URL;
300
+ if (frontendCallbackUrl) {
301
+ payload.frontend_callback_url = frontendCallbackUrl;
302
+ }
303
+ if (metadata) {
304
+ payload.metadata = metadata;
305
+ }
301
306
  const response = await fetch(
302
- `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/guardian-info`,
307
+ `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/guardian/initiate-verification`,
303
308
  {
304
309
  method: "POST",
305
310
  headers: {
@@ -308,24 +313,26 @@ var submitGuardianInfo = async ({
308
313
  },
309
314
  body: JSON.stringify(payload),
310
315
  signal
311
- // Add abort signal for request cancellation
312
316
  }
313
317
  );
314
318
  if (!response.ok) {
315
- let errorMessage = `Failed to submit guardian info: ${response.status} ${response.statusText}`;
319
+ let errorMessage = `Failed to initiate verification: ${response.status} ${response.statusText}`;
316
320
  if (response.status === 400) {
317
- errorMessage = "Invalid request data";
321
+ try {
322
+ const errorData = await response.json();
323
+ errorMessage = errorData.message || "Invalid request data";
324
+ } catch {
325
+ errorMessage = "Invalid request data";
326
+ }
318
327
  } else if (response.status === 401) {
319
328
  errorMessage = "Unauthorized: Invalid or expired token";
320
- } else if (response.status === 403) {
321
- errorMessage = "Forbidden: Access denied";
322
329
  } else if (response.status === 404) {
323
330
  errorMessage = "Organization or workspace not found";
324
331
  } else if (response.status === 422) {
325
332
  try {
326
333
  const errorData = await response.json();
327
- errorMessage = errorData.message || "Validation error";
328
- } catch (parseError) {
334
+ errorMessage = errorData.message || "Validation error: Please check your input";
335
+ } catch {
329
336
  errorMessage = "Validation error: Please check your input";
330
337
  }
331
338
  } else if (response.status >= 500) {
@@ -335,63 +342,98 @@ var submitGuardianInfo = async ({
335
342
  error.status = response.status;
336
343
  throw error;
337
344
  }
345
+ const responseData = await response.json();
346
+ const detail = responseData.detail || responseData;
347
+ if (detail.already_verified) {
348
+ return {
349
+ success: true,
350
+ guardian_info_uuid: detail.guardian_info_uuid,
351
+ already_verified: true,
352
+ verification_reference: detail.verification_reference
353
+ };
354
+ }
355
+ if (!detail.session_token || !detail.digilocker_redirect_url) {
356
+ throw new Error("Invalid response: missing session_token or digilocker_redirect_url");
357
+ }
358
+ return {
359
+ success: (_a = detail.success) != null ? _a : true,
360
+ guardian_info_uuid: detail.guardian_info_uuid,
361
+ already_verified: false,
362
+ session_token: detail.session_token,
363
+ digilocker_redirect_url: detail.digilocker_redirect_url,
364
+ expires_at: detail.expires_at
365
+ };
338
366
  } catch (error) {
339
- console.error("Error submitting guardian info:", error instanceof Error ? error.message : error);
367
+ console.error("Error initiating guardian verification:", error instanceof Error ? error.message : error);
340
368
  throw error;
341
369
  }
342
370
  };
343
- var fetchTTSAudioUrls = async ({
371
+ var verifyGuardianStatus = async ({
344
372
  accessToken,
345
373
  baseUrl,
346
- noticeUuid,
347
- language,
374
+ sessionToken,
348
375
  signal
349
- // AbortSignal for request cancellation
350
376
  }) => {
351
- try {
352
- if (!accessToken || !noticeUuid || !language) {
353
- throw new Error("accessToken, noticeUuid, and language are required");
354
- }
355
- const decodedToken = decodeTokenSafely(accessToken);
356
- if (!decodedToken) {
357
- throw new Error("Invalid access token");
358
- }
359
- const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
360
- if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
361
- throw new Error("Invalid token: missing organization or workspace UUID");
362
- }
363
- const apiBaseUrl = baseUrl || BASE_URL;
364
- const url = `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/notices/${noticeUuid}/audio/${language}`;
365
- const response = await fetch(url, {
366
- method: "GET",
377
+ var _a, _b, _c, _d, _e, _f, _g, _h;
378
+ if (!accessToken || !sessionToken) {
379
+ throw new Error("accessToken and sessionToken are required");
380
+ }
381
+ const decodedToken = decodeTokenSafely(accessToken);
382
+ if (!decodedToken) {
383
+ throw new Error("Invalid access token");
384
+ }
385
+ const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
386
+ if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
387
+ throw new Error("Invalid token: missing organization or workspace UUID");
388
+ }
389
+ const apiBaseUrl = baseUrl || BASE_URL;
390
+ const response = await fetch(
391
+ `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/guardian/verify-status`,
392
+ {
393
+ method: "POST",
367
394
  headers: {
368
395
  Authorization: `Bearer ${accessToken}`,
369
- Accept: "application/json",
370
396
  "Content-Type": "application/json"
371
397
  },
398
+ body: JSON.stringify({
399
+ session_token: sessionToken
400
+ }),
372
401
  signal
373
- // Add abort signal for request cancellation
374
- });
375
- if (!response.ok) {
376
- let errorMessage = `Failed to fetch TTS audio URLs: ${response.status} ${response.statusText}`;
377
- if (response.status === 401) {
378
- errorMessage = "Unauthorized: Invalid or expired token";
379
- } else if (response.status === 403) {
380
- errorMessage = "Forbidden: Access denied";
381
- } else if (response.status === 404) {
382
- errorMessage = "Notice or audio not found";
383
- } else if (response.status >= 500) {
384
- errorMessage = "Server error: Please try again later";
402
+ }
403
+ );
404
+ if (!response.ok) {
405
+ let errorMessage = `Failed to check verification status: ${response.status} ${response.statusText}`;
406
+ if (response.status === 400) {
407
+ try {
408
+ const errorData = await response.json();
409
+ errorMessage = errorData.message || ((_a = errorData.detail) == null ? void 0 : _a.message) || "Invalid session token";
410
+ } catch {
411
+ errorMessage = "Invalid session token";
385
412
  }
386
- const error = new Error(errorMessage);
387
- error.status = response.status;
388
- throw error;
413
+ } else if (response.status === 401) {
414
+ errorMessage = "Unauthorized: Invalid or expired token";
415
+ } else if (response.status === 404) {
416
+ errorMessage = "Verification session not found or expired. Please restart the verification process.";
417
+ } else if (response.status === 410) {
418
+ errorMessage = "Verification session has expired. Please restart the verification process.";
419
+ } else if (response.status >= 500) {
420
+ errorMessage = "Server error: Please try again later";
389
421
  }
390
- const data = await response.json();
391
- return data;
392
- } catch (error) {
422
+ const error = new Error(errorMessage);
423
+ error.status = response.status;
393
424
  throw error;
394
425
  }
426
+ const responseData = await response.json();
427
+ const detail = responseData.detail || responseData;
428
+ return {
429
+ status: (_b = detail.status) != null ? _b : "in_progress",
430
+ guardian_info_uuid: (_c = detail.guardian_info_uuid) != null ? _c : null,
431
+ verification_reference: (_d = detail.verification_reference) != null ? _d : null,
432
+ guardian_details: (_e = detail.guardian_details) != null ? _e : null,
433
+ error: (_f = detail.error) != null ? _f : null,
434
+ error_code: (_g = detail.error_code) != null ? _g : null,
435
+ can_retry: (_h = detail.can_retry) != null ? _h : true
436
+ };
395
437
  };
396
438
 
397
439
  // src/RedactoNoticeConsent/assets/redacto-logo.png
@@ -409,6 +451,52 @@ var DPO_AUDIO_ELEMENT_MAP = [
409
451
  { audioKey: "dpo_anchor_text_audio_url", elementId: "dpo-dpo-link" }
410
452
  ];
411
453
 
454
+ // src/RedactoNoticeConsent/language-codes.ts
455
+ var LANGUAGE_NAME_TO_BCP47 = {
456
+ English: "en",
457
+ Assamese: "as",
458
+ Bengali: "bn",
459
+ Bodo: "brx",
460
+ Dogri: "doi",
461
+ Gujarati: "gu",
462
+ Hindi: "hi",
463
+ Kannada: "kn",
464
+ Kashmiri: "ks",
465
+ Konkani: "gom",
466
+ Maithili: "mai",
467
+ Malayalam: "ml",
468
+ Manipuri: "mni-Mtei",
469
+ Marathi: "mr",
470
+ Nepali: "ne",
471
+ Odia: "or",
472
+ Punjabi: "pa",
473
+ Sanskrit: "sa",
474
+ Santali: "sat",
475
+ Sindhi: "sd",
476
+ Tamil: "ta",
477
+ Telugu: "te",
478
+ Urdu: "ur"
479
+ };
480
+ var KNOWN_BCP47_CODES = new Set(Object.values(LANGUAGE_NAME_TO_BCP47));
481
+ var toBcp47Code = (language) => {
482
+ if (LANGUAGE_NAME_TO_BCP47[language]) {
483
+ return LANGUAGE_NAME_TO_BCP47[language];
484
+ }
485
+ if (KNOWN_BCP47_CODES.has(language)) {
486
+ return language;
487
+ }
488
+ const lower = language.toLowerCase();
489
+ for (const [name, code] of Object.entries(LANGUAGE_NAME_TO_BCP47)) {
490
+ if (name.toLowerCase() === lower) {
491
+ return code;
492
+ }
493
+ }
494
+ if (/^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/.test(language)) {
495
+ return language;
496
+ }
497
+ return "en";
498
+ };
499
+
412
500
  // src/RedactoNoticeConsent/injectStyles.ts
413
501
  var stylesInjected = false;
414
502
  var injectCheckboxStyles = () => {
@@ -539,6 +627,17 @@ var injectCheckboxStyles = () => {
539
627
  outline: 3px solid #FFF9C4 !important;
540
628
  outline-offset: 2px !important;
541
629
  }
630
+
631
+ /* Spinner animation for verification screens */
632
+ @keyframes spin {
633
+ from { transform: rotate(0deg); }
634
+ to { transform: rotate(360deg); }
635
+ }
636
+
637
+ /* DigiLocker callback retry button hover */
638
+ .redacto-dl-retry-btn:hover {
639
+ background-color: #2563eb !important;
640
+ }
542
641
  `;
543
642
  document.head.appendChild(style);
544
643
  stylesInjected = true;
@@ -921,6 +1020,69 @@ var useMediaQuery = (query) => {
921
1020
  return matches;
922
1021
  };
923
1022
 
1023
+ // src/RedactoNoticeConsent/services/storage.ts
1024
+ var STORAGE_KEY = "redacto_consent_sdk_state";
1025
+ var STATE_EXPIRY_MS = 30 * 60 * 1e3;
1026
+ var isBrowser = () => {
1027
+ return typeof window !== "undefined" && typeof window.sessionStorage !== "undefined";
1028
+ };
1029
+ var saveConsentState = (state) => {
1030
+ if (!isBrowser()) {
1031
+ console.warn("SessionStorage not available - state not persisted");
1032
+ return;
1033
+ }
1034
+ try {
1035
+ const stateWithTimestamp = {
1036
+ ...state,
1037
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1038
+ };
1039
+ sessionStorage.setItem(STORAGE_KEY, JSON.stringify(stateWithTimestamp));
1040
+ } catch (error) {
1041
+ console.error("Failed to save consent state:", error);
1042
+ }
1043
+ };
1044
+ var getConsentState = () => {
1045
+ if (!isBrowser()) {
1046
+ return null;
1047
+ }
1048
+ try {
1049
+ const savedState = sessionStorage.getItem(STORAGE_KEY);
1050
+ if (!savedState) {
1051
+ return null;
1052
+ }
1053
+ const parsed = JSON.parse(savedState);
1054
+ const stateAge = Date.now() - new Date(parsed.timestamp).getTime();
1055
+ if (stateAge > STATE_EXPIRY_MS) {
1056
+ clearConsentState();
1057
+ return null;
1058
+ }
1059
+ return parsed;
1060
+ } catch (error) {
1061
+ console.error("Failed to get consent state:", error);
1062
+ return null;
1063
+ }
1064
+ };
1065
+ var clearConsentState = () => {
1066
+ if (!isBrowser()) {
1067
+ return;
1068
+ }
1069
+ try {
1070
+ sessionStorage.removeItem(STORAGE_KEY);
1071
+ } catch (error) {
1072
+ console.error("Failed to clear consent state:", error);
1073
+ }
1074
+ };
1075
+ var updateConsentState = (updates) => {
1076
+ const currentState = getConsentState();
1077
+ if (currentState) {
1078
+ saveConsentState({
1079
+ ...currentState,
1080
+ ...updates,
1081
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1082
+ });
1083
+ }
1084
+ };
1085
+
924
1086
  // src/RedactoNoticeConsent/RedactoNoticeConsent.tsx
925
1087
  var import_jsx_runtime = require("react/jsx-runtime");
926
1088
  var PurposeItem = ({
@@ -1228,6 +1390,12 @@ var PurposeItem = ({
1228
1390
  )
1229
1391
  ] }, purpose.uuid);
1230
1392
  };
1393
+ var RELATIONSHIP_OPTIONS = [
1394
+ { value: "Father", label: "Father" },
1395
+ { value: "Mother", label: "Mother" },
1396
+ { value: "Legal Guardian", label: "Legal Guardian" },
1397
+ { value: "Grandparent", label: "Grandparent" }
1398
+ ];
1231
1399
  var GuardianForm = ({
1232
1400
  formData,
1233
1401
  errors,
@@ -1244,34 +1412,128 @@ var GuardianForm = ({
1244
1412
  declineButtonStyle,
1245
1413
  logoUrl
1246
1414
  }) => {
1415
+ var _a, _b, _c, _d, _e;
1416
+ const [isDropdownOpen, setIsDropdownOpen] = (0, import_react2.useState)(false);
1417
+ const [focusedIndex, setFocusedIndex] = (0, import_react2.useState)(-1);
1418
+ const [openUpward, setOpenUpward] = (0, import_react2.useState)(false);
1419
+ const dropdownRef = (0, import_react2.useRef)(null);
1420
+ const optionsRef = (0, import_react2.useRef)(null);
1421
+ (0, import_react2.useEffect)(() => {
1422
+ if (!isDropdownOpen) return;
1423
+ const handleClickOutside = (e) => {
1424
+ if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
1425
+ setIsDropdownOpen(false);
1426
+ setFocusedIndex(-1);
1427
+ }
1428
+ };
1429
+ document.addEventListener("mousedown", handleClickOutside);
1430
+ return () => document.removeEventListener("mousedown", handleClickOutside);
1431
+ }, [isDropdownOpen]);
1432
+ (0, import_react2.useEffect)(() => {
1433
+ if (!isDropdownOpen || !dropdownRef.current) return;
1434
+ const rect = dropdownRef.current.getBoundingClientRect();
1435
+ const spaceBelow = window.innerHeight - rect.bottom;
1436
+ const dropdownHeight = 220;
1437
+ setOpenUpward(spaceBelow < dropdownHeight && rect.top > dropdownHeight);
1438
+ }, [isDropdownOpen]);
1439
+ const handleSelect = (value) => {
1440
+ onChange("guardianRelationship", value);
1441
+ setIsDropdownOpen(false);
1442
+ setFocusedIndex(-1);
1443
+ };
1444
+ const handleKeyDown = (e) => {
1445
+ if (!isDropdownOpen) {
1446
+ if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
1447
+ e.preventDefault();
1448
+ setIsDropdownOpen(true);
1449
+ setFocusedIndex(0);
1450
+ }
1451
+ return;
1452
+ }
1453
+ switch (e.key) {
1454
+ case "ArrowDown":
1455
+ e.preventDefault();
1456
+ setFocusedIndex((prev) => Math.min(prev + 1, RELATIONSHIP_OPTIONS.length - 1));
1457
+ break;
1458
+ case "ArrowUp":
1459
+ e.preventDefault();
1460
+ setFocusedIndex((prev) => Math.max(prev - 1, 0));
1461
+ break;
1462
+ case "Enter":
1463
+ case " ":
1464
+ e.preventDefault();
1465
+ if (focusedIndex >= 0) handleSelect(RELATIONSHIP_OPTIONS[focusedIndex].value);
1466
+ break;
1467
+ case "Escape":
1468
+ case "Tab":
1469
+ setIsDropdownOpen(false);
1470
+ setFocusedIndex(-1);
1471
+ break;
1472
+ }
1473
+ };
1474
+ (0, import_react2.useEffect)(() => {
1475
+ var _a2;
1476
+ if (focusedIndex >= 0 && optionsRef.current) {
1477
+ const option = optionsRef.current.children[focusedIndex];
1478
+ (_a2 = option == null ? void 0 : option.scrollIntoView) == null ? void 0 : _a2.call(option, { block: "nearest" });
1479
+ }
1480
+ }, [focusedIndex]);
1481
+ const borderColor = (settings == null ? void 0 : settings.borderColor) || "#d0d5dd";
1482
+ const textColor = (settings == null ? void 0 : settings.textColor) || "#344054";
1483
+ const bgColor = (settings == null ? void 0 : settings.backgroundColor) || "#ffffff";
1484
+ const radius = (settings == null ? void 0 : settings.borderRadius) || "8px";
1485
+ const font = (settings == null ? void 0 : settings.font) || "inherit";
1247
1486
  const inputStyle = {
1248
1487
  width: "100%",
1249
1488
  padding: "10px 12px",
1250
- border: `1px solid ${(settings == null ? void 0 : settings.borderColor) || "#d0d5dd"}`,
1251
- borderRadius: (settings == null ? void 0 : settings.borderRadius) || "8px",
1489
+ border: `1px solid ${borderColor}`,
1490
+ borderRadius: radius,
1252
1491
  fontSize: "13px",
1253
- fontFamily: (settings == null ? void 0 : settings.font) || "inherit",
1254
- color: (settings == null ? void 0 : settings.textColor) || "#344054",
1255
- backgroundColor: (settings == null ? void 0 : settings.backgroundColor) || "#ffffff",
1256
- marginBottom: "4px",
1257
- boxSizing: "border-box"
1492
+ fontFamily: font,
1493
+ color: textColor,
1494
+ backgroundColor: bgColor,
1495
+ marginBottom: "2px",
1496
+ boxSizing: "border-box",
1497
+ outline: "none"
1258
1498
  };
1259
1499
  const errorStyle = {
1260
1500
  color: "#DC2626",
1261
- fontSize: "12px",
1501
+ fontSize: "11px",
1262
1502
  marginTop: "4px",
1263
- marginBottom: "16px"
1503
+ marginBottom: "0px"
1264
1504
  };
1265
1505
  const labelStyle = {
1266
1506
  ...componentTextStyle,
1267
1507
  fontSize: "13px",
1268
1508
  fontWeight: "500",
1269
- marginBottom: "6px",
1509
+ marginBottom: "4px",
1270
1510
  display: "block"
1271
1511
  };
1512
+ const hintStyle = {
1513
+ fontSize: "11px",
1514
+ color: "#667085",
1515
+ marginTop: "2px",
1516
+ marginBottom: "0px",
1517
+ lineHeight: "1.4"
1518
+ };
1519
+ const infoBannerStyle = {
1520
+ display: "flex",
1521
+ alignItems: "flex-start",
1522
+ gap: "8px",
1523
+ padding: "10px 12px",
1524
+ backgroundColor: "#EFF6FF",
1525
+ border: "1px solid #BFDBFE",
1526
+ borderRadius: radius,
1527
+ marginBottom: "16px",
1528
+ fontSize: "12px",
1529
+ lineHeight: "1.5",
1530
+ color: "#1E40AF"
1531
+ };
1532
+ const selectedLabel = (_a = RELATIONSHIP_OPTIONS.find((o) => o.value === formData.guardianRelationship)) == null ? void 0 : _a.label;
1533
+ const hasError = Boolean(errors.guardianRelationship);
1272
1534
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1273
1535
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...styles3.topSection, ...componentTextStyle }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles3.topLeft, children: [
1274
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { style: styles3.logo, src: logoUrl, alt: "Redacto Logo" }),
1536
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { style: styles3.logo, src: logoUrl, alt: "Logo" }),
1275
1537
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1276
1538
  "h2",
1277
1539
  {
@@ -1283,7 +1545,7 @@ var GuardianForm = ({
1283
1545
  fontSize: "16px",
1284
1546
  fontWeight: 600
1285
1547
  },
1286
- children: "Guardian Information Required"
1548
+ children: "Guardian Verification"
1287
1549
  }
1288
1550
  )
1289
1551
  ] }) }),
@@ -1303,31 +1565,49 @@ var GuardianForm = ({
1303
1565
  ...styles3.privacyText,
1304
1566
  ...responsiveStyles.privacyText,
1305
1567
  ...componentTextStyle,
1306
- fontSize: "14px",
1307
- lineHeight: "1.4"
1568
+ fontSize: "13px",
1569
+ lineHeight: "1.5",
1570
+ marginBottom: "12px"
1308
1571
  },
1309
- children: "As you are under the age of consent, we need to collect information about your legal guardian to proceed."
1572
+ children: "Since you are under the age of consent, a legal guardian must verify their identity via DigiLocker before you can proceed."
1310
1573
  }
1311
1574
  ),
1575
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: infoBannerStyle, children: [
1576
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1577
+ "svg",
1578
+ {
1579
+ width: "16",
1580
+ height: "16",
1581
+ viewBox: "0 0 24 24",
1582
+ fill: "none",
1583
+ stroke: "#2563EB",
1584
+ strokeWidth: "2",
1585
+ strokeLinecap: "round",
1586
+ strokeLinejoin: "round",
1587
+ style: { flexShrink: 0, marginTop: "1px" },
1588
+ children: [
1589
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
1590
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "12", y1: "16", x2: "12", y2: "12" }),
1591
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "12", y1: "8", x2: "12.01", y2: "8" })
1592
+ ]
1593
+ }
1594
+ ),
1595
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "The guardian's name will be matched against their DigiLocker identity document (e.g. Aadhaar). Please enter the name exactly as it appears on the document." })
1596
+ ] }),
1312
1597
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1313
1598
  "div",
1314
1599
  {
1315
1600
  style: {
1316
1601
  display: "flex",
1317
1602
  flexDirection: "column",
1318
- gap: "12px",
1319
- marginTop: "16px"
1603
+ gap: "14px"
1320
1604
  },
1321
1605
  children: [
1322
1606
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1323
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1324
- "label",
1325
- {
1326
- htmlFor: "guardianName",
1327
- style: { ...labelStyle, marginBottom: "8px", display: "block" },
1328
- children: "Name of Guardian *"
1329
- }
1330
- ),
1607
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { htmlFor: "guardianName", style: labelStyle, children: [
1608
+ "Guardian's Full Name ",
1609
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "#DC2626" }, children: "*" })
1610
+ ] }),
1331
1611
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1332
1612
  "input",
1333
1613
  {
@@ -1337,62 +1617,170 @@ var GuardianForm = ({
1337
1617
  onChange: (e) => onChange("guardianName", e.target.value),
1338
1618
  style: {
1339
1619
  ...inputStyle,
1340
- borderColor: errors.guardianName ? "#DC2626" : inputStyle.border
1620
+ borderColor: errors.guardianName ? "#DC2626" : borderColor
1341
1621
  },
1342
- placeholder: "Enter guardian's full name"
1622
+ placeholder: "e.g. Rajesh Kumar",
1623
+ autoComplete: "name",
1624
+ "aria-describedby": "guardianName-hint"
1343
1625
  }
1344
1626
  ),
1345
- errors.guardianName && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: errorStyle, children: errors.guardianName })
1627
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { id: "guardianName-hint", style: hintStyle, children: "Enter the full name as per government ID (Aadhaar, PAN, etc.)" }),
1628
+ errors.guardianName && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: errorStyle, role: "alert", children: errors.guardianName })
1346
1629
  ] }),
1347
1630
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1348
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1349
- "label",
1350
- {
1351
- htmlFor: "guardianContact",
1352
- style: { ...labelStyle, marginBottom: "8px", display: "block" },
1353
- children: "Contact of Guardian *"
1354
- }
1355
- ),
1631
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { htmlFor: "guardianContact", style: labelStyle, children: [
1632
+ "Guardian's Phone Number ",
1633
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "#DC2626" }, children: "*" })
1634
+ ] }),
1356
1635
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1357
1636
  "input",
1358
1637
  {
1359
1638
  id: "guardianContact",
1360
- type: "text",
1639
+ type: "tel",
1640
+ inputMode: "tel",
1361
1641
  value: formData.guardianContact,
1362
1642
  onChange: (e) => onChange("guardianContact", e.target.value),
1363
1643
  style: {
1364
1644
  ...inputStyle,
1365
- borderColor: errors.guardianContact ? "#DC2626" : inputStyle.border
1645
+ borderColor: errors.guardianContact ? "#DC2626" : borderColor
1366
1646
  },
1367
- placeholder: "Enter guardian's email or phone number"
1647
+ placeholder: "e.g. 9876543210",
1648
+ autoComplete: "tel",
1649
+ "aria-describedby": "guardianContact-hint"
1368
1650
  }
1369
1651
  ),
1370
- errors.guardianContact && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: errorStyle, children: errors.guardianContact })
1652
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { id: "guardianContact-hint", style: hintStyle, children: "Mobile number linked to guardian's Aadhaar" }),
1653
+ errors.guardianContact && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: errorStyle, role: "alert", children: errors.guardianContact })
1371
1654
  ] }),
1372
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1373
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1374
- "label",
1655
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ref: dropdownRef, style: { position: "relative" }, children: [
1656
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { id: "guardianRelationship-label", style: labelStyle, children: [
1657
+ "Relationship ",
1658
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "#DC2626" }, children: "*" })
1659
+ ] }),
1660
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1661
+ "div",
1375
1662
  {
1376
- htmlFor: "guardianRelationship",
1377
- style: { ...labelStyle, marginBottom: "8px", display: "block" },
1378
- children: "Relationship to Guardian *"
1663
+ role: "combobox",
1664
+ "aria-expanded": isDropdownOpen,
1665
+ "aria-haspopup": "listbox",
1666
+ "aria-labelledby": "guardianRelationship-label",
1667
+ "aria-controls": "guardianRelationship-listbox",
1668
+ "aria-activedescendant": focusedIndex >= 0 ? `guardianRelationship-option-${focusedIndex}` : void 0,
1669
+ tabIndex: 0,
1670
+ onClick: () => {
1671
+ setIsDropdownOpen((prev) => !prev);
1672
+ setFocusedIndex(-1);
1673
+ },
1674
+ onKeyDown: handleKeyDown,
1675
+ style: {
1676
+ ...inputStyle,
1677
+ borderColor: hasError ? "#DC2626" : isDropdownOpen ? ((_c = (_b = settings == null ? void 0 : settings.button) == null ? void 0 : _b.accept) == null ? void 0 : _c.backgroundColor) || "#3b82f6" : borderColor,
1678
+ boxShadow: isDropdownOpen ? `0 0 0 1px ${((_e = (_d = settings == null ? void 0 : settings.button) == null ? void 0 : _d.accept) == null ? void 0 : _e.backgroundColor) || "#3b82f6"}` : "none",
1679
+ color: selectedLabel ? textColor : "#9CA3AF",
1680
+ cursor: "pointer",
1681
+ display: "flex",
1682
+ alignItems: "center",
1683
+ justifyContent: "space-between",
1684
+ userSelect: "none",
1685
+ transition: "border-color 0.15s, box-shadow 0.15s"
1686
+ },
1687
+ children: [
1688
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: selectedLabel || "Select relationship" }),
1689
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1690
+ "svg",
1691
+ {
1692
+ width: "14",
1693
+ height: "14",
1694
+ viewBox: "0 0 24 24",
1695
+ fill: "none",
1696
+ stroke: "#667085",
1697
+ strokeWidth: "2",
1698
+ strokeLinecap: "round",
1699
+ strokeLinejoin: "round",
1700
+ style: {
1701
+ flexShrink: 0,
1702
+ marginLeft: "8px",
1703
+ transform: isDropdownOpen ? "rotate(180deg)" : "rotate(0deg)",
1704
+ transition: "transform 0.2s"
1705
+ },
1706
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "6 9 12 15 18 9" })
1707
+ }
1708
+ )
1709
+ ]
1379
1710
  }
1380
1711
  ),
1381
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1382
- "input",
1712
+ isDropdownOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1713
+ "div",
1383
1714
  {
1384
- id: "guardianRelationship",
1385
- type: "text",
1386
- value: formData.guardianRelationship,
1387
- onChange: (e) => onChange("guardianRelationship", e.target.value),
1715
+ ref: optionsRef,
1716
+ id: "guardianRelationship-listbox",
1717
+ role: "listbox",
1718
+ "aria-labelledby": "guardianRelationship-label",
1388
1719
  style: {
1389
- ...inputStyle,
1390
- borderColor: errors.guardianRelationship ? "#DC2626" : inputStyle.border
1720
+ position: "absolute",
1721
+ ...openUpward ? { bottom: "calc(100% + 4px)" } : { top: "calc(100% + 4px)" },
1722
+ left: 0,
1723
+ right: 0,
1724
+ backgroundColor: bgColor,
1725
+ border: `1px solid ${borderColor}`,
1726
+ borderRadius: radius,
1727
+ boxShadow: openUpward ? "0 -4px 12px rgba(0, 0, 0, 0.1), 0 -1px 3px rgba(0, 0, 0, 0.06)" : "0 4px 12px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.06)",
1728
+ zIndex: 50,
1729
+ overflow: "hidden",
1730
+ padding: "4px 0",
1731
+ maxHeight: "220px",
1732
+ overflowY: "auto"
1391
1733
  },
1392
- placeholder: "e.g., Parent, Legal Guardian, etc."
1734
+ children: RELATIONSHIP_OPTIONS.map((option, index) => {
1735
+ var _a2, _b2;
1736
+ const isSelected = formData.guardianRelationship === option.value;
1737
+ const isFocused = focusedIndex === index;
1738
+ const accentColor = ((_b2 = (_a2 = settings == null ? void 0 : settings.button) == null ? void 0 : _a2.accept) == null ? void 0 : _b2.backgroundColor) || "#3b82f6";
1739
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1740
+ "div",
1741
+ {
1742
+ id: `guardianRelationship-option-${index}`,
1743
+ role: "option",
1744
+ "aria-selected": isSelected,
1745
+ onClick: () => handleSelect(option.value),
1746
+ onMouseEnter: () => setFocusedIndex(index),
1747
+ style: {
1748
+ padding: "9px 12px",
1749
+ fontSize: "13px",
1750
+ fontFamily: font,
1751
+ color: isSelected ? accentColor : textColor,
1752
+ backgroundColor: isFocused ? "#F3F4F6" : "transparent",
1753
+ cursor: "pointer",
1754
+ display: "flex",
1755
+ alignItems: "center",
1756
+ justifyContent: "space-between",
1757
+ transition: "background-color 0.1s"
1758
+ },
1759
+ children: [
1760
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: option.label }),
1761
+ isSelected && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1762
+ "svg",
1763
+ {
1764
+ width: "14",
1765
+ height: "14",
1766
+ viewBox: "0 0 24 24",
1767
+ fill: "none",
1768
+ stroke: accentColor,
1769
+ strokeWidth: "2.5",
1770
+ strokeLinecap: "round",
1771
+ strokeLinejoin: "round",
1772
+ style: { flexShrink: 0 },
1773
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "20 6 9 17 4 12" })
1774
+ }
1775
+ )
1776
+ ]
1777
+ },
1778
+ option.value
1779
+ );
1780
+ })
1393
1781
  }
1394
1782
  ),
1395
- errors.guardianRelationship && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: errorStyle, children: errors.guardianRelationship })
1783
+ errors.guardianRelationship && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: errorStyle, role: "alert", children: errors.guardianRelationship })
1396
1784
  ] })
1397
1785
  ]
1398
1786
  }
@@ -1403,7 +1791,7 @@ var GuardianForm = ({
1403
1791
  role: "alert",
1404
1792
  style: {
1405
1793
  color: "#DC2626",
1406
- fontSize: "14px",
1794
+ fontSize: "13px",
1407
1795
  marginTop: "16px",
1408
1796
  padding: "12px",
1409
1797
  borderRadius: "6px",
@@ -1439,8 +1827,8 @@ var GuardianForm = ({
1439
1827
  onClick: onNext,
1440
1828
  disabled: isSubmitting,
1441
1829
  type: "button",
1442
- "aria-label": isSubmitting ? "Submitting guardian information" : "Submit guardian information and proceed",
1443
- children: isSubmitting ? "Submitting..." : "Next"
1830
+ "aria-label": isSubmitting ? "Verifying guardian identity" : "Proceed to guardian verification",
1831
+ children: isSubmitting ? "Verifying..." : "Verify via DigiLocker"
1444
1832
  }
1445
1833
  ),
1446
1834
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1454,7 +1842,7 @@ var GuardianForm = ({
1454
1842
  },
1455
1843
  onClick: onDecline,
1456
1844
  type: "button",
1457
- "aria-label": "Cancel guardian information form",
1845
+ "aria-label": "Cancel",
1458
1846
  children: "Cancel"
1459
1847
  }
1460
1848
  )
@@ -1463,6 +1851,266 @@ var GuardianForm = ({
1463
1851
  )
1464
1852
  ] });
1465
1853
  };
1854
+ var VerificationScreen = ({
1855
+ onBack,
1856
+ onContinue,
1857
+ isInitiating,
1858
+ isPolling,
1859
+ isVerificationComplete,
1860
+ isAutoTransitioning,
1861
+ error,
1862
+ errorCode,
1863
+ canRetry,
1864
+ styles: styles3,
1865
+ responsiveStyles,
1866
+ settings,
1867
+ componentHeadingStyle,
1868
+ componentTextStyle,
1869
+ acceptButtonStyle,
1870
+ declineButtonStyle,
1871
+ logoUrl
1872
+ }) => {
1873
+ var _a, _b;
1874
+ const errorStyle = {
1875
+ color: "#DC2626",
1876
+ fontSize: "14px",
1877
+ marginTop: "16px",
1878
+ padding: "12px",
1879
+ borderRadius: "6px",
1880
+ border: "1px solid #FCA5A5",
1881
+ backgroundColor: "#FEF2F2",
1882
+ textAlign: "center"
1883
+ };
1884
+ const statusStyle = {
1885
+ color: "#059669",
1886
+ fontSize: "14px",
1887
+ marginTop: "16px",
1888
+ padding: "12px",
1889
+ borderRadius: "6px",
1890
+ border: "1px solid #A7F3D0",
1891
+ backgroundColor: "#D1FAE5",
1892
+ textAlign: "center"
1893
+ };
1894
+ const spinnerStyle = {
1895
+ width: "48px",
1896
+ height: "48px",
1897
+ border: "4px solid #e5e7eb",
1898
+ borderTopColor: ((_b = (_a = settings == null ? void 0 : settings.button) == null ? void 0 : _a.accept) == null ? void 0 : _b.backgroundColor) || "#3b82f6",
1899
+ borderRadius: "50%",
1900
+ animation: "spin 1s linear infinite",
1901
+ margin: "24px auto"
1902
+ };
1903
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1904
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...styles3.topSection, ...componentTextStyle }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles3.topLeft, children: [
1905
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { style: styles3.logo, src: logoUrl, alt: "Logo" }),
1906
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1907
+ "h2",
1908
+ {
1909
+ id: "verification-title",
1910
+ style: {
1911
+ ...styles3.title,
1912
+ ...responsiveStyles.title,
1913
+ ...componentHeadingStyle,
1914
+ fontSize: "16px",
1915
+ fontWeight: 600
1916
+ },
1917
+ children: "Guardian Verification"
1918
+ }
1919
+ )
1920
+ ] }) }),
1921
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1922
+ "div",
1923
+ {
1924
+ style: {
1925
+ ...styles3.middleSection,
1926
+ ...responsiveStyles.middleSection,
1927
+ ...componentTextStyle,
1928
+ textAlign: "center"
1929
+ },
1930
+ children: [
1931
+ isVerificationComplete && !error ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1932
+ "div",
1933
+ {
1934
+ style: {
1935
+ display: "flex",
1936
+ flexDirection: "column",
1937
+ alignItems: "center",
1938
+ justifyContent: "center",
1939
+ flex: 1
1940
+ },
1941
+ children: [
1942
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1943
+ "div",
1944
+ {
1945
+ style: {
1946
+ width: "64px",
1947
+ height: "64px",
1948
+ borderRadius: "50%",
1949
+ backgroundColor: "#D1FAE5",
1950
+ display: "flex",
1951
+ alignItems: "center",
1952
+ justifyContent: "center"
1953
+ },
1954
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1955
+ "svg",
1956
+ {
1957
+ width: "32",
1958
+ height: "32",
1959
+ viewBox: "0 0 24 24",
1960
+ fill: "none",
1961
+ stroke: "#059669",
1962
+ strokeWidth: "2",
1963
+ strokeLinecap: "round",
1964
+ strokeLinejoin: "round",
1965
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "20 6 9 17 4 12" })
1966
+ }
1967
+ )
1968
+ }
1969
+ ),
1970
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1971
+ "p",
1972
+ {
1973
+ style: {
1974
+ ...componentHeadingStyle,
1975
+ fontSize: "16px",
1976
+ fontWeight: 600,
1977
+ lineHeight: "1.5",
1978
+ marginTop: "12px",
1979
+ marginBottom: "24px"
1980
+ },
1981
+ children: "Verification Completed"
1982
+ }
1983
+ )
1984
+ ]
1985
+ }
1986
+ ) : (isInitiating || isPolling) && !error ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1987
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: spinnerStyle }),
1988
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: statusStyle, children: isInitiating ? "Opening DigiLocker in a new tab..." : "Please complete the verification in the DigiLocker tab. This page will update automatically when verification is complete." })
1989
+ ] }) : null,
1990
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1991
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1992
+ "div",
1993
+ {
1994
+ style: {
1995
+ width: "64px",
1996
+ height: "64px",
1997
+ borderRadius: "50%",
1998
+ backgroundColor: "#fee2e2",
1999
+ display: "flex",
2000
+ alignItems: "center",
2001
+ justifyContent: "center",
2002
+ margin: "24px auto"
2003
+ },
2004
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2005
+ "svg",
2006
+ {
2007
+ width: "32",
2008
+ height: "32",
2009
+ viewBox: "0 0 24 24",
2010
+ fill: "none",
2011
+ stroke: "#dc2626",
2012
+ strokeWidth: "2",
2013
+ strokeLinecap: "round",
2014
+ strokeLinejoin: "round",
2015
+ children: [
2016
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
2017
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
2018
+ ]
2019
+ }
2020
+ )
2021
+ }
2022
+ ),
2023
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2024
+ "p",
2025
+ {
2026
+ style: {
2027
+ ...styles3.privacyText,
2028
+ ...responsiveStyles.privacyText,
2029
+ ...componentTextStyle,
2030
+ fontSize: "14px",
2031
+ lineHeight: "1.5",
2032
+ marginBottom: "12px"
2033
+ },
2034
+ children: "Verification failed"
2035
+ }
2036
+ ),
2037
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: errorStyle, children: error })
2038
+ ] })
2039
+ ]
2040
+ }
2041
+ ),
2042
+ isAutoTransitioning ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2043
+ "div",
2044
+ {
2045
+ style: {
2046
+ ...styles3.bottomSection,
2047
+ ...responsiveStyles.bottomSection,
2048
+ ...componentTextStyle,
2049
+ paddingTop: "12px",
2050
+ display: "flex",
2051
+ gap: "12px",
2052
+ justifyContent: "center"
2053
+ },
2054
+ children: isVerificationComplete && !error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2055
+ "button",
2056
+ {
2057
+ style: {
2058
+ ...styles3.button,
2059
+ ...responsiveStyles.button,
2060
+ ...acceptButtonStyle
2061
+ },
2062
+ onClick: onContinue,
2063
+ type: "button",
2064
+ "aria-label": "Continue to consent form",
2065
+ children: "Continue"
2066
+ }
2067
+ ) : error && canRetry ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2068
+ "button",
2069
+ {
2070
+ style: {
2071
+ ...styles3.button,
2072
+ ...responsiveStyles.button,
2073
+ ...styles3.acceptButton,
2074
+ ...acceptButtonStyle
2075
+ },
2076
+ onClick: () => onBack(errorCode === "GUARDIAN_UNDER_18"),
2077
+ type: "button",
2078
+ "aria-label": errorCode === "GUARDIAN_UNDER_18" ? "Change guardian details" : "Try verification again",
2079
+ children: errorCode === "GUARDIAN_UNDER_18" ? "Change Guardian" : "Try Again"
2080
+ }
2081
+ ) : error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2082
+ "button",
2083
+ {
2084
+ style: {
2085
+ ...styles3.button,
2086
+ ...responsiveStyles.button,
2087
+ ...styles3.cancelButton,
2088
+ ...declineButtonStyle
2089
+ },
2090
+ onClick: () => onBack(false),
2091
+ type: "button",
2092
+ "aria-label": "Go back",
2093
+ children: "Go Back"
2094
+ }
2095
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2096
+ "button",
2097
+ {
2098
+ style: {
2099
+ ...styles3.button,
2100
+ ...responsiveStyles.button,
2101
+ ...styles3.cancelButton,
2102
+ ...declineButtonStyle
2103
+ },
2104
+ onClick: () => onBack(false),
2105
+ type: "button",
2106
+ "aria-label": "Cancel verification",
2107
+ children: "Cancel"
2108
+ }
2109
+ )
2110
+ }
2111
+ )
2112
+ ] });
2113
+ };
1466
2114
  var AgeVerification = ({
1467
2115
  onYes,
1468
2116
  onNo,
@@ -1625,6 +2273,10 @@ var RedactoNoticeConsent = ({
1625
2273
  settings,
1626
2274
  applicationId,
1627
2275
  validateAgainst = "all",
2276
+ digilockerMode = "redirect",
2277
+ // Default to full page redirect (DigiLocker blocks iframe embedding)
2278
+ digilockerCallbackUrl = "/digilocker-callback",
2279
+ // Default callback path
1628
2280
  includeFullyConsentedData = false,
1629
2281
  reviewModeButtonText = "Continue"
1630
2282
  }) => {
@@ -1680,6 +2332,8 @@ var RedactoNoticeConsent = ({
1680
2332
  const [currentAudioIndex, setCurrentAudioIndex] = (0, import_react2.useState)(0);
1681
2333
  const [highlightedElementId, setHighlightedElementId] = (0, import_react2.useState)(null);
1682
2334
  const [showAgeVerification, setShowAgeVerification] = (0, import_react2.useState)(false);
2335
+ const [isMinorFlow, setIsMinorFlow] = (0, import_react2.useState)(false);
2336
+ const [selfDeclaredAdult, setSelfDeclaredAdult] = (0, import_react2.useState)(false);
1683
2337
  const [showGuardianForm, setShowGuardianForm] = (0, import_react2.useState)(false);
1684
2338
  const [isSubmittingGuardian, setIsSubmittingGuardian] = (0, import_react2.useState)(false);
1685
2339
  const [guardianFormData, setGuardianFormData] = (0, import_react2.useState)({
@@ -1688,12 +2342,32 @@ var RedactoNoticeConsent = ({
1688
2342
  guardianRelationship: ""
1689
2343
  });
1690
2344
  const [guardianFormErrors, setGuardianFormErrors] = (0, import_react2.useState)({});
2345
+ const [showVerificationScreen, setShowVerificationScreen] = (0, import_react2.useState)(false);
2346
+ const [verificationReference, setVerificationReference] = (0, import_react2.useState)(null);
2347
+ const [isInitiatingVerification, setIsInitiatingVerification] = (0, import_react2.useState)(false);
2348
+ const [verificationError, setVerificationError] = (0, import_react2.useState)(
2349
+ null
2350
+ );
2351
+ const [verificationErrorCode, setVerificationErrorCode] = (0, import_react2.useState)(null);
2352
+ const [canRetryVerification, setCanRetryVerification] = (0, import_react2.useState)(false);
2353
+ const [isPollingStatus, setIsPollingStatus] = (0, import_react2.useState)(false);
2354
+ const [isVerificationComplete, setIsVerificationComplete] = (0, import_react2.useState)(false);
2355
+ const [isAutoTransitioning, setIsAutoTransitioning] = (0, import_react2.useState)(false);
1691
2356
  const modalRef = (0, import_react2.useRef)(null);
1692
2357
  const firstFocusableRef = (0, import_react2.useRef)(null);
1693
2358
  const lastFocusableRef = (0, import_react2.useRef)(null);
1694
2359
  const abortControllerRef = (0, import_react2.useRef)(null);
1695
2360
  const isRequestInProgressRef = (0, import_react2.useRef)(false);
1696
2361
  const currentAudioRef = (0, import_react2.useRef)(null);
2362
+ const popupCheckIntervalRef = (0, import_react2.useRef)(null);
2363
+ const statusPollIntervalRef = (0, import_react2.useRef)(null);
2364
+ const verificationPopupRef = (0, import_react2.useRef)(null);
2365
+ const pollingCleanupRef = (0, import_react2.useRef)(null);
2366
+ const autoTransitionTimerRef = (0, import_react2.useRef)(null);
2367
+ const digilockerRedirectTimeoutRef = (0, import_react2.useRef)(null);
2368
+ const isInitialMountRef = (0, import_react2.useRef)(true);
2369
+ const isPollingStatusRef = (0, import_react2.useRef)(false);
2370
+ const verificationSessionTokenRef = (0, import_react2.useRef)(null);
1697
2371
  const isMobile = useMediaQuery("(max-width: 768px)");
1698
2372
  const responsiveStyles = (0, import_react2.useMemo)(
1699
2373
  () => ({
@@ -1801,6 +2475,7 @@ var RedactoNoticeConsent = ({
1801
2475
  }, [content, selectedDataElements]);
1802
2476
  const acceptDisabled = isSubmitting || !areAllRequiredElementsChecked;
1803
2477
  (0, import_react2.useEffect)(() => {
2478
+ var _a2, _b2;
1804
2479
  clearApiCache();
1805
2480
  isRequestInProgressRef.current = false;
1806
2481
  setContent(null);
@@ -1816,6 +2491,8 @@ var RedactoNoticeConsent = ({
1816
2491
  setIsSubmittingAccept(false);
1817
2492
  setIsSubmittingDecline(false);
1818
2493
  setShowAgeVerification(false);
2494
+ setIsMinorFlow(false);
2495
+ setSelfDeclaredAdult(false);
1819
2496
  setShowGuardianForm(false);
1820
2497
  setIsSubmittingGuardian(false);
1821
2498
  setGuardianFormData({
@@ -1824,6 +2501,41 @@ var RedactoNoticeConsent = ({
1824
2501
  guardianRelationship: ""
1825
2502
  });
1826
2503
  setGuardianFormErrors({});
2504
+ setShowVerificationScreen(false);
2505
+ setVerificationReference(null);
2506
+ setIsInitiatingVerification(false);
2507
+ setVerificationError(null);
2508
+ setVerificationErrorCode(null);
2509
+ setCanRetryVerification(false);
2510
+ setIsVerificationComplete(false);
2511
+ setIsAutoTransitioning(false);
2512
+ setIsPollingStatus(false);
2513
+ isPollingStatusRef.current = false;
2514
+ verificationSessionTokenRef.current = null;
2515
+ if (popupCheckIntervalRef.current) {
2516
+ clearInterval(popupCheckIntervalRef.current);
2517
+ popupCheckIntervalRef.current = null;
2518
+ }
2519
+ if (statusPollIntervalRef.current) {
2520
+ clearTimeout(statusPollIntervalRef.current);
2521
+ statusPollIntervalRef.current = null;
2522
+ }
2523
+ (_a2 = pollingCleanupRef.current) == null ? void 0 : _a2.call(pollingCleanupRef);
2524
+ pollingCleanupRef.current = null;
2525
+ (_b2 = verificationPopupRef.current) == null ? void 0 : _b2.close();
2526
+ verificationPopupRef.current = null;
2527
+ if (autoTransitionTimerRef.current) {
2528
+ clearTimeout(autoTransitionTimerRef.current);
2529
+ autoTransitionTimerRef.current = null;
2530
+ }
2531
+ if (digilockerRedirectTimeoutRef.current) {
2532
+ clearTimeout(digilockerRedirectTimeoutRef.current);
2533
+ digilockerRedirectTimeoutRef.current = null;
2534
+ }
2535
+ if (!isInitialMountRef.current) {
2536
+ clearConsentState();
2537
+ }
2538
+ isInitialMountRef.current = false;
1827
2539
  }, [accessToken]);
1828
2540
  (0, import_react2.useEffect)(() => {
1829
2541
  if (hasAlreadyConsented) {
@@ -2032,7 +2744,11 @@ var RedactoNoticeConsent = ({
2032
2744
  setInitialDataElementSelections({});
2033
2745
  }
2034
2746
  if (consentContentData.detail.is_minor) {
2035
- setShowAgeVerification(true);
2747
+ setIsMinorFlow(true);
2748
+ const persistedState = getConsentState();
2749
+ if (!(persistedState == null ? void 0 : persistedState.sessionToken) && !(persistedState == null ? void 0 : persistedState.verificationReference)) {
2750
+ setShowAgeVerification(true);
2751
+ }
2036
2752
  }
2037
2753
  setIsLoading(false);
2038
2754
  } catch (err) {
@@ -2131,6 +2847,10 @@ var RedactoNoticeConsent = ({
2131
2847
  const handleAccept = async () => {
2132
2848
  const submitController = new AbortController();
2133
2849
  try {
2850
+ if (isMinorFlow && !verificationReference) {
2851
+ setErrorMessage("Guardian verification is required before consent can be submitted.");
2852
+ return;
2853
+ }
2134
2854
  setIsSubmittingAccept(true);
2135
2855
  setIsSubmitting(true);
2136
2856
  setErrorMessage(null);
@@ -2165,7 +2885,10 @@ var RedactoNoticeConsent = ({
2165
2885
  };
2166
2886
  }),
2167
2887
  declined: false,
2888
+ language: toBcp47Code(selectedLanguage),
2168
2889
  meta_data: applicationId ? { specific_uuid: applicationId } : void 0,
2890
+ guardianVerificationReference: verificationReference || void 0,
2891
+ selfDeclaredAdult: selfDeclaredAdult || void 0,
2169
2892
  signal: submitController.signal
2170
2893
  });
2171
2894
  }
@@ -2230,17 +2953,102 @@ var RedactoNoticeConsent = ({
2230
2953
  try {
2231
2954
  setIsSubmittingGuardian(true);
2232
2955
  setGuardianFormErrors({});
2233
- await submitGuardianInfo({
2956
+ setVerificationError(null);
2957
+ const callbackUrl = digilockerMode === "popup" ? new URL(digilockerCallbackUrl, window.location.origin).href : void 0;
2958
+ const response = await initiateGuardianVerification({
2234
2959
  accessToken,
2235
2960
  baseUrl,
2236
2961
  guardianName: guardianFormData.guardianName,
2237
2962
  guardianContact: guardianFormData.guardianContact,
2238
2963
  guardianRelationship: guardianFormData.guardianRelationship,
2964
+ frontendCallbackUrl: callbackUrl,
2965
+ // Only for popup mode
2239
2966
  signal: guardianController.signal
2240
2967
  });
2968
+ setIsSubmittingGuardian(false);
2969
+ if (response.already_verified && response.verification_reference) {
2970
+ setVerificationReference(response.verification_reference);
2971
+ setShowGuardianForm(false);
2972
+ setShowVerificationScreen(true);
2973
+ setIsInitiatingVerification(false);
2974
+ setIsVerificationComplete(true);
2975
+ setIsAutoTransitioning(true);
2976
+ autoTransitionTimerRef.current = setTimeout(() => {
2977
+ setShowVerificationScreen(false);
2978
+ setIsVerificationComplete(false);
2979
+ setIsAutoTransitioning(false);
2980
+ autoTransitionTimerRef.current = null;
2981
+ }, 1e3);
2982
+ return;
2983
+ }
2984
+ if (!response.session_token || !response.digilocker_redirect_url) {
2985
+ throw new Error("Invalid response from server: missing required fields");
2986
+ }
2241
2987
  setShowGuardianForm(false);
2988
+ setShowVerificationScreen(true);
2989
+ setIsInitiatingVerification(true);
2990
+ if (digilockerMode === "redirect") {
2991
+ saveConsentState({
2992
+ flowState: "digilocker-pending",
2993
+ sessionToken: response.session_token,
2994
+ guardianInfoUuid: response.guardian_info_uuid,
2995
+ selectedPurposes,
2996
+ selectedDataElements,
2997
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2998
+ originalUrl: window.location.href,
2999
+ noticeId,
3000
+ baseUrl
3001
+ });
3002
+ try {
3003
+ const openedWindow = window.open(
3004
+ response.digilocker_redirect_url,
3005
+ "_blank"
3006
+ );
3007
+ if (!openedWindow) {
3008
+ console.warn("Popup blocked \u2014 cannot open DigiLocker in new tab");
3009
+ setIsInitiatingVerification(false);
3010
+ setVerificationError(
3011
+ "Unable to open DigiLocker in a new tab. Please allow popups for this site and try again."
3012
+ );
3013
+ return;
3014
+ }
3015
+ try {
3016
+ openedWindow.opener = null;
3017
+ } catch {
3018
+ }
3019
+ } catch (error) {
3020
+ console.error("Failed to open DigiLocker in new tab:", error);
3021
+ setIsInitiatingVerification(false);
3022
+ setVerificationError(
3023
+ "Unable to open DigiLocker. Please allow popups for this site and try again."
3024
+ );
3025
+ return;
3026
+ }
3027
+ setIsInitiatingVerification(false);
3028
+ setIsPollingStatus(true);
3029
+ isPollingStatusRef.current = true;
3030
+ startStatusPolling(response.session_token);
3031
+ } else {
3032
+ setIsInitiatingVerification(true);
3033
+ const popup = window.open(
3034
+ response.digilocker_redirect_url,
3035
+ "digilocker_verification",
3036
+ "width=600,height=700,scrollbars=yes,resizable=yes"
3037
+ );
3038
+ if (popup) {
3039
+ verificationPopupRef.current = popup;
3040
+ startPopupMonitoring(popup);
3041
+ startStatusPolling(response.session_token);
3042
+ } else {
3043
+ setIsInitiatingVerification(false);
3044
+ setVerificationError(
3045
+ "Popup blocked. Please allow popups for this site or use redirect mode."
3046
+ );
3047
+ }
3048
+ }
2242
3049
  } catch (error) {
2243
3050
  const err = error;
3051
+ setIsInitiatingVerification(false);
2244
3052
  if (err.status === 422) {
2245
3053
  if (err.message.includes("guardian_contact")) {
2246
3054
  setGuardianFormErrors({ guardianContact: err.message });
@@ -2251,23 +3059,265 @@ var RedactoNoticeConsent = ({
2251
3059
  } else {
2252
3060
  setGuardianFormErrors({ general: err.message });
2253
3061
  }
3062
+ setShowVerificationScreen(false);
3063
+ setShowGuardianForm(true);
2254
3064
  } else {
2255
3065
  setGuardianFormErrors({
2256
- general: err.message || "Failed to submit guardian information. Please try again."
3066
+ general: err.message || "Failed to initiate verification. Please try again."
2257
3067
  });
3068
+ setShowVerificationScreen(false);
3069
+ setShowGuardianForm(true);
2258
3070
  onError == null ? void 0 : onError(err);
2259
3071
  }
2260
3072
  } finally {
2261
3073
  setIsSubmittingGuardian(false);
2262
3074
  }
2263
- }, [accessToken, baseUrl, guardianFormData, onError]);
3075
+ }, [accessToken, baseUrl, guardianFormData, digilockerMode, digilockerCallbackUrl, selectedPurposes, selectedDataElements, noticeId, onError]);
2264
3076
  const handleAgeVerificationYes = (0, import_react2.useCallback)(() => {
2265
3077
  setShowAgeVerification(false);
3078
+ setIsMinorFlow(false);
3079
+ setSelfDeclaredAdult(true);
2266
3080
  }, []);
2267
3081
  const handleAgeVerificationNo = (0, import_react2.useCallback)(() => {
2268
3082
  setShowAgeVerification(false);
2269
3083
  setShowGuardianForm(true);
2270
3084
  }, []);
3085
+ const handleVerificationContinue = (0, import_react2.useCallback)(() => {
3086
+ setShowVerificationScreen(false);
3087
+ setIsVerificationComplete(false);
3088
+ }, []);
3089
+ const handlePopupClosed = (0, import_react2.useCallback)(async () => {
3090
+ var _a2, _b2;
3091
+ if (popupCheckIntervalRef.current) {
3092
+ clearInterval(popupCheckIntervalRef.current);
3093
+ popupCheckIntervalRef.current = null;
3094
+ }
3095
+ if (statusPollIntervalRef.current) {
3096
+ clearTimeout(statusPollIntervalRef.current);
3097
+ statusPollIntervalRef.current = null;
3098
+ }
3099
+ (_a2 = pollingCleanupRef.current) == null ? void 0 : _a2.call(pollingCleanupRef);
3100
+ pollingCleanupRef.current = null;
3101
+ verificationPopupRef.current = null;
3102
+ if (!isPollingStatusRef.current) return;
3103
+ const sessionToken = verificationSessionTokenRef.current;
3104
+ if (sessionToken) {
3105
+ try {
3106
+ const response = await verifyGuardianStatus({
3107
+ accessToken,
3108
+ baseUrl,
3109
+ sessionToken
3110
+ });
3111
+ if (response.status === "verified") {
3112
+ const ref = response.verification_reference || ((_b2 = response.guardian_details) == null ? void 0 : _b2.verification_reference);
3113
+ if (ref) {
3114
+ setVerificationReference(ref);
3115
+ setIsVerificationComplete(true);
3116
+ setIsPollingStatus(false);
3117
+ isPollingStatusRef.current = false;
3118
+ setIsInitiatingVerification(false);
3119
+ clearConsentState();
3120
+ return;
3121
+ }
3122
+ }
3123
+ } catch {
3124
+ }
3125
+ }
3126
+ setVerificationError(
3127
+ "Verification window was closed. Please try again."
3128
+ );
3129
+ setIsPollingStatus(false);
3130
+ isPollingStatusRef.current = false;
3131
+ setIsInitiatingVerification(false);
3132
+ }, [accessToken, baseUrl]);
3133
+ const startPopupMonitoring = (0, import_react2.useCallback)((popup) => {
3134
+ popupCheckIntervalRef.current = window.setInterval(() => {
3135
+ if (popup.closed) {
3136
+ handlePopupClosed();
3137
+ }
3138
+ }, 500);
3139
+ }, [handlePopupClosed]);
3140
+ const getVerificationErrorMessage = (0, import_react2.useCallback)(
3141
+ (errorCode, fallbackError) => {
3142
+ switch (errorCode) {
3143
+ case "GUARDIAN_UNDER_18":
3144
+ return "The guardian must be 18 years or older. Please provide details of a different guardian.";
3145
+ case "TOKEN_FAILED":
3146
+ return "DigiLocker verification could not be completed. Please try again.";
3147
+ case "DIGILOCKER_AUTH_FAILED":
3148
+ return "DigiLocker authorization was cancelled or failed. Please try again.";
3149
+ case "NO_AUTH_CODE":
3150
+ return "DigiLocker did not return an authorization code. Please try again.";
3151
+ case "SESSION_EXPIRED":
3152
+ return "Verification session has expired. Please try again.";
3153
+ default:
3154
+ return fallbackError || "Verification failed. Please try again.";
3155
+ }
3156
+ },
3157
+ []
3158
+ );
3159
+ const startStatusPolling = (0, import_react2.useCallback)(
3160
+ (token) => {
3161
+ verificationSessionTokenRef.current = token;
3162
+ setIsPollingStatus(true);
3163
+ isPollingStatusRef.current = true;
3164
+ setVerificationError(null);
3165
+ setVerificationErrorCode(null);
3166
+ setCanRetryVerification(false);
3167
+ let attempts = 0;
3168
+ let consecutiveErrors = 0;
3169
+ let paused = false;
3170
+ const MAX_ATTEMPTS = 40;
3171
+ const MAX_CONSECUTIVE_ERRORS = 3;
3172
+ const POLL_INTERVAL = 3e3;
3173
+ const stopPolling = () => {
3174
+ var _a2;
3175
+ if (statusPollIntervalRef.current) {
3176
+ clearTimeout(statusPollIntervalRef.current);
3177
+ statusPollIntervalRef.current = null;
3178
+ }
3179
+ if (popupCheckIntervalRef.current) {
3180
+ clearInterval(popupCheckIntervalRef.current);
3181
+ popupCheckIntervalRef.current = null;
3182
+ }
3183
+ (_a2 = verificationPopupRef.current) == null ? void 0 : _a2.close();
3184
+ verificationPopupRef.current = null;
3185
+ document.removeEventListener("visibilitychange", onVisibilityChange);
3186
+ pollingCleanupRef.current = null;
3187
+ setIsPollingStatus(false);
3188
+ isPollingStatusRef.current = false;
3189
+ setIsInitiatingVerification(false);
3190
+ };
3191
+ const setError = (message, errorCode, canRetry) => {
3192
+ stopPolling();
3193
+ setVerificationError(message);
3194
+ setVerificationErrorCode(errorCode);
3195
+ setCanRetryVerification(canRetry);
3196
+ };
3197
+ const poll = async () => {
3198
+ var _a2;
3199
+ if (paused) return;
3200
+ attempts++;
3201
+ if (attempts > MAX_ATTEMPTS) {
3202
+ setError(
3203
+ "Verification is taking longer than expected. Please try again.",
3204
+ "SESSION_EXPIRED",
3205
+ true
3206
+ );
3207
+ return;
3208
+ }
3209
+ try {
3210
+ const response = await verifyGuardianStatus({
3211
+ accessToken,
3212
+ baseUrl,
3213
+ sessionToken: token
3214
+ });
3215
+ consecutiveErrors = 0;
3216
+ if (response.status === "verified") {
3217
+ stopPolling();
3218
+ const ref = response.verification_reference || ((_a2 = response.guardian_details) == null ? void 0 : _a2.verification_reference);
3219
+ if (!ref) {
3220
+ setError(
3221
+ "Verification completed but no reference received. Please try again.",
3222
+ null,
3223
+ true
3224
+ );
3225
+ return;
3226
+ }
3227
+ setVerificationReference(ref);
3228
+ setIsVerificationComplete(true);
3229
+ clearConsentState();
3230
+ return;
3231
+ }
3232
+ if (response.status === "failed" || response.status === "expired") {
3233
+ setError(
3234
+ getVerificationErrorMessage(response.error_code, response.error),
3235
+ response.error_code,
3236
+ response.can_retry
3237
+ );
3238
+ return;
3239
+ }
3240
+ if (!paused) {
3241
+ statusPollIntervalRef.current = window.setTimeout(poll, POLL_INTERVAL);
3242
+ }
3243
+ } catch (error) {
3244
+ const err = error;
3245
+ if (err.status === 404 || err.status === 410) {
3246
+ setError(
3247
+ "Verification session not found or expired. Please try again.",
3248
+ "SESSION_EXPIRED",
3249
+ true
3250
+ );
3251
+ return;
3252
+ }
3253
+ consecutiveErrors++;
3254
+ if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
3255
+ setError(
3256
+ err.message || "Unable to check verification status. Please try again.",
3257
+ null,
3258
+ true
3259
+ );
3260
+ return;
3261
+ }
3262
+ statusPollIntervalRef.current = window.setTimeout(poll, POLL_INTERVAL);
3263
+ }
3264
+ };
3265
+ const onVisibilityChange = () => {
3266
+ if (document.hidden) {
3267
+ paused = true;
3268
+ if (statusPollIntervalRef.current) {
3269
+ clearTimeout(statusPollIntervalRef.current);
3270
+ statusPollIntervalRef.current = null;
3271
+ }
3272
+ } else {
3273
+ paused = false;
3274
+ poll();
3275
+ }
3276
+ };
3277
+ document.addEventListener("visibilitychange", onVisibilityChange);
3278
+ pollingCleanupRef.current = () => {
3279
+ document.removeEventListener("visibilitychange", onVisibilityChange);
3280
+ };
3281
+ statusPollIntervalRef.current = window.setTimeout(poll, POLL_INTERVAL);
3282
+ },
3283
+ [accessToken, baseUrl, getVerificationErrorMessage]
3284
+ );
3285
+ const handleBackToGuardianForm = (0, import_react2.useCallback)((clearName = false) => {
3286
+ var _a2, _b2;
3287
+ (_a2 = verificationPopupRef.current) == null ? void 0 : _a2.close();
3288
+ verificationPopupRef.current = null;
3289
+ if (popupCheckIntervalRef.current) {
3290
+ clearInterval(popupCheckIntervalRef.current);
3291
+ popupCheckIntervalRef.current = null;
3292
+ }
3293
+ if (statusPollIntervalRef.current) {
3294
+ clearTimeout(statusPollIntervalRef.current);
3295
+ statusPollIntervalRef.current = null;
3296
+ }
3297
+ if (autoTransitionTimerRef.current) {
3298
+ clearTimeout(autoTransitionTimerRef.current);
3299
+ autoTransitionTimerRef.current = null;
3300
+ }
3301
+ if (digilockerRedirectTimeoutRef.current) {
3302
+ clearTimeout(digilockerRedirectTimeoutRef.current);
3303
+ digilockerRedirectTimeoutRef.current = null;
3304
+ }
3305
+ (_b2 = pollingCleanupRef.current) == null ? void 0 : _b2.call(pollingCleanupRef);
3306
+ pollingCleanupRef.current = null;
3307
+ setShowVerificationScreen(false);
3308
+ setShowGuardianForm(true);
3309
+ setVerificationError(null);
3310
+ setVerificationErrorCode(null);
3311
+ setCanRetryVerification(false);
3312
+ setIsInitiatingVerification(false);
3313
+ setIsPollingStatus(false);
3314
+ isPollingStatusRef.current = false;
3315
+ setIsVerificationComplete(false);
3316
+ if (clearName) {
3317
+ setGuardianFormData((prev) => ({ ...prev, guardianName: "" }));
3318
+ }
3319
+ clearConsentState();
3320
+ }, []);
2271
3321
  const availableLanguages = (0, import_react2.useMemo)(() => {
2272
3322
  if (!content) return [language];
2273
3323
  const languages = ["English"];
@@ -2677,12 +3727,67 @@ var RedactoNoticeConsent = ({
2677
3727
  }, [content, stopAudio]);
2678
3728
  (0, import_react2.useEffect)(() => {
2679
3729
  return () => {
3730
+ var _a2, _b2;
2680
3731
  if (abortControllerRef.current) {
2681
3732
  abortControllerRef.current.abort();
2682
3733
  }
2683
3734
  stopAudio();
3735
+ if (popupCheckIntervalRef.current) {
3736
+ clearInterval(popupCheckIntervalRef.current);
3737
+ }
3738
+ if (statusPollIntervalRef.current) {
3739
+ clearTimeout(statusPollIntervalRef.current);
3740
+ }
3741
+ (_a2 = pollingCleanupRef.current) == null ? void 0 : _a2.call(pollingCleanupRef);
3742
+ pollingCleanupRef.current = null;
3743
+ (_b2 = verificationPopupRef.current) == null ? void 0 : _b2.close();
3744
+ if (autoTransitionTimerRef.current) {
3745
+ clearTimeout(autoTransitionTimerRef.current);
3746
+ autoTransitionTimerRef.current = null;
3747
+ }
3748
+ if (digilockerRedirectTimeoutRef.current) {
3749
+ clearTimeout(digilockerRedirectTimeoutRef.current);
3750
+ digilockerRedirectTimeoutRef.current = null;
3751
+ }
2684
3752
  };
2685
3753
  }, [stopAudio]);
3754
+ (0, import_react2.useEffect)(() => {
3755
+ const persistedState = getConsentState();
3756
+ if (!persistedState) return;
3757
+ if (persistedState.noticeId && persistedState.noticeId !== noticeId) {
3758
+ clearConsentState();
3759
+ return;
3760
+ }
3761
+ const REDIRECT_STATE_MAX_AGE_MS = 15 * 60 * 1e3;
3762
+ if (persistedState.timestamp) {
3763
+ const stateAge = Date.now() - new Date(persistedState.timestamp).getTime();
3764
+ if (stateAge > REDIRECT_STATE_MAX_AGE_MS) {
3765
+ clearConsentState();
3766
+ return;
3767
+ }
3768
+ }
3769
+ if (persistedState.flowState === "digilocker-pending" && persistedState.sessionToken) {
3770
+ setShowVerificationScreen(true);
3771
+ setShowGuardianForm(false);
3772
+ setShowAgeVerification(false);
3773
+ startStatusPolling(persistedState.sessionToken);
3774
+ } else if (persistedState.verificationResult === "verified" && persistedState.verificationReference) {
3775
+ setVerificationReference(persistedState.verificationReference);
3776
+ setShowVerificationScreen(true);
3777
+ setIsVerificationComplete(true);
3778
+ setShowGuardianForm(false);
3779
+ setShowAgeVerification(false);
3780
+ clearConsentState();
3781
+ } else if (persistedState.verificationResult === "failed" || persistedState.verificationResult === "expired") {
3782
+ setShowVerificationScreen(true);
3783
+ setShowGuardianForm(false);
3784
+ setShowAgeVerification(false);
3785
+ setVerificationError(
3786
+ persistedState.verificationResult === "expired" ? "Verification session has expired. Please try again." : "Verification failed. Please try again."
3787
+ );
3788
+ clearConsentState();
3789
+ }
3790
+ }, [startStatusPolling, noticeId]);
2686
3791
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: hasAlreadyConsented ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2687
3792
  blockUI && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2688
3793
  "div",
@@ -2868,6 +3973,27 @@ var RedactoNoticeConsent = ({
2868
3973
  declineButtonStyle,
2869
3974
  logoUrl: (content == null ? void 0 : content.logo_url) || redacto_logo_default
2870
3975
  }
3976
+ ) }) : showVerificationScreen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...styles.content, ...responsiveStyles.content }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3977
+ VerificationScreen,
3978
+ {
3979
+ onBack: handleBackToGuardianForm,
3980
+ onContinue: handleVerificationContinue,
3981
+ isInitiating: isInitiatingVerification,
3982
+ isPolling: isPollingStatus,
3983
+ isVerificationComplete,
3984
+ isAutoTransitioning,
3985
+ error: verificationError,
3986
+ errorCode: verificationErrorCode,
3987
+ canRetry: canRetryVerification,
3988
+ styles,
3989
+ responsiveStyles,
3990
+ settings,
3991
+ componentHeadingStyle,
3992
+ componentTextStyle,
3993
+ acceptButtonStyle,
3994
+ declineButtonStyle,
3995
+ logoUrl: (content == null ? void 0 : content.logo_url) || redacto_logo_default
3996
+ }
2871
3997
  ) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { ...styles.content, ...responsiveStyles.content }, children: [
2872
3998
  errorMessage && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2873
3999
  "div",
@@ -3501,60 +4627,27 @@ var submitConsentEvent2 = async ({
3501
4627
  var _a, _b;
3502
4628
  try {
3503
4629
  const apiBaseUrl = baseUrl || BASE_URL2;
3504
- let payload;
3505
- if (isLegacyApi(apiBaseUrl)) {
3506
- const primaryEmail = typeof window !== "undefined" ? localStorage.getItem("userEmail") : null;
3507
- payload = {
3508
- primary_email: primaryEmail || void 0,
3509
- notice_uuid: noticeUuid,
3510
- source: "WEB",
3511
- declined: Boolean(declined),
3512
- consents: {
3513
- purposes: purposes.map((purpose) => {
3514
- if (!purpose.uuid) {
3515
- throw new Error(`Invalid purpose data: missing uuid`);
3516
- }
3517
- return {
3518
- uuid: purpose.uuid,
3519
- name: purpose.name,
3520
- description: purpose.description || "",
3521
- industries: purpose.industries || "",
3522
- selected: Boolean(purpose.selected),
3523
- data_elements: (purpose.data_elements || []).map((element) => ({
3524
- uuid: element.uuid,
3525
- name: element.name,
3526
- enabled: Boolean(element.enabled),
3527
- required: Boolean(element.required),
3528
- selected: element.required ? true : Boolean(element.selected)
3529
- }))
3530
- };
3531
- })
3532
- },
3533
- meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid } : void 0
3534
- };
3535
- } else {
3536
- payload = {
3537
- notice_uuid: noticeUuid,
3538
- purposes: purposes.map((purpose) => {
3539
- if (!purpose.uuid) {
3540
- throw new Error(`Invalid purpose data: missing uuid`);
3541
- }
3542
- const dataElements = purpose.data_elements.filter((element) => element.enabled).map((element) => ({
3543
- uuid: element.uuid,
3544
- selected: element.required ? true : Boolean(element.selected)
3545
- }));
3546
- return {
3547
- purpose_uuid: purpose.uuid,
3548
- selected: Boolean(purpose.selected),
3549
- data_elements: dataElements.length > 0 ? dataElements : void 0
3550
- };
3551
- }),
3552
- select_all_mandatory: false,
3553
- source: "WEB",
3554
- declined,
3555
- meta_data
3556
- };
3557
- }
4630
+ const payload = {
4631
+ notice_uuid: noticeUuid,
4632
+ purposes: purposes.map((purpose) => {
4633
+ if (!purpose.uuid) {
4634
+ throw new Error(`Invalid purpose data: missing uuid`);
4635
+ }
4636
+ const dataElements = purpose.data_elements.filter((element) => element.enabled).map((element) => ({
4637
+ uuid: element.uuid,
4638
+ selected: element.required ? true : Boolean(element.selected)
4639
+ }));
4640
+ return {
4641
+ purpose_uuid: purpose.uuid,
4642
+ selected: Boolean(purpose.selected),
4643
+ data_elements: dataElements.length > 0 ? dataElements : void 0
4644
+ };
4645
+ }),
4646
+ select_all_mandatory: false,
4647
+ source: "WEB",
4648
+ declined,
4649
+ meta_data
4650
+ };
3558
4651
  const response = await fetch(
3559
4652
  `${apiBaseUrl}/public/organisations/${org_uuid}/workspaces/${workspace_uuid}/submit-consent`,
3560
4653
  {
@@ -4504,8 +5597,482 @@ var RedactoConsentInlineComponent = ({
4504
5597
  };
4505
5598
  RedactoConsentInlineComponent.displayName = "RedactoConsentInline";
4506
5599
  var RedactoConsentInline = (0, import_react4.memo)(RedactoConsentInlineComponent);
5600
+
5601
+ // src/RedactoNoticeConsent/components/DigiLockerCallback.tsx
5602
+ var import_react5 = require("react");
5603
+ var import_jsx_runtime3 = require("react/jsx-runtime");
5604
+ var DigiLockerCallback = ({
5605
+ accessToken: propAccessToken,
5606
+ baseUrl,
5607
+ onSuccess,
5608
+ onError
5609
+ }) => {
5610
+ const [status, setStatus] = (0, import_react5.useState)("processing");
5611
+ const [message, setMessage] = (0, import_react5.useState)("Verifying your guardian...");
5612
+ const [errorDetail, setErrorDetail] = (0, import_react5.useState)(null);
5613
+ const retryTimeoutRef = (0, import_react5.useRef)(null);
5614
+ const redirectTimeoutRef = (0, import_react5.useRef)(null);
5615
+ const isMountedRef = (0, import_react5.useRef)(true);
5616
+ const getErrorMessage = (errorCode) => {
5617
+ switch (errorCode) {
5618
+ case "GUARDIAN_UNDER_18":
5619
+ return "Guardian verification failed: The guardian must be 18 years or older.";
5620
+ case "SESSION_EXPIRED":
5621
+ return "Verification session has expired. Please restart the verification process.";
5622
+ case "TOKEN_FAILED":
5623
+ return "DigiLocker authentication failed. Please try again.";
5624
+ case "DIGILOCKER_AUTH_FAILED":
5625
+ return "DigiLocker authorization was cancelled or failed. Please try again.";
5626
+ case "NO_AUTH_CODE":
5627
+ return "Missing authorization code from DigiLocker. Please try again.";
5628
+ default:
5629
+ return "Verification failed. Please try again.";
5630
+ }
5631
+ };
5632
+ const redirectToOriginalPage = (0, import_react5.useCallback)(
5633
+ (originalUrl, verificationResult, verificationReference) => {
5634
+ updateConsentState({
5635
+ verificationResult,
5636
+ verificationReference,
5637
+ flowState: verificationResult === "verified" ? "consent-form" : "error"
5638
+ });
5639
+ redirectTimeoutRef.current = setTimeout(() => {
5640
+ window.location.href = originalUrl;
5641
+ }, 1500);
5642
+ },
5643
+ []
5644
+ );
5645
+ const handleCallback = (0, import_react5.useCallback)(async () => {
5646
+ var _a, _b;
5647
+ try {
5648
+ const params = new URLSearchParams(window.location.search);
5649
+ const sessionToken = params.get("session_token");
5650
+ const urlStatus = params.get("status");
5651
+ const verificationReference = params.get("verification_reference");
5652
+ const errorCode = params.get("error_code");
5653
+ const persistedState = getConsentState();
5654
+ const originalUrl = (persistedState == null ? void 0 : persistedState.originalUrl) || "/";
5655
+ const storedBaseUrl = persistedState == null ? void 0 : persistedState.baseUrl;
5656
+ const storedSessionToken = persistedState == null ? void 0 : persistedState.sessionToken;
5657
+ const accessToken = propAccessToken;
5658
+ if (!accessToken) {
5659
+ throw new Error("Access token not provided. Please pass accessToken as a prop.");
5660
+ }
5661
+ const activeSessionToken = sessionToken || storedSessionToken;
5662
+ if (!activeSessionToken) {
5663
+ throw new Error("Session token not found. Please restart the verification process.");
5664
+ }
5665
+ let isInIframe = false;
5666
+ try {
5667
+ isInIframe = window.self !== window.top;
5668
+ } catch {
5669
+ isInIframe = true;
5670
+ }
5671
+ if (urlStatus === "verified" && verificationReference) {
5672
+ setStatus("success");
5673
+ setMessage("Guardian verified successfully! Redirecting...");
5674
+ onSuccess == null ? void 0 : onSuccess(verificationReference);
5675
+ if (isInIframe && window.parent) {
5676
+ window.parent.postMessage(
5677
+ {
5678
+ type: "DIGILOCKER_VERIFICATION_STATUS",
5679
+ status: "verified",
5680
+ verificationReference
5681
+ },
5682
+ window.location.origin
5683
+ );
5684
+ return;
5685
+ }
5686
+ redirectToOriginalPage(originalUrl, "verified", verificationReference);
5687
+ return;
5688
+ }
5689
+ if (urlStatus === "failed" || urlStatus === "expired") {
5690
+ const urlErrorCode = errorCode != null ? errorCode : void 0;
5691
+ const errorMessage = getErrorMessage(urlErrorCode);
5692
+ setStatus("error");
5693
+ setMessage("Verification failed");
5694
+ setErrorDetail(errorMessage);
5695
+ onError == null ? void 0 : onError(new Error(errorMessage), urlErrorCode);
5696
+ if (isInIframe && window.parent) {
5697
+ window.parent.postMessage(
5698
+ {
5699
+ type: "DIGILOCKER_VERIFICATION_STATUS",
5700
+ status: urlStatus,
5701
+ errorCode: urlErrorCode,
5702
+ error: errorMessage
5703
+ },
5704
+ window.location.origin
5705
+ );
5706
+ return;
5707
+ }
5708
+ redirectToOriginalPage(originalUrl, urlStatus);
5709
+ return;
5710
+ }
5711
+ setMessage("Processing verification...");
5712
+ const apiBaseUrl = baseUrl || storedBaseUrl;
5713
+ const response = await verifyGuardianStatus({
5714
+ accessToken,
5715
+ baseUrl: apiBaseUrl,
5716
+ sessionToken: activeSessionToken
5717
+ });
5718
+ if (!isMountedRef.current) return;
5719
+ if (response.status === "verified") {
5720
+ const verificationRef = response.verification_reference || ((_a = response.guardian_details) == null ? void 0 : _a.verification_reference) || void 0;
5721
+ if (!verificationRef) {
5722
+ setStatus("error");
5723
+ setMessage("Verification failed");
5724
+ setErrorDetail("Verification completed but no reference received. Please try again.");
5725
+ onError == null ? void 0 : onError(new Error("Verification completed but no reference received."));
5726
+ redirectToOriginalPage(originalUrl, "failed");
5727
+ return;
5728
+ }
5729
+ setStatus("success");
5730
+ setMessage("Guardian verified successfully! Redirecting...");
5731
+ onSuccess == null ? void 0 : onSuccess(verificationRef);
5732
+ if (isInIframe && window.parent) {
5733
+ window.parent.postMessage(
5734
+ {
5735
+ type: "DIGILOCKER_VERIFICATION_STATUS",
5736
+ status: "verified",
5737
+ verificationReference: verificationRef
5738
+ },
5739
+ window.location.origin
5740
+ );
5741
+ return;
5742
+ }
5743
+ redirectToOriginalPage(originalUrl, "verified", verificationRef);
5744
+ } else if (response.status === "failed" || response.status === "expired") {
5745
+ const responseErrorCode = (_b = response.error_code) != null ? _b : void 0;
5746
+ const errorMessage = response.error || getErrorMessage(responseErrorCode);
5747
+ setStatus("error");
5748
+ setMessage("Verification failed");
5749
+ setErrorDetail(errorMessage);
5750
+ onError == null ? void 0 : onError(new Error(errorMessage), responseErrorCode);
5751
+ if (isInIframe && window.parent) {
5752
+ window.parent.postMessage(
5753
+ {
5754
+ type: "DIGILOCKER_VERIFICATION_STATUS",
5755
+ status: response.status,
5756
+ errorCode: responseErrorCode,
5757
+ error: errorMessage
5758
+ },
5759
+ window.location.origin
5760
+ );
5761
+ return;
5762
+ }
5763
+ redirectToOriginalPage(originalUrl, response.status);
5764
+ } else {
5765
+ setMessage("Verification is still in progress. Please wait...");
5766
+ const MAX_RETRIES = 3;
5767
+ let retryCount = 0;
5768
+ const attemptRetry = () => {
5769
+ retryCount++;
5770
+ const delay = 3e3 * Math.pow(2, retryCount - 1);
5771
+ retryTimeoutRef.current = setTimeout(async () => {
5772
+ var _a2, _b2;
5773
+ if (!isMountedRef.current) return;
5774
+ try {
5775
+ const retryResponse = await verifyGuardianStatus({
5776
+ accessToken,
5777
+ baseUrl: apiBaseUrl,
5778
+ sessionToken: activeSessionToken
5779
+ });
5780
+ if (!isMountedRef.current) return;
5781
+ const retryRef = retryResponse.verification_reference || ((_a2 = retryResponse.guardian_details) == null ? void 0 : _a2.verification_reference) || void 0;
5782
+ if (retryResponse.status === "verified" && retryRef) {
5783
+ setStatus("success");
5784
+ setMessage("Guardian verified successfully! Redirecting...");
5785
+ onSuccess == null ? void 0 : onSuccess(retryRef);
5786
+ if (isInIframe && window.parent) {
5787
+ window.parent.postMessage(
5788
+ {
5789
+ type: "DIGILOCKER_VERIFICATION_STATUS",
5790
+ status: "verified",
5791
+ verificationReference: retryRef
5792
+ },
5793
+ window.location.origin
5794
+ );
5795
+ return;
5796
+ }
5797
+ redirectToOriginalPage(originalUrl, "verified", retryRef);
5798
+ } else if (retryResponse.status === "failed" || retryResponse.status === "expired") {
5799
+ const retryErrorCode = (_b2 = retryResponse.error_code) != null ? _b2 : void 0;
5800
+ const retryErrorMessage = retryResponse.error || getErrorMessage(retryErrorCode) || "Verification could not be completed. Please try again.";
5801
+ setStatus("error");
5802
+ setMessage("Verification failed");
5803
+ setErrorDetail(retryErrorMessage);
5804
+ onError == null ? void 0 : onError(new Error(retryErrorMessage), retryErrorCode);
5805
+ if (isInIframe && window.parent) {
5806
+ window.parent.postMessage(
5807
+ {
5808
+ type: "DIGILOCKER_VERIFICATION_STATUS",
5809
+ status: retryResponse.status,
5810
+ errorCode: retryErrorCode,
5811
+ error: retryErrorMessage
5812
+ },
5813
+ window.location.origin
5814
+ );
5815
+ return;
5816
+ }
5817
+ redirectToOriginalPage(originalUrl, retryResponse.status);
5818
+ } else if (retryCount < MAX_RETRIES) {
5819
+ setMessage("Verification taking longer than expected. Please wait...");
5820
+ attemptRetry();
5821
+ } else {
5822
+ const timeoutMessage = "Verification could not be completed in time. Please try again.";
5823
+ setStatus("error");
5824
+ setMessage("Verification failed");
5825
+ setErrorDetail(timeoutMessage);
5826
+ onError == null ? void 0 : onError(new Error(timeoutMessage));
5827
+ redirectToOriginalPage(originalUrl, "failed");
5828
+ }
5829
+ } catch (retryErr) {
5830
+ if (!isMountedRef.current) return;
5831
+ if (retryCount < MAX_RETRIES) {
5832
+ attemptRetry();
5833
+ } else {
5834
+ setStatus("error");
5835
+ setMessage("Verification failed");
5836
+ setErrorDetail(retryErr.message);
5837
+ onError == null ? void 0 : onError(retryErr);
5838
+ redirectToOriginalPage(originalUrl, "failed");
5839
+ }
5840
+ }
5841
+ }, delay);
5842
+ };
5843
+ attemptRetry();
5844
+ }
5845
+ } catch (error) {
5846
+ console.error("DigiLocker callback error:", error);
5847
+ setStatus("error");
5848
+ const err = error;
5849
+ setMessage("Verification failed");
5850
+ setErrorDetail(err.message || "An error occurred during verification");
5851
+ onError == null ? void 0 : onError(err);
5852
+ const persistedState = getConsentState();
5853
+ if (persistedState == null ? void 0 : persistedState.originalUrl) {
5854
+ redirectTimeoutRef.current = setTimeout(() => {
5855
+ redirectToOriginalPage(persistedState.originalUrl, "failed");
5856
+ }, 3e3);
5857
+ }
5858
+ }
5859
+ }, [propAccessToken, baseUrl, onSuccess, onError, redirectToOriginalPage]);
5860
+ (0, import_react5.useEffect)(() => {
5861
+ injectCheckboxStyles();
5862
+ handleCallback();
5863
+ return () => {
5864
+ isMountedRef.current = false;
5865
+ if (retryTimeoutRef.current) clearTimeout(retryTimeoutRef.current);
5866
+ if (redirectTimeoutRef.current) clearTimeout(redirectTimeoutRef.current);
5867
+ };
5868
+ }, [handleCallback]);
5869
+ const handleRetry = (0, import_react5.useCallback)(() => {
5870
+ const persistedState = getConsentState();
5871
+ if (persistedState == null ? void 0 : persistedState.originalUrl) {
5872
+ clearConsentState();
5873
+ window.location.href = persistedState.originalUrl;
5874
+ } else {
5875
+ window.location.href = "/";
5876
+ }
5877
+ }, []);
5878
+ const containerStyle = {
5879
+ display: "flex",
5880
+ alignItems: "center",
5881
+ justifyContent: "center",
5882
+ minHeight: "100vh",
5883
+ backgroundColor: "#f8fafc",
5884
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
5885
+ padding: "24px",
5886
+ boxSizing: "border-box"
5887
+ };
5888
+ const cardStyle = {
5889
+ backgroundColor: "#ffffff",
5890
+ borderRadius: "16px",
5891
+ boxShadow: "0 1px 3px rgba(0, 0, 0, 0.08), 0 8px 30px rgba(0, 0, 0, 0.06)",
5892
+ padding: "48px 40px 40px",
5893
+ textAlign: "center",
5894
+ maxWidth: "420px",
5895
+ width: "100%"
5896
+ };
5897
+ const spinnerContainerStyle = {
5898
+ width: "72px",
5899
+ height: "72px",
5900
+ margin: "0 auto 28px",
5901
+ position: "relative"
5902
+ };
5903
+ const spinnerStyle = {
5904
+ width: "72px",
5905
+ height: "72px",
5906
+ border: "3px solid #e5e7eb",
5907
+ borderTopColor: "#4f87ff",
5908
+ borderRadius: "50%",
5909
+ animation: "redacto-dl-spin 0.8s linear infinite",
5910
+ boxSizing: "border-box"
5911
+ };
5912
+ const iconContainerStyle = {
5913
+ width: "72px",
5914
+ height: "72px",
5915
+ borderRadius: "50%",
5916
+ display: "flex",
5917
+ alignItems: "center",
5918
+ justifyContent: "center",
5919
+ margin: "0 auto 28px",
5920
+ animation: "redacto-dl-pop 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards",
5921
+ position: "relative"
5922
+ };
5923
+ const successIconStyle = {
5924
+ ...iconContainerStyle,
5925
+ backgroundColor: "#d1fae5",
5926
+ color: "#059669"
5927
+ };
5928
+ const errorIconStyle = {
5929
+ ...iconContainerStyle,
5930
+ backgroundColor: "#fee2e2",
5931
+ color: "#dc2626"
5932
+ };
5933
+ const titleStyle = {
5934
+ fontSize: "22px",
5935
+ fontWeight: 700,
5936
+ color: "#111827",
5937
+ margin: "0 0 8px 0",
5938
+ letterSpacing: "-0.01em"
5939
+ };
5940
+ const messageStyle = {
5941
+ fontSize: "14px",
5942
+ color: "#6b7280",
5943
+ marginBottom: "0",
5944
+ lineHeight: 1.6
5945
+ };
5946
+ const subtextStyle = {
5947
+ fontSize: "13px",
5948
+ color: "#9ca3af",
5949
+ marginTop: "16px",
5950
+ lineHeight: 1.5
5951
+ };
5952
+ const errorDetailStyle = {
5953
+ fontSize: "13px",
5954
+ color: "#dc2626",
5955
+ marginTop: "16px",
5956
+ padding: "12px 16px",
5957
+ backgroundColor: "#fef2f2",
5958
+ borderRadius: "10px",
5959
+ border: "1px solid #fecaca",
5960
+ lineHeight: 1.5,
5961
+ textAlign: "left"
5962
+ };
5963
+ const buttonStyle = {
5964
+ marginTop: "24px",
5965
+ padding: "12px 28px",
5966
+ backgroundColor: "#4f87ff",
5967
+ color: "#ffffff",
5968
+ border: "none",
5969
+ borderRadius: "10px",
5970
+ fontSize: "15px",
5971
+ fontWeight: 600,
5972
+ cursor: "pointer",
5973
+ transition: "background-color 0.2s, transform 0.15s",
5974
+ width: "100%"
5975
+ };
5976
+ const keyframesStyle = `
5977
+ @keyframes redacto-dl-spin {
5978
+ to { transform: rotate(360deg); }
5979
+ }
5980
+ @keyframes redacto-dl-pop {
5981
+ 0% { transform: scale(0.3); opacity: 0; }
5982
+ 60% { transform: scale(1.08); opacity: 1; }
5983
+ 100% { transform: scale(1); opacity: 1; }
5984
+ }
5985
+ @keyframes redacto-dl-check {
5986
+ 0% { stroke-dashoffset: 24; }
5987
+ 100% { stroke-dashoffset: 0; }
5988
+ }
5989
+ @keyframes redacto-dl-pulse {
5990
+ 0%, 100% { transform: scale(1); opacity: 0.6; }
5991
+ 50% { transform: scale(1.15); opacity: 0; }
5992
+ }
5993
+ .redacto-dl-retry-btn:hover {
5994
+ background-color: #3d6fdb !important;
5995
+ transform: translateY(-1px);
5996
+ }
5997
+ .redacto-dl-retry-btn:active {
5998
+ transform: translateY(0);
5999
+ }
6000
+ `;
6001
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
6002
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { dangerouslySetInnerHTML: { __html: keyframesStyle } }),
6003
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: containerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: cardStyle, children: [
6004
+ status === "processing" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
6005
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: spinnerContainerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: spinnerStyle }) }),
6006
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { style: titleStyle, children: "Processing Verification" }),
6007
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: messageStyle, children: message }),
6008
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: subtextStyle, children: "Please wait while we verify your guardian's identity..." })
6009
+ ] }),
6010
+ status === "success" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
6011
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { position: "relative", width: "72px", height: "72px", margin: "0 auto 28px" }, children: [
6012
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
6013
+ position: "absolute",
6014
+ inset: "-8px",
6015
+ borderRadius: "50%",
6016
+ border: "2px solid #d1fae5",
6017
+ animation: "redacto-dl-pulse 1.5s ease-out infinite"
6018
+ } }),
6019
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: successIconStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6020
+ "svg",
6021
+ {
6022
+ width: "30",
6023
+ height: "30",
6024
+ viewBox: "0 0 24 24",
6025
+ fill: "none",
6026
+ stroke: "currentColor",
6027
+ strokeWidth: "2.5",
6028
+ strokeLinecap: "round",
6029
+ strokeLinejoin: "round",
6030
+ style: { strokeDasharray: 24, animation: "redacto-dl-check 0.4s ease-out 0.3s forwards", strokeDashoffset: 24 },
6031
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "20 6 9 17 4 12" })
6032
+ }
6033
+ ) })
6034
+ ] }),
6035
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { style: titleStyle, children: "Verification Successful" }),
6036
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: messageStyle, children: message }),
6037
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: subtextStyle, children: "You will be redirected automatically." })
6038
+ ] }),
6039
+ status === "error" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
6040
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: errorIconStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
6041
+ "svg",
6042
+ {
6043
+ width: "30",
6044
+ height: "30",
6045
+ viewBox: "0 0 24 24",
6046
+ fill: "none",
6047
+ stroke: "currentColor",
6048
+ strokeWidth: "2.5",
6049
+ strokeLinecap: "round",
6050
+ strokeLinejoin: "round",
6051
+ children: [
6052
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
6053
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
6054
+ ]
6055
+ }
6056
+ ) }),
6057
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { style: titleStyle, children: "Verification Failed" }),
6058
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: messageStyle, children: message }),
6059
+ errorDetail && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: errorDetailStyle, children: errorDetail }),
6060
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6061
+ "button",
6062
+ {
6063
+ className: "redacto-dl-retry-btn",
6064
+ style: buttonStyle,
6065
+ onClick: handleRetry,
6066
+ children: "Try Again"
6067
+ }
6068
+ )
6069
+ ] })
6070
+ ] }) })
6071
+ ] });
6072
+ };
4507
6073
  // Annotate the CommonJS export names for ESM import in node:
4508
6074
  0 && (module.exports = {
6075
+ DigiLockerCallback,
4509
6076
  RedactoNoticeConsent,
4510
6077
  RedactoNoticeConsentInline
4511
6078
  });