@redacto.io/consent-sdk-js 1.0.0-beta.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/redacto.d.mts +48 -3
- package/dist/redacto.d.ts +48 -3
- package/dist/redacto.global.js +2 -2
- package/dist/redacto.global.js.map +1 -1
- package/dist/redacto.js +541 -152
- package/dist/redacto.mjs +541 -152
- package/package.json +1 -1
package/dist/redacto.js
CHANGED
|
@@ -182,17 +182,6 @@ function createMediaQuery(query) {
|
|
|
182
182
|
};
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
// src/core/jwt-decode.ts
|
|
186
|
-
var import_jwt_decode = require("jwt-decode");
|
|
187
|
-
function decodeTokenSafely(token) {
|
|
188
|
-
try {
|
|
189
|
-
return (0, import_jwt_decode.jwtDecode)(token);
|
|
190
|
-
} catch (error) {
|
|
191
|
-
console.warn("Failed to decode JWT token:", error);
|
|
192
|
-
return null;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
185
|
// src/services/cache.ts
|
|
197
186
|
var apiCache = /* @__PURE__ */ new Map();
|
|
198
187
|
var CACHE_TTL = 5 * 60 * 1e3;
|
|
@@ -213,6 +202,141 @@ function clearApiCache() {
|
|
|
213
202
|
apiCache.clear();
|
|
214
203
|
}
|
|
215
204
|
|
|
205
|
+
// src/core/jwt-decode.ts
|
|
206
|
+
var import_jwt_decode = require("jwt-decode");
|
|
207
|
+
function decodeTokenSafely(token) {
|
|
208
|
+
try {
|
|
209
|
+
return (0, import_jwt_decode.jwtDecode)(token);
|
|
210
|
+
} catch (error) {
|
|
211
|
+
console.warn("Failed to decode JWT token:", error);
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// src/api/sandbox.ts
|
|
217
|
+
var SANDBOX_TOKEN_HEADER = "X-Consent-Token";
|
|
218
|
+
var resolveSandboxAuth = (options) => {
|
|
219
|
+
var _a, _b, _c, _d, _e, _f;
|
|
220
|
+
const token = (_a = options.token) == null ? void 0 : _a.trim();
|
|
221
|
+
if (!token) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
const organisationUuid = (_b = options.organisationUuid) == null ? void 0 : _b.trim();
|
|
225
|
+
const workspaceUuid = (_c = options.workspaceUuid) == null ? void 0 : _c.trim();
|
|
226
|
+
const ucic = (_d = options.sandboxUcic) == null ? void 0 : _d.trim();
|
|
227
|
+
const email = (_e = options.sandboxEmail) == null ? void 0 : _e.trim();
|
|
228
|
+
const mobile = (_f = options.sandboxMobile) == null ? void 0 : _f.trim();
|
|
229
|
+
if (!organisationUuid || !workspaceUuid) {
|
|
230
|
+
throw new Error(
|
|
231
|
+
"Sandbox mode requires organisationUuid and workspaceUuid"
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
if (!ucic && !email && !mobile) {
|
|
235
|
+
throw new Error("Sandbox mode requires a UCIC, email, or mobile");
|
|
236
|
+
}
|
|
237
|
+
return { token, ucic, email, mobile, organisationUuid, workspaceUuid };
|
|
238
|
+
};
|
|
239
|
+
var resolveRequestAuth = (accessToken, options) => {
|
|
240
|
+
const sandbox = resolveSandboxAuth(options);
|
|
241
|
+
if (sandbox) {
|
|
242
|
+
return {
|
|
243
|
+
organisationUuid: sandbox.organisationUuid,
|
|
244
|
+
workspaceUuid: sandbox.workspaceUuid,
|
|
245
|
+
sandbox
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
const decodedToken = accessToken ? decodeTokenSafely(accessToken) : null;
|
|
249
|
+
if (!decodedToken) {
|
|
250
|
+
throw new Error("Invalid access token");
|
|
251
|
+
}
|
|
252
|
+
const { organisation_uuid, workspace_uuid } = decodedToken;
|
|
253
|
+
if (!organisation_uuid || !workspace_uuid) {
|
|
254
|
+
throw new Error("Invalid token: missing organization or workspace UUID");
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
organisationUuid: organisation_uuid,
|
|
258
|
+
workspaceUuid: workspace_uuid,
|
|
259
|
+
sandbox: null
|
|
260
|
+
};
|
|
261
|
+
};
|
|
262
|
+
var buildAuthHeaders = (accessToken, sandbox) => {
|
|
263
|
+
if (sandbox) {
|
|
264
|
+
return { [SANDBOX_TOKEN_HEADER]: sandbox.token };
|
|
265
|
+
}
|
|
266
|
+
return { Authorization: `Bearer ${accessToken}` };
|
|
267
|
+
};
|
|
268
|
+
var sandboxBodyIdentity = (sandbox) => {
|
|
269
|
+
const payload = {};
|
|
270
|
+
if (sandbox.ucic) {
|
|
271
|
+
payload.org_user_id = sandbox.ucic;
|
|
272
|
+
}
|
|
273
|
+
if (sandbox.email) {
|
|
274
|
+
payload.primary_email = sandbox.email;
|
|
275
|
+
}
|
|
276
|
+
if (sandbox.mobile) {
|
|
277
|
+
payload.primary_mobile = sandbox.mobile;
|
|
278
|
+
}
|
|
279
|
+
return payload;
|
|
280
|
+
};
|
|
281
|
+
var sandboxQueryIdentity = (sandbox) => sandboxBodyIdentity(sandbox);
|
|
282
|
+
|
|
283
|
+
// src/api/constants.ts
|
|
284
|
+
var CODE_BY_NAME = /* @__PURE__ */ new Map([
|
|
285
|
+
["english", "en"],
|
|
286
|
+
["assamese", "as"],
|
|
287
|
+
["bengali", "bn"],
|
|
288
|
+
["bodo", "brx"],
|
|
289
|
+
["dogri", "doi"],
|
|
290
|
+
["goan konkani", "gom"],
|
|
291
|
+
["gujarati", "gu"],
|
|
292
|
+
["hindi", "hi"],
|
|
293
|
+
["kannada", "kn"],
|
|
294
|
+
["kashmiri", "ks"],
|
|
295
|
+
["maithili", "mai"],
|
|
296
|
+
["malayalam", "ml"],
|
|
297
|
+
["manipuri", "mni-Mtei"],
|
|
298
|
+
["marathi", "mr"],
|
|
299
|
+
["nepali", "ne"],
|
|
300
|
+
["odia", "or"],
|
|
301
|
+
["punjabi", "pa"],
|
|
302
|
+
["sanskrit", "sa"],
|
|
303
|
+
["santali", "sat"],
|
|
304
|
+
["sindhi", "sd"],
|
|
305
|
+
["tamil", "ta"],
|
|
306
|
+
["telugu", "te"],
|
|
307
|
+
["urdu", "ur"]
|
|
308
|
+
]);
|
|
309
|
+
var CODE_BY_LOWER = new Map(
|
|
310
|
+
[...CODE_BY_NAME.values()].map((c) => [c.toLowerCase(), c])
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
// src/api/language.ts
|
|
314
|
+
function toLanguageCode(lang) {
|
|
315
|
+
var _a, _b;
|
|
316
|
+
if (!lang) {
|
|
317
|
+
return void 0;
|
|
318
|
+
}
|
|
319
|
+
const trimmed = lang.trim();
|
|
320
|
+
if (!trimmed) {
|
|
321
|
+
return void 0;
|
|
322
|
+
}
|
|
323
|
+
const lower = trimmed.toLowerCase();
|
|
324
|
+
return (_b = (_a = CODE_BY_LOWER.get(lower)) != null ? _a : CODE_BY_NAME.get(lower)) != null ? _b : trimmed;
|
|
325
|
+
}
|
|
326
|
+
function resolveDisplayLanguage(requested, defaultLanguage, translations) {
|
|
327
|
+
const fallback = defaultLanguage || "en";
|
|
328
|
+
const requestedCode = toLanguageCode(requested);
|
|
329
|
+
if (!requestedCode) {
|
|
330
|
+
return fallback;
|
|
331
|
+
}
|
|
332
|
+
for (const key of Object.keys(translations || {})) {
|
|
333
|
+
if (toLanguageCode(key) === requestedCode) {
|
|
334
|
+
return key;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return fallback;
|
|
338
|
+
}
|
|
339
|
+
|
|
216
340
|
// src/api/index.ts
|
|
217
341
|
var BASE_URL = "https://api.redacto.io/consent";
|
|
218
342
|
var BASE_URL_PUBLIC = "https://api.redacto.tech/consent";
|
|
@@ -224,17 +348,22 @@ var fetchConsentContent = async ({
|
|
|
224
348
|
specific_uuid,
|
|
225
349
|
validate_against = "all",
|
|
226
350
|
include_fully_consented_data = false,
|
|
227
|
-
signal
|
|
351
|
+
signal,
|
|
352
|
+
...sandboxParams
|
|
228
353
|
}) => {
|
|
229
|
-
if (!noticeId || !
|
|
230
|
-
throw new Error("noticeId
|
|
354
|
+
if (!noticeId || !validate_against) {
|
|
355
|
+
throw new Error("noticeId is required");
|
|
231
356
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
357
|
+
if (!accessToken && !sandboxParams.token) {
|
|
358
|
+
throw new Error("accessToken or token is required");
|
|
359
|
+
}
|
|
360
|
+
const { organisationUuid: ORG, workspaceUuid: WS, sandbox } = resolveRequestAuth(
|
|
361
|
+
accessToken,
|
|
362
|
+
sandboxParams
|
|
363
|
+
);
|
|
236
364
|
const apiBaseUrl = baseUrl || BASE_URL;
|
|
237
|
-
const
|
|
365
|
+
const cacheIdentity = sandbox ? `sandbox:${sandbox.organisationUuid}:${sandbox.workspaceUuid}:${sandbox.ucic || sandbox.email || sandbox.mobile}` : accessToken;
|
|
366
|
+
const cacheKey = `${cacheIdentity}-${noticeId}-${validate_against}-${language}-${specific_uuid || ""}-${include_fully_consented_data}`;
|
|
238
367
|
if (validate_against === "all" && !specific_uuid) {
|
|
239
368
|
const cached = getCachedData(cacheKey);
|
|
240
369
|
if (cached) return cached;
|
|
@@ -243,10 +372,15 @@ var fetchConsentContent = async ({
|
|
|
243
372
|
if (specific_uuid) url.searchParams.append("specific_uuid", specific_uuid);
|
|
244
373
|
url.searchParams.append("validate_against", validate_against);
|
|
245
374
|
if (include_fully_consented_data) url.searchParams.append("include_fully_consented_data", "true");
|
|
375
|
+
if (sandbox) {
|
|
376
|
+
for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
|
|
377
|
+
if (value) url.searchParams.append(key, value);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
246
380
|
const response = await fetch(url.toString(), {
|
|
247
381
|
method: "GET",
|
|
248
382
|
headers: {
|
|
249
|
-
|
|
383
|
+
...buildAuthHeaders(accessToken, sandbox),
|
|
250
384
|
"Accept-Language": language
|
|
251
385
|
},
|
|
252
386
|
signal
|
|
@@ -272,18 +406,24 @@ var fetchConsentContentPublic = async ({
|
|
|
272
406
|
org_uuid,
|
|
273
407
|
workspace_uuid,
|
|
274
408
|
notice_uuid,
|
|
409
|
+
accessToken,
|
|
275
410
|
baseUrl,
|
|
276
411
|
language = "en",
|
|
277
412
|
specific_uuid
|
|
278
413
|
}) => {
|
|
279
414
|
const apiBaseUrl = baseUrl || BASE_URL_PUBLIC;
|
|
415
|
+
const noticesUrl = `${apiBaseUrl}/public/organisations/${org_uuid}/workspaces/${workspace_uuid}/notices`;
|
|
280
416
|
const url = new URL(
|
|
281
|
-
`${
|
|
417
|
+
accessToken ? `${noticesUrl}/${notice_uuid}` : `${noticesUrl}/get-notice/${notice_uuid}`
|
|
282
418
|
);
|
|
283
419
|
if (specific_uuid) url.searchParams.append("specific_uuid", specific_uuid);
|
|
284
420
|
const response = await fetch(url.toString(), {
|
|
285
421
|
method: "GET",
|
|
286
|
-
headers: {
|
|
422
|
+
headers: {
|
|
423
|
+
"Content-Type": "application/json",
|
|
424
|
+
"Accept-Language": language,
|
|
425
|
+
...accessToken ? { Authorization: `Bearer ${accessToken}` } : {}
|
|
426
|
+
}
|
|
287
427
|
});
|
|
288
428
|
if (response.status === 401) {
|
|
289
429
|
const error = new Error("Unauthorized");
|
|
@@ -310,16 +450,21 @@ var submitConsentEvent = async ({
|
|
|
310
450
|
meta_data,
|
|
311
451
|
guardianVerificationReference,
|
|
312
452
|
selfDeclaredAdult,
|
|
313
|
-
|
|
453
|
+
language,
|
|
454
|
+
signal,
|
|
455
|
+
...sandboxParams
|
|
314
456
|
}) => {
|
|
315
|
-
if (!noticeUuid || !
|
|
316
|
-
throw new Error("noticeUuid
|
|
457
|
+
if (!noticeUuid || !Array.isArray(purposes)) {
|
|
458
|
+
throw new Error("noticeUuid and purposes array are required");
|
|
317
459
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
460
|
+
if (!accessToken && !sandboxParams.token) {
|
|
461
|
+
throw new Error("accessToken or token is required");
|
|
462
|
+
}
|
|
463
|
+
const { organisationUuid: ORG, workspaceUuid: WS, sandbox } = resolveRequestAuth(
|
|
464
|
+
accessToken,
|
|
465
|
+
sandboxParams
|
|
466
|
+
);
|
|
467
|
+
const apiBaseUrl = (sandbox ? null : ledgerBaseUrl) || baseUrl || BASE_URL;
|
|
323
468
|
const validatedPurposes = purposes.map((purpose) => {
|
|
324
469
|
if (!purpose.uuid) throw new Error("Invalid purpose data: missing uuid");
|
|
325
470
|
const dataElements = (purpose.data_elements || []).filter((element) => element.enabled).map((element) => ({
|
|
@@ -340,17 +485,19 @@ var submitConsentEvent = async ({
|
|
|
340
485
|
declined: Boolean(declined),
|
|
341
486
|
meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid, minor_age: meta_data.minor_age } : void 0,
|
|
342
487
|
guardian_verification_reference: guardianVerificationReference,
|
|
343
|
-
self_declared_adult: selfDeclaredAdult
|
|
488
|
+
self_declared_adult: selfDeclaredAdult,
|
|
489
|
+
language: toLanguageCode(language)
|
|
344
490
|
};
|
|
491
|
+
const requestBody = sandbox ? { ...payload, ...sandboxBodyIdentity(sandbox) } : payload;
|
|
345
492
|
const response = await fetch(
|
|
346
493
|
`${apiBaseUrl}/public/organisations/${ORG}/workspaces/${WS}/submit-consent`,
|
|
347
494
|
{
|
|
348
495
|
method: "POST",
|
|
349
496
|
headers: {
|
|
350
|
-
|
|
497
|
+
...buildAuthHeaders(accessToken, sandbox),
|
|
351
498
|
"Content-Type": "application/json"
|
|
352
499
|
},
|
|
353
|
-
body: JSON.stringify(
|
|
500
|
+
body: JSON.stringify(requestBody),
|
|
354
501
|
signal
|
|
355
502
|
}
|
|
356
503
|
);
|
|
@@ -382,7 +529,8 @@ var submitConsentEventPublic = async ({
|
|
|
382
529
|
noticeUuid,
|
|
383
530
|
purposes,
|
|
384
531
|
declined,
|
|
385
|
-
meta_data
|
|
532
|
+
meta_data,
|
|
533
|
+
language
|
|
386
534
|
}) => {
|
|
387
535
|
var _a, _b;
|
|
388
536
|
const apiBaseUrl = ledgerBaseUrl || baseUrl || BASE_URL_PUBLIC;
|
|
@@ -403,7 +551,8 @@ var submitConsentEventPublic = async ({
|
|
|
403
551
|
select_all_mandatory: false,
|
|
404
552
|
source: "WEB",
|
|
405
553
|
declined,
|
|
406
|
-
meta_data
|
|
554
|
+
meta_data,
|
|
555
|
+
language: toLanguageCode(language)
|
|
407
556
|
};
|
|
408
557
|
const response = await fetch(
|
|
409
558
|
`${apiBaseUrl}/public/organisations/${org_uuid}/workspaces/${workspace_uuid}/submit-consent`,
|
|
@@ -442,23 +591,32 @@ var submitConsentEventPublic = async ({
|
|
|
442
591
|
var fetchTTSAudioUrls = async ({
|
|
443
592
|
accessToken,
|
|
444
593
|
baseUrl,
|
|
594
|
+
ledgerBaseUrl,
|
|
445
595
|
noticeUuid,
|
|
446
596
|
language,
|
|
447
|
-
signal
|
|
597
|
+
signal,
|
|
598
|
+
...sandboxParams
|
|
448
599
|
}) => {
|
|
449
|
-
if (!accessToken || !noticeUuid || !language) {
|
|
450
|
-
throw new Error("accessToken, noticeUuid, and language are required");
|
|
600
|
+
if (!accessToken && !sandboxParams.token || !noticeUuid || !language) {
|
|
601
|
+
throw new Error("accessToken (or token), noticeUuid, and language are required");
|
|
451
602
|
}
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
const apiBaseUrl = baseUrl || BASE_URL;
|
|
457
|
-
const url =
|
|
458
|
-
|
|
603
|
+
const { organisationUuid: ORG, workspaceUuid: WS, sandbox } = resolveRequestAuth(
|
|
604
|
+
accessToken,
|
|
605
|
+
sandboxParams
|
|
606
|
+
);
|
|
607
|
+
const apiBaseUrl = (sandbox ? null : ledgerBaseUrl) || baseUrl || BASE_URL;
|
|
608
|
+
const url = new URL(
|
|
609
|
+
`${apiBaseUrl}/public/organisations/${ORG}/workspaces/${WS}/notices/${noticeUuid}/audio/${language}`
|
|
610
|
+
);
|
|
611
|
+
if (sandbox) {
|
|
612
|
+
for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
|
|
613
|
+
if (value) url.searchParams.append(key, value);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
const response = await fetch(url.toString(), {
|
|
459
617
|
method: "GET",
|
|
460
618
|
headers: {
|
|
461
|
-
|
|
619
|
+
...buildAuthHeaders(accessToken, sandbox),
|
|
462
620
|
Accept: "application/json"
|
|
463
621
|
},
|
|
464
622
|
signal
|
|
@@ -925,6 +1083,42 @@ var styles = {
|
|
|
925
1083
|
padding: 0,
|
|
926
1084
|
boxSizing: "border-box"
|
|
927
1085
|
},
|
|
1086
|
+
// The main notice header is two stacked rows: the logo alone on row 1,
|
|
1087
|
+
// aligned per active_config.logo_position, then the title and the
|
|
1088
|
+
// TTS/language controls on row 2. `topSection` and `topLeft` above keep their
|
|
1089
|
+
// single-row shape for the age-verification, guardian and verification
|
|
1090
|
+
// screens, which have no controls to place and share those styles.
|
|
1091
|
+
// Mirrors consent-sdk-react/src/RedactoNoticeConsent/styles.ts.
|
|
1092
|
+
headerStack: {
|
|
1093
|
+
display: "flex",
|
|
1094
|
+
flexDirection: "column",
|
|
1095
|
+
alignItems: "stretch",
|
|
1096
|
+
gap: "10px",
|
|
1097
|
+
paddingBottom: "15px",
|
|
1098
|
+
borderBottom: "none",
|
|
1099
|
+
backgroundColor: "#ffffff",
|
|
1100
|
+
margin: 0,
|
|
1101
|
+
boxSizing: "border-box",
|
|
1102
|
+
textAlign: "left"
|
|
1103
|
+
},
|
|
1104
|
+
logoRow: {
|
|
1105
|
+
display: "flex",
|
|
1106
|
+
alignItems: "center",
|
|
1107
|
+
width: "100%",
|
|
1108
|
+
margin: 0,
|
|
1109
|
+
padding: 0,
|
|
1110
|
+
boxSizing: "border-box"
|
|
1111
|
+
},
|
|
1112
|
+
headerRow: {
|
|
1113
|
+
display: "flex",
|
|
1114
|
+
alignItems: "center",
|
|
1115
|
+
justifyContent: "space-between",
|
|
1116
|
+
gap: "10px",
|
|
1117
|
+
width: "100%",
|
|
1118
|
+
margin: 0,
|
|
1119
|
+
padding: 0,
|
|
1120
|
+
boxSizing: "border-box"
|
|
1121
|
+
},
|
|
928
1122
|
logo: {
|
|
929
1123
|
height: "32px",
|
|
930
1124
|
width: "auto",
|
|
@@ -1131,6 +1325,32 @@ var styles = {
|
|
|
1131
1325
|
margin: 0,
|
|
1132
1326
|
boxSizing: "border-box"
|
|
1133
1327
|
},
|
|
1328
|
+
/**
|
|
1329
|
+
* The main notice footer. Wide: one row with decline pushed left and the
|
|
1330
|
+
* accepts grouped right. Narrow: a full-width stack. Review mode keeps
|
|
1331
|
+
* `bottomSection` — it collapses to a single button.
|
|
1332
|
+
*/
|
|
1333
|
+
actionButtonRow: {
|
|
1334
|
+
display: "flex",
|
|
1335
|
+
flexDirection: "row",
|
|
1336
|
+
alignItems: "center",
|
|
1337
|
+
gap: "12px",
|
|
1338
|
+
paddingTop: "15px",
|
|
1339
|
+
borderTop: "none",
|
|
1340
|
+
backgroundColor: "#ffffff",
|
|
1341
|
+
margin: 0,
|
|
1342
|
+
boxSizing: "border-box"
|
|
1343
|
+
},
|
|
1344
|
+
actionButtonStack: {
|
|
1345
|
+
display: "flex",
|
|
1346
|
+
flexDirection: "column",
|
|
1347
|
+
gap: "12px",
|
|
1348
|
+
paddingTop: "15px",
|
|
1349
|
+
borderTop: "none",
|
|
1350
|
+
backgroundColor: "#ffffff",
|
|
1351
|
+
margin: 0,
|
|
1352
|
+
boxSizing: "border-box"
|
|
1353
|
+
},
|
|
1134
1354
|
button: {
|
|
1135
1355
|
width: "100%",
|
|
1136
1356
|
borderRadius: "8px",
|
|
@@ -1156,6 +1376,9 @@ var styles = {
|
|
|
1156
1376
|
},
|
|
1157
1377
|
languageSelectorContainer: {
|
|
1158
1378
|
position: "relative",
|
|
1379
|
+
// Shares `headerRow` with the title, which grows; without this a long
|
|
1380
|
+
// heading squeezes the language button instead of wrapping itself.
|
|
1381
|
+
flexShrink: 0,
|
|
1159
1382
|
margin: 0,
|
|
1160
1383
|
padding: 0,
|
|
1161
1384
|
boxSizing: "border-box"
|
|
@@ -1214,6 +1437,51 @@ var styles = {
|
|
|
1214
1437
|
}
|
|
1215
1438
|
};
|
|
1216
1439
|
|
|
1440
|
+
// src/constants.ts
|
|
1441
|
+
var DEFAULT_LOGO_POSITION = "left";
|
|
1442
|
+
var DEFAULT_ACCEPT_ALL_BUTTON_TEXT = "Accept All";
|
|
1443
|
+
var DEFAULT_ACCEPT_SELECTED_BUTTON_TEXT = "Accept Selected";
|
|
1444
|
+
var DEFAULT_DECLINE_BUTTON_TEXT = "Decline";
|
|
1445
|
+
var ACCEPT_ALL_BUTTON_ID = "accept-all-button-text";
|
|
1446
|
+
var ACCEPT_SELECTED_BUTTON_ID = "confirm-button-text";
|
|
1447
|
+
var DECLINE_BUTTON_ID = "decline-button-text";
|
|
1448
|
+
var SUBMITTING_BUTTON_TEXT = "Submitting...";
|
|
1449
|
+
var LOGO_POSITION_JUSTIFY = {
|
|
1450
|
+
left: "flex-start",
|
|
1451
|
+
center: "center",
|
|
1452
|
+
right: "flex-end"
|
|
1453
|
+
};
|
|
1454
|
+
var REQUIRED_MARKER_GLYPH = "*";
|
|
1455
|
+
var REQUIRED_MARKER_ARIA_LABEL = "required";
|
|
1456
|
+
var REQUIRED_MARKER_COLOR = "red";
|
|
1457
|
+
var REQUIRED_MARKER_GAP = "4px";
|
|
1458
|
+
var REQUIRED_MARKER_STYLE = {
|
|
1459
|
+
color: REQUIRED_MARKER_COLOR,
|
|
1460
|
+
marginLeft: REQUIRED_MARKER_GAP
|
|
1461
|
+
};
|
|
1462
|
+
var DPO_AUDIO_ELEMENT_MAP = [
|
|
1463
|
+
{ audioKey: "grievance_text_audio_url", elementId: "dpo-grievance-text" },
|
|
1464
|
+
{ audioKey: "grievance_anchor_text_audio_url", elementId: "dpo-grievance-link" },
|
|
1465
|
+
{ audioKey: "grievance_email_connector_text_audio_url", elementId: "dpo-grievance-email-connector" },
|
|
1466
|
+
{ audioKey: "grievance_email_audio_url", elementId: "dpo-grievance-email" },
|
|
1467
|
+
{ audioKey: "dp_board_text_audio_url", elementId: "dpo-dp-board-text" },
|
|
1468
|
+
{ audioKey: "dp_board_anchor_text_audio_url", elementId: "dpo-dp-board-link" },
|
|
1469
|
+
{ audioKey: "dpo_text_audio_url", elementId: "dpo-dpo-text" },
|
|
1470
|
+
{ audioKey: "dpo_anchor_text_audio_url", elementId: "dpo-dpo-link" }
|
|
1471
|
+
];
|
|
1472
|
+
|
|
1473
|
+
// src/core/required-marker.ts
|
|
1474
|
+
function createRequiredMarker(label = REQUIRED_MARKER_ARIA_LABEL) {
|
|
1475
|
+
return el(
|
|
1476
|
+
"span",
|
|
1477
|
+
{ style: REQUIRED_MARKER_STYLE, "aria-label": label },
|
|
1478
|
+
REQUIRED_MARKER_GLYPH
|
|
1479
|
+
);
|
|
1480
|
+
}
|
|
1481
|
+
function hasRequiredDataElement(purpose) {
|
|
1482
|
+
return purpose.data_elements.some((element) => element.required);
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1217
1485
|
// src/modal/purposes.ts
|
|
1218
1486
|
function renderModalPurposes(purposes, options) {
|
|
1219
1487
|
var _a, _b;
|
|
@@ -1279,6 +1547,9 @@ function renderModalPurposes(purposes, options) {
|
|
|
1279
1547
|
}
|
|
1280
1548
|
});
|
|
1281
1549
|
titleEl.textContent = getTranslatedText("purposes.name", purpose.name, purpose.uuid);
|
|
1550
|
+
if (hasRequiredDataElement(purpose)) {
|
|
1551
|
+
titleEl.appendChild(createRequiredMarker());
|
|
1552
|
+
}
|
|
1282
1553
|
titleElements.set(purpose.uuid, titleEl);
|
|
1283
1554
|
const descEl = el("h3", {
|
|
1284
1555
|
id: `purpose-description-${purpose.uuid}`,
|
|
@@ -1346,9 +1617,7 @@ function renderModalPurposes(purposes, options) {
|
|
|
1346
1617
|
labelSpan.textContent = getTranslatedText("data_elements.name", dataElement.name, dataElement.uuid);
|
|
1347
1618
|
dataElementLabels.set(combinedId, labelSpan);
|
|
1348
1619
|
if (dataElement.required) {
|
|
1349
|
-
|
|
1350
|
-
asterisk.textContent = "*";
|
|
1351
|
-
labelSpan.appendChild(asterisk);
|
|
1620
|
+
labelSpan.appendChild(createRequiredMarker());
|
|
1352
1621
|
}
|
|
1353
1622
|
let deRightElement;
|
|
1354
1623
|
if (!isAlreadyConsented) {
|
|
@@ -1717,18 +1986,6 @@ var updateConsentState = (updates) => {
|
|
|
1717
1986
|
}
|
|
1718
1987
|
};
|
|
1719
1988
|
|
|
1720
|
-
// src/constants.ts
|
|
1721
|
-
var DPO_AUDIO_ELEMENT_MAP = [
|
|
1722
|
-
{ audioKey: "grievance_text_audio_url", elementId: "dpo-grievance-text" },
|
|
1723
|
-
{ audioKey: "grievance_anchor_text_audio_url", elementId: "dpo-grievance-link" },
|
|
1724
|
-
{ audioKey: "grievance_email_connector_text_audio_url", elementId: "dpo-grievance-email-connector" },
|
|
1725
|
-
{ audioKey: "grievance_email_audio_url", elementId: "dpo-grievance-email" },
|
|
1726
|
-
{ audioKey: "dp_board_text_audio_url", elementId: "dpo-dp-board-text" },
|
|
1727
|
-
{ audioKey: "dp_board_anchor_text_audio_url", elementId: "dpo-dp-board-link" },
|
|
1728
|
-
{ audioKey: "dpo_text_audio_url", elementId: "dpo-dpo-text" },
|
|
1729
|
-
{ audioKey: "dpo_anchor_text_audio_url", elementId: "dpo-dpo-link" }
|
|
1730
|
-
];
|
|
1731
|
-
|
|
1732
1989
|
// src/modal/tts.ts
|
|
1733
1990
|
var TTSManager = class {
|
|
1734
1991
|
/**
|
|
@@ -1776,9 +2033,16 @@ var TTSManager = class {
|
|
|
1776
2033
|
const response = await fetchTTSAudioUrls({
|
|
1777
2034
|
accessToken: params.accessToken,
|
|
1778
2035
|
baseUrl: params.baseUrl,
|
|
2036
|
+
ledgerBaseUrl: params.ledgerBaseUrl,
|
|
1779
2037
|
noticeUuid: params.noticeUuid,
|
|
1780
2038
|
language: params.language,
|
|
1781
|
-
signal: this.abortController.signal
|
|
2039
|
+
signal: this.abortController.signal,
|
|
2040
|
+
token: params.token,
|
|
2041
|
+
sandboxUcic: params.sandboxUcic,
|
|
2042
|
+
sandboxEmail: params.sandboxEmail,
|
|
2043
|
+
sandboxMobile: params.sandboxMobile,
|
|
2044
|
+
organisationUuid: params.organisationUuid,
|
|
2045
|
+
workspaceUuid: params.workspaceUuid
|
|
1782
2046
|
});
|
|
1783
2047
|
this.audioUrls = response.detail;
|
|
1784
2048
|
this.buildSequences(params.content);
|
|
@@ -1832,8 +2096,9 @@ var TTSManager = class {
|
|
|
1832
2096
|
push(audioUrl, mapping.elementId);
|
|
1833
2097
|
}
|
|
1834
2098
|
}
|
|
1835
|
-
push(urls.
|
|
1836
|
-
push(urls.
|
|
2099
|
+
push(urls.accept_all_button_text_audio_url, ACCEPT_ALL_BUTTON_ID);
|
|
2100
|
+
push(urls.confirm_button_text_audio_url, ACCEPT_SELECTED_BUTTON_ID);
|
|
2101
|
+
push(urls.decline_button_text_audio_url, DECLINE_BUTTON_ID);
|
|
1837
2102
|
this.audioSequence = audioSeq;
|
|
1838
2103
|
this.elementIdSequence = elementSeq;
|
|
1839
2104
|
}
|
|
@@ -2724,14 +2989,17 @@ function renderVerificationScreen(options) {
|
|
|
2724
2989
|
|
|
2725
2990
|
// src/modal/colors.ts
|
|
2726
2991
|
function resolveColors(settings, activeConfig) {
|
|
2727
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
2992
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
|
|
2728
2993
|
const primaryColor = (activeConfig == null ? void 0 : activeConfig.primary_color) || "#4f87ff";
|
|
2729
2994
|
const secondaryColor = (activeConfig == null ? void 0 : activeConfig.secondary_color) || "#4f87ff";
|
|
2730
2995
|
return {
|
|
2731
2996
|
acceptBg: ((_b = (_a = settings == null ? void 0 : settings.button) == null ? void 0 : _a.accept) == null ? void 0 : _b.backgroundColor) || primaryColor,
|
|
2732
2997
|
acceptText: ((_d = (_c = settings == null ? void 0 : settings.button) == null ? void 0 : _c.accept) == null ? void 0 : _d.textColor) || "#ffffff",
|
|
2733
|
-
|
|
2734
|
-
|
|
2998
|
+
acceptShortcutBg: ((_f = (_e = settings == null ? void 0 : settings.button) == null ? void 0 : _e.acceptAll) == null ? void 0 : _f.backgroundColor) || "#ffffff",
|
|
2999
|
+
acceptShortcutText: ((_h = (_g = settings == null ? void 0 : settings.button) == null ? void 0 : _g.acceptAll) == null ? void 0 : _h.textColor) || primaryColor,
|
|
3000
|
+
acceptShortcutBorder: primaryColor,
|
|
3001
|
+
declineBg: ((_j = (_i = settings == null ? void 0 : settings.button) == null ? void 0 : _i.decline) == null ? void 0 : _j.backgroundColor) || "#ffffff",
|
|
3002
|
+
declineText: ((_l = (_k = settings == null ? void 0 : settings.button) == null ? void 0 : _k.decline) == null ? void 0 : _l.textColor) || "#000000",
|
|
2735
3003
|
declineBorder: (settings == null ? void 0 : settings.borderColor) || "#d0d5dd",
|
|
2736
3004
|
link: (settings == null ? void 0 : settings.link) || secondaryColor,
|
|
2737
3005
|
heading: (settings == null ? void 0 : settings.headingColor) || "#323B4B",
|
|
@@ -2743,6 +3011,31 @@ function resolveColors(settings, activeConfig) {
|
|
|
2743
3011
|
};
|
|
2744
3012
|
}
|
|
2745
3013
|
|
|
3014
|
+
// src/modal/consentSelection.ts
|
|
3015
|
+
var dataElementKey = (purposeUuid, elementUuid) => `${purposeUuid}-${elementUuid}`;
|
|
3016
|
+
var buildSelectionForMode = (purposes, mode, current) => {
|
|
3017
|
+
if (mode === "selected") {
|
|
3018
|
+
return current;
|
|
3019
|
+
}
|
|
3020
|
+
const selectedPurposes = {};
|
|
3021
|
+
const selectedDataElements = {};
|
|
3022
|
+
purposes.forEach((purpose) => {
|
|
3023
|
+
selectedPurposes[purpose.uuid] = true;
|
|
3024
|
+
purpose.data_elements.forEach((element) => {
|
|
3025
|
+
selectedDataElements[dataElementKey(purpose.uuid, element.uuid)] = element.enabled;
|
|
3026
|
+
});
|
|
3027
|
+
});
|
|
3028
|
+
return { selectedPurposes, selectedDataElements };
|
|
3029
|
+
};
|
|
3030
|
+
|
|
3031
|
+
// src/modal/layout.ts
|
|
3032
|
+
function resolveLogoPosition(raw) {
|
|
3033
|
+
return raw === "center" || raw === "right" ? raw : DEFAULT_LOGO_POSITION;
|
|
3034
|
+
}
|
|
3035
|
+
function logoRowJustify(position) {
|
|
3036
|
+
return LOGO_POSITION_JUSTIFY[position];
|
|
3037
|
+
}
|
|
3038
|
+
|
|
2746
3039
|
// src/modal/index.ts
|
|
2747
3040
|
var ModalController = class {
|
|
2748
3041
|
constructor(options) {
|
|
@@ -2767,6 +3060,7 @@ var ModalController = class {
|
|
|
2767
3060
|
this.visibilityChangeHandler = null;
|
|
2768
3061
|
// DOM refs for targeted updates
|
|
2769
3062
|
this.acceptBtn = null;
|
|
3063
|
+
this.acceptAllBtn = null;
|
|
2770
3064
|
this.declineBtn = null;
|
|
2771
3065
|
this.middleSection = null;
|
|
2772
3066
|
this.errorEl = null;
|
|
@@ -2847,21 +3141,47 @@ var ModalController = class {
|
|
|
2847
3141
|
this.langSelectorRefs = null;
|
|
2848
3142
|
}
|
|
2849
3143
|
// ─── Private ───
|
|
3144
|
+
/**
|
|
3145
|
+
* Sandbox auth options to forward to every API call. When `token` is set the
|
|
3146
|
+
* API layer sends the single `X-Consent-Token` header, carries every supplied
|
|
3147
|
+
* identifier in each request's payload, and reads org/workspace from these
|
|
3148
|
+
* fields instead of decoding the JWT. Empty in the normal JWT flow.
|
|
3149
|
+
*/
|
|
3150
|
+
sandboxParams() {
|
|
3151
|
+
var _a;
|
|
3152
|
+
const legacySubject = (_a = this.options.sandboxSubject) == null ? void 0 : _a.trim();
|
|
3153
|
+
const legacyEmail = (legacySubject == null ? void 0 : legacySubject.includes("@")) ? legacySubject : void 0;
|
|
3154
|
+
const legacyMobile = legacySubject && !legacySubject.includes("@") ? legacySubject : void 0;
|
|
3155
|
+
return {
|
|
3156
|
+
token: this.options.token,
|
|
3157
|
+
// The static token carries no identity; carry every supplied identifier
|
|
3158
|
+
// (ucic/email/mobile) so the server can pick the principal by precedence
|
|
3159
|
+
// (UCIC > email > mobile) and keep the rest as the sandbox contact.
|
|
3160
|
+
// sandboxUcic / sandboxEmail / sandboxMobile stay as fallbacks.
|
|
3161
|
+
sandboxUcic: this.options.ucic || this.options.sandboxUcic,
|
|
3162
|
+
sandboxEmail: this.options.email || this.options.sandboxEmail || legacyEmail,
|
|
3163
|
+
sandboxMobile: this.options.mobile || this.options.sandboxMobile || legacyMobile,
|
|
3164
|
+
organisationUuid: this.options.organisationUuid,
|
|
3165
|
+
workspaceUuid: this.options.workspaceUuid
|
|
3166
|
+
};
|
|
3167
|
+
}
|
|
2850
3168
|
async fetchNotice() {
|
|
2851
3169
|
var _a, _b, _c, _d;
|
|
2852
3170
|
this.abortController = new AbortController();
|
|
2853
3171
|
this.store.setState({ isLoading: true, fetchError: null });
|
|
2854
3172
|
try {
|
|
3173
|
+
const noticeBaseUrl = this.options.ledgerBaseUrl && !this.options.applicationId && !this.options.token ? this.options.ledgerBaseUrl : this.options.baseUrl;
|
|
2855
3174
|
const data = await fetchConsentContent({
|
|
2856
3175
|
noticeId: this.options.noticeId,
|
|
2857
3176
|
accessToken: this.options.accessToken,
|
|
2858
3177
|
refreshToken: this.options.refreshToken,
|
|
2859
|
-
baseUrl:
|
|
3178
|
+
baseUrl: noticeBaseUrl,
|
|
2860
3179
|
language: this.options.language,
|
|
2861
3180
|
specific_uuid: this.options.applicationId,
|
|
2862
3181
|
validate_against: this.options.validateAgainst || "all",
|
|
2863
3182
|
include_fully_consented_data: this.options.includeFullyConsentedData,
|
|
2864
|
-
signal: this.abortController.signal
|
|
3183
|
+
signal: this.abortController.signal,
|
|
3184
|
+
...this.sandboxParams()
|
|
2865
3185
|
});
|
|
2866
3186
|
const activeConfig = data.detail.active_config;
|
|
2867
3187
|
let purposeSelections = data.detail.purpose_selections;
|
|
@@ -2923,10 +3243,11 @@ var ModalController = class {
|
|
|
2923
3243
|
if (this.options.includeFullyConsentedData && needsConsentPurposes.length === 0 && alreadyConsentedPurposes.length > 0) {
|
|
2924
3244
|
isFullyConsentedReviewMode = true;
|
|
2925
3245
|
}
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
3246
|
+
const selectedLanguage = resolveDisplayLanguage(
|
|
3247
|
+
this.options.language,
|
|
3248
|
+
activeConfig.default_language,
|
|
3249
|
+
activeConfig.supported_languages_and_translations
|
|
3250
|
+
);
|
|
2930
3251
|
let isMinorFlow = false;
|
|
2931
3252
|
let showAgeVerification = false;
|
|
2932
3253
|
let restoreState = {};
|
|
@@ -3224,49 +3545,32 @@ var ModalController = class {
|
|
|
3224
3545
|
(_g = this.ttsManager) == null ? void 0 : _g.fetchAudioUrls({
|
|
3225
3546
|
accessToken: this.options.accessToken,
|
|
3226
3547
|
baseUrl: this.options.baseUrl,
|
|
3548
|
+
ledgerBaseUrl: this.options.ledgerBaseUrl,
|
|
3227
3549
|
noticeUuid: activeConfig.notice_uuid,
|
|
3228
3550
|
language: this.getLanguageCode(state.selectedLanguage),
|
|
3229
|
-
content: activeConfig
|
|
3551
|
+
content: activeConfig,
|
|
3552
|
+
...this.sandboxParams()
|
|
3230
3553
|
});
|
|
3231
3554
|
const topRight = el("div", {
|
|
3232
3555
|
style: { display: "flex", alignItems: "center", gap: "8px", flexShrink: 0 }
|
|
3233
3556
|
}, this.ttsButton, this.langSelectorRefs.container);
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
{
|
|
3239
|
-
style: {
|
|
3240
|
-
display: "flex",
|
|
3241
|
-
alignItems: "center",
|
|
3242
|
-
gap: "10px",
|
|
3243
|
-
width: "100%"
|
|
3244
|
-
}
|
|
3245
|
-
},
|
|
3246
|
-
...logoImg ? [logoImg] : [],
|
|
3557
|
+
const logoPosition = resolveLogoPosition(activeConfig.logo_position);
|
|
3558
|
+
const headerRows = [];
|
|
3559
|
+
if (logoImg) {
|
|
3560
|
+
headerRows.push(
|
|
3247
3561
|
el(
|
|
3248
3562
|
"div",
|
|
3249
|
-
{ style: {
|
|
3250
|
-
|
|
3563
|
+
{ style: { ...styles.logoRow, justifyContent: logoRowJustify(logoPosition) } },
|
|
3564
|
+
logoImg
|
|
3251
3565
|
)
|
|
3252
3566
|
);
|
|
3253
|
-
topSection = el(
|
|
3254
|
-
"div",
|
|
3255
|
-
{
|
|
3256
|
-
style: {
|
|
3257
|
-
...styles.topSection,
|
|
3258
|
-
flexDirection: "column",
|
|
3259
|
-
alignItems: "stretch",
|
|
3260
|
-
flexWrap: "nowrap"
|
|
3261
|
-
}
|
|
3262
|
-
},
|
|
3263
|
-
headerRow,
|
|
3264
|
-
this.noticeTitleEl
|
|
3265
|
-
);
|
|
3266
|
-
} else {
|
|
3267
|
-
const topLeft = el("div", { style: styles.topLeft }, logoImg, this.noticeTitleEl);
|
|
3268
|
-
topSection = el("div", { style: styles.topSection }, topLeft, topRight);
|
|
3269
3567
|
}
|
|
3568
|
+
headerRows.push(el("div", { style: styles.headerRow }, this.noticeTitleEl, topRight));
|
|
3569
|
+
const topSection = el(
|
|
3570
|
+
"div",
|
|
3571
|
+
{ style: { ...styles.headerStack, gap: isMobile ? "12px" : "10px" } },
|
|
3572
|
+
...headerRows
|
|
3573
|
+
);
|
|
3270
3574
|
this.middleSection = el("div", { style: styles.middleSection });
|
|
3271
3575
|
const baseTextStyle = { ...styles.privacyText, color: textColor, fontFamily: font, fontSize: isMobile ? "14px" : "16px" };
|
|
3272
3576
|
const noticeTextSpan = el("span", { id: "privacy-notice-text" });
|
|
@@ -3457,24 +3761,57 @@ var ModalController = class {
|
|
|
3457
3761
|
display: "none"
|
|
3458
3762
|
}
|
|
3459
3763
|
});
|
|
3460
|
-
const acceptBtnText = state.isFullyConsentedReviewMode ? this.options.reviewModeButtonText || "Continue" : this.getTranslatedText(
|
|
3764
|
+
const acceptBtnText = state.isFullyConsentedReviewMode ? this.options.reviewModeButtonText || "Continue" : this.getTranslatedText(
|
|
3765
|
+
"confirm_button_text",
|
|
3766
|
+
activeConfig.confirm_button_text || DEFAULT_ACCEPT_SELECTED_BUTTON_TEXT
|
|
3767
|
+
);
|
|
3768
|
+
const gridButtonBox = {
|
|
3769
|
+
fontSize: isMobile ? "14px" : "16px",
|
|
3770
|
+
padding: state.isFullyConsentedReviewMode ? isMobile ? "10px 20px" : "9px 45px" : "9px 20px",
|
|
3771
|
+
width: state.isFullyConsentedReviewMode || isMobile ? "100%" : "auto",
|
|
3772
|
+
whiteSpace: "normal",
|
|
3773
|
+
wordBreak: "break-word"
|
|
3774
|
+
};
|
|
3461
3775
|
const acceptStyle = {
|
|
3462
3776
|
...styles.button,
|
|
3463
|
-
...styles.acceptButton,
|
|
3464
|
-
|
|
3465
|
-
|
|
3777
|
+
...state.isFullyConsentedReviewMode ? { ...styles.acceptButton, backgroundColor: colors.acceptBg, color: colors.acceptText } : {
|
|
3778
|
+
...styles.cancelButton,
|
|
3779
|
+
backgroundColor: "#ffffff",
|
|
3780
|
+
color: colors.acceptShortcutText,
|
|
3781
|
+
borderColor: colors.acceptShortcutBorder
|
|
3782
|
+
},
|
|
3466
3783
|
borderRadius: colors.borderRadius,
|
|
3467
|
-
|
|
3468
|
-
padding: isMobile ? "10px 20px" : "9px 45px"
|
|
3784
|
+
...gridButtonBox
|
|
3469
3785
|
};
|
|
3470
3786
|
this.acceptBtn = el("button", {
|
|
3471
|
-
id:
|
|
3787
|
+
id: ACCEPT_SELECTED_BUTTON_ID,
|
|
3472
3788
|
style: acceptStyle,
|
|
3473
3789
|
type: "button",
|
|
3474
|
-
onClick: () => this.handleAccept()
|
|
3790
|
+
onClick: () => this.handleAccept("selected")
|
|
3475
3791
|
});
|
|
3476
3792
|
this.acceptBtn.textContent = acceptBtnText;
|
|
3477
|
-
const
|
|
3793
|
+
const shortcutStyle = {
|
|
3794
|
+
...styles.button,
|
|
3795
|
+
...styles.acceptButton,
|
|
3796
|
+
backgroundColor: colors.acceptBg,
|
|
3797
|
+
color: colors.acceptText,
|
|
3798
|
+
borderRadius: colors.borderRadius,
|
|
3799
|
+
...gridButtonBox
|
|
3800
|
+
};
|
|
3801
|
+
this.acceptAllBtn = el("button", {
|
|
3802
|
+
id: ACCEPT_ALL_BUTTON_ID,
|
|
3803
|
+
style: shortcutStyle,
|
|
3804
|
+
type: "button",
|
|
3805
|
+
onClick: () => this.handleAccept("all")
|
|
3806
|
+
});
|
|
3807
|
+
this.acceptAllBtn.textContent = this.getTranslatedText(
|
|
3808
|
+
"accept_all_button_text",
|
|
3809
|
+
activeConfig.accept_all_button_text || DEFAULT_ACCEPT_ALL_BUTTON_TEXT
|
|
3810
|
+
);
|
|
3811
|
+
const declineBtnText = this.getTranslatedText(
|
|
3812
|
+
"decline_button_text",
|
|
3813
|
+
activeConfig.decline_button_text || DEFAULT_DECLINE_BUTTON_TEXT
|
|
3814
|
+
);
|
|
3478
3815
|
const declineStyle = {
|
|
3479
3816
|
...styles.button,
|
|
3480
3817
|
...styles.cancelButton,
|
|
@@ -3482,11 +3819,13 @@ var ModalController = class {
|
|
|
3482
3819
|
color: colors.declineText,
|
|
3483
3820
|
borderColor: colors.declineBorder,
|
|
3484
3821
|
borderRadius: colors.borderRadius,
|
|
3485
|
-
|
|
3486
|
-
|
|
3822
|
+
...gridButtonBox,
|
|
3823
|
+
// Pushes everything after decline to the right edge, which is the whole
|
|
3824
|
+
// of the wide layout. Inert in the stack and in review mode.
|
|
3825
|
+
marginRight: state.isFullyConsentedReviewMode || isMobile ? "0" : "auto"
|
|
3487
3826
|
};
|
|
3488
3827
|
this.declineBtn = el("button", {
|
|
3489
|
-
id:
|
|
3828
|
+
id: DECLINE_BUTTON_ID,
|
|
3490
3829
|
style: declineStyle,
|
|
3491
3830
|
type: "button",
|
|
3492
3831
|
onClick: () => this.handleDecline()
|
|
@@ -3494,12 +3833,19 @@ var ModalController = class {
|
|
|
3494
3833
|
if (!state.isFullyConsentedReviewMode) {
|
|
3495
3834
|
this.declineBtn.textContent = declineBtnText;
|
|
3496
3835
|
}
|
|
3497
|
-
const bottomSection = el("div", {
|
|
3498
|
-
|
|
3836
|
+
const bottomSection = el("div", {
|
|
3837
|
+
style: state.isFullyConsentedReviewMode ? styles.bottomSection : isMobile ? styles.actionButtonStack : styles.actionButtonRow
|
|
3838
|
+
});
|
|
3839
|
+
if (state.isFullyConsentedReviewMode) {
|
|
3840
|
+
bottomSection.appendChild(this.acceptBtn);
|
|
3841
|
+
} else if (isMobile) {
|
|
3842
|
+
bottomSection.appendChild(this.acceptAllBtn);
|
|
3499
3843
|
bottomSection.appendChild(this.acceptBtn);
|
|
3500
3844
|
bottomSection.appendChild(this.declineBtn);
|
|
3501
3845
|
} else {
|
|
3846
|
+
bottomSection.appendChild(this.declineBtn);
|
|
3502
3847
|
bottomSection.appendChild(this.acceptBtn);
|
|
3848
|
+
bottomSection.appendChild(this.acceptAllBtn);
|
|
3503
3849
|
}
|
|
3504
3850
|
const contentWrapper = el(
|
|
3505
3851
|
"div",
|
|
@@ -3655,7 +4001,12 @@ var ModalController = class {
|
|
|
3655
4001
|
selectedPurposes: { ...state.selectedPurposes, [purposeUuid]: shouldCheckPurpose }
|
|
3656
4002
|
});
|
|
3657
4003
|
}
|
|
3658
|
-
|
|
4004
|
+
/**
|
|
4005
|
+
* Shared by all three accept buttons. The mode only decides which
|
|
4006
|
+
* purposes/data elements are submitted — everything downstream, including
|
|
4007
|
+
* the host's `onAccept` callback, is identical.
|
|
4008
|
+
*/
|
|
4009
|
+
async handleAccept(mode = "selected") {
|
|
3659
4010
|
var _a, _b, _c;
|
|
3660
4011
|
const state = this.store.getState();
|
|
3661
4012
|
if (state.isSubmitting) return;
|
|
@@ -3674,6 +4025,16 @@ var ModalController = class {
|
|
|
3674
4025
|
if (!content) return;
|
|
3675
4026
|
this.abortController = new AbortController();
|
|
3676
4027
|
this.store.setState({ isSubmitting: true, isSubmittingAccept: true, errorMessage: null });
|
|
4028
|
+
const selection = buildSelectionForMode(content.purposes, mode, {
|
|
4029
|
+
selectedPurposes: state.selectedPurposes,
|
|
4030
|
+
selectedDataElements: state.selectedDataElements
|
|
4031
|
+
});
|
|
4032
|
+
if (mode !== "selected") {
|
|
4033
|
+
this.store.setState({
|
|
4034
|
+
selectedPurposes: selection.selectedPurposes,
|
|
4035
|
+
selectedDataElements: selection.selectedDataElements
|
|
4036
|
+
});
|
|
4037
|
+
}
|
|
3677
4038
|
try {
|
|
3678
4039
|
const purposes = content.purposes.map((purpose) => {
|
|
3679
4040
|
const dataElements = purpose.data_elements.filter((de) => de.enabled).map((de) => ({
|
|
@@ -3681,13 +4042,13 @@ var ModalController = class {
|
|
|
3681
4042
|
name: de.name,
|
|
3682
4043
|
enabled: de.enabled,
|
|
3683
4044
|
required: de.required,
|
|
3684
|
-
selected:
|
|
4045
|
+
selected: selection.selectedDataElements[`${purpose.uuid}-${de.uuid}`] || false
|
|
3685
4046
|
}));
|
|
3686
4047
|
return {
|
|
3687
4048
|
uuid: purpose.uuid,
|
|
3688
4049
|
name: purpose.name,
|
|
3689
4050
|
description: purpose.description,
|
|
3690
|
-
selected:
|
|
4051
|
+
selected: selection.selectedPurposes[purpose.uuid] || false,
|
|
3691
4052
|
data_elements: dataElements
|
|
3692
4053
|
};
|
|
3693
4054
|
});
|
|
@@ -3701,7 +4062,9 @@ var ModalController = class {
|
|
|
3701
4062
|
meta_data: this.options.applicationId ? { specific_uuid: this.options.applicationId } : void 0,
|
|
3702
4063
|
guardianVerificationReference: state.verificationReference || void 0,
|
|
3703
4064
|
selfDeclaredAdult: state.selfDeclaredAdult || void 0,
|
|
3704
|
-
|
|
4065
|
+
language: state.selectedLanguage,
|
|
4066
|
+
signal: this.abortController.signal,
|
|
4067
|
+
...this.sandboxParams()
|
|
3705
4068
|
});
|
|
3706
4069
|
this.options.onAccept();
|
|
3707
4070
|
this.destroy();
|
|
@@ -3747,11 +4110,16 @@ var ModalController = class {
|
|
|
3747
4110
|
}
|
|
3748
4111
|
updateAcceptButtonState(state) {
|
|
3749
4112
|
if (!this.acceptBtn || state.isFullyConsentedReviewMode) return;
|
|
4113
|
+
const submitting = state.isSubmittingAccept || state.isSubmittingDecline;
|
|
3750
4114
|
const allRequiredChecked = this.areAllRequiredChecked(state);
|
|
3751
|
-
const
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
4115
|
+
const setDisabled = (btn, disabled) => {
|
|
4116
|
+
if (!btn) return;
|
|
4117
|
+
btn.disabled = disabled;
|
|
4118
|
+
btn.style.opacity = disabled ? "0.5" : "1";
|
|
4119
|
+
btn.style.cursor = disabled ? "not-allowed" : "pointer";
|
|
4120
|
+
};
|
|
4121
|
+
setDisabled(this.acceptBtn, !allRequiredChecked || submitting);
|
|
4122
|
+
setDisabled(this.acceptAllBtn, submitting);
|
|
3755
4123
|
}
|
|
3756
4124
|
updateCollapseState(state) {
|
|
3757
4125
|
const updateRefs = (refs) => {
|
|
@@ -4124,25 +4492,34 @@ var ModalController = class {
|
|
|
4124
4492
|
}
|
|
4125
4493
|
updateButtonStates(state) {
|
|
4126
4494
|
var _a;
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4495
|
+
const content = (_a = state.content) == null ? void 0 : _a.detail.active_config;
|
|
4496
|
+
const applySubmittingState = (btn, idleLabel) => {
|
|
4497
|
+
if (!btn) return;
|
|
4498
|
+
btn.textContent = state.isSubmittingAccept ? SUBMITTING_BUTTON_TEXT : idleLabel;
|
|
4499
|
+
btn.disabled = state.isSubmittingAccept;
|
|
4500
|
+
btn.style.opacity = state.isSubmittingAccept ? "0.7" : "1";
|
|
4501
|
+
btn.style.cursor = state.isSubmittingAccept ? "not-allowed" : "pointer";
|
|
4502
|
+
};
|
|
4503
|
+
applySubmittingState(
|
|
4504
|
+
this.acceptBtn,
|
|
4505
|
+
state.isFullyConsentedReviewMode ? this.options.reviewModeButtonText || "Continue" : this.getTranslatedText(
|
|
4506
|
+
"confirm_button_text",
|
|
4507
|
+
(content == null ? void 0 : content.confirm_button_text) || DEFAULT_ACCEPT_SELECTED_BUTTON_TEXT
|
|
4508
|
+
)
|
|
4509
|
+
);
|
|
4510
|
+
applySubmittingState(
|
|
4511
|
+
this.acceptAllBtn,
|
|
4512
|
+
this.getTranslatedText(
|
|
4513
|
+
"accept_all_button_text",
|
|
4514
|
+
(content == null ? void 0 : content.accept_all_button_text) || DEFAULT_ACCEPT_ALL_BUTTON_TEXT
|
|
4515
|
+
)
|
|
4516
|
+
);
|
|
4141
4517
|
if (this.declineBtn) {
|
|
4142
4518
|
this.declineBtn.disabled = state.isSubmitting;
|
|
4143
4519
|
this.declineBtn.style.opacity = state.isSubmitting ? "0.7" : "1";
|
|
4144
4520
|
this.declineBtn.style.cursor = state.isSubmitting ? "not-allowed" : "pointer";
|
|
4145
4521
|
}
|
|
4522
|
+
this.updateAcceptButtonState(state);
|
|
4146
4523
|
}
|
|
4147
4524
|
};
|
|
4148
4525
|
|
|
@@ -4343,6 +4720,9 @@ function renderInlinePurposes(purposes, options) {
|
|
|
4343
4720
|
}
|
|
4344
4721
|
});
|
|
4345
4722
|
titleEl.textContent = getTranslatedText("purposes.name", purpose.name, purpose.uuid);
|
|
4723
|
+
if (hasRequiredDataElement(purpose)) {
|
|
4724
|
+
titleEl.appendChild(createRequiredMarker());
|
|
4725
|
+
}
|
|
4346
4726
|
titleElements.set(purpose.uuid, titleEl);
|
|
4347
4727
|
const descEl = el("h3", {
|
|
4348
4728
|
style: {
|
|
@@ -4395,12 +4775,7 @@ function renderInlinePurposes(purposes, options) {
|
|
|
4395
4775
|
labelSpan.textContent = getTranslatedText("data_elements.name", dataElement.name, dataElement.uuid);
|
|
4396
4776
|
dataElementLabels.set(combinedId, labelSpan);
|
|
4397
4777
|
if (dataElement.required) {
|
|
4398
|
-
|
|
4399
|
-
style: { color: "red", marginLeft: "4px" },
|
|
4400
|
-
"aria-label": "required"
|
|
4401
|
-
});
|
|
4402
|
-
asterisk.textContent = "*";
|
|
4403
|
-
labelSpan.appendChild(asterisk);
|
|
4778
|
+
labelSpan.appendChild(createRequiredMarker());
|
|
4404
4779
|
}
|
|
4405
4780
|
const deCheckbox = el("input", {
|
|
4406
4781
|
type: "checkbox",
|
|
@@ -4446,6 +4821,8 @@ var InlineController = class {
|
|
|
4446
4821
|
this.errorEl = null;
|
|
4447
4822
|
this.hasSubmitted = false;
|
|
4448
4823
|
this.hasCheckedLedgerConsent = false;
|
|
4824
|
+
this.fetchedWithToken = false;
|
|
4825
|
+
this.fetchGeneration = 0;
|
|
4449
4826
|
this.unsubscribe = null;
|
|
4450
4827
|
this.abortController = null;
|
|
4451
4828
|
this.options = options;
|
|
@@ -4503,6 +4880,10 @@ var InlineController = class {
|
|
|
4503
4880
|
}
|
|
4504
4881
|
updateToken(accessToken) {
|
|
4505
4882
|
this.options = { ...this.options, accessToken };
|
|
4883
|
+
if (accessToken && !this.fetchedWithToken) {
|
|
4884
|
+
void this.fetchNotice();
|
|
4885
|
+
return;
|
|
4886
|
+
}
|
|
4506
4887
|
if (this.shouldCheckLedgerConsent()) {
|
|
4507
4888
|
void this.checkLedgerConsent();
|
|
4508
4889
|
return;
|
|
@@ -4518,17 +4899,20 @@ var InlineController = class {
|
|
|
4518
4899
|
}
|
|
4519
4900
|
async fetchNotice() {
|
|
4520
4901
|
var _a, _b, _c, _d;
|
|
4521
|
-
|
|
4902
|
+
const generation = ++this.fetchGeneration;
|
|
4903
|
+
this.fetchedWithToken = Boolean(this.options.accessToken);
|
|
4904
|
+
this.store.setState({ isLoading: true, fetchError: null, hasAlreadyConsented: false });
|
|
4522
4905
|
try {
|
|
4523
4906
|
const data = await fetchConsentContentPublic({
|
|
4524
4907
|
org_uuid: this.options.org_uuid,
|
|
4525
4908
|
workspace_uuid: this.options.workspace_uuid,
|
|
4526
4909
|
notice_uuid: this.options.notice_uuid,
|
|
4910
|
+
accessToken: this.options.accessToken,
|
|
4527
4911
|
baseUrl: this.options.baseUrl,
|
|
4528
4912
|
language: this.options.language,
|
|
4529
4913
|
specific_uuid: this.options.applicationId
|
|
4530
4914
|
});
|
|
4531
|
-
if (this.isDestroyed()) {
|
|
4915
|
+
if (this.isDestroyed() || generation !== this.fetchGeneration) {
|
|
4532
4916
|
return;
|
|
4533
4917
|
}
|
|
4534
4918
|
const activeConfig = data.detail.active_config;
|
|
@@ -4546,13 +4930,17 @@ var InlineController = class {
|
|
|
4546
4930
|
this.store.setState({
|
|
4547
4931
|
content: activeConfig,
|
|
4548
4932
|
isLoading: false,
|
|
4549
|
-
selectedLanguage:
|
|
4933
|
+
selectedLanguage: resolveDisplayLanguage(
|
|
4934
|
+
this.options.language,
|
|
4935
|
+
activeConfig.default_language,
|
|
4936
|
+
activeConfig.supported_languages_and_translations
|
|
4937
|
+
),
|
|
4550
4938
|
collapsedPurposes: initialCollapsed,
|
|
4551
4939
|
selectedPurposes: initialPurposes,
|
|
4552
4940
|
selectedDataElements: initialElements
|
|
4553
4941
|
});
|
|
4554
4942
|
} catch (err) {
|
|
4555
|
-
if (this.isDestroyed()) {
|
|
4943
|
+
if (this.isDestroyed() || generation !== this.fetchGeneration) {
|
|
4556
4944
|
return;
|
|
4557
4945
|
}
|
|
4558
4946
|
const error = err;
|
|
@@ -4869,7 +5257,8 @@ var InlineController = class {
|
|
|
4869
5257
|
};
|
|
4870
5258
|
}),
|
|
4871
5259
|
declined: false,
|
|
4872
|
-
meta_data: this.options.applicationId ? { specific_uuid: this.options.applicationId } : void 0
|
|
5260
|
+
meta_data: this.options.applicationId ? { specific_uuid: this.options.applicationId } : void 0,
|
|
5261
|
+
language: state.selectedLanguage
|
|
4873
5262
|
});
|
|
4874
5263
|
(_b = (_a = this.options).onAccept) == null ? void 0 : _b.call(_a);
|
|
4875
5264
|
this.hasSubmitted = false;
|