@redacto.io/consent-sdk-react 3.4.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
  });
@@ -30,21 +31,6 @@ var import_react2 = require("react");
30
31
 
31
32
  // src/RedactoNoticeConsent/api/index.ts
32
33
  var import_jwt_decode = require("jwt-decode");
33
-
34
- // src/utils/isLegacyApi.ts
35
- function isLegacyApi(baseUrl) {
36
- try {
37
- return new URL(baseUrl).hostname.endsWith(".redacto.io");
38
- } catch {
39
- try {
40
- return new URL(`https://${baseUrl}`).hostname.endsWith(".redacto.io");
41
- } catch {
42
- return false;
43
- }
44
- }
45
- }
46
-
47
- // src/RedactoNoticeConsent/api/index.ts
48
34
  var BASE_URL = "https://api.redacto.io/consent";
49
35
  var apiCache = /* @__PURE__ */ new Map();
50
36
  var CACHE_TTL = 5 * 60 * 1e3;
@@ -81,73 +67,66 @@ var fetchConsentContent = async ({
81
67
  validate_against = "all",
82
68
  include_fully_consented_data = false,
83
69
  signal
84
- // AbortSignal for request cancellation
85
70
  }) => {
86
- try {
87
- if (!noticeId || !accessToken || !validate_against) {
88
- throw new Error("noticeId and accessToken are required");
89
- }
90
- const decodedToken = decodeTokenSafely(accessToken);
91
- if (!decodedToken) {
92
- throw new Error("Invalid access token");
93
- }
94
- const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
95
- if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
96
- throw new Error("Invalid token: missing organization or workspace UUID");
97
- }
98
- const apiBaseUrl = baseUrl || BASE_URL;
99
- const cacheKey = `${accessToken}-${noticeId}-${validate_against}-${language}-${specific_uuid || ""}-${include_fully_consented_data}`;
100
- if (validate_against === "all" && !specific_uuid) {
101
- const cachedData = getCachedData(cacheKey);
102
- if (cachedData) {
103
- return cachedData;
104
- }
105
- }
106
- const url = new URL(
107
- `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/notices/${noticeId}`
108
- );
109
- if (specific_uuid) {
110
- url.searchParams.append("specific_uuid", specific_uuid);
111
- }
112
- url.searchParams.append("validate_against", validate_against);
113
- if (include_fully_consented_data) {
114
- url.searchParams.append("include_fully_consented_data", "true");
115
- }
116
- const response = await fetch(url.toString(), {
117
- method: "GET",
118
- headers: {
119
- Authorization: `Bearer ${accessToken}`,
120
- "Accept-Language": language,
121
- "Content-Type": "application/json"
122
- },
123
- signal
124
- // Add abort signal for request cancellation
125
- });
126
- if (!response.ok) {
127
- let errorMessage = `Failed to fetch consent content: ${response.status} ${response.statusText}`;
128
- if (response.status === 401) {
129
- errorMessage = "Unauthorized: Invalid or expired token";
130
- } else if (response.status === 403) {
131
- errorMessage = "Forbidden: Access denied";
132
- } else if (response.status === 404) {
133
- errorMessage = "Notice not found";
134
- } else if (response.status === 409) {
135
- errorMessage = "User has already provided consent";
136
- } else if (response.status >= 500) {
137
- errorMessage = "Server error: Please try again later";
138
- }
139
- const error = new Error(errorMessage);
140
- error.status = response.status;
141
- throw error;
71
+ if (!noticeId || !accessToken || !validate_against) {
72
+ throw new Error("noticeId and accessToken are required");
73
+ }
74
+ const decodedToken = decodeTokenSafely(accessToken);
75
+ if (!decodedToken) {
76
+ throw new Error("Invalid access token");
77
+ }
78
+ const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
79
+ if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
80
+ throw new Error("Invalid token: missing organization or workspace UUID");
81
+ }
82
+ const apiBaseUrl = baseUrl || BASE_URL;
83
+ const cacheKey = `${accessToken}-${noticeId}-${validate_against}-${language}-${specific_uuid || ""}-${include_fully_consented_data}`;
84
+ if (validate_against === "all" && !specific_uuid) {
85
+ const cachedData = getCachedData(cacheKey);
86
+ if (cachedData) {
87
+ return cachedData;
142
88
  }
143
- const data = await response.json();
144
- if (validate_against === "all" && !specific_uuid) {
145
- setCachedData(cacheKey, data);
89
+ }
90
+ const url = new URL(
91
+ `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/notices/${noticeId}`
92
+ );
93
+ if (specific_uuid) {
94
+ url.searchParams.append("specific_uuid", specific_uuid);
95
+ }
96
+ url.searchParams.append("validate_against", validate_against);
97
+ if (include_fully_consented_data) {
98
+ url.searchParams.append("include_fully_consented_data", "true");
99
+ }
100
+ const response = await fetch(url.toString(), {
101
+ method: "GET",
102
+ headers: {
103
+ Authorization: `Bearer ${accessToken}`,
104
+ "Accept-Language": language
105
+ },
106
+ signal
107
+ });
108
+ if (!response.ok) {
109
+ let errorMessage = `Failed to fetch consent content: ${response.status} ${response.statusText}`;
110
+ if (response.status === 401) {
111
+ errorMessage = "Unauthorized: Invalid or expired token";
112
+ } else if (response.status === 403) {
113
+ errorMessage = "Forbidden: Access denied";
114
+ } else if (response.status === 404) {
115
+ errorMessage = "Notice not found";
116
+ } else if (response.status === 409) {
117
+ errorMessage = "User has already provided consent";
118
+ } else if (response.status >= 500) {
119
+ errorMessage = "Server error: Please try again later";
146
120
  }
147
- return data;
148
- } catch (error) {
121
+ const error = new Error(errorMessage);
122
+ error.status = response.status;
149
123
  throw error;
150
124
  }
125
+ const data = await response.json();
126
+ if (validate_against === "all" && !specific_uuid) {
127
+ setCachedData(cacheKey, data);
128
+ }
129
+ return data;
151
130
  };
152
131
  var submitConsentEvent = async ({
153
132
  accessToken,
@@ -156,133 +135,151 @@ var submitConsentEvent = async ({
156
135
  purposes,
157
136
  declined,
158
137
  meta_data,
138
+ guardianVerificationReference,
139
+ selfDeclaredAdult,
159
140
  signal
160
- // AbortSignal for request cancellation
161
141
  }) => {
162
- try {
163
- if (!noticeUuid || !accessToken || !Array.isArray(purposes)) {
164
- throw new Error("noticeUuid, accessToken, and purposes array are required");
165
- }
166
- const decodedToken = decodeTokenSafely(accessToken);
167
- if (!decodedToken) {
168
- throw new Error("Invalid access token");
169
- }
170
- const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
171
- if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
172
- throw new Error("Invalid token: missing organization or workspace UUID");
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`);
173
157
  }
174
- const apiBaseUrl = baseUrl || BASE_URL;
175
- let payload;
176
- if (isLegacyApi(apiBaseUrl)) {
177
- const primaryEmail = typeof window !== "undefined" ? localStorage.getItem("userEmail") : null;
178
- payload = {
179
- primary_email: primaryEmail || void 0,
180
- notice_uuid: noticeUuid,
181
- source: "WEB",
182
- declined: Boolean(declined),
183
- consents: {
184
- purposes: purposes.map((purpose) => {
185
- if (!purpose.uuid) {
186
- throw new Error(`Invalid purpose data: missing uuid`);
187
- }
188
- return {
189
- uuid: purpose.uuid,
190
- name: purpose.name,
191
- description: purpose.description || "",
192
- industries: purpose.industries || "",
193
- selected: Boolean(purpose.selected),
194
- data_elements: (purpose.data_elements || []).map((element) => ({
195
- uuid: element.uuid,
196
- name: element.name,
197
- enabled: Boolean(element.enabled),
198
- required: Boolean(element.required),
199
- selected: element.required ? true : Boolean(element.selected)
200
- }))
201
- };
202
- })
203
- },
204
- meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid } : void 0
205
- };
206
- } else {
207
- const validatedPurposes = purposes.map((purpose) => {
208
- if (!purpose.uuid) {
209
- throw new Error(`Invalid purpose data: missing uuid`);
210
- }
211
- const dataElements = (purpose.data_elements || []).filter((element) => element.enabled).map((element) => ({
212
- uuid: element.uuid,
213
- selected: element.required ? true : Boolean(element.selected)
214
- }));
215
- return {
216
- purpose_uuid: purpose.uuid,
217
- selected: Boolean(purpose.selected),
218
- data_elements: dataElements.length > 0 ? dataElements : void 0
219
- };
220
- });
221
- payload = {
222
- notice_uuid: noticeUuid,
223
- purposes: validatedPurposes,
224
- select_all_mandatory: false,
225
- source: "WEB",
226
- declined: Boolean(declined),
227
- meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid } : void 0
228
- };
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
166
+ };
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
229
188
  }
230
- const response = await fetch(
231
- `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/submit-consent`,
232
- {
233
- method: "POST",
234
- headers: {
235
- Authorization: `Bearer ${accessToken}`,
236
- "Content-Type": "application/json"
237
- },
238
- body: JSON.stringify(payload),
239
- signal
240
- // Add abort signal for request cancellation
241
- }
242
- );
243
- if (!response.ok) {
244
- let errorMessage = `Failed to submit consent: ${response.status} ${response.statusText}`;
245
- if (response.status === 400) {
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 {
246
197
  errorMessage = "Invalid request data";
247
- } else if (response.status === 401) {
248
- errorMessage = "Unauthorized: Invalid or expired token";
249
- } else if (response.status === 403) {
250
- errorMessage = "Forbidden: Access denied";
251
- } else if (response.status === 404) {
252
- errorMessage = "Notice not found";
253
- } else if (response.status >= 500) {
254
- errorMessage = "Server error: Please try again later";
255
198
  }
256
- const error = new Error(errorMessage);
257
- error.status = response.status;
258
- 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";
259
207
  }
260
- apiCache.clear();
261
- } catch (error) {
208
+ const error = new Error(errorMessage);
209
+ error.status = response.status;
262
210
  throw error;
263
211
  }
212
+ apiCache.clear();
264
213
  };
265
- var submitGuardianInfo = async ({
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;
255
+ throw error;
256
+ }
257
+ const data = await response.json();
258
+ return data;
259
+ };
260
+ var initiateGuardianVerification = async ({
266
261
  accessToken,
267
262
  baseUrl,
268
263
  guardianName,
269
264
  guardianContact,
270
265
  guardianRelationship,
266
+ frontendCallbackUrl,
267
+ metadata,
271
268
  signal
272
- // AbortSignal for request cancellation
273
269
  }) => {
270
+ var _a;
274
271
  try {
275
272
  if (!accessToken) {
276
273
  throw new Error("accessToken is required");
277
274
  }
278
275
  if (!(guardianName == null ? void 0 : guardianName.trim())) {
279
- throw new Error("guardianName is required and cannot be empty");
276
+ throw new Error("Guardian name is required");
280
277
  }
281
278
  if (!(guardianContact == null ? void 0 : guardianContact.trim())) {
282
- throw new Error("guardianContact is required and cannot be empty");
279
+ throw new Error("Guardian contact is required");
283
280
  }
284
281
  if (!(guardianRelationship == null ? void 0 : guardianRelationship.trim())) {
285
- throw new Error("guardianRelationship is required and cannot be empty");
282
+ throw new Error("Guardian relationship is required");
286
283
  }
287
284
  const decodedToken = decodeTokenSafely(accessToken);
288
285
  if (!decodedToken) {
@@ -292,14 +289,20 @@ var submitGuardianInfo = async ({
292
289
  if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
293
290
  throw new Error("Invalid token: missing organization or workspace UUID");
294
291
  }
292
+ const apiBaseUrl = baseUrl || BASE_URL;
295
293
  const payload = {
296
294
  guardian_name: guardianName.trim(),
297
295
  guardian_contact: guardianContact.trim(),
298
296
  guardian_relationship: guardianRelationship.trim()
299
297
  };
300
- const apiBaseUrl = baseUrl || BASE_URL;
298
+ if (frontendCallbackUrl) {
299
+ payload.frontend_callback_url = frontendCallbackUrl;
300
+ }
301
+ if (metadata) {
302
+ payload.metadata = metadata;
303
+ }
301
304
  const response = await fetch(
302
- `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/guardian-info`,
305
+ `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/guardian/initiate-verification`,
303
306
  {
304
307
  method: "POST",
305
308
  headers: {
@@ -308,24 +311,26 @@ var submitGuardianInfo = async ({
308
311
  },
309
312
  body: JSON.stringify(payload),
310
313
  signal
311
- // Add abort signal for request cancellation
312
314
  }
313
315
  );
314
316
  if (!response.ok) {
315
- let errorMessage = `Failed to submit guardian info: ${response.status} ${response.statusText}`;
317
+ let errorMessage = `Failed to initiate verification: ${response.status} ${response.statusText}`;
316
318
  if (response.status === 400) {
317
- 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
+ }
318
325
  } else if (response.status === 401) {
319
326
  errorMessage = "Unauthorized: Invalid or expired token";
320
- } else if (response.status === 403) {
321
- errorMessage = "Forbidden: Access denied";
322
327
  } else if (response.status === 404) {
323
328
  errorMessage = "Organization or workspace not found";
324
329
  } else if (response.status === 422) {
325
330
  try {
326
331
  const errorData = await response.json();
327
- errorMessage = errorData.message || "Validation error";
328
- } catch (parseError) {
332
+ errorMessage = errorData.message || "Validation error: Please check your input";
333
+ } catch {
329
334
  errorMessage = "Validation error: Please check your input";
330
335
  }
331
336
  } else if (response.status >= 500) {
@@ -335,63 +340,98 @@ var submitGuardianInfo = async ({
335
340
  error.status = response.status;
336
341
  throw error;
337
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
+ };
338
364
  } catch (error) {
339
- 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);
340
366
  throw error;
341
367
  }
342
368
  };
343
- var fetchTTSAudioUrls = async ({
369
+ var verifyGuardianStatus = async ({
344
370
  accessToken,
345
371
  baseUrl,
346
- noticeUuid,
347
- language,
372
+ sessionToken,
348
373
  signal
349
- // AbortSignal for request cancellation
350
374
  }) => {
351
- try {
352
- if (!accessToken || !noticeUuid || !language) {
353
- throw new Error("accessToken, noticeUuid, and language are required");
354
- }
355
- const decodedToken = decodeTokenSafely(accessToken);
356
- if (!decodedToken) {
357
- throw new Error("Invalid access token");
358
- }
359
- const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
360
- if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
361
- throw new Error("Invalid token: missing organization or workspace UUID");
362
- }
363
- const apiBaseUrl = baseUrl || BASE_URL;
364
- const url = `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/notices/${noticeUuid}/audio/${language}`;
365
- const response = await fetch(url, {
366
- method: "GET",
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",
367
392
  headers: {
368
393
  Authorization: `Bearer ${accessToken}`,
369
- Accept: "application/json",
370
394
  "Content-Type": "application/json"
371
395
  },
396
+ body: JSON.stringify({
397
+ session_token: sessionToken
398
+ }),
372
399
  signal
373
- // Add abort signal for request cancellation
374
- });
375
- if (!response.ok) {
376
- let errorMessage = `Failed to fetch TTS audio URLs: ${response.status} ${response.statusText}`;
377
- if (response.status === 401) {
378
- errorMessage = "Unauthorized: Invalid or expired token";
379
- } else if (response.status === 403) {
380
- errorMessage = "Forbidden: Access denied";
381
- } else if (response.status === 404) {
382
- errorMessage = "Notice or audio not found";
383
- } else if (response.status >= 500) {
384
- errorMessage = "Server error: Please try again later";
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";
385
410
  }
386
- const error = new Error(errorMessage);
387
- error.status = response.status;
388
- 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";
389
419
  }
390
- const data = await response.json();
391
- return data;
392
- } catch (error) {
420
+ const error = new Error(errorMessage);
421
+ error.status = response.status;
393
422
  throw error;
394
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
+ };
395
435
  };
396
436
 
397
437
  // src/RedactoNoticeConsent/assets/redacto-logo.png
@@ -539,6 +579,17 @@ var injectCheckboxStyles = () => {
539
579
  outline: 3px solid #FFF9C4 !important;
540
580
  outline-offset: 2px !important;
541
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
+ }
542
593
  `;
543
594
  document.head.appendChild(style);
544
595
  stylesInjected = true;
@@ -921,6 +972,69 @@ var useMediaQuery = (query) => {
921
972
  return matches;
922
973
  };
923
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
+
924
1038
  // src/RedactoNoticeConsent/RedactoNoticeConsent.tsx
925
1039
  var import_jsx_runtime = require("react/jsx-runtime");
926
1040
  var PurposeItem = ({
@@ -1228,6 +1342,12 @@ var PurposeItem = ({
1228
1342
  )
1229
1343
  ] }, purpose.uuid);
1230
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
+ ];
1231
1351
  var GuardianForm = ({
1232
1352
  formData,
1233
1353
  errors,
@@ -1244,34 +1364,128 @@ var GuardianForm = ({
1244
1364
  declineButtonStyle,
1245
1365
  logoUrl
1246
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";
1247
1438
  const inputStyle = {
1248
1439
  width: "100%",
1249
1440
  padding: "10px 12px",
1250
- border: `1px solid ${(settings == null ? void 0 : settings.borderColor) || "#d0d5dd"}`,
1251
- borderRadius: (settings == null ? void 0 : settings.borderRadius) || "8px",
1441
+ border: `1px solid ${borderColor}`,
1442
+ borderRadius: radius,
1252
1443
  fontSize: "13px",
1253
- fontFamily: (settings == null ? void 0 : settings.font) || "inherit",
1254
- color: (settings == null ? void 0 : settings.textColor) || "#344054",
1255
- backgroundColor: (settings == null ? void 0 : settings.backgroundColor) || "#ffffff",
1256
- marginBottom: "4px",
1257
- boxSizing: "border-box"
1444
+ fontFamily: font,
1445
+ color: textColor,
1446
+ backgroundColor: bgColor,
1447
+ marginBottom: "2px",
1448
+ boxSizing: "border-box",
1449
+ outline: "none"
1258
1450
  };
1259
1451
  const errorStyle = {
1260
1452
  color: "#DC2626",
1261
- fontSize: "12px",
1453
+ fontSize: "11px",
1262
1454
  marginTop: "4px",
1263
- marginBottom: "16px"
1455
+ marginBottom: "0px"
1264
1456
  };
1265
1457
  const labelStyle = {
1266
1458
  ...componentTextStyle,
1267
1459
  fontSize: "13px",
1268
1460
  fontWeight: "500",
1269
- marginBottom: "6px",
1461
+ marginBottom: "4px",
1270
1462
  display: "block"
1271
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);
1272
1486
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1273
1487
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...styles3.topSection, ...componentTextStyle }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles3.topLeft, children: [
1274
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { style: styles3.logo, src: logoUrl, alt: "Redacto Logo" }),
1488
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { style: styles3.logo, src: logoUrl, alt: "Logo" }),
1275
1489
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1276
1490
  "h2",
1277
1491
  {
@@ -1283,7 +1497,7 @@ var GuardianForm = ({
1283
1497
  fontSize: "16px",
1284
1498
  fontWeight: 600
1285
1499
  },
1286
- children: "Guardian Information Required"
1500
+ children: "Guardian Verification"
1287
1501
  }
1288
1502
  )
1289
1503
  ] }) }),
@@ -1303,31 +1517,49 @@ var GuardianForm = ({
1303
1517
  ...styles3.privacyText,
1304
1518
  ...responsiveStyles.privacyText,
1305
1519
  ...componentTextStyle,
1306
- fontSize: "14px",
1307
- lineHeight: "1.4"
1520
+ fontSize: "13px",
1521
+ lineHeight: "1.5",
1522
+ marginBottom: "12px"
1308
1523
  },
1309
- 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."
1310
1525
  }
1311
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
+ ] }),
1312
1549
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1313
1550
  "div",
