@redacto.io/consent-sdk-react 3.5.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  });
@@ -66,73 +67,66 @@ var fetchConsentContent = async ({
66
67
  validate_against = "all",
67
68
  include_fully_consented_data = false,
68
69
  signal
69
- // AbortSignal for request cancellation
70
70
  }) => {
71
- try {
72
- if (!noticeId || !accessToken || !validate_against) {
73
- throw new Error("noticeId and accessToken are required");
74
- }
75
- const decodedToken = decodeTokenSafely(accessToken);
76
- if (!decodedToken) {
77
- throw new Error("Invalid access token");
78
- }
79
- const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
80
- if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
81
- throw new Error("Invalid token: missing organization or workspace UUID");
82
- }
83
- const apiBaseUrl = baseUrl || BASE_URL;
84
- const cacheKey = `${accessToken}-${noticeId}-${validate_against}-${language}-${specific_uuid || ""}-${include_fully_consented_data}`;
85
- if (validate_against === "all" && !specific_uuid) {
86
- const cachedData = getCachedData(cacheKey);
87
- if (cachedData) {
88
- return cachedData;
89
- }
90
- }
91
- const url = new URL(
92
- `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/notices/${noticeId}`
93
- );
94
- if (specific_uuid) {
95
- url.searchParams.append("specific_uuid", specific_uuid);
96
- }
97
- url.searchParams.append("validate_against", validate_against);
98
- if (include_fully_consented_data) {
99
- url.searchParams.append("include_fully_consented_data", "true");
100
- }
101
- const response = await fetch(url.toString(), {
102
- method: "GET",
103
- headers: {
104
- Authorization: `Bearer ${accessToken}`,
105
- "Accept-Language": language,
106
- "Content-Type": "application/json"
107
- },
108
- signal
109
- // Add abort signal for request cancellation
110
- });
111
- if (!response.ok) {
112
- let errorMessage = `Failed to fetch consent content: ${response.status} ${response.statusText}`;
113
- if (response.status === 401) {
114
- errorMessage = "Unauthorized: Invalid or expired token";
115
- } else if (response.status === 403) {
116
- errorMessage = "Forbidden: Access denied";
117
- } else if (response.status === 404) {
118
- errorMessage = "Notice not found";
119
- } else if (response.status === 409) {
120
- errorMessage = "User has already provided consent";
121
- } else if (response.status >= 500) {
122
- errorMessage = "Server error: Please try again later";
123
- }
124
- const error = new Error(errorMessage);
125
- error.status = response.status;
126
- 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;
127
88
  }
128
- const data = await response.json();
129
- if (validate_against === "all" && !specific_uuid) {
130
- 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";
131
120
  }
132
- return data;
133
- } catch (error) {
121
+ const error = new Error(errorMessage);
122
+ error.status = response.status;
134
123
  throw error;
135
124
  }
125
+ const data = await response.json();
126
+ if (validate_against === "all" && !specific_uuid) {
127
+ setCachedData(cacheKey, data);
128
+ }
129
+ return data;
136
130
  };