1314
1551
  {
1315
1552
  style: {
1316
1553
  display: "flex",
1317
1554
  flexDirection: "column",
1318
- gap: "12px",
1319
- marginTop: "16px"
1555
+ gap: "14px"
1320
1556
  },
1321
1557
  children: [
1322
1558
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1323
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1324
- "label",
1325
- {
1326
- htmlFor: "guardianName",
1327
- style: { ...labelStyle, marginBottom: "8px", display: "block" },
1328
- children: "Name of Guardian *"
1329
- }
1330
- ),
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
+ ] }),
1331
1563
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1332
1564
  "input",
1333
1565
  {
@@ -1337,62 +1569,170 @@ var GuardianForm = ({
1337
1569
  onChange: (e) => onChange("guardianName", e.target.value),
1338
1570
  style: {
1339
1571
  ...inputStyle,
1340
- borderColor: errors.guardianName ? "#DC2626" : inputStyle.border
1572
+ borderColor: errors.guardianName ? "#DC2626" : borderColor
1341
1573
  },
1342
- placeholder: "Enter guardian's full name"
1574
+ placeholder: "e.g. Rajesh Kumar",
1575
+ autoComplete: "name",
1576
+ "aria-describedby": "guardianName-hint"
1343
1577
  }
1344
1578
  ),
1345
- 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 })
1346
1581
  ] }),
1347
1582
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1348
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1349
- "label",
1350
- {
1351
- htmlFor: "guardianContact",
1352
- style: { ...labelStyle, marginBottom: "8px", display: "block" },
1353
- children: "Contact of Guardian *"
1354
- }
1355
- ),
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
+ ] }),
1356
1587
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1357
1588
  "input",
1358
1589
  {
1359
1590
  id: "guardianContact",
1360
- type: "text",
1591
+ type: "tel",
1592
+ inputMode: "tel",
1361
1593
  value: formData.guardianContact,
1362
1594
  onChange: (e) => onChange("guardianContact", e.target.value),
1363
1595
  style: {
1364
1596
  ...inputStyle,
1365
- borderColor: errors.guardianContact ? "#DC2626" : inputStyle.border
1597
+ borderColor: errors.guardianContact ? "#DC2626" : borderColor
1366
1598
  },
1367
- placeholder: "Enter guardian's email or phone number"
1599
+ placeholder: "e.g. 9876543210",
1600
+ autoComplete: "tel",
1601
+ "aria-describedby": "guardianContact-hint"
1368
1602
  }
1369
1603
  ),
1370
- 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 })
1371
1606
  ] }),
1372
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1373
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1374
- "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",
1375
1614
  {
1376
- htmlFor: "guardianRelationship",
1377
- style: { ...labelStyle, marginBottom: "8px", display: "block" },
1378
- 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
+ ]
1379
1662
  }