137
131
  var submitConsentEvent = async ({
138
132
  accessToken,
@@ -141,100 +135,151 @@ var submitConsentEvent = async ({
141
135
  purposes,
142
136
  declined,
143
137
  meta_data,
138
+ guardianVerificationReference,
139
+ selfDeclaredAdult,
144
140
  signal
145
- // AbortSignal for request cancellation
146
141
  }) => {
147
- try {
148
- if (!noticeUuid || !accessToken || !Array.isArray(purposes)) {
149
- throw new Error("noticeUuid, accessToken, and purposes array are required");
150
- }
151
- const decodedToken = decodeTokenSafely(accessToken);
152
- if (!decodedToken) {
153
- throw new Error("Invalid access token");
154
- }
155
- const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
156
- if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
157
- throw new Error("Invalid token: missing organization or workspace UUID");
142
+ if (!noticeUuid || !accessToken || !Array.isArray(purposes)) {
143
+ throw new Error("noticeUuid, accessToken, and purposes array are required");
144
+ }
145
+ const decodedToken = decodeTokenSafely(accessToken);
146
+ if (!decodedToken) {
147
+ throw new Error("Invalid access token");
148
+ }
149
+ const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
150
+ if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
151
+ throw new Error("Invalid token: missing organization or workspace UUID");
152
+ }
153
+ const apiBaseUrl = baseUrl || BASE_URL;
154
+ const validatedPurposes = purposes.map((purpose) => {
155
+ if (!purpose.uuid) {
156
+ throw new Error(`Invalid purpose data: missing uuid`);
158
157
  }
159
- const apiBaseUrl = baseUrl || BASE_URL;
160
- const validatedPurposes = purposes.map((purpose) => {
161
- if (!purpose.uuid) {
162
- throw new Error(`Invalid purpose data: missing uuid`);
163
- }
164
- const dataElements = (purpose.data_elements || []).filter((element) => element.enabled).map((element) => ({
165
- uuid: element.uuid,
166
- selected: element.required ? true : Boolean(element.selected)
167
- }));
168
- return {
169
- purpose_uuid: purpose.uuid,
170
- selected: Boolean(purpose.selected),
171
- data_elements: dataElements.length > 0 ? dataElements : void 0
172
- };
173
- });
174
- const payload = {
175
- notice_uuid: noticeUuid,
176
- purposes: validatedPurposes,
177
- select_all_mandatory: false,
178
- source: "WEB",
179
- declined: Boolean(declined),
180
- meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid } : void 0
158
+ const dataElements = (purpose.data_elements || []).filter((element) => element.enabled).map((element) => ({
159
+ uuid: element.uuid,
160
+ selected: element.required ? true : Boolean(element.selected)
161
+ }));
162
+ return {
163
+ purpose_uuid: purpose.uuid,
164
+ selected: Boolean(purpose.selected),
165
+ data_elements: dataElements.length > 0 ? dataElements : void 0
181
166
  };
182
- const response = await fetch(
183
- `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/submit-consent`,
184
- {
185
- method: "POST",
186
- headers: {
187
- Authorization: `Bearer ${accessToken}`,
188
- "Content-Type": "application/json"
189
- },
190
- body: JSON.stringify(payload),
191
- signal
192
- // Add abort signal for request cancellation
193
- }
194
- );
195
- if (!response.ok) {
196
- let errorMessage = `Failed to submit consent: ${response.status} ${response.statusText}`;
197
- if (response.status === 400) {
167
+ });
168
+ const payload = {
169
+ notice_uuid: noticeUuid,
170
+ purposes: validatedPurposes,
171
+ select_all_mandatory: false,
172
+ source: "WEB",
173
+ declined: Boolean(declined),
174
+ meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid, minor_age: meta_data.minor_age } : void 0,
175
+ guardian_verification_reference: guardianVerificationReference,
176
+ self_declared_adult: selfDeclaredAdult
177
+ };
178
+ const response = await fetch(
179
+ `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/submit-consent`,
180
+ {
181
+ method: "POST",
182
+ headers: {
183
+ Authorization: `Bearer ${accessToken}`,
184
+ "Content-Type": "application/json"
185
+ },
186
+ body: JSON.stringify(payload),
187
+ signal
188
+ }
189
+ );
190
+ if (!response.ok) {
191
+ let errorMessage = `Failed to submit consent: ${response.status} ${response.statusText}`;
192
+ if (response.status === 400) {
193
+ try {
194
+ const errorData = await response.json();
195
+ errorMessage = errorData.message || "Invalid request data";
196
+ } catch {
198
197
  errorMessage = "Invalid request data";
199
- } else if (response.status === 401) {
200
- errorMessage = "Unauthorized: Invalid or expired token";
201
- } else if (response.status === 403) {
202
- errorMessage = "Forbidden: Access denied";
203
- } else if (response.status === 404) {
204
- errorMessage = "Notice not found";
205
- } else if (response.status >= 500) {
206
- errorMessage = "Server error: Please try again later";
207
198
  }
208
- const error = new Error(errorMessage);
209
- error.status = response.status;
210
- throw error;
199
+ } else if (response.status === 401) {
200
+ errorMessage = "Unauthorized: Invalid or expired token";
201
+ } else if (response.status === 403) {
202
+ errorMessage = "Forbidden: Access denied";
203
+ } else if (response.status === 404) {
204
+ errorMessage = "Notice not found";
205
+ } else if (response.status >= 500) {
206
+ errorMessage = "Server error: Please try again later";
211
207
  }
212
- apiCache.clear();
213
- } catch (error) {
208
+ const error = new Error(errorMessage);
209
+ error.status = response.status;
210
+ throw error;
211
+ }
212
+ apiCache.clear();
213
+ };
214
+ var fetchTTSAudioUrls = async ({
215
+ accessToken,
216
+ baseUrl,
217
+ noticeUuid,
218
+ language,
219
+ signal
220
+ }) => {
221
+ if (!accessToken || !noticeUuid || !language) {
222
+ throw new Error("accessToken, noticeUuid, and language are required");
223
+ }
224
+ const decodedToken = decodeTokenSafely(accessToken);
225
+ if (!decodedToken) {
226
+ throw new Error("Invalid access token");
227
+ }
228
+ const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
229
+ if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
230
+ throw new Error("Invalid token: missing organization or workspace UUID");
231
+ }
232
+ const apiBaseUrl = baseUrl || BASE_URL;
233
+ const url = `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/notices/${noticeUuid}/audio/${language}`;
234
+ const response = await fetch(url, {
235
+ method: "GET",
236
+ headers: {
237
+ Authorization: `Bearer ${accessToken}`,
238
+ Accept: "application/json"
239
+ },
240
+ signal
241
+ });
242
+ if (!response.ok) {
243
+ let errorMessage = `Failed to fetch TTS audio URLs: ${response.status} ${response.statusText}`;
244
+ if (response.status === 401) {
245
+ errorMessage = "Unauthorized: Invalid or expired token";
246
+ } else if (response.status === 403) {
247
+ errorMessage = "Forbidden: Access denied";
248
+ } else if (response.status === 404) {
249
+ errorMessage = "Notice or audio not found";
250
+ } else if (response.status >= 500) {
251
+ errorMessage = "Server error: Please try again later";
252
+ }
253
+ const error = new Error(errorMessage);
254
+ error.status = response.status;
214
255
  throw error;
215
256
  }
257
+ const data = await response.json();
258
+ return data;
216
259
  };
217
- var submitGuardianInfo = async ({
260
+ var initiateGuardianVerification = async ({
218
261
  accessToken,
219
262
  baseUrl,
220
263
  guardianName,
221
264
  guardianContact,
222
265
  guardianRelationship,
266
+ frontendCallbackUrl,
267
+ metadata,
223
268
  signal
224
- // AbortSignal for request cancellation
225
269
  }) => {
270
+ var _a;
226
271
  try {
227
272
  if (!accessToken) {
228
273
  throw new Error("accessToken is required");
229
274
  }
230
275
  if (!(guardianName == null ? void 0 : guardianName.trim())) {
231
- throw new Error("guardianName is required and cannot be empty");
276
+ throw new Error("Guardian name is required");
232
277
  }
233
278
  if (!(guardianContact == null ? void 0 : guardianContact.trim())) {
234
- throw new Error("guardianContact is required and cannot be empty");
279
+ throw new Error("Guardian contact is required");
235
280
  }
236
281
  if (!(guardianRelationship == null ? void 0 : guardianRelationship.trim())) {
237
- throw new Error("guardianRelationship is required and cannot be empty");
282
+ throw new Error("Guardian relationship is required");
238
283
  }
239
284
  const decodedToken = decodeTokenSafely(accessToken);
240
285
  if (!decodedToken) {
@@ -244,14 +289,20 @@ var submitGuardianInfo = async ({
244
289
  if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
245
290
  throw new Error("Invalid token: missing organization or workspace UUID");
246
291
  }
292
+ const apiBaseUrl = baseUrl || BASE_URL;
247
293
  const payload = {
248
294
  guardian_name: guardianName.trim(),
249
295
  guardian_contact: guardianContact.trim(),
250
296
  guardian_relationship: guardianRelationship.trim()
251
297
  };
252
- const apiBaseUrl = baseUrl || BASE_URL;
298
+ if (frontendCallbackUrl) {
299
+ payload.frontend_callback_url = frontendCallbackUrl;
300
+ }
301
+ if (metadata) {
302
+ payload.metadata = metadata;
303
+ }
253
304
  const response = await fetch(
254
- `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/guardian-info`,
305
+ `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/guardian/initiate-verification`,
255
306
  {
256
307
  method: "POST",
257
308
  headers: {
@@ -260,24 +311,26 @@ var submitGuardianInfo = async ({
260
311
  },
261
312
  body: JSON.stringify(payload),
262
313
  signal
263
- // Add abort signal for request cancellation
264
314
  }
265
315
  );
266
316
  if (!response.ok) {
267
- let errorMessage = `Failed to submit guardian info: ${response.status} ${response.statusText}`;
317
+ let errorMessage = `Failed to initiate verification: ${response.status} ${response.statusText}`;
268
318
  if (response.status === 400) {
269
- errorMessage = "Invalid request data";
319
+ try {
320
+ const errorData = await response.json();
321
+ errorMessage = errorData.message || "Invalid request data";
322
+ } catch {
323
+ errorMessage = "Invalid request data";
324
+ }
270
325
  } else if (response.status === 401) {
271
326
  errorMessage = "Unauthorized: Invalid or expired token";
272
- } else if (response.status === 403) {
273
- errorMessage = "Forbidden: Access denied";
274
327
  } else if (response.status === 404) {
275
328
  errorMessage = "Organization or workspace not found";
276
329
  } else if (response.status === 422) {
277
330
  try {
278
331
  const errorData = await response.json();
279
- errorMessage = errorData.message || "Validation error";
280
- } catch (parseError) {
332
+ errorMessage = errorData.message || "Validation error: Please check your input";
333
+ } catch {
281
334
  errorMessage = "Validation error: Please check your input";
282
335
  }
283
336
  } else if (response.status >= 500) {
@@ -287,63 +340,98 @@ var submitGuardianInfo = async ({
287
340
  error.status = response.status;
288
341
  throw error;
289
342
  }
343
+ const responseData = await response.json();
344
+ const detail = responseData.detail || responseData;
345
+ if (detail.already_verified) {
346
+ return {
347
+ success: true,
348
+ guardian_info_uuid: detail.guardian_info_uuid,
349
+ already_verified: true,
350
+ verification_reference: detail.verification_reference
351
+ };
352
+ }
353
+ if (!detail.session_token || !detail.digilocker_redirect_url) {
354
+ throw new Error("Invalid response: missing session_token or digilocker_redirect_url");
355
+ }
356
+ return {
357
+ success: (_a = detail.success) != null ? _a : true,
358
+ guardian_info_uuid: detail.guardian_info_uuid,
359
+ already_verified: false,
360
+ session_token: detail.session_token,
361
+ digilocker_redirect_url: detail.digilocker_redirect_url,
362
+ expires_at: detail.expires_at
363
+ };
290
364
  } catch (error) {
291
- console.error("Error submitting guardian info:", error instanceof Error ? error.message : error);
365
+ console.error("Error initiating guardian verification:", error instanceof Error ? error.message : error);
292
366
  throw error;
293
367
  }
294
368
  };
295
- var fetchTTSAudioUrls = async ({
369
+ var verifyGuardianStatus = async ({
296
370
  accessToken,
297
371
  baseUrl,
298
- noticeUuid,
299
- language,
372
+ sessionToken,
300
373
  signal
301
- // AbortSignal for request cancellation
302
374
  }) => {
303
- try {
304
- if (!accessToken || !noticeUuid || !language) {
305
- throw new Error("accessToken, noticeUuid, and language are required");
306
- }
307
- const decodedToken = decodeTokenSafely(accessToken);
308
- if (!decodedToken) {
309
- throw new Error("Invalid access token");
310
- }
311
- const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
312
- if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
313
- throw new Error("Invalid token: missing organization or workspace UUID");
314
- }
315
- const apiBaseUrl = baseUrl || BASE_URL;
316
- const url = `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/notices/${noticeUuid}/audio/${language}`;
317
- const response = await fetch(url, {
318
- method: "GET",
375
+ var _a, _b, _c, _d, _e, _f, _g, _h;
376
+ if (!accessToken || !sessionToken) {
377
+ throw new Error("accessToken and sessionToken are required");
378
+ }
379
+ const decodedToken = decodeTokenSafely(accessToken);
380
+ if (!decodedToken) {
381
+ throw new Error("Invalid access token");
382
+ }
383
+ const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
384
+ if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
385
+ throw new Error("Invalid token: missing organization or workspace UUID");
386
+ }
387
+ const apiBaseUrl = baseUrl || BASE_URL;
388
+ const response = await fetch(
389
+ `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/guardian/verify-status`,
390
+ {
391
+ method: "POST",
319
392
  headers: {
320
393
  Authorization: `Bearer ${accessToken}`,
321
- Accept: "application/json",
322
394
  "Content-Type": "application/json"
323
395
  },
396
+ body: JSON.stringify({
397
+ session_token: sessionToken
398
+ }),
324
399
  signal
325
- // Add abort signal for request cancellation
326
- });
327
- if (!response.ok) {
328
- let errorMessage = `Failed to fetch TTS audio URLs: ${response.status} ${response.statusText}`;
329
- if (response.status === 401) {
330
- errorMessage = "Unauthorized: Invalid or expired token";
331
- } else if (response.status === 403) {
332
- errorMessage = "Forbidden: Access denied";
333
- } else if (response.status === 404) {
334
- errorMessage = "Notice or audio not found";
335
- } else if (response.status >= 500) {
336
- errorMessage = "Server error: Please try again later";
400
+ }
401
+ );
402
+ if (!response.ok) {
403
+ let errorMessage = `Failed to check verification status: ${response.status} ${response.statusText}`;
404
+ if (response.status === 400) {
405
+ try {
406
+ const errorData = await response.json();
407
+ errorMessage = errorData.message || ((_a = errorData.detail) == null ? void 0 : _a.message) || "Invalid session token";
408
+ } catch {
409
+ errorMessage = "Invalid session token";
337
410
  }
338
- const error = new Error(errorMessage);
339
- error.status = response.status;
340
- throw error;
411
+ } else if (response.status === 401) {
412
+ errorMessage = "Unauthorized: Invalid or expired token";
413
+ } else if (response.status === 404) {
414
+ errorMessage = "Verification session not found or expired. Please restart the verification process.";
415
+ } else if (response.status === 410) {
416
+ errorMessage = "Verification session has expired. Please restart the verification process.";
417
+ } else if (response.status >= 500) {
418
+ errorMessage = "Server error: Please try again later";
341
419
  }
342
- const data = await response.json();
343
- return data;
344
- } catch (error) {
420
+ const error = new Error(errorMessage);
421
+ error.status = response.status;
345
422
  throw error;
346
423
  }
424
+ const responseData = await response.json();
425
+ const detail = responseData.detail || responseData;
426
+ return {
427
+ status: (_b = detail.status) != null ? _b : "in_progress",
428
+ guardian_info_uuid: (_c = detail.guardian_info_uuid) != null ? _c : null,
429
+ verification_reference: (_d = detail.verification_reference) != null ? _d : null,
430
+ guardian_details: (_e = detail.guardian_details) != null ? _e : null,
431
+ error: (_f = detail.error) != null ? _f : null,
432
+ error_code: (_g = detail.error_code) != null ? _g : null,
433
+ can_retry: (_h = detail.can_retry) != null ? _h : true
434
+ };
347
435
  };
348
436
 
349
437
  // src/RedactoNoticeConsent/assets/redacto-logo.png
@@ -491,6 +579,17 @@ var injectCheckboxStyles = () => {
491
579
  outline: 3px solid #FFF9C4 !important;
492
580
  outline-offset: 2px !important;
493
581
  }
582
+
583
+ /* Spinner animation for verification screens */
584
+ @keyframes spin {
585
+ from { transform: rotate(0deg); }
586
+ to { transform: rotate(360deg); }
587
+ }
588
+
589
+ /* DigiLocker callback retry button hover */
590
+ .redacto-dl-retry-btn:hover {
591
+ background-color: #2563eb !important;
592
+ }
494
593
  `;
495
594
  document.head.appendChild(style);
496
595
  stylesInjected = true;
@@ -873,6 +972,69 @@ var useMediaQuery = (query) => {
873
972
  return matches;
874
973
  };
875
974
 
975
+ // src/RedactoNoticeConsent/services/storage.ts
976
+ var STORAGE_KEY = "redacto_consent_sdk_state";
977
+ var STATE_EXPIRY_MS = 30 * 60 * 1e3;
978
+ var isBrowser = () => {
979
+ return typeof window !== "undefined" && typeof window.sessionStorage !== "undefined";
980
+ };
981
+ var saveConsentState = (state) => {
982
+ if (!isBrowser()) {
983
+ console.warn("SessionStorage not available - state not persisted");
984
+ return;
985
+ }
986
+ try {
987
+ const stateWithTimestamp = {
988
+ ...state,
989
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
990
+ };
991
+ sessionStorage.setItem(STORAGE_KEY, JSON.stringify(stateWithTimestamp));
992
+ } catch (error) {
993
+ console.error("Failed to save consent state:", error);
994
+ }
995
+ };
996
+ var getConsentState = () => {
997
+ if (!isBrowser()) {
998
+ return null;
999
+ }
1000
+ try {
1001
+ const savedState = sessionStorage.getItem(STORAGE_KEY);
1002
+ if (!savedState) {
1003
+ return null;
1004
+ }
1005
+ const parsed = JSON.parse(savedState);
1006
+ const stateAge = Date.now() - new Date(parsed.timestamp).getTime();
1007
+ if (stateAge > STATE_EXPIRY_MS) {
1008
+ clearConsentState();
1009
+ return null;
1010
+ }
1011
+ return parsed;
1012
+ } catch (error) {
1013
+ console.error("Failed to get consent state:", error);
1014
+ return null;
1015
+ }
1016
+ };
1017
+ var clearConsentState = () => {
1018
+ if (!isBrowser()) {
1019
+ return;
1020
+ }
1021
+ try {
1022
+ sessionStorage.removeItem(STORAGE_KEY);
1023
+ } catch (error) {
1024
+ console.error("Failed to clear consent state:", error);
1025
+ }
1026
+ };
1027
+ var updateConsentState = (updates) => {
1028
+ const currentState = getConsentState();
1029
+ if (currentState) {
1030
+ saveConsentState({
1031
+ ...currentState,
1032
+ ...updates,
1033
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1034
+ });
1035
+ }
1036
+ };
1037
+
876
1038
  // src/RedactoNoticeConsent/RedactoNoticeConsent.tsx
877
1039
  var import_jsx_runtime = require("react/jsx-runtime");
878
1040
  var PurposeItem = ({
@@ -1180,6 +1342,12 @@ var PurposeItem = ({
1180
1342
  )
1181
1343
  ] }, purpose.uuid);
1182
1344
  };
1345
+ var RELATIONSHIP_OPTIONS = [
1346
+ { value: "Father", label: "Father" },
1347
+ { value: "Mother", label: "Mother" },
1348
+ { value: "Legal Guardian", label: "Legal Guardian" },
1349
+ { value: "Grandparent", label: "Grandparent" }
1350
+ ];
1183
1351
  var GuardianForm = ({
1184
1352
  formData,
1185
1353
  errors,
@@ -1196,34 +1364,128 @@ var GuardianForm = ({
1196
1364
  declineButtonStyle,
1197
1365
  logoUrl
1198
1366
  }) => {
1367
+ var _a, _b, _c, _d, _e;
1368
+ const [isDropdownOpen, setIsDropdownOpen] = (0, import_react2.useState)(false);
1369
+ const [focusedIndex, setFocusedIndex] = (0, import_react2.useState)(-1);
1370
+ const [openUpward, setOpenUpward] = (0, import_react2.useState)(false);
1371
+ const dropdownRef = (0, import_react2.useRef)(null);
1372
+ const optionsRef = (0, import_react2.useRef)(null);
1373
+ (0, import_react2.useEffect)(() => {
1374
+ if (!isDropdownOpen) return;
1375
+ const handleClickOutside = (e) => {
1376
+ if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
1377
+ setIsDropdownOpen(false);
1378
+ setFocusedIndex(-1);
1379
+ }
1380
+ };
1381
+ document.addEventListener("mousedown", handleClickOutside);
1382
+ return () => document.removeEventListener("mousedown", handleClickOutside);
1383
+ }, [isDropdownOpen]);
1384
+ (0, import_react2.useEffect)(() => {
1385
+ if (!isDropdownOpen || !dropdownRef.current) return;
1386
+ const rect = dropdownRef.current.getBoundingClientRect();
1387
+ const spaceBelow = window.innerHeight - rect.bottom;
1388
+ const dropdownHeight = 220;
1389
+ setOpenUpward(spaceBelow < dropdownHeight && rect.top > dropdownHeight);
1390
+ }, [isDropdownOpen]);
1391
+ const handleSelect = (value) => {
1392
+ onChange("guardianRelationship", value);
1393
+ setIsDropdownOpen(false);
1394
+ setFocusedIndex(-1);
1395
+ };
1396
+ const handleKeyDown = (e) => {
1397
+ if (!isDropdownOpen) {
1398
+ if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
1399
+ e.preventDefault();
1400
+ setIsDropdownOpen(true);
1401
+ setFocusedIndex(0);
1402
+ }
1403
+ return;
1404
+ }
1405
+ switch (e.key) {
1406
+ case "ArrowDown":
1407
+ e.preventDefault();
1408
+ setFocusedIndex((prev) => Math.min(prev + 1, RELATIONSHIP_OPTIONS.length - 1));
1409
+ break;
1410
+ case "ArrowUp":
1411
+ e.preventDefault();
1412
+ setFocusedIndex((prev) => Math.max(prev - 1, 0));
1413
+ break;
1414
+ case "Enter":
1415
+ case " ":
1416
+ e.preventDefault();
1417
+ if (focusedIndex >= 0) handleSelect(RELATIONSHIP_OPTIONS[focusedIndex].value);
1418
+ break;
1419
+ case "Escape":
1420
+ case "Tab":
1421
+ setIsDropdownOpen(false);
1422
+ setFocusedIndex(-1);
1423
+ break;
1424
+ }
1425
+ };
1426
+ (0, import_react2.useEffect)(() => {
1427
+ var _a2;
1428
+ if (focusedIndex >= 0 && optionsRef.current) {
1429
+ const option = optionsRef.current.children[focusedIndex];
1430
+ (_a2 = option == null ? void 0 : option.scrollIntoView) == null ? void 0 : _a2.call(option, { block: "nearest" });
1431
+ }
1432
+ }, [focusedIndex]);
1433
+ const borderColor = (settings == null ? void 0 : settings.borderColor) || "#d0d5dd";
1434
+ const textColor = (settings == null ? void 0 : settings.textColor) || "#344054";
1435
+ const bgColor = (settings == null ? void 0 : settings.backgroundColor) || "#ffffff";
1436
+ const radius = (settings == null ? void 0 : settings.borderRadius) || "8px";
1437
+ const font = (settings == null ? void 0 : settings.font) || "inherit";
1199
1438
  const inputStyle = {
1200
1439
  width: "100%",
1201
1440
  padding: "10px 12px",
1202
- border: `1px solid ${(settings == null ? void 0 : settings.borderColor) || "#d0d5dd"}`,
1203
- borderRadius: (settings == null ? void 0 : settings.borderRadius) || "8px",
1441
+ border: `1px solid ${borderColor}`,
1442
+ borderRadius: radius,
1204
1443
  fontSize: "13px",
1205
- fontFamily: (settings == null ? void 0 : settings.font) || "inherit",
1206
- color: (settings == null ? void 0 : settings.textColor) || "#344054",
1207
- backgroundColor: (settings == null ? void 0 : settings.backgroundColor) || "#ffffff",
1208
- marginBottom: "4px",
1209
- boxSizing: "border-box"
1444
+ fontFamily: font,
1445
+ color: textColor,
1446
+ backgroundColor: bgColor,
1447
+ marginBottom: "2px",
1448
+ boxSizing: "border-box",
1449
+ outline: "none"
1210
1450
  };
1211
1451
  const errorStyle = {
1212
1452
  color: "#DC2626",
1213
- fontSize: "12px",
1453
+ fontSize: "11px",
1214
1454
  marginTop: "4px",
1215
- marginBottom: "16px"
1455
+ marginBottom: "0px"
1216
1456
  };
1217
1457
  const labelStyle = {
1218
1458
  ...componentTextStyle,
1219
1459
  fontSize: "13px",
1220
1460
  fontWeight: "500",
1221
- marginBottom: "6px",
1461
+ marginBottom: "4px",
1222
1462
  display: "block"
1223
1463
  };
1464
+ const hintStyle = {
1465
+ fontSize: "11px",
1466
+ color: "#667085",
1467
+ marginTop: "2px",
1468
+ marginBottom: "0px",
1469
+ lineHeight: "1.4"
1470
+ };
1471
+ const infoBannerStyle = {
1472
+ display: "flex",
1473
+ alignItems: "flex-start",
1474
+ gap: "8px",
1475
+ padding: "10px 12px",
1476
+ backgroundColor: "#EFF6FF",
1477
+ border: "1px solid #BFDBFE",
1478
+ borderRadius: radius,
1479
+ marginBottom: "16px",
1480
+ fontSize: "12px",
1481
+ lineHeight: "1.5",
1482
+ color: "#1E40AF"
1483
+ };
1484
+ const selectedLabel = (_a = RELATIONSHIP_OPTIONS.find((o) => o.value === formData.guardianRelationship)) == null ? void 0 : _a.label;
1485
+ const hasError = Boolean(errors.guardianRelationship);
1224
1486
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1225
1487
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...styles3.topSection, ...componentTextStyle }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles3.topLeft, children: [
1226
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { style: styles3.logo, src: logoUrl, alt: "Redacto Logo" }),
1488
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { style: styles3.logo, src: logoUrl, alt: "Logo" }),
1227
1489
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1228
1490
  "h2",
1229
1491
  {
@@ -1235,7 +1497,7 @@ var GuardianForm = ({
1235
1497
  fontSize: "16px",
1236
1498
  fontWeight: 600
1237
1499
  },
1238
- children: "Guardian Information Required"
1500
+ children: "Guardian Verification"
1239
1501
  }
1240
1502
  )
1241
1503
  ] }) }),
@@ -1255,31 +1517,49 @@ var GuardianForm = ({
1255
1517
  ...styles3.privacyText,
1256
1518
  ...responsiveStyles.privacyText,
1257
1519
  ...componentTextStyle,
1258
- fontSize: "14px",
1259
- lineHeight: "1.4"
1520
+ fontSize: "13px",
1521
+ lineHeight: "1.5",
1522
+ marginBottom: "12px"
1260
1523
  },
1261
- children: "As you are under the age of consent, we need to collect information about your legal guardian to proceed."
1524
+ children: "Since you are under the age of consent, a legal guardian must verify their identity via DigiLocker before you can proceed."
1262
1525
  }
1263
1526
  ),
1527
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: infoBannerStyle, children: [
1528
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1529
+ "svg",
1530
+ {
1531
+ width: "16",
1532
+ height: "16",
1533
+ viewBox: "0 0 24 24",
1534
+ fill: "none",
1535
+ stroke: "#2563EB",
1536
+ strokeWidth: "2",
1537
+ strokeLinecap: "round",
1538
+ strokeLinejoin: "round",
1539
+ style: { flexShrink: 0, marginTop: "1px" },
1540
+ children: [
1541
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
1542
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "12", y1: "16", x2: "12", y2: "12" }),
1543
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "12", y1: "8", x2: "12.01", y2: "8" })
1544
+ ]
1545
+ }
1546
+ ),
1547
+ /* @__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." })
1548
+ ] }),
1264
1549
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1265
1550
  "div",
1266
1551
  {
1267
1552
  style: {
1268
1553
  display: "flex",
1269
1554
  flexDirection: "column",
1270
- gap: "12px",
1271
- marginTop: "16px"
1555
+ gap: "14px"
1272
1556
  },
1273
1557
  children: [
1274
1558
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1275
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1276
- "label",
1277
- {
1278
- htmlFor: "guardianName",
1279
- style: { ...labelStyle, marginBottom: "8px", display: "block" },
1280
- children: "Name of Guardian *"
1281
- }
1282
- ),
1559
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { htmlFor: "guardianName", style: labelStyle, children: [
1560
+ "Guardian's Full Name ",
1561
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "#DC2626" }, children: "*" })
1562
+ ] }),
1283
1563
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1284
1564
  "input",
1285
1565
  {
@@ -1289,62 +1569,170 @@ var GuardianForm = ({
1289
1569
  onChange: (e) => onChange("guardianName", e.target.value),
1290
1570
  style: {
1291
1571
  ...inputStyle,
1292
- borderColor: errors.guardianName ? "#DC2626" : inputStyle.border
1572
+ borderColor: errors.guardianName ? "#DC2626" : borderColor
1293
1573
  },
1294
- placeholder: "Enter guardian's full name"
1574
+ placeholder: "e.g. Rajesh Kumar",
1575
+ autoComplete: "name",
1576
+ "aria-describedby": "guardianName-hint"
1295
1577
  }
1296
1578
  ),
1297
- errors.guardianName && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: errorStyle, children: errors.guardianName })
1579
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { id: "guardianName-hint", style: hintStyle, children: "Enter the full name as per government ID (Aadhaar, PAN, etc.)" }),
1580
+ errors.guardianName && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: errorStyle, role: "alert", children: errors.guardianName })
1298
1581
  ] }),
1299
1582
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1300
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1301
- "label",
1302
- {
1303
- htmlFor: "guardianContact",
1304
- style: { ...labelStyle, marginBottom: "8px", display: "block" },
1305
- children: "Contact of Guardian *"
1306
- }
1307
- ),
1583
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { htmlFor: "guardianContact", style: labelStyle, children: [
1584
+ "Guardian's Phone Number ",
1585
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "#DC2626" }, children: "*" })
1586
+ ] }),
1308
1587
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1309
1588
  "input",
1310
1589
  {
1311
1590
  id: "guardianContact",
1312
- type: "text",
1591
+ type: "tel",
1592
+ inputMode: "tel",
1313
1593
  value: formData.guardianContact,
1314
1594
  onChange: (e) => onChange("guardianContact", e.target.value),
1315
1595
  style: {
1316
1596
  ...inputStyle,
1317
- borderColor: errors.guardianContact ? "#DC2626" : inputStyle.border
1597
+ borderColor: errors.guardianContact ? "#DC2626" : borderColor
1318
1598
  },
1319
- placeholder: "Enter guardian's email or phone number"
1599
+ placeholder: "e.g. 9876543210",
1600
+ autoComplete: "tel",
1601
+ "aria-describedby": "guardianContact-hint"
1320
1602
  }
1321
1603
  ),
1322
- errors.guardianContact && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: errorStyle, children: errors.guardianContact })
1604
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { id: "guardianContact-hint", style: hintStyle, children: "Mobile number linked to guardian's Aadhaar" }),
1605
+ errors.guardianContact && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: errorStyle, role: "alert", children: errors.guardianContact })
1323
1606
  ] }),
1324
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1325
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1326
- "label",
1607
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ref: dropdownRef, style: { position: "relative" }, children: [
1608
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { id: "guardianRelationship-label", style: labelStyle, children: [
1609
+ "Relationship ",
1610
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "#DC2626" }, children: "*" })
1611
+ ] }),
1612
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1613
+ "div",
1327
1614
  {
1328
- htmlFor: "guardianRelationship",
1329
- style: { ...labelStyle, marginBottom: "8px", display: "block" },
1330
- children: "Relationship to Guardian *"
1615
+ role: "combobox",
1616
+ "aria-expanded": isDropdownOpen,
1617
+ "aria-haspopup": "listbox",
1618
+ "aria-labelledby": "guardianRelationship-label",
1619
+ "aria-controls": "guardianRelationship-listbox",
1620
+ "aria-activedescendant": focusedIndex >= 0 ? `guardianRelationship-option-${focusedIndex}` : void 0,
1621
+ tabIndex: 0,
1622
+ onClick: () => {
1623
+ setIsDropdownOpen((prev) => !prev);
1624
+ setFocusedIndex(-1);
1625
+ },
1626
+ onKeyDown: handleKeyDown,
1627
+ style: {
1628
+ ...inputStyle,
1629
+ borderColor: hasError ? "#DC2626" : isDropdownOpen ? ((_c = (_b = settings == null ? void 0 : settings.button) == null ? void 0 : _b.accept) == null ? void 0 : _c.backgroundColor) || "#3b82f6" : borderColor,
1630
+ 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",
1631
+ color: selectedLabel ? textColor : "#9CA3AF",
1632
+ cursor: "pointer",
1633
+ display: "flex",
1634
+ alignItems: "center",
1635
+ justifyContent: "space-between",
1636
+ userSelect: "none",
1637
+ transition: "border-color 0.15s, box-shadow 0.15s"
1638
+ },
1639
+ children: [
1640
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: selectedLabel || "Select relationship" }),
1641
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1642
+ "svg",
1643
+ {
1644
+ width: "14",
1645
+ height: "14",
1646
+ viewBox: "0 0 24 24",
1647
+ fill: "none",
1648
+ stroke: "#667085",
1649
+ strokeWidth: "2",
1650
+ strokeLinecap: "round",
1651
+ strokeLinejoin: "round",
1652
+ style: {
1653
+ flexShrink: 0,
1654
+ marginLeft: "8px",
1655
+ transform: isDropdownOpen ? "rotate(180deg)" : "rotate(0deg)",
1656
+ transition: "transform 0.2s"
1657
+ },
1658
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "6 9 12 15 18 9" })
1659
+ }
1660
+ )
1661
+ ]
1331
1662
  }
1332
1663
  ),
1333
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1334
- "input",
1664
+ isDropdownOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1665
+ "div",
1335
1666
  {
1336
- id: "guardianRelationship",
1337
- type: "text",
1338
- value: formData.guardianRelationship,
1339
- onChange: (e) => onChange("guardianRelationship", e.target.value),
1667
+ ref: optionsRef,
1668
+ id: "guardianRelationship-listbox",
1669
+ role: "listbox",
1670
+ "aria-labelledby": "guardianRelationship-label",
1340
1671
  style: {
1341
- ...inputStyle,
1342
- borderColor: errors.guardianRelationship ? "#DC2626" : inputStyle.border
1672
+ position: "absolute",
1673
+ ...openUpward ? { bottom: "calc(100% + 4px)" } : { top: "calc(100% + 4px)" },
1674
+ left: 0,
1675
+ right: 0,
1676
+ backgroundColor: bgColor,
1677
+ border: `1px solid ${borderColor}`,
1678
+ borderRadius: radius,
1679
+ 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)",
1680
+ zIndex: 50,
1681
+ overflow: "hidden",
1682
+ padding: "4px 0",
1683
+ maxHeight: "220px",
1684
+ overflowY: "auto"
1343
1685
  },
1344
- placeholder: "e.g., Parent, Legal Guardian, etc."
1686
+ children: RELATIONSHIP_OPTIONS.map((option, index) => {
1687
+ var _a2, _b2;
1688
+ const isSelected = formData.guardianRelationship === option.value;
1689
+ const isFocused = focusedIndex === index;
1690
+ const accentColor = ((_b2 = (_a2 = settings == null ? void 0 : settings.button) == null ? void 0 : _a2.accept) == null ? void 0 : _b2.backgroundColor) || "#3b82f6";
1691
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1692
+ "div",
1693
+ {
1694
+ id: `guardianRelationship-option-${index}`,
1695
+ role: "option",
1696
+ "aria-selected": isSelected,
1697
+ onClick: () => handleSelect(option.value),
1698
+ onMouseEnter: () => setFocusedIndex(index),
1699
+ style: {
1700
+ padding: "9px 12px",
1701
+ fontSize: "13px",
1702
+ fontFamily: font,
1703
+ color: isSelected ? accentColor : textColor,
1704
+ backgroundColor: isFocused ? "#F3F4F6" : "transparent",
1705
+ cursor: "pointer",
1706
+ display: "flex",
1707
+ alignItems: "center",
1708
+ justifyContent: "space-between",
1709
+ transition: "background-color 0.1s"
1710
+ },
1711
+ children: [
1712
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: option.label }),
1713
+ isSelected && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1714
+ "svg",
1715
+ {
1716
+ width: "14",
1717
+ height: "14",
1718
+ viewBox: "0 0 24 24",
1719
+ fill: "none",
1720
+ stroke: accentColor,
1721
+ strokeWidth: "2.5",
1722
+ strokeLinecap: "round",
1723
+ strokeLinejoin: "round",
1724
+ style: { flexShrink: 0 },
1725
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "20 6 9 17 4 12" })
1726
+ }
1727
+ )
1728
+ ]
1729
+ },
1730
+ option.value
1731
+ );
1732
+ })
1345
1733
  }
1346
1734
  ),
1347
- errors.guardianRelationship && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: errorStyle, children: errors.guardianRelationship })
1735
+ errors.guardianRelationship && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: errorStyle, role: "alert", children: errors.guardianRelationship })
1348
1736
  ] })
1349
1737
  ]
1350
1738
  }
@@ -1355,7 +1743,7 @@ var GuardianForm = ({
1355
1743
  role: "alert",
1356
1744
  style: {
1357
1745
  color: "#DC2626",
1358
- fontSize: "14px",
1746
+ fontSize: "13px",
1359
1747
  marginTop: "16px",
1360
1748
  padding: "12px",
1361
1749
  borderRadius: "6px",
@@ -1391,8 +1779,8 @@ var GuardianForm = ({
1391
1779
  onClick: onNext,
1392
1780
  disabled: isSubmitting,
1393
1781
  type: "button",
1394
- "aria-label": isSubmitting ? "Submitting guardian information" : "Submit guardian information and proceed",
1395
- children: isSubmitting ? "Submitting..." : "Next"
1782
+ "aria-label": isSubmitting ? "Verifying guardian identity" : "Proceed to guardian verification",
1783
+ children: isSubmitting ? "Verifying..." : "Verify via DigiLocker"
1396
1784
  }
1397
1785
  ),
1398
1786
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1406,7 +1794,7 @@ var GuardianForm = ({
1406
1794
  },
1407
1795
  onClick: onDecline,
1408
1796
  type: "button",
1409
- "aria-label": "Cancel guardian information form",
1797
+ "aria-label": "Cancel",
1410
1798
  children: "Cancel"
1411
1799
  }
1412
1800
  )
@@ -1415,6 +1803,266 @@ var GuardianForm = ({
1415
1803
  )
1416
1804
  ] });
1417
1805
  };
1806
+ var VerificationScreen = ({
1807
+ onBack,
1808
+ onContinue,
1809
+ isInitiating,
1810
+ isPolling,
1811
+ isVerificationComplete,
1812
+ isAutoTransitioning,
1813
+ error,
1814
+ errorCode,
1815
+ canRetry,
1816
+ styles: styles3,
1817
+ responsiveStyles,
1818
+ settings,
1819
+ componentHeadingStyle,
1820
+ componentTextStyle,
1821
+ acceptButtonStyle,
1822
+ declineButtonStyle,
1823
+ logoUrl
1824
+ }) => {
1825
+ var _a, _b;
1826
+ const errorStyle = {
1827
+ color: "#DC2626",
1828
+ fontSize: "14px",
1829
+ marginTop: "16px",
1830
+ padding: "12px",
1831
+ borderRadius: "6px",
1832
+ border: "1px solid #FCA5A5",
1833
+ backgroundColor: "#FEF2F2",
1834
+ textAlign: "center"
1835
+ };
1836
+ const statusStyle = {
1837
+ color: "#059669",
1838
+ fontSize: "14px",
1839
+ marginTop: "16px",
1840
+ padding: "12px",
1841
+ borderRadius: "6px",
1842
+ border: "1px solid #A7F3D0",
1843
+ backgroundColor: "#D1FAE5",
1844
+ textAlign: "center"
1845
+ };
1846
+ const spinnerStyle = {
1847
+ width: "48px",
1848
+ height: "48px",
1849
+ border: "4px solid #e5e7eb",
1850
+ borderTopColor: ((_b = (_a = settings == null ? void 0 : settings.button) == null ? void 0 : _a.accept) == null ? void 0 : _b.backgroundColor) || "#3b82f6",
1851
+ borderRadius: "50%",
1852
+ animation: "spin 1s linear infinite",
1853
+ margin: "24px auto"
1854
+ };
1855
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1856
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...styles3.topSection, ...componentTextStyle }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles3.topLeft, children: [
1857
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { style: styles3.logo, src: logoUrl, alt: "Logo" }),
1858
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1859
+ "h2",
1860
+ {
1861
+ id: "verification-title",
1862
+ style: {
1863
+ ...styles3.title,
1864
+ ...responsiveStyles.title,
1865
+ ...componentHeadingStyle,
1866
+ fontSize: "16px",
1867
+ fontWeight: 600
1868
+ },
1869
+ children: "Guardian Verification"
1870
+ }
1871
+ )
1872
+ ] }) }),
1873
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1874
+ "div",
1875
+ {
1876
+ style: {
1877
+ ...styles3.middleSection,
1878
+ ...responsiveStyles.middleSection,
1879
+ ...componentTextStyle,
1880
+ textAlign: "center"
1881
+ },
1882
+ children: [
1883
+ isVerificationComplete && !error ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1884
+ "div",
1885
+ {
1886
+ style: {
1887
+ display: "flex",
1888
+ flexDirection: "column",
1889
+ alignItems: "center",
1890
+ justifyContent: "center",
1891
+ flex: 1
1892
+ },
1893
+ children: [
1894
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1895
+ "div",
1896
+ {
1897
+ style: {
1898
+ width: "64px",
1899
+ height: "64px",
1900
+ borderRadius: "50%",
1901
+ backgroundColor: "#D1FAE5",
1902
+ display: "flex",
1903
+ alignItems: "center",
1904
+ justifyContent: "center"
1905
+ },
1906
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1907
+ "svg",
1908
+ {
1909
+ width: "32",
1910
+ height: "32",
1911
+ viewBox: "0 0 24 24",
1912
+ fill: "none",
1913
+ stroke: "#059669",
1914
+ strokeWidth: "2",
1915
+ strokeLinecap: "round",
1916
+ strokeLinejoin: "round",
1917
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "20 6 9 17 4 12" })
1918
+ }
1919
+ )
1920
+ }
1921
+ ),
1922
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1923
+ "p",
1924
+ {
1925
+ style: {
1926
+ ...componentHeadingStyle,
1927
+ fontSize: "16px",
1928
+ fontWeight: 600,
1929
+ lineHeight: "1.5",
1930
+ marginTop: "12px",
1931
+ marginBottom: "24px"
1932
+ },
1933
+ children: "Verification Completed"
1934
+ }
1935
+ )
1936
+ ]
1937
+ }
1938
+ ) : (isInitiating || isPolling) && !error ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1939
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: spinnerStyle }),
1940
+ /* @__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." })
1941
+ ] }) : null,
1942
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1943
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1944
+ "div",
1945
+ {
1946
+ style: {
1947
+ width: "64px",
1948
+ height: "64px",
1949
+ borderRadius: "50%",
1950
+ backgroundColor: "#fee2e2",
1951
+ display: "flex",
1952
+ alignItems: "center",
1953
+ justifyContent: "center",
1954
+ margin: "24px auto"
1955
+ },
1956
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1957
+ "svg",
1958
+ {
1959
+ width: "32",
1960
+ height: "32",
1961
+ viewBox: "0 0 24 24",
1962
+ fill: "none",
1963
+ stroke: "#dc2626",
1964
+ strokeWidth: "2",
1965
+ strokeLinecap: "round",
1966
+ strokeLinejoin: "round",
1967
+ children: [
1968
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
1969
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
1970
+ ]
1971
+ }
1972
+ )
1973
+ }
1974
+ ),
1975
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1976
+ "p",
1977
+ {
1978
+ style: {
1979
+ ...styles3.privacyText,
1980
+ ...responsiveStyles.privacyText,
1981
+ ...componentTextStyle,
1982
+ fontSize: "14px",
1983
+ lineHeight: "1.5",
1984
+ marginBottom: "12px"
1985
+ },
1986
+ children: "Verification failed"
1987
+ }
1988
+ ),
1989
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: errorStyle, children: error })
1990
+ ] })
1991
+ ]
1992
+ }
1993
+ ),
1994
+ isAutoTransitioning ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1995
+ "div",
1996
+ {
1997
+ style: {
1998
+ ...styles3.bottomSection,
1999
+ ...responsiveStyles.bottomSection,
2000
+ ...componentTextStyle,
2001
+ paddingTop: "12px",
2002
+ display: "flex",
2003
+ gap: "12px",
2004
+ justifyContent: "center"
2005
+ },
2006
+ children: isVerificationComplete && !error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2007
+ "button",
2008
+ {
2009
+ style: {
2010
+ ...styles3.button,
2011
+ ...responsiveStyles.button,
2012
+ ...acceptButtonStyle
2013
+ },
2014
+ onClick: onContinue,
2015
+ type: "button",
2016
+ "aria-label": "Continue to consent form",
2017
+ children: "Continue"
2018
+ }
2019
+ ) : error && canRetry ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2020
+ "button",
2021
+ {
2022
+ style: {
2023
+ ...styles3.button,
2024
+ ...responsiveStyles.button,
2025
+ ...styles3.acceptButton,
2026
+ ...acceptButtonStyle
2027
+ },
2028
+ onClick: () => onBack(errorCode === "GUARDIAN_UNDER_18"),
2029
+ type: "button",
2030
+ "aria-label": errorCode === "GUARDIAN_UNDER_18" ? "Change guardian details" : "Try verification again",
2031
+ children: errorCode === "GUARDIAN_UNDER_18" ? "Change Guardian" : "Try Again"
2032
+ }
2033
+ ) : error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2034
+ "button",
2035
+ {
2036
+ style: {
2037
+ ...styles3.button,
2038
+ ...responsiveStyles.button,
2039
+ ...styles3.cancelButton,
2040
+ ...declineButtonStyle
2041
+ },
2042
+ onClick: () => onBack(false),
2043
+ type: "button",
2044
+ "aria-label": "Go back",
2045
+ children: "Go Back"
2046
+ }
2047
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2048
+ "button",
2049
+ {
2050
+ style: {
2051
+ ...styles3.button,
2052
+ ...responsiveStyles.button,
2053
+ ...styles3.cancelButton,
2054
+ ...declineButtonStyle
2055
+ },
2056
+ onClick: () => onBack(false),
2057
+ type: "button",
2058
+ "aria-label": "Cancel verification",
2059
+ children: "Cancel"
2060
+ }
2061
+ )
2062
+ }
2063
+ )
2064
+ ] });
2065
+ };
1418
2066
  var AgeVerification = ({
1419
2067
  onYes,
1420
2068
  onNo,
@@ -1577,6 +2225,10 @@ var RedactoNoticeConsent = ({
1577
2225
  settings,
1578
2226
  applicationId,
1579
2227
  validateAgainst = "all",
2228
+ digilockerMode = "redirect",
2229
+ // Default to full page redirect (DigiLocker blocks iframe embedding)
2230
+ digilockerCallbackUrl = "/digilocker-callback",
2231
+ // Default callback path
1580
2232
  includeFullyConsentedData = false,
1581
2233
  reviewModeButtonText = "Continue"
1582
2234
  }) => {
@@ -1632,6 +2284,8 @@ var RedactoNoticeConsent = ({
1632
2284
  const [currentAudioIndex, setCurrentAudioIndex] = (0, import_react2.useState)(0);
1633
2285
  const [highlightedElementId, setHighlightedElementId] = (0, import_react2.useState)(null);
1634
2286
  const [showAgeVerification, setShowAgeVerification] = (0, import_react2.useState)(false);
2287
+ const [isMinorFlow, setIsMinorFlow] = (0, import_react2.useState)(false);
2288
+ const [selfDeclaredAdult, setSelfDeclaredAdult] = (0, import_react2.useState)(false);
1635
2289
  const [showGuardianForm, setShowGuardianForm] = (0, import_react2.useState)(false);
1636
2290
  const [isSubmittingGuardian, setIsSubmittingGuardian] = (0, import_react2.useState)(false);
1637
2291
  const [guardianFormData, setGuardianFormData] = (0, import_react2.useState)({
@@ -1640,12 +2294,32 @@ var RedactoNoticeConsent = ({
1640
2294
  guardianRelationship: ""
1641
2295
  });
1642
2296
  const [guardianFormErrors, setGuardianFormErrors] = (0, import_react2.useState)({});
2297
+ const [showVerificationScreen, setShowVerificationScreen] = (0, import_react2.useState)(false);
2298
+ const [verificationReference, setVerificationReference] = (0, import_react2.useState)(null);
2299
+ const [isInitiatingVerification, setIsInitiatingVerification] = (0, import_react2.useState)(false);
2300
+ const [verificationError, setVerificationError] = (0, import_react2.useState)(
2301
+ null
2302
+ );
2303
+ const [verificationErrorCode, setVerificationErrorCode] = (0, import_react2.useState)(null);
2304
+ const [canRetryVerification, setCanRetryVerification] = (0, import_react2.useState)(false);
2305
+ const [isPollingStatus, setIsPollingStatus] = (0, import_react2.useState)(false);
2306
+ const [isVerificationComplete, setIsVerificationComplete] = (0, import_react2.useState)(false);
2307
+ const [isAutoTransitioning, setIsAutoTransitioning] = (0, import_react2.useState)(false);
1643
2308
  const modalRef = (0, import_react2.useRef)(null);
1644
2309
  const firstFocusableRef = (0, import_react2.useRef)(null);
1645
2310
  const lastFocusableRef = (0, import_react2.useRef)(null);
1646
2311
  const abortControllerRef = (0, import_react2.useRef)(null);
1647
2312
  const isRequestInProgressRef = (0, import_react2.useRef)(false);
1648
2313
  const currentAudioRef = (0, import_react2.useRef)(null);
2314
+ const popupCheckIntervalRef = (0, import_react2.useRef)(null);
2315
+ const statusPollIntervalRef = (0, import_react2.useRef)(null);
2316
+ const verificationPopupRef = (0, import_react2.useRef)(null);
2317
+ const pollingCleanupRef = (0, import_react2.useRef)(null);
2318
+ const autoTransitionTimerRef = (0, import_react2.useRef)(null);
2319
+ const digilockerRedirectTimeoutRef = (0, import_react2.useRef)(null);
2320
+ const isInitialMountRef = (0, import_react2.useRef)(true);
2321
+ const isPollingStatusRef = (0, import_react2.useRef)(false);
2322
+ const verificationSessionTokenRef = (0, import_react2.useRef)(null);
1649
2323
  const isMobile = useMediaQuery("(max-width: 768px)");
1650
2324
  const responsiveStyles = (0, import_react2.useMemo)(
1651
2325
  () => ({
@@ -1753,6 +2427,7 @@ var RedactoNoticeConsent = ({
1753
2427
  }, [content, selectedDataElements]);
1754
2428
  const acceptDisabled = isSubmitting || !areAllRequiredElementsChecked;
1755
2429
  (0, import_react2.useEffect)(() => {
2430
+ var _a2, _b2;
1756
2431
  clearApiCache();
1757
2432
  isRequestInProgressRef.current = false;
1758
2433
  setContent(null);
@@ -1768,6 +2443,8 @@ var RedactoNoticeConsent = ({
1768
2443
  setIsSubmittingAccept(false);
1769
2444
  setIsSubmittingDecline(false);
1770
2445
  setShowAgeVerification(false);
2446
+ setIsMinorFlow(false);
2447
+ setSelfDeclaredAdult(false);
1771
2448
  setShowGuardianForm(false);
1772
2449
  setIsSubmittingGuardian(false);
1773
2450
  setGuardianFormData({
@@ -1776,6 +2453,41 @@ var RedactoNoticeConsent = ({
1776
2453
  guardianRelationship: ""
1777
2454
  });
1778
2455
  setGuardianFormErrors({});
2456
+ setShowVerificationScreen(false);
2457
+ setVerificationReference(null);
2458
+ setIsInitiatingVerification(false);
2459
+ setVerificationError(null);
2460
+ setVerificationErrorCode(null);
2461
+ setCanRetryVerification(false);
2462
+ setIsVerificationComplete(false);
2463
+ setIsAutoTransitioning(false);
2464
+ setIsPollingStatus(false);
2465
+ isPollingStatusRef.current = false;
2466
+ verificationSessionTokenRef.current = null;
2467
+ if (popupCheckIntervalRef.current) {
2468
+ clearInterval(popupCheckIntervalRef.current);
2469
+ popupCheckIntervalRef.current = null;
2470
+ }
2471
+ if (statusPollIntervalRef.current) {
2472
+ clearTimeout(statusPollIntervalRef.current);
2473
+ statusPollIntervalRef.current = null;
2474
+ }
2475
+ (_a2 = pollingCleanupRef.current) == null ? void 0 : _a2.call(pollingCleanupRef);
2476
+ pollingCleanupRef.current = null;
2477
+ (_b2 = verificationPopupRef.current) == null ? void 0 : _b2.close();
2478
+ verificationPopupRef.current = null;
2479
+ if (autoTransitionTimerRef.current) {
2480
+ clearTimeout(autoTransitionTimerRef.current);
2481
+ autoTransitionTimerRef.current = null;
2482
+ }
2483
+ if (digilockerRedirectTimeoutRef.current) {
2484
+ clearTimeout(digilockerRedirectTimeoutRef.current);
2485
+ digilockerRedirectTimeoutRef.current = null;
2486
+ }
2487
+ if (!isInitialMountRef.current) {
2488
+ clearConsentState();
2489
+ }
2490
+ isInitialMountRef.current = false;
1779
2491
  }, [accessToken]);
1780
2492
  (0, import_react2.useEffect)(() => {
1781
2493
  if (hasAlreadyConsented) {
@@ -1984,7 +2696,11 @@ var RedactoNoticeConsent = ({
1984
2696
  setInitialDataElementSelections({});
1985
2697
  }
1986
2698
  if (consentContentData.detail.is_minor) {
1987
- setShowAgeVerification(true);
2699
+ setIsMinorFlow(true);
2700
+ const persistedState = getConsentState();
2701
+ if (!(persistedState == null ? void 0 : persistedState.sessionToken) && !(persistedState == null ? void 0 : persistedState.verificationReference)) {
2702
+ setShowAgeVerification(true);
2703
+ }
1988
2704
  }
1989
2705
  setIsLoading(false);
1990
2706
  } catch (err) {
@@ -2083,6 +2799,10 @@ var RedactoNoticeConsent = ({
2083
2799
  const handleAccept = async () => {
2084
2800
  const submitController = new AbortController();
2085
2801
  try {
2802
+ if (isMinorFlow && !verificationReference) {
2803
+ setErrorMessage("Guardian verification is required before consent can be submitted.");
2804
+ return;
2805
+ }
2086
2806
  setIsSubmittingAccept(true);
2087
2807
  setIsSubmitting(true);
2088
2808
  setErrorMessage(null);
@@ -2118,6 +2838,8 @@ var RedactoNoticeConsent = ({
2118
2838
  }),
2119
2839
  declined: false,
2120
2840
  meta_data: applicationId ? { specific_uuid: applicationId } : void 0,
2841
+ guardianVerificationReference: verificationReference || void 0,
2842
+ selfDeclaredAdult: selfDeclaredAdult || void 0,
2121
2843
  signal: submitController.signal
2122
2844
  });
2123
2845
  }
@@ -2182,17 +2904,102 @@ var RedactoNoticeConsent = ({
2182
2904
  try {
2183
2905
  setIsSubmittingGuardian(true);
2184
2906
  setGuardianFormErrors({});
2185
- await submitGuardianInfo({
2907
+ setVerificationError(null);
2908
+ const callbackUrl = digilockerMode === "popup" ? new URL(digilockerCallbackUrl, window.location.origin).href : void 0;
2909
+ const response = await initiateGuardianVerification({
2186
2910
  accessToken,
2187
2911
  baseUrl,
2188
2912
  guardianName: guardianFormData.guardianName,
2189
2913
  guardianContact: guardianFormData.guardianContact,
2190
2914
  guardianRelationship: guardianFormData.guardianRelationship,
2915
+ frontendCallbackUrl: callbackUrl,
2916
+ // Only for popup mode
2191
2917
  signal: guardianController.signal
2192
2918
  });
2919
+ setIsSubmittingGuardian(false);
2920
+ if (response.already_verified && response.verification_reference) {
2921
+ setVerificationReference(response.verification_reference);
2922
+ setShowGuardianForm(false);
2923
+ setShowVerificationScreen(true);
2924
+ setIsInitiatingVerification(false);
2925
+ setIsVerificationComplete(true);
2926
+ setIsAutoTransitioning(true);
2927
+ autoTransitionTimerRef.current = setTimeout(() => {
2928
+ setShowVerificationScreen(false);
2929
+ setIsVerificationComplete(false);
2930
+ setIsAutoTransitioning(false);
2931
+ autoTransitionTimerRef.current = null;
2932
+ }, 1e3);
2933
+ return;
2934
+ }
2935
+ if (!response.session_token || !response.digilocker_redirect_url) {
2936
+ throw new Error("Invalid response from server: missing required fields");
2937
+ }
2193
2938
  setShowGuardianForm(false);
2939
+ setShowVerificationScreen(true);
2940
+ setIsInitiatingVerification(true);
2941
+ if (digilockerMode === "redirect") {
2942
+ saveConsentState({
2943
+ flowState: "digilocker-pending",
2944
+ sessionToken: response.session_token,
2945
+ guardianInfoUuid: response.guardian_info_uuid,
2946
+ selectedPurposes,
2947
+ selectedDataElements,
2948
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2949
+ originalUrl: window.location.href,
2950
+ noticeId,
2951
+ baseUrl
2952
+ });
2953
+ try {
2954
+ const openedWindow = window.open(
2955
+ response.digilocker_redirect_url,
2956
+ "_blank"
2957
+ );
2958
+ if (!openedWindow) {
2959
+ console.warn("Popup blocked \u2014 cannot open DigiLocker in new tab");
2960
+ setIsInitiatingVerification(false);
2961
+ setVerificationError(
2962
+ "Unable to open DigiLocker in a new tab. Please allow popups for this site and try again."
2963
+ );
2964
+ return;
2965
+ }
2966
+ try {
2967
+ openedWindow.opener = null;
2968
+ } catch {
2969
+ }
2970
+ } catch (error) {
2971
+ console.error("Failed to open DigiLocker in new tab:", error);
2972
+ setIsInitiatingVerification(false);
2973
+ setVerificationError(
2974
+ "Unable to open DigiLocker. Please allow popups for this site and try again."
2975
+ );
2976
+ return;
2977
+ }
2978
+ setIsInitiatingVerification(false);
2979
+ setIsPollingStatus(true);
2980
+ isPollingStatusRef.current = true;
2981
+ startStatusPolling(response.session_token);
2982
+ } else {
2983
+ setIsInitiatingVerification(true);
2984
+ const popup = window.open(
2985
+ response.digilocker_redirect_url,
2986
+ "digilocker_verification",
2987
+ "width=600,height=700,scrollbars=yes,resizable=yes"
2988
+ );
2989
+ if (popup) {
2990
+ verificationPopupRef.current = popup;
2991
+ startPopupMonitoring(popup);
2992
+ startStatusPolling(response.session_token);
2993
+ } else {
2994
+ setIsInitiatingVerification(false);
2995
+ setVerificationError(
2996
+ "Popup blocked. Please allow popups for this site or use redirect mode."
2997
+ );
2998
+ }
2999
+ }
2194
3000
  } catch (error) {
2195
3001
  const err = error;
3002
+ setIsInitiatingVerification(false);
2196
3003
  if (err.status === 422) {
2197
3004
  if (err.message.includes("guardian_contact")) {
2198
3005
  setGuardianFormErrors({ guardianContact: err.message });
@@ -2203,23 +3010,265 @@ var RedactoNoticeConsent = ({
2203
3010
  } else {
2204
3011
  setGuardianFormErrors({ general: err.message });
2205
3012
  }
3013
+ setShowVerificationScreen(false);
3014
+ setShowGuardianForm(true);
2206
3015
  } else {
2207
3016
  setGuardianFormErrors({
2208
- general: err.message || "Failed to submit guardian information. Please try again."
3017
+ general: err.message || "Failed to initiate verification. Please try again."
2209
3018
  });
3019
+ setShowVerificationScreen(false);
3020
+ setShowGuardianForm(true);
2210
3021
  onError == null ? void 0 : onError(err);
2211
3022
  }
2212
3023
  } finally {
2213
3024
  setIsSubmittingGuardian(false);
2214
3025
  }
2215
- }, [accessToken, baseUrl, guardianFormData, onError]);
3026
+ }, [accessToken, baseUrl, guardianFormData, digilockerMode, digilockerCallbackUrl, selectedPurposes, selectedDataElements, noticeId, onError]);
2216
3027
  const handleAgeVerificationYes = (0, import_react2.useCallback)(() => {
2217
3028
  setShowAgeVerification(false);
3029
+ setIsMinorFlow(false);
3030
+ setSelfDeclaredAdult(true);
2218
3031
  }, []);
2219
3032
  const handleAgeVerificationNo = (0, import_react2.useCallback)(() => {
2220
3033
  setShowAgeVerification(false);
2221
3034
  setShowGuardianForm(true);
2222
3035
  }, []);
3036
+ const handleVerificationContinue = (0, import_react2.useCallback)(() => {
3037
+ setShowVerificationScreen(false);
3038
+ setIsVerificationComplete(false);
3039
+ }, []);
3040
+ const handlePopupClosed = (0, import_react2.useCallback)(async () => {
3041
+ var _a2, _b2;
3042
+ if (popupCheckIntervalRef.current) {
3043
+ clearInterval(popupCheckIntervalRef.current);
3044
+ popupCheckIntervalRef.current = null;
3045
+ }
3046
+ if (statusPollIntervalRef.current) {
3047
+ clearTimeout(statusPollIntervalRef.current);
3048
+ statusPollIntervalRef.current = null;
3049
+ }
3050
+ (_a2 = pollingCleanupRef.current) == null ? void 0 : _a2.call(pollingCleanupRef);
3051
+ pollingCleanupRef.current = null;
3052
+ verificationPopupRef.current = null;
3053
+ if (!isPollingStatusRef.current) return;
3054
+ const sessionToken = verificationSessionTokenRef.current;
3055
+ if (sessionToken) {
3056
+ try {
3057
+ const response = await verifyGuardianStatus({
3058
+ accessToken,
3059
+ baseUrl,
3060
+ sessionToken
3061
+ });
3062
+ if (response.status === "verified") {
3063
+ const ref = response.verification_reference || ((_b2 = response.guardian_details) == null ? void 0 : _b2.verification_reference);
3064
+ if (ref) {
3065
+ setVerificationReference(ref);
3066
+ setIsVerificationComplete(true);
3067
+ setIsPollingStatus(false);
3068
+ isPollingStatusRef.current = false;
3069
+ setIsInitiatingVerification(false);
3070
+ clearConsentState();
3071
+ return;
3072
+ }
3073
+ }
3074
+ } catch {
3075
+ }
3076
+ }
3077
+ setVerificationError(
3078
+ "Verification window was closed. Please try again."
3079
+ );
3080
+ setIsPollingStatus(false);
3081
+ isPollingStatusRef.current = false;
3082
+ setIsInitiatingVerification(false);
3083
+ }, [accessToken, baseUrl]);
3084
+ const startPopupMonitoring = (0, import_react2.useCallback)((popup) => {
3085
+ popupCheckIntervalRef.current = window.setInterval(() => {
3086
+ if (popup.closed) {
3087
+ handlePopupClosed();
3088
+ }
3089
+ }, 500);
3090
+ }, [handlePopupClosed]);
3091
+ const getVerificationErrorMessage = (0, import_react2.useCallback)(
3092
+ (errorCode, fallbackError) => {
3093
+ switch (errorCode) {
3094
+ case "GUARDIAN_UNDER_18":
3095
+ return "The guardian must be 18 years or older. Please provide details of a different guardian.";
3096
+ case "TOKEN_FAILED":
3097
+ return "DigiLocker verification could not be completed. Please try again.";
3098
+ case "DIGILOCKER_AUTH_FAILED":
3099
+ return "DigiLocker authorization was cancelled or failed. Please try again.";
3100
+ case "NO_AUTH_CODE":
3101
+ return "DigiLocker did not return an authorization code. Please try again.";
3102
+ case "SESSION_EXPIRED":
3103
+ return "Verification session has expired. Please try again.";
3104
+ default:
3105
+ return fallbackError || "Verification failed. Please try again.";
3106
+ }
3107
+ },
3108
+ []
3109
+ );
3110
+ const startStatusPolling = (0, import_react2.useCallback)(
3111
+ (token) => {
3112
+ verificationSessionTokenRef.current = token;
3113
+ setIsPollingStatus(true);
3114
+ isPollingStatusRef.current = true;
3115
+ setVerificationError(null);
3116
+ setVerificationErrorCode(null);
3117
+ setCanRetryVerification(false);
3118
+ let attempts = 0;
3119
+ let consecutiveErrors = 0;
3120
+ let paused = false;
3121
+ const MAX_ATTEMPTS = 40;
3122
+ const MAX_CONSECUTIVE_ERRORS = 3;
3123
+ const POLL_INTERVAL = 3e3;
3124
+ const stopPolling = () => {
3125
+ var _a2;
3126
+ if (statusPollIntervalRef.current) {
3127
+ clearTimeout(statusPollIntervalRef.current);
3128
+ statusPollIntervalRef.current = null;
3129
+ }
3130
+ if (popupCheckIntervalRef.current) {
3131
+ clearInterval(popupCheckIntervalRef.current);
3132
+ popupCheckIntervalRef.current = null;
3133
+ }
3134
+ (_a2 = verificationPopupRef.current) == null ? void 0 : _a2.close();
3135
+ verificationPopupRef.current = null;
3136
+ document.removeEventListener("visibilitychange", onVisibilityChange);
3137
+ pollingCleanupRef.current = null;
3138
+ setIsPollingStatus(false);
3139
+ isPollingStatusRef.current = false;
3140
+ setIsInitiatingVerification(false);
3141
+ };
3142
+ const setError = (message, errorCode, canRetry) => {
3143
+ stopPolling();
3144
+ setVerificationError(message);
3145
+ setVerificationErrorCode(errorCode);
3146
+ setCanRetryVerification(canRetry);
3147
+ };
3148
+ const poll = async () => {
3149
+ var _a2;
3150
+ if (paused) return;
3151
+ attempts++;
3152
+ if (attempts > MAX_ATTEMPTS) {
3153
+ setError(
3154
+ "Verification is taking longer than expected. Please try again.",
3155
+ "SESSION_EXPIRED",
3156
+ true
3157
+ );
3158
+ return;
3159
+ }
3160
+ try {
3161
+ const response = await verifyGuardianStatus({
3162
+ accessToken,
3163
+ baseUrl,
3164
+ sessionToken: token
3165
+ });
3166
+ consecutiveErrors = 0;
3167
+ if (response.status === "verified") {
3168
+ stopPolling();
3169
+ const ref = response.verification_reference || ((_a2 = response.guardian_details) == null ? void 0 : _a2.verification_reference);
3170
+ if (!ref) {
3171
+ setError(
3172
+ "Verification completed but no reference received. Please try again.",
3173
+ null,
3174
+ true
3175
+ );
3176
+ return;
3177
+ }
3178
+ setVerificationReference(ref);
3179
+ setIsVerificationComplete(true);
3180
+ clearConsentState();
3181
+ return;
3182
+ }
3183
+ if (response.status === "failed" || response.status === "expired") {
3184
+ setError(
3185
+ getVerificationErrorMessage(response.error_code, response.error),
3186
+ response.error_code,
3187
+ response.can_retry
3188
+ );
3189
+ return;
3190
+ }
3191
+ if (!paused) {
3192
+ statusPollIntervalRef.current = window.setTimeout(poll, POLL_INTERVAL);
3193
+ }
3194
+ } catch (error) {
3195
+ const err = error;
3196
+ if (err.status === 404 || err.status === 410) {
3197
+ setError(
3198
+ "Verification session not found or expired. Please try again.",
3199
+ "SESSION_EXPIRED",
3200
+ true
3201
+ );
3202
+ return;
3203
+ }
3204
+ consecutiveErrors++;
3205
+ if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
3206
+ setError(
3207
+ err.message || "Unable to check verification status. Please try again.",
3208
+ null,
3209
+ true
3210
+ );
3211
+ return;
3212
+ }
3213
+ statusPollIntervalRef.current = window.setTimeout(poll, POLL_INTERVAL);
3214
+ }
3215
+ };
3216
+ const onVisibilityChange = () => {
3217
+ if (document.hidden) {
3218
+ paused = true;
3219
+ if (statusPollIntervalRef.current) {
3220
+ clearTimeout(statusPollIntervalRef.current);
3221
+ statusPollIntervalRef.current = null;
3222
+ }
3223
+ } else {
3224
+ paused = false;
3225
+ poll();
3226
+ }
3227
+ };
3228
+ document.addEventListener("visibilitychange", onVisibilityChange);
3229
+ pollingCleanupRef.current = () => {
3230
+ document.removeEventListener("visibilitychange", onVisibilityChange);
3231
+ };
3232
+ statusPollIntervalRef.current = window.setTimeout(poll, POLL_INTERVAL);
3233
+ },
3234
+ [accessToken, baseUrl, getVerificationErrorMessage]
3235
+ );
3236
+ const handleBackToGuardianForm = (0, import_react2.useCallback)((clearName = false) => {
3237
+ var _a2, _b2;
3238
+ (_a2 = verificationPopupRef.current) == null ? void 0 : _a2.close();
3239
+ verificationPopupRef.current = null;
3240
+ if (popupCheckIntervalRef.current) {
3241
+ clearInterval(popupCheckIntervalRef.current);
3242
+ popupCheckIntervalRef.current = null;
3243
+ }
3244
+ if (statusPollIntervalRef.current) {
3245
+ clearTimeout(statusPollIntervalRef.current);
3246
+ statusPollIntervalRef.current = null;
3247
+ }
3248
+ if (autoTransitionTimerRef.current) {
3249
+ clearTimeout(autoTransitionTimerRef.current);
3250
+ autoTransitionTimerRef.current = null;
3251
+ }
3252
+ if (digilockerRedirectTimeoutRef.current) {
3253
+ clearTimeout(digilockerRedirectTimeoutRef.current);
3254
+ digilockerRedirectTimeoutRef.current = null;
3255
+ }
3256
+ (_b2 = pollingCleanupRef.current) == null ? void 0 : _b2.call(pollingCleanupRef);
3257
+ pollingCleanupRef.current = null;
3258
+ setShowVerificationScreen(false);
3259
+ setShowGuardianForm(true);
3260
+ setVerificationError(null);
3261
+ setVerificationErrorCode(null);
3262
+ setCanRetryVerification(false);
3263
+ setIsInitiatingVerification(false);
3264
+ setIsPollingStatus(false);
3265
+ isPollingStatusRef.current = false;
3266
+ setIsVerificationComplete(false);
3267
+ if (clearName) {
3268
+ setGuardianFormData((prev) => ({ ...prev, guardianName: "" }));
3269
+ }
3270
+ clearConsentState();
3271
+ }, []);
2223
3272
  const availableLanguages = (0, import_react2.useMemo)(() => {
2224
3273
  if (!content) return [language];
2225
3274
  const languages = ["English"];
@@ -2629,12 +3678,67 @@ var RedactoNoticeConsent = ({
2629
3678
  }, [content, stopAudio]);
2630
3679
  (0, import_react2.useEffect)(() => {
2631
3680
  return () => {
3681
+ var _a2, _b2;
2632
3682
  if (abortControllerRef.current) {
2633
3683
  abortControllerRef.current.abort();
2634
3684
  }
2635
3685
  stopAudio();
3686
+ if (popupCheckIntervalRef.current) {
3687
+ clearInterval(popupCheckIntervalRef.current);
3688
+ }
3689
+ if (statusPollIntervalRef.current) {
3690
+ clearTimeout(statusPollIntervalRef.current);
3691
+ }
3692
+ (_a2 = pollingCleanupRef.current) == null ? void 0 : _a2.call(pollingCleanupRef);
3693
+ pollingCleanupRef.current = null;
3694
+ (_b2 = verificationPopupRef.current) == null ? void 0 : _b2.close();
3695
+ if (autoTransitionTimerRef.current) {
3696
+ clearTimeout(autoTransitionTimerRef.current);
3697
+ autoTransitionTimerRef.current = null;
3698
+ }
3699
+ if (digilockerRedirectTimeoutRef.current) {
3700
+ clearTimeout(digilockerRedirectTimeoutRef.current);
3701
+ digilockerRedirectTimeoutRef.current = null;
3702
+ }
2636
3703
  };
2637
3704
  }, [stopAudio]);
3705
+ (0, import_react2.useEffect)(() => {
3706
+ const persistedState = getConsentState();
3707
+ if (!persistedState) return;
3708
+ if (persistedState.noticeId && persistedState.noticeId !== noticeId) {
3709
+ clearConsentState();
3710
+ return;
3711
+ }
3712
+ const REDIRECT_STATE_MAX_AGE_MS = 15 * 60 * 1e3;
3713
+ if (persistedState.timestamp) {
3714
+ const stateAge = Date.now() - new Date(persistedState.timestamp).getTime();
3715
+ if (stateAge > REDIRECT_STATE_MAX_AGE_MS) {
3716
+ clearConsentState();
3717
+ return;
3718
+ }
3719
+ }
3720
+ if (persistedState.flowState === "digilocker-pending" && persistedState.sessionToken) {
3721
+ setShowVerificationScreen(true);
3722
+ setShowGuardianForm(false);
3723
+ setShowAgeVerification(false);
3724
+ startStatusPolling(persistedState.sessionToken);
3725
+ } else if (persistedState.verificationResult === "verified" && persistedState.verificationReference) {
3726
+ setVerificationReference(persistedState.verificationReference);
3727
+ setShowVerificationScreen(true);
3728
+ setIsVerificationComplete(true);
3729
+ setShowGuardianForm(false);
3730
+ setShowAgeVerification(false);
3731
+ clearConsentState();
3732
+ } else if (persistedState.verificationResult === "failed" || persistedState.verificationResult === "expired") {
3733
+ setShowVerificationScreen(true);
3734
+ setShowGuardianForm(false);
3735
+ setShowAgeVerification(false);
3736
+ setVerificationError(
3737
+ persistedState.verificationResult === "expired" ? "Verification session has expired. Please try again." : "Verification failed. Please try again."
3738
+ );
3739
+ clearConsentState();
3740
+ }
3741
+ }, [startStatusPolling, noticeId]);
2638
3742
  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: [
2639
3743
  blockUI && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2640
3744
  "div",
@@ -2820,6 +3924,27 @@ var RedactoNoticeConsent = ({
2820
3924
  declineButtonStyle,
2821
3925
  logoUrl: (content == null ? void 0 : content.logo_url) || redacto_logo_default
2822
3926
  }
3927
+ ) }) : showVerificationScreen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...styles.content, ...responsiveStyles.content }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3928
+ VerificationScreen,
3929
+ {
3930
+ onBack: handleBackToGuardianForm,
3931
+ onContinue: handleVerificationContinue,
3932
+ isInitiating: isInitiatingVerification,
3933
+ isPolling: isPollingStatus,
3934
+ isVerificationComplete,
3935
+ isAutoTransitioning,
3936
+ error: verificationError,
3937
+ errorCode: verificationErrorCode,
3938
+ canRetry: canRetryVerification,
3939
+ styles,
3940
+ responsiveStyles,
3941
+ settings,
3942
+ componentHeadingStyle,
3943
+ componentTextStyle,
3944
+ acceptButtonStyle,
3945
+ declineButtonStyle,
3946
+ logoUrl: (content == null ? void 0 : content.logo_url) || redacto_logo_default
3947
+ }
2823
3948
  ) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { ...styles.content, ...responsiveStyles.content }, children: [
2824
3949
  errorMessage && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2825
3950
  "div",
@@ -4423,8 +5548,425 @@ var RedactoConsentInlineComponent = ({
4423
5548
  };
4424
5549
  RedactoConsentInlineComponent.displayName = "RedactoConsentInline";
4425
5550
  var RedactoConsentInline = (0, import_react4.memo)(RedactoConsentInlineComponent);
5551
+
5552
+ // src/RedactoNoticeConsent/components/DigiLockerCallback.tsx
5553
+ var import_react5 = require("react");
5554
+ var import_jsx_runtime3 = require("react/jsx-runtime");
5555
+ var DigiLockerCallback = ({
5556
+ accessToken: propAccessToken,
5557
+ baseUrl,
5558
+ onSuccess,
5559
+ onError
5560
+ }) => {
5561
+ const [status, setStatus] = (0, import_react5.useState)("processing");
5562
+ const [message, setMessage] = (0, import_react5.useState)("Verifying your guardian...");
5563
+ const [errorDetail, setErrorDetail] = (0, import_react5.useState)(null);
5564
+ const retryTimeoutRef = (0, import_react5.useRef)(null);
5565
+ const redirectTimeoutRef = (0, import_react5.useRef)(null);
5566
+ const isMountedRef = (0, import_react5.useRef)(true);
5567
+ const getErrorMessage = (errorCode) => {
5568
+ switch (errorCode) {
5569
+ case "GUARDIAN_UNDER_18":
5570
+ return "Guardian verification failed: The guardian must be 18 years or older.";
5571
+ case "SESSION_EXPIRED":
5572
+ return "Verification session has expired. Please restart the verification process.";
5573
+ case "TOKEN_FAILED":
5574
+ return "DigiLocker authentication failed. Please try again.";
5575
+ case "DIGILOCKER_AUTH_FAILED":
5576
+ return "DigiLocker authorization was cancelled or failed. Please try again.";
5577
+ case "NO_AUTH_CODE":
5578
+ return "Missing authorization code from DigiLocker. Please try again.";
5579
+ default:
5580
+ return "Verification failed. Please try again.";
5581
+ }
5582
+ };
5583
+ const redirectToOriginalPage = (0, import_react5.useCallback)(
5584
+ (originalUrl, verificationResult, verificationReference) => {
5585
+ updateConsentState({
5586
+ verificationResult,
5587
+ verificationReference,
5588
+ flowState: verificationResult === "verified" ? "consent-form" : "error"
5589
+ });
5590
+ redirectTimeoutRef.current = setTimeout(() => {
5591
+ window.location.href = originalUrl;
5592
+ }, 1500);
5593
+ },
5594
+ []
5595
+ );
5596
+ const handleCallback = (0, import_react5.useCallback)(async () => {
5597
+ var _a, _b;
5598
+ try {
5599
+ const params = new URLSearchParams(window.location.search);
5600
+ const sessionToken = params.get("session_token");
5601
+ const urlStatus = params.get("status");
5602
+ const verificationReference = params.get("verification_reference");
5603
+ const errorCode = params.get("error_code");
5604
+ const persistedState = getConsentState();
5605
+ const originalUrl = (persistedState == null ? void 0 : persistedState.originalUrl) || "/";
5606
+ const storedBaseUrl = persistedState == null ? void 0 : persistedState.baseUrl;
5607
+ const storedSessionToken = persistedState == null ? void 0 : persistedState.sessionToken;
5608
+ const accessToken = propAccessToken;
5609
+ if (!accessToken) {
5610
+ throw new Error("Access token not provided. Please pass accessToken as a prop.");
5611
+ }
5612
+ const activeSessionToken = sessionToken || storedSessionToken;
5613
+ if (!activeSessionToken) {
5614
+ throw new Error("Session token not found. Please restart the verification process.");
5615
+ }
5616
+ let isInIframe = false;
5617
+ try {
5618
+ isInIframe = window.self !== window.top;
5619
+ } catch {
5620
+ isInIframe = true;
5621
+ }
5622
+ if (urlStatus === "verified" && verificationReference) {
5623
+ setStatus("success");
5624
+ setMessage("Guardian verified successfully! Redirecting...");
5625
+ onSuccess == null ? void 0 : onSuccess(verificationReference);
5626
+ if (isInIframe && window.parent) {
5627
+ window.parent.postMessage(
5628
+ {
5629
+ type: "DIGILOCKER_VERIFICATION_STATUS",
5630
+ status: "verified",
5631
+ verificationReference
5632
+ },
5633
+ window.location.origin
5634
+ );
5635
+ return;
5636
+ }
5637
+ redirectToOriginalPage(originalUrl, "verified", verificationReference);
5638
+ return;
5639
+ }
5640
+ if (urlStatus === "failed" || urlStatus === "expired") {
5641
+ const urlErrorCode = errorCode != null ? errorCode : void 0;
5642
+ const errorMessage = getErrorMessage(urlErrorCode);
5643
+ setStatus("error");
5644
+ setMessage("Verification failed");
5645
+ setErrorDetail(errorMessage);
5646
+ onError == null ? void 0 : onError(new Error(errorMessage), urlErrorCode);
5647
+ if (isInIframe && window.parent) {
5648
+ window.parent.postMessage(
5649
+ {
5650
+ type: "DIGILOCKER_VERIFICATION_STATUS",
5651
+ status: urlStatus,
5652
+ errorCode: urlErrorCode,
5653
+ error: errorMessage
5654
+ },
5655
+ window.location.origin
5656
+ );
5657
+ return;
5658
+ }
5659
+ redirectToOriginalPage(originalUrl, urlStatus);
5660
+ return;
5661
+ }
5662
+ setMessage("Processing verification...");
5663
+ const apiBaseUrl = baseUrl || storedBaseUrl;
5664
+ const response = await verifyGuardianStatus({
5665
+ accessToken,
5666
+ baseUrl: apiBaseUrl,
5667
+ sessionToken: activeSessionToken
5668
+ });
5669
+ if (!isMountedRef.current) return;
5670
+ if (response.status === "verified") {
5671
+ const verificationRef = response.verification_reference || ((_a = response.guardian_details) == null ? void 0 : _a.verification_reference) || void 0;
5672
+ if (!verificationRef) {
5673
+ setStatus("error");
5674
+ setMessage("Verification failed");
5675
+ setErrorDetail("Verification completed but no reference received. Please try again.");
5676
+ onError == null ? void 0 : onError(new Error("Verification completed but no reference received."));
5677
+ redirectToOriginalPage(originalUrl, "failed");
5678
+ return;
5679
+ }
5680
+ setStatus("success");
5681
+ setMessage("Guardian verified successfully! Redirecting...");
5682
+ onSuccess == null ? void 0 : onSuccess(verificationRef);
5683
+ if (isInIframe && window.parent) {
5684
+ window.parent.postMessage(
5685
+ {
5686
+ type: "DIGILOCKER_VERIFICATION_STATUS",
5687
+ status: "verified",
5688
+ verificationReference: verificationRef
5689
+ },
5690
+ window.location.origin
5691
+ );
5692
+ return;
5693
+ }
5694
+ redirectToOriginalPage(originalUrl, "verified", verificationRef);
5695
+ } else if (response.status === "failed" || response.status === "expired") {
5696
+ const responseErrorCode = (_b = response.error_code) != null ? _b : void 0;
5697
+ const errorMessage = response.error || getErrorMessage(responseErrorCode);
5698
+ setStatus("error");
5699
+ setMessage("Verification failed");
5700
+ setErrorDetail(errorMessage);
5701
+ onError == null ? void 0 : onError(new Error(errorMessage), responseErrorCode);
5702
+ if (isInIframe && window.parent) {
5703
+ window.parent.postMessage(
5704
+ {
5705
+ type: "DIGILOCKER_VERIFICATION_STATUS",
5706
+ status: response.status,
5707
+ errorCode: responseErrorCode,
5708
+ error: errorMessage
5709
+ },
5710
+ window.location.origin
5711
+ );
5712
+ return;
5713
+ }
5714
+ redirectToOriginalPage(originalUrl, response.status);
5715
+ } else {
5716
+ setMessage("Verification is still in progress. Please wait...");
5717
+ const MAX_RETRIES = 3;
5718
+ let retryCount = 0;
5719
+ const attemptRetry = () => {
5720
+ retryCount++;
5721
+ const delay = 3e3 * Math.pow(2, retryCount - 1);
5722
+ retryTimeoutRef.current = setTimeout(async () => {
5723
+ var _a2, _b2;
5724
+ if (!isMountedRef.current) return;
5725
+ try {
5726
+ const retryResponse = await verifyGuardianStatus({
5727
+ accessToken,
5728
+ baseUrl: apiBaseUrl,
5729
+ sessionToken: activeSessionToken
5730
+ });
5731
+ if (!isMountedRef.current) return;
5732
+ const retryRef = retryResponse.verification_reference || ((_a2 = retryResponse.guardian_details) == null ? void 0 : _a2.verification_reference) || void 0;
5733
+ if (retryResponse.status === "verified" && retryRef) {
5734
+ setStatus("success");
5735
+ setMessage("Guardian verified successfully! Redirecting...");
5736
+ onSuccess == null ? void 0 : onSuccess(retryRef);
5737
+ if (isInIframe && window.parent) {
5738
+ window.parent.postMessage(
5739
+ {
5740
+ type: "DIGILOCKER_VERIFICATION_STATUS",
5741
+ status: "verified",
5742
+ verificationReference: retryRef
5743
+ },
5744
+ window.location.origin
5745
+ );
5746
+ return;
5747
+ }
5748
+ redirectToOriginalPage(originalUrl, "verified", retryRef);
5749
+ } else if (retryResponse.status === "failed" || retryResponse.status === "expired") {
5750
+ const retryErrorCode = (_b2 = retryResponse.error_code) != null ? _b2 : void 0;
5751
+ const retryErrorMessage = retryResponse.error || getErrorMessage(retryErrorCode) || "Verification could not be completed. Please try again.";
5752
+ setStatus("error");
5753
+ setMessage("Verification failed");
5754
+ setErrorDetail(retryErrorMessage);
5755
+ onError == null ? void 0 : onError(new Error(retryErrorMessage), retryErrorCode);
5756
+ if (isInIframe && window.parent) {
5757
+ window.parent.postMessage(
5758
+ {
5759
+ type: "DIGILOCKER_VERIFICATION_STATUS",
5760
+ status: retryResponse.status,
5761
+ errorCode: retryErrorCode,
5762
+ error: retryErrorMessage
5763
+ },
5764
+ window.location.origin
5765
+ );
5766
+ return;
5767
+ }
5768
+ redirectToOriginalPage(originalUrl, retryResponse.status);
5769
+ } else if (retryCount < MAX_RETRIES) {
5770
+ setMessage("Verification taking longer than expected. Please wait...");
5771
+ attemptRetry();
5772
+ } else {
5773
+ const timeoutMessage = "Verification could not be completed in time. Please try again.";
5774
+ setStatus("error");
5775
+ setMessage("Verification failed");
5776
+ setErrorDetail(timeoutMessage);
5777
+ onError == null ? void 0 : onError(new Error(timeoutMessage));
5778
+ redirectToOriginalPage(originalUrl, "failed");
5779
+ }
5780
+ } catch (retryErr) {
5781
+ if (!isMountedRef.current) return;
5782
+ if (retryCount < MAX_RETRIES) {
5783
+ attemptRetry();
5784
+ } else {
5785
+ setStatus("error");
5786
+ setMessage("Verification failed");
5787
+ setErrorDetail(retryErr.message);
5788
+ onError == null ? void 0 : onError(retryErr);
5789
+ redirectToOriginalPage(originalUrl, "failed");
5790
+ }
5791
+ }
5792
+ }, delay);
5793
+ };
5794
+ attemptRetry();
5795
+ }
5796
+ } catch (error) {
5797
+ console.error("DigiLocker callback error:", error);
5798
+ setStatus("error");
5799
+ const err = error;
5800
+ setMessage("Verification failed");
5801
+ setErrorDetail(err.message || "An error occurred during verification");
5802
+ onError == null ? void 0 : onError(err);
5803
+ const persistedState = getConsentState();
5804
+ if (persistedState == null ? void 0 : persistedState.originalUrl) {
5805
+ redirectTimeoutRef.current = setTimeout(() => {
5806
+ redirectToOriginalPage(persistedState.originalUrl, "failed");
5807
+ }, 3e3);
5808
+ }
5809
+ }
5810
+ }, [propAccessToken, baseUrl, onSuccess, onError, redirectToOriginalPage]);
5811
+ (0, import_react5.useEffect)(() => {
5812
+ injectCheckboxStyles();
5813
+ handleCallback();
5814
+ return () => {
5815
+ isMountedRef.current = false;
5816
+ if (retryTimeoutRef.current) clearTimeout(retryTimeoutRef.current);
5817
+ if (redirectTimeoutRef.current) clearTimeout(redirectTimeoutRef.current);
5818
+ };
5819
+ }, [handleCallback]);
5820
+ const handleRetry = (0, import_react5.useCallback)(() => {
5821
+ const persistedState = getConsentState();
5822
+ if (persistedState == null ? void 0 : persistedState.originalUrl) {
5823
+ clearConsentState();
5824
+ window.location.href = persistedState.originalUrl;
5825
+ } else {
5826
+ window.location.href = "/";
5827
+ }
5828
+ }, []);
5829
+ const containerStyle = {
5830
+ display: "flex",
5831
+ alignItems: "center",
5832
+ justifyContent: "center",
5833
+ minHeight: "100vh",
5834
+ backgroundColor: "#f9fafb",
5835
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
5836
+ padding: "20px",
5837
+ boxSizing: "border-box"
5838
+ };
5839
+ const cardStyle = {
5840
+ backgroundColor: "#ffffff",
5841
+ borderRadius: "12px",
5842
+ boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
5843
+ padding: "40px",
5844
+ textAlign: "center",
5845
+ maxWidth: "400px",
5846
+ width: "100%"
5847
+ };
5848
+ const spinnerStyle = {
5849
+ width: "48px",
5850
+ height: "48px",
5851
+ border: "4px solid #e5e7eb",
5852
+ borderTopColor: "#3b82f6",
5853
+ borderRadius: "50%",
5854
+ animation: "spin 1s linear infinite",
5855
+ margin: "0 auto 24px"
5856
+ };
5857
+ const iconStyle = {
5858
+ width: "64px",
5859
+ height: "64px",
5860
+ borderRadius: "50%",
5861
+ display: "flex",
5862
+ alignItems: "center",
5863
+ justifyContent: "center",
5864
+ margin: "0 auto 24px"
5865
+ };
5866
+ const successIconStyle = {
5867
+ ...iconStyle,
5868
+ backgroundColor: "#d1fae5",
5869
+ color: "#059669"
5870
+ };
5871
+ const errorIconStyle = {
5872
+ ...iconStyle,
5873
+ backgroundColor: "#fee2e2",
5874
+ color: "#dc2626"
5875
+ };
5876
+ const titleStyle = {
5877
+ fontSize: "20px",
5878
+ fontWeight: 600,
5879
+ color: "#111827",
5880
+ margin: "0 0 12px 0"
5881
+ };
5882
+ const messageStyle = {
5883
+ fontSize: "14px",
5884
+ color: "#6b7280",
5885
+ marginBottom: "8px",
5886
+ lineHeight: 1.5
5887
+ };
5888
+ const errorDetailStyle = {
5889
+ fontSize: "13px",
5890
+ color: "#dc2626",
5891
+ marginTop: "12px",
5892
+ padding: "12px",
5893
+ backgroundColor: "#fef2f2",
5894
+ borderRadius: "6px",
5895
+ border: "1px solid #fecaca"
5896
+ };
5897
+ const buttonStyle = {
5898
+ marginTop: "20px",
5899
+ padding: "10px 24px",
5900
+ backgroundColor: "#3b82f6",
5901
+ color: "#ffffff",
5902
+ border: "none",
5903
+ borderRadius: "6px",
5904
+ fontSize: "14px",
5905
+ fontWeight: 500,
5906
+ cursor: "pointer",
5907
+ transition: "background-color 0.2s"
5908
+ };
5909
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: containerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: cardStyle, children: [
5910
+ status === "processing" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
5911
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: spinnerStyle }),
5912
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { style: titleStyle, children: "Processing Verification" }),
5913
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: messageStyle, children: message }),
5914
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: { ...messageStyle, fontSize: "12px", color: "#9ca3af" }, children: "Please wait while we verify your guardian's identity..." })
5915
+ ] }),
5916
+ status === "success" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
5917
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: successIconStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5918
+ "svg",
5919
+ {
5920
+ width: "32",
5921
+ height: "32",
5922
+ viewBox: "0 0 24 24",
5923
+ fill: "none",
5924
+ stroke: "currentColor",
5925
+ strokeWidth: "2",
5926
+ strokeLinecap: "round",
5927
+ strokeLinejoin: "round",
5928
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "20 6 9 17 4 12" })
5929
+ }
5930
+ ) }),
5931
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { style: titleStyle, children: "Verification Successful" }),
5932
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: messageStyle, children: message })
5933
+ ] }),
5934
+ status === "error" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
5935
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: errorIconStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5936
+ "svg",
5937
+ {
5938
+ width: "32",
5939
+ height: "32",
5940
+ viewBox: "0 0 24 24",
5941
+ fill: "none",
5942
+ stroke: "currentColor",
5943
+ strokeWidth: "2",
5944
+ strokeLinecap: "round",
5945
+ strokeLinejoin: "round",
5946
+ children: [
5947
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
5948
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
5949
+ ]
5950
+ }
5951
+ ) }),
5952
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { style: titleStyle, children: "Verification Failed" }),
5953
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: messageStyle, children: message }),
5954
+ errorDetail && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: errorDetailStyle, children: errorDetail }),
5955
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5956
+ "button",
5957
+ {
5958
+ className: "redacto-dl-retry-btn",
5959
+ style: buttonStyle,
5960
+ onClick: handleRetry,
5961
+ children: "Try Again"
5962
+ }
5963
+ )
5964
+ ] })
5965
+ ] }) });
5966
+ };
4426
5967
  // Annotate the CommonJS export names for ESM import in node:
4427
5968
  0 && (module.exports = {
5969
+ DigiLockerCallback,
4428
5970
  RedactoNoticeConsent,
4429
5971
  RedactoNoticeConsentInline
4430
5972
  });