1380
1663
  ),
1381
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1382
- "input",
1664
+ isDropdownOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1665
+ "div",
1383
1666
  {
1384
- id: "guardianRelationship",
1385
- type: "text",
1386
- value: formData.guardianRelationship,
1387
- onChange: (e) => onChange("guardianRelationship", e.target.value),
1667
+ ref: optionsRef,
1668
+ id: "guardianRelationship-listbox",
1669
+ role: "listbox",
1670
+ "aria-labelledby": "guardianRelationship-label",
1388
1671
  style: {
1389
- ...inputStyle,
1390
- 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"
1391
1685
  },
1392
- 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
+ })
1393
1733
  }
1394
1734
  ),
1395
- 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 })
1396
1736
  ] })
1397
1737
  ]
1398
1738
  }
@@ -1403,7 +1743,7 @@ var GuardianForm = ({
1403
1743
  role: "alert",
1404
1744
  style: {
1405
1745
  color: "#DC2626",
1406
- fontSize: "14px",
1746
+ fontSize: "13px",
1407
1747
  marginTop: "16px",
1408
1748
  padding: "12px",
1409
1749
  borderRadius: "6px",
@@ -1439,8 +1779,8 @@ var GuardianForm = ({
1439
1779
  onClick: onNext,
1440
1780
  disabled: isSubmitting,
1441
1781
  type: "button",
1442
- "aria-label": isSubmitting ? "Submitting guardian information" : "Submit guardian information and proceed",
1443
- children: isSubmitting ? "Submitting..." : "Next"
1782
+ "aria-label": isSubmitting ? "Verifying guardian identity" : "Proceed to guardian verification",
1783
+ children: isSubmitting ? "Verifying..." : "Verify via DigiLocker"
1444
1784
  }
1445
1785
  ),
1446
1786
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1454,7 +1794,7 @@ var GuardianForm = ({
1454
1794
  },
1455
1795
  onClick: onDecline,
1456
1796
  type: "button",
1457
- "aria-label": "Cancel guardian information form",
1797
+ "aria-label": "Cancel",
1458
1798
  children: "Cancel"
1459
1799
  }
1460
1800
  )
@@ -1463,6 +1803,266 @@ var GuardianForm = ({
1463
1803
  )
1464
1804
  ] });
1465
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
+ };
1466
2066
  var AgeVerification = ({
1467
2067
  onYes,
1468
2068
  onNo,
@@ -1625,6 +2225,10 @@ var RedactoNoticeConsent = ({
1625
2225
  settings,
1626
2226
  applicationId,
1627
2227
  validateAgainst = "all",
2228
+ digilockerMode = "redirect",
2229
+ // Default to full page redirect (DigiLocker blocks iframe embedding)
2230
+ digilockerCallbackUrl = "/digilocker-callback",
2231
+ // Default callback path
1628
2232
  includeFullyConsentedData = false,
1629
2233
  reviewModeButtonText = "Continue"
1630
2234
  }) => {
@@ -1680,6 +2284,8 @@ var RedactoNoticeConsent = ({
1680
2284
  const [currentAudioIndex, setCurrentAudioIndex] = (0, import_react2.useState)(0);
1681
2285
  const [highlightedElementId, setHighlightedElementId] = (0, import_react2.useState)(null);
1682
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);
1683
2289
  const [showGuardianForm, setShowGuardianForm] = (0, import_react2.useState)(false);
1684
2290
  const [isSubmittingGuardian, setIsSubmittingGuardian] = (0, import_react2.useState)(false);
1685
2291
  const [guardianFormData, setGuardianFormData] = (0, import_react2.useState)({
@@ -1688,12 +2294,32 @@ var RedactoNoticeConsent = ({
1688
2294
  guardianRelationship: ""
1689
2295
  });
1690
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);
1691
2308
  const modalRef = (0, import_react2.useRef)(null);
1692
2309
  const firstFocusableRef = (0, import_react2.useRef)(null);
1693
2310
  const lastFocusableRef = (0, import_react2.useRef)(null);
1694
2311
  const abortControllerRef = (0, import_react2.useRef)(null);
1695
2312
  const isRequestInProgressRef = (0, import_react2.useRef)(false);
1696
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);
1697
2323
  const isMobile = useMediaQuery("(max-width: 768px)");
1698
2324
  const responsiveStyles = (0, import_react2.useMemo)(
1699
2325
  () => ({
@@ -1801,6 +2427,7 @@ var RedactoNoticeConsent = ({
1801
2427
  }, [content, selectedDataElements]);
1802
2428
  const acceptDisabled = isSubmitting || !areAllRequiredElementsChecked;
1803
2429
  (0, import_react2.useEffect)(() => {
2430
+ var _a2, _b2;
1804
2431
  clearApiCache();
1805
2432
  isRequestInProgressRef.current = false;
1806
2433
  setContent(null);
@@ -1816,6 +2443,8 @@ var RedactoNoticeConsent = ({
1816
2443
  setIsSubmittingAccept(false);
1817
2444
  setIsSubmittingDecline(false);
1818
2445
  setShowAgeVerification(false);
2446
+ setIsMinorFlow(false);
2447
+ setSelfDeclaredAdult(false);
1819
2448
  setShowGuardianForm(false);
1820
2449
  setIsSubmittingGuardian(false);
1821
2450
  setGuardianFormData({
@@ -1824,6 +2453,41 @@ var RedactoNoticeConsent = ({
1824
2453
  guardianRelationship: ""
1825
2454
  });
1826
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;
1827
2491
  }, [accessToken]);
1828
2492
  (0, import_react2.useEffect)(() => {
1829
2493
  if (hasAlreadyConsented) {
@@ -2032,7 +2696,11 @@ var RedactoNoticeConsent = ({
2032
2696
  setInitialDataElementSelections({});
2033
2697
  }
2034
2698
  if (consentContentData.detail.is_minor) {
2035
- 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
+ }
2036
2704
  }
2037
2705
  setIsLoading(false);
2038
2706
  } catch (err) {
@@ -2131,6 +2799,10 @@ var RedactoNoticeConsent = ({
2131
2799
  const handleAccept = async () => {
2132
2800
  const submitController = new AbortController();
2133
2801
  try {
2802
+ if (isMinorFlow && !verificationReference) {
2803
+ setErrorMessage("Guardian verification is required before consent can be submitted.");
2804
+ return;
2805
+ }
2134
2806
  setIsSubmittingAccept(true);
2135
2807
  setIsSubmitting(true);
2136
2808
  setErrorMessage(null);
@@ -2166,6 +2838,8 @@ var RedactoNoticeConsent = ({
2166
2838
  }),
2167
2839
  declined: false,
2168
2840
  meta_data: applicationId ? { specific_uuid: applicationId } : void 0,
2841
+ guardianVerificationReference: verificationReference || void 0,
2842
+ selfDeclaredAdult: selfDeclaredAdult || void 0,
2169
2843
  signal: submitController.signal
2170
2844
  });
2171
2845
  }
@@ -2230,17 +2904,102 @@ var RedactoNoticeConsent = ({
2230
2904
  try {
2231
2905
  setIsSubmittingGuardian(true);
2232
2906
  setGuardianFormErrors({});
2233
- await submitGuardianInfo({
2907
+ setVerificationError(null);
2908
+ const callbackUrl = digilockerMode === "popup" ? new URL(digilockerCallbackUrl, window.location.origin).href : void 0;
2909
+ const response = await initiateGuardianVerification({
2234
2910
  accessToken,
2235
2911
  baseUrl,
2236
2912
  guardianName: guardianFormData.guardianName,
2237
2913
  guardianContact: guardianFormData.guardianContact,
2238
2914
  guardianRelationship: guardianFormData.guardianRelationship,
2915
+ frontendCallbackUrl: callbackUrl,
2916
+ // Only for popup mode
2239
2917
  signal: guardianController.signal
2240
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
+ }
2241
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
+ }
2242
3000
  } catch (error) {
2243
3001
  const err = error;
3002
+ setIsInitiatingVerification(false);
2244
3003
  if (err.status === 422) {
2245
3004
  if (err.message.includes("guardian_contact")) {
2246
3005
  setGuardianFormErrors({ guardianContact: err.message });
@@ -2251,23 +3010,265 @@ var RedactoNoticeConsent = ({
2251
3010
  } else {
2252
3011
  setGuardianFormErrors({ general: err.message });
2253
3012
  }
3013
+ setShowVerificationScreen(false);
3014
+ setShowGuardianForm(true);
2254
3015
  } else {
2255
3016
  setGuardianFormErrors({
2256
- general: err.message || "Failed to submit guardian information. Please try again."
3017
+ general: err.message || "Failed to initiate verification. Please try again."
2257
3018
  });
3019
+ setShowVerificationScreen(false);
3020
+ setShowGuardianForm(true);
2258
3021
  onError == null ? void 0 : onError(err);
2259
3022
  }
2260
3023
  } finally {
2261
3024
  setIsSubmittingGuardian(false);
2262
3025
  }
2263
- }, [accessToken, baseUrl, guardianFormData, onError]);
3026
+ }, [accessToken, baseUrl, guardianFormData, digilockerMode, digilockerCallbackUrl, selectedPurposes, selectedDataElements, noticeId, onError]);
2264
3027
  const handleAgeVerificationYes = (0, import_react2.useCallback)(() => {
2265
3028
  setShowAgeVerification(false);
3029
+ setIsMinorFlow(false);
3030
+ setSelfDeclaredAdult(true);
2266
3031
  }, []);
2267
3032
  const handleAgeVerificationNo = (0, import_react2.useCallback)(() => {
2268
3033
  setShowAgeVerification(false);
2269
3034
  setShowGuardianForm(true);
2270
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
+ }, []);
2271
3272
  const availableLanguages = (0, import_react2.useMemo)(() => {
2272
3273
  if (!content) return [language];
2273
3274
  const languages = ["English"];
@@ -2677,12 +3678,67 @@ var RedactoNoticeConsent = ({
2677
3678
  }, [content, stopAudio]);
2678
3679
  (0, import_react2.useEffect)(() => {
2679
3680
  return () => {
3681
+ var _a2, _b2;
2680
3682
  if (abortControllerRef.current) {
2681
3683
  abortControllerRef.current.abort();
2682
3684
  }
2683
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
+ }
2684
3703
  };
2685
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]);
2686
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: [
2687
3743
  blockUI && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2688
3744
  "div",
@@ -2868,6 +3924,27 @@ var RedactoNoticeConsent = ({
2868
3924
  declineButtonStyle,
2869
3925
  logoUrl: (content == null ? void 0 : content.logo_url) || redacto_logo_default
2870
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
+ }
2871
3948
  ) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { ...styles.content, ...responsiveStyles.content }, children: [
2872
3949
  errorMessage && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2873
3950
  "div",
@@ -3501,60 +4578,27 @@ var submitConsentEvent2 = async ({
3501
4578
  var _a, _b;
3502
4579
  try {
3503
4580
  const apiBaseUrl = baseUrl || BASE_URL2;
3504
- let payload;
3505
- if (isLegacyApi(apiBaseUrl)) {
3506
- const primaryEmail = typeof window !== "undefined" ? localStorage.getItem("userEmail") : null;
3507
- payload = {
3508
- primary_email: primaryEmail || void 0,
3509
- notice_uuid: noticeUuid,
3510
- source: "WEB",
3511
- declined: Boolean(declined),
3512
- consents: {
3513
- purposes: purposes.map((purpose) => {
3514
- if (!purpose.uuid) {
3515
- throw new Error(`Invalid purpose data: missing uuid`);
3516
- }
3517
- return {
3518
- uuid: purpose.uuid,
3519
- name: purpose.name,
3520
- description: purpose.description || "",
3521
- industries: purpose.industries || "",
3522
- selected: Boolean(purpose.selected),
3523
- data_elements: (purpose.data_elements || []).map((element) => ({
3524
- uuid: element.uuid,
3525
- name: element.name,
3526
- enabled: Boolean(element.enabled),
3527
- required: Boolean(element.required),
3528
- selected: element.required ? true : Boolean(element.selected)
3529
- }))
3530
- };
3531
- })
3532
- },
3533
- meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid } : void 0
3534
- };
3535
- } else {
3536
- payload = {
3537
- notice_uuid: noticeUuid,
3538
- purposes: purposes.map((purpose) => {
3539
- if (!purpose.uuid) {
3540
- throw new Error(`Invalid purpose data: missing uuid`);
3541
- }
3542
- const dataElements = purpose.data_elements.filter((element) => element.enabled).map((element) => ({
3543
- uuid: element.uuid,
3544
- selected: element.required ? true : Boolean(element.selected)
3545
- }));
3546
- return {
3547
- purpose_uuid: purpose.uuid,
3548
- selected: Boolean(purpose.selected),
3549
- data_elements: dataElements.length > 0 ? dataElements : void 0
3550
- };
3551
- }),
3552
- select_all_mandatory: false,
3553
- source: "WEB",
3554
- declined,
3555
- meta_data
3556
- };
3557
- }
4581
+ const payload = {
4582
+ notice_uuid: noticeUuid,
4583
+ purposes: purposes.map((purpose) => {
4584
+ if (!purpose.uuid) {
4585
+ throw new Error(`Invalid purpose data: missing uuid`);
4586
+ }
4587
+ const dataElements = purpose.data_elements.filter((element) => element.enabled).map((element) => ({
4588
+ uuid: element.uuid,
4589
+ selected: element.required ? true : Boolean(element.selected)
4590
+ }));
4591
+ return {
4592
+ purpose_uuid: purpose.uuid,
4593
+ selected: Boolean(purpose.selected),
4594
+ data_elements: dataElements.length > 0 ? dataElements : void 0
4595
+ };
4596
+ }),
4597
+ select_all_mandatory: false,
4598
+ source: "WEB",
4599
+ declined,
4600
+ meta_data
4601
+ };
3558
4602
  const response = await fetch(
3559
4603
  `${apiBaseUrl}/public/organisations/${org_uuid}/workspaces/${workspace_uuid}/submit-consent`,
3560
4604
  {
@@ -4504,8 +5548,425 @@ var RedactoConsentInlineComponent = ({
4504
5548
  };
4505
5549
  RedactoConsentInlineComponent.displayName = "RedactoConsentInline";
4506
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
+ };
4507
5967
  // Annotate the CommonJS export names for ESM import in node:
4508
5968
  0 && (module.exports = {
5969
+ DigiLockerCallback,
4509
5970
  RedactoNoticeConsent,
4510
5971
  RedactoNoticeConsentInline
4511
5972
  });