@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.mjs
CHANGED
|
@@ -152,17 +152,6 @@ function createMediaQuery(query) {
|
|
|
152
152
|
};
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
// src/core/jwt-decode.ts
|
|
156
|
-
import { jwtDecode } from "jwt-decode";
|
|
157
|
-
function decodeTokenSafely(token) {
|
|
158
|
-
try {
|
|
159
|
-
return jwtDecode(token);
|
|
160
|
-
} catch (error) {
|
|
161
|
-
console.warn("Failed to decode JWT token:", error);
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
155
|
// src/services/cache.ts
|
|
167
156
|
var apiCache = /* @__PURE__ */ new Map();
|
|
168
157
|
var CACHE_TTL = 5 * 60 * 1e3;
|
|
@@ -183,6 +172,141 @@ function clearApiCache() {
|
|
|
183
172
|
apiCache.clear();
|
|
184
173
|
}
|
|
185
174
|
|
|
175
|
+
// src/core/jwt-decode.ts
|
|
176
|
+
import { jwtDecode } from "jwt-decode";
|
|
177
|
+
function decodeTokenSafely(token) {
|
|
178
|
+
try {
|
|
179
|
+
return jwtDecode(token);
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.warn("Failed to decode JWT token:", error);
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/api/sandbox.ts
|
|
187
|
+
var SANDBOX_TOKEN_HEADER = "X-Consent-Token";
|
|
188
|
+
var resolveSandboxAuth = (options) => {
|
|
189
|
+
var _a, _b, _c, _d, _e, _f;
|
|
190
|
+
const token = (_a = options.token) == null ? void 0 : _a.trim();
|
|
191
|
+
if (!token) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
const organisationUuid = (_b = options.organisationUuid) == null ? void 0 : _b.trim();
|
|
195
|
+
const workspaceUuid = (_c = options.workspaceUuid) == null ? void 0 : _c.trim();
|
|
196
|
+
const ucic = (_d = options.sandboxUcic) == null ? void 0 : _d.trim();
|
|
197
|
+
const email = (_e = options.sandboxEmail) == null ? void 0 : _e.trim();
|
|
198
|
+
const mobile = (_f = options.sandboxMobile) == null ? void 0 : _f.trim();
|
|
199
|
+
if (!organisationUuid || !workspaceUuid) {
|
|
200
|
+
throw new Error(
|
|
201
|
+
"Sandbox mode requires organisationUuid and workspaceUuid"
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
if (!ucic && !email && !mobile) {
|
|
205
|
+
throw new Error("Sandbox mode requires a UCIC, email, or mobile");
|
|
206
|
+
}
|
|
207
|
+
return { token, ucic, email, mobile, organisationUuid, workspaceUuid };
|
|
208
|
+
};
|
|
209
|
+
var resolveRequestAuth = (accessToken, options) => {
|
|
210
|
+
const sandbox = resolveSandboxAuth(options);
|
|
211
|
+
if (sandbox) {
|
|
212
|
+
return {
|
|
213
|
+
organisationUuid: sandbox.organisationUuid,
|
|
214
|
+
workspaceUuid: sandbox.workspaceUuid,
|
|
215
|
+
sandbox
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
const decodedToken = accessToken ? decodeTokenSafely(accessToken) : null;
|
|
219
|
+
if (!decodedToken) {
|
|
220
|
+
throw new Error("Invalid access token");
|
|
221
|
+
}
|
|
222
|
+
const { organisation_uuid, workspace_uuid } = decodedToken;
|
|
223
|
+
if (!organisation_uuid || !workspace_uuid) {
|
|
224
|
+
throw new Error("Invalid token: missing organization or workspace UUID");
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
organisationUuid: organisation_uuid,
|
|
228
|
+
workspaceUuid: workspace_uuid,
|
|
229
|
+
sandbox: null
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
var buildAuthHeaders = (accessToken, sandbox) => {
|
|
233
|
+
if (sandbox) {
|
|
234
|
+
return { [SANDBOX_TOKEN_HEADER]: sandbox.token };
|
|
235
|
+
}
|
|
236
|
+
return { Authorization: `Bearer ${accessToken}` };
|
|
237
|
+
};
|
|
238
|
+
var sandboxBodyIdentity = (sandbox) => {
|
|
239
|
+
const payload = {};
|
|
240
|
+
if (sandbox.ucic) {
|
|
241
|
+
payload.org_user_id = sandbox.ucic;
|
|
242
|
+
}
|
|
243
|
+
if (sandbox.email) {
|
|
244
|
+
payload.primary_email = sandbox.email;
|
|
245
|
+
}
|
|
246
|
+
if (sandbox.mobile) {
|
|
247
|
+
payload.primary_mobile = sandbox.mobile;
|
|
248
|
+
}
|
|
249
|
+
return payload;
|
|
250
|
+
};
|
|
251
|
+
var sandboxQueryIdentity = (sandbox) => sandboxBodyIdentity(sandbox);
|
|
252
|
+
|
|
253
|
+
// src/api/constants.ts
|
|
254
|
+
var CODE_BY_NAME = /* @__PURE__ */ new Map([
|
|
255
|
+
["english", "en"],
|
|
256
|
+
["assamese", "as"],
|
|
257
|
+
["bengali", "bn"],
|
|
258
|
+
["bodo", "brx"],
|
|
259
|
+
["dogri", "doi"],
|
|
260
|
+
["goan konkani", "gom"],
|
|
261
|
+
["gujarati", "gu"],
|
|
262
|
+
["hindi", "hi"],
|
|
263
|
+
["kannada", "kn"],
|
|
264
|
+
["kashmiri", "ks"],
|
|
265
|
+
["maithili", "mai"],
|
|
266
|
+
["malayalam", "ml"],
|
|
267
|
+
["manipuri", "mni-Mtei"],
|
|
268
|
+
["marathi", "mr"],
|
|
269
|
+
["nepali", "ne"],
|
|
270
|
+
["odia", "or"],
|
|
271
|
+
["punjabi", "pa"],
|
|
272
|
+
["sanskrit", "sa"],
|
|
273
|
+
["santali", "sat"],
|
|
274
|
+
["sindhi", "sd"],
|
|
275
|
+
["tamil", "ta"],
|
|
276
|
+
["telugu", "te"],
|
|
277
|
+
["urdu", "ur"]
|
|
278
|
+
]);
|
|
279
|
+
var CODE_BY_LOWER = new Map(
|
|
280
|
+
[...CODE_BY_NAME.values()].map((c) => [c.toLowerCase(), c])
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
// src/api/language.ts
|
|
284
|
+
function toLanguageCode(lang) {
|
|
285
|
+
var _a, _b;
|
|
286
|
+
if (!lang) {
|
|
287
|
+
return void 0;
|
|
288
|
+
}
|
|
289
|
+
const trimmed = lang.trim();
|
|
290
|
+
if (!trimmed) {
|
|
291
|
+
return void 0;
|
|
292
|
+
}
|
|
293
|
+
const lower = trimmed.toLowerCase();
|
|
294
|
+
return (_b = (_a = CODE_BY_LOWER.get(lower)) != null ? _a : CODE_BY_NAME.get(lower)) != null ? _b : trimmed;
|
|
295
|
+
}
|
|
296
|
+
function resolveDisplayLanguage(requested, defaultLanguage, translations) {
|
|
297
|
+
const fallback = defaultLanguage || "en";
|
|
298
|
+
const requestedCode = toLanguageCode(requested);
|
|
299
|
+
if (!requestedCode) {
|
|
300
|
+
return fallback;
|
|
301
|
+
}
|
|
302
|
+
for (const key of Object.keys(translations || {})) {
|
|
303
|
+
if (toLanguageCode(key) === requestedCode) {
|
|
304
|
+
return key;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return fallback;
|
|
308
|
+
}
|
|
309
|
+
|
|
186
310
|
// src/api/index.ts
|
|
187
311
|
var BASE_URL = "https://api.redacto.io/consent";
|
|
188
312
|
var BASE_URL_PUBLIC = "https://api.redacto.tech/consent";
|
|
@@ -194,17 +318,22 @@ var fetchConsentContent = async ({
|
|
|
194
318
|
specific_uuid,
|
|
195
319
|
validate_against = "all",
|
|
196
320
|
include_fully_consented_data = false,
|
|
197
|
-
signal
|
|
321
|
+
signal,
|
|
322
|
+
...sandboxParams
|
|
198
323
|
}) => {
|
|
199
|
-
if (!noticeId || !
|
|
200
|
-
throw new Error("noticeId
|
|
324
|
+
if (!noticeId || !validate_against) {
|
|
325
|
+
throw new Error("noticeId is required");
|
|
201
326
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
327
|
+
if (!accessToken && !sandboxParams.token) {
|
|
328
|
+
throw new Error("accessToken or token is required");
|
|
329
|
+
}
|
|
330
|
+
const { organisationUuid: ORG, workspaceUuid: WS, sandbox } = resolveRequestAuth(
|
|
331
|
+
accessToken,
|
|
332
|
+
sandboxParams
|
|
333
|
+
);
|
|
206
334
|
const apiBaseUrl = baseUrl || BASE_URL;
|
|
207
|
-
const
|
|
335
|
+
const cacheIdentity = sandbox ? `sandbox:${sandbox.organisationUuid}:${sandbox.workspaceUuid}:${sandbox.ucic || sandbox.email || sandbox.mobile}` : accessToken;
|
|
336
|
+
const cacheKey = `${cacheIdentity}-${noticeId}-${validate_against}-${language}-${specific_uuid || ""}-${include_fully_consented_data}`;
|
|
208
337
|
if (validate_against === "all" && !specific_uuid) {
|
|
209
338
|
const cached = getCachedData(cacheKey);
|
|
210
339
|
if (cached) return cached;
|
|
@@ -213,10 +342,15 @@ var fetchConsentContent = async ({
|
|
|
213
342
|
if (specific_uuid) url.searchParams.append("specific_uuid", specific_uuid);
|
|
214
343
|
url.searchParams.append("validate_against", validate_against);
|
|
215
344
|
if (include_fully_consented_data) url.searchParams.append("include_fully_consented_data", "true");
|
|
345
|
+
if (sandbox) {
|
|
346
|
+
for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
|
|
347
|
+
if (value) url.searchParams.append(key, value);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
216
350
|
const response = await fetch(url.toString(), {
|
|
217
351
|
method: "GET",
|
|
218
352
|
headers: {
|
|
219
|
-
|
|
353
|
+
...buildAuthHeaders(accessToken, sandbox),
|
|
220
354
|
"Accept-Language": language
|
|
221
355
|
},
|
|
222
356
|
signal
|
|
@@ -242,18 +376,24 @@ var fetchConsentContentPublic = async ({
|
|
|
242
376
|
org_uuid,
|
|
243
377
|
workspace_uuid,
|
|
244
378
|
notice_uuid,
|
|
379
|
+
accessToken,
|
|
245
380
|
baseUrl,
|
|
246
381
|
language = "en",
|
|
247
382
|
specific_uuid
|
|
248
383
|
}) => {
|
|
249
384
|
const apiBaseUrl = baseUrl || BASE_URL_PUBLIC;
|
|
385
|
+
const noticesUrl = `${apiBaseUrl}/public/organisations/${org_uuid}/workspaces/${workspace_uuid}/notices`;
|
|
250
386
|
const url = new URL(
|
|
251
|
-
`${
|
|
387
|
+
accessToken ? `${noticesUrl}/${notice_uuid}` : `${noticesUrl}/get-notice/${notice_uuid}`
|
|
252
388
|
);
|
|
253
389
|
if (specific_uuid) url.searchParams.append("specific_uuid", specific_uuid);
|
|
254
390
|
const response = await fetch(url.toString(), {
|
|
255
391
|
method: "GET",
|
|
256
|
-
headers: {
|
|
392
|
+
headers: {
|
|
393
|
+
"Content-Type": "application/json",
|
|
394
|
+
"Accept-Language": language,
|
|
395
|
+
...accessToken ? { Authorization: `Bearer ${accessToken}` } : {}
|
|
396
|
+
}
|
|
257
397
|
});
|
|
258
398
|
if (response.status === 401) {
|
|
259
399
|
const error = new Error("Unauthorized");
|
|
@@ -280,16 +420,21 @@ var submitConsentEvent = async ({
|
|
|
280
420
|
meta_data,
|
|
281
421
|
guardianVerificationReference,
|
|
282
422
|
selfDeclaredAdult,
|
|
283
|
-
|
|
423
|
+
language,
|
|
424
|
+
signal,
|
|
425
|
+
...sandboxParams
|
|
284
426
|
}) => {
|
|
285
|
-
if (!noticeUuid || !
|
|
286
|
-
throw new Error("noticeUuid
|
|
427
|
+
if (!noticeUuid || !Array.isArray(purposes)) {
|
|
428
|
+
throw new Error("noticeUuid and purposes array are required");
|
|
287
429
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
430
|
+
if (!accessToken && !sandboxParams.token) {
|
|
431
|
+
throw new Error("accessToken or token is required");
|
|
432
|
+
}
|
|
433
|
+
const { organisationUuid: ORG, workspaceUuid: WS, sandbox } = resolveRequestAuth(
|
|
434
|
+
accessToken,
|
|
435
|
+
sandboxParams
|
|
436
|
+
);
|
|
437
|
+
const apiBaseUrl = (sandbox ? null : ledgerBaseUrl) || baseUrl || BASE_URL;
|
|
293
438
|
const validatedPurposes = purposes.map((purpose) => {
|
|
294
439
|
if (!purpose.uuid) throw new Error("Invalid purpose data: missing uuid");
|
|
295
440
|
const dataElements = (purpose.data_elements || []).filter((element) => element.enabled).map((element) => ({
|
|
@@ -310,17 +455,19 @@ var submitConsentEvent = async ({
|
|
|
310
455
|
declined: Boolean(declined),
|
|
311
456
|
meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid, minor_age: meta_data.minor_age } : void 0,
|
|
312
457
|
guardian_verification_reference: guardianVerificationReference,
|
|
313
|
-
self_declared_adult: selfDeclaredAdult
|
|
458
|
+
self_declared_adult: selfDeclaredAdult,
|
|
459
|
+
language: toLanguageCode(language)
|
|
314
460
|
};
|
|
461
|
+
const requestBody = sandbox ? { ...payload, ...sandboxBodyIdentity(sandbox) } : payload;
|
|
315
462
|
const response = await fetch(
|
|
316
463
|
`${apiBaseUrl}/public/organisations/${ORG}/workspaces/${WS}/submit-consent`,
|
|
317
464
|
{
|
|
318
465
|
method: "POST",
|
|
319
466
|
headers: {
|
|
320
|
-
|
|
467
|
+
...buildAuthHeaders(accessToken, sandbox),
|
|
321
468
|
"Content-Type": "application/json"
|
|
322
469
|
},
|
|
323
|
-
body: JSON.stringify(
|
|
470
|
+
body: JSON.stringify(requestBody),
|
|
324
471
|
signal
|
|
325
472
|
}
|
|
326
473
|
);
|
|
@@ -352,7 +499,8 @@ var submitConsentEventPublic = async ({
|
|
|
352
499
|
noticeUuid,
|
|
353
500
|
purposes,
|
|
354
501
|
declined,
|
|
355
|
-
meta_data
|
|
502
|
+
meta_data,
|
|
503
|
+
language
|
|
356
504
|
}) => {
|
|
357
505
|
var _a, _b;
|
|
358
506
|
const apiBaseUrl = ledgerBaseUrl || baseUrl || BASE_URL_PUBLIC;
|
|
@@ -373,7 +521,8 @@ var submitConsentEventPublic = async ({
|
|
|
373
521
|
select_all_mandatory: false,
|
|
374
522
|
source: "WEB",
|
|
375
523
|
declined,
|
|
376
|
-
meta_data
|
|
524
|
+
meta_data,
|
|
525
|
+
language: toLanguageCode(language)
|
|
377
526
|
};
|
|
378
527
|
const response = await fetch(
|
|
379
528
|
`${apiBaseUrl}/public/organisations/${org_uuid}/workspaces/${workspace_uuid}/submit-consent`,
|
|
@@ -412,23 +561,32 @@ var submitConsentEventPublic = async ({
|
|
|
412
561
|
var fetchTTSAudioUrls = async ({
|
|
413
562
|
accessToken,
|
|
414
563
|
baseUrl,
|
|
564
|
+
ledgerBaseUrl,
|
|
415
565
|
noticeUuid,
|
|
416
566
|
language,
|
|
417
|
-
signal
|
|
567
|
+
signal,
|
|
568
|
+
...sandboxParams
|
|
418
569
|
}) => {
|
|
419
|
-
if (!accessToken || !noticeUuid || !language) {
|
|
420
|
-
throw new Error("accessToken, noticeUuid, and language are required");
|
|
570
|
+
if (!accessToken && !sandboxParams.token || !noticeUuid || !language) {
|
|
571
|
+
throw new Error("accessToken (or token), noticeUuid, and language are required");
|
|
421
572
|
}
|
|
422
|
-
const
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
const apiBaseUrl = baseUrl || BASE_URL;
|
|
427
|
-
const url =
|
|
428
|
-
|
|
573
|
+
const { organisationUuid: ORG, workspaceUuid: WS, sandbox } = resolveRequestAuth(
|
|
574
|
+
accessToken,
|
|
575
|
+
sandboxParams
|
|
576
|
+
);
|
|
577
|
+
const apiBaseUrl = (sandbox ? null : ledgerBaseUrl) || baseUrl || BASE_URL;
|
|
578
|
+
const url = new URL(
|
|
579
|
+
`${apiBaseUrl}/public/organisations/${ORG}/workspaces/${WS}/notices/${noticeUuid}/audio/${language}`
|
|
580
|
+
);
|
|
581
|
+
if (sandbox) {
|
|
582
|
+
for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
|
|
583
|
+
if (value) url.searchParams.append(key, value);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
const response = await fetch(url.toString(), {
|
|
429
587
|
method: "GET",
|
|
430
588
|
headers: {
|
|
431
|
-
|
|
589
|
+
...buildAuthHeaders(accessToken, sandbox),
|
|
432
590
|
Accept: "application/json"
|
|
433
591
|
},
|
|
434
592
|
signal
|
|
@@ -895,6 +1053,42 @@ var styles = {
|
|
|
895
1053
|
padding: 0,
|
|
896
1054
|
boxSizing: "border-box"
|
|
897
1055
|
},
|
|
1056
|
+
// The main notice header is two stacked rows: the logo alone on row 1,
|
|
1057
|
+
// aligned per active_config.logo_position, then the title and the
|
|
1058
|
+
// TTS/language controls on row 2. `topSection` and `topLeft` above keep their
|
|
1059
|
+
// single-row shape for the age-verification, guardian and verification
|
|
1060
|
+
// screens, which have no controls to place and share those styles.
|
|
1061
|
+
// Mirrors consent-sdk-react/src/RedactoNoticeConsent/styles.ts.
|
|
1062
|
+
headerStack: {
|
|
1063
|
+
display: "flex",
|
|
1064
|
+
flexDirection: "column",
|
|
1065
|
+
alignItems: "stretch",
|
|
1066
|
+
gap: "10px",
|
|
1067
|
+
paddingBottom: "15px",
|
|
1068
|
+
borderBottom: "none",
|
|
1069
|
+
backgroundColor: "#ffffff",
|
|
1070
|
+
margin: 0,
|
|
1071
|
+
boxSizing: "border-box",
|
|
1072
|
+
textAlign: "left"
|
|
1073
|
+
},
|
|
1074
|
+
logoRow: {
|
|
1075
|
+
display: "flex",
|
|
1076
|
+
alignItems: "center",
|
|
1077
|
+
width: "100%",
|
|
1078
|
+
margin: 0,
|
|
1079
|
+
padding: 0,
|
|
1080
|
+
boxSizing: "border-box"
|
|
1081
|
+
},
|
|
1082
|
+
headerRow: {
|
|
1083
|
+
display: "flex",
|
|
1084
|
+
alignItems: "center",
|
|
1085
|
+
justifyContent: "space-between",
|
|
1086
|
+
gap: "10px",
|
|
1087
|
+
width: "100%",
|
|
1088
|
+
margin: 0,
|
|
1089
|
+
padding: 0,
|
|
1090
|
+
boxSizing: "border-box"
|
|
1091
|
+
},
|
|
898
1092
|
logo: {
|
|
899
1093
|
height: "32px",
|
|
900
1094
|
width: "auto",
|
|
@@ -1101,6 +1295,32 @@ var styles = {
|
|
|
1101
1295
|
margin: 0,
|
|
1102
1296
|
boxSizing: "border-box"
|
|
1103
1297
|
},
|
|
1298
|
+
/**
|
|
1299
|
+
* The main notice footer. Wide: one row with decline pushed left and the
|
|
1300
|
+
* accepts grouped right. Narrow: a full-width stack. Review mode keeps
|
|
1301
|
+
* `bottomSection` — it collapses to a single button.
|
|
1302
|
+
*/
|
|
1303
|
+
actionButtonRow: {
|
|
1304
|
+
display: "flex",
|
|
1305
|
+
flexDirection: "row",
|
|
1306
|
+
alignItems: "center",
|
|
1307
|
+
gap: "12px",
|
|
1308
|
+
paddingTop: "15px",
|
|
1309
|
+
borderTop: "none",
|
|
1310
|
+
backgroundColor: "#ffffff",
|
|
1311
|
+
margin: 0,
|
|
1312
|
+
boxSizing: "border-box"
|
|
1313
|
+
},
|
|
1314
|
+
actionButtonStack: {
|
|
1315
|
+
display: "flex",
|
|
1316
|
+
flexDirection: "column",
|
|
1317
|
+
gap: "12px",
|
|
1318
|
+
paddingTop: "15px",
|
|
1319
|
+
borderTop: "none",
|
|
1320
|
+
backgroundColor: "#ffffff",
|
|
1321
|
+
margin: 0,
|
|
1322
|
+
boxSizing: "border-box"
|
|
1323
|
+
},
|
|
1104
1324
|
button: {
|
|
1105
1325
|
width: "100%",
|
|
1106
1326
|
borderRadius: "8px",
|
|
@@ -1126,6 +1346,9 @@ var styles = {
|
|
|
1126
1346
|
},
|
|
1127
1347
|
languageSelectorContainer: {
|
|
1128
1348
|
position: "relative",
|
|
1349
|
+
// Shares `headerRow` with the title, which grows; without this a long
|
|
1350
|
+
// heading squeezes the language button instead of wrapping itself.
|
|
1351
|
+
flexShrink: 0,
|
|
1129
1352
|
margin: 0,
|
|
1130
1353
|
padding: 0,
|
|
1131
1354
|
boxSizing: "border-box"
|
|
@@ -1184,6 +1407,51 @@ var styles = {
|
|
|
1184
1407
|
}
|
|
1185
1408
|
};
|
|
1186
1409
|
|
|
1410
|
+
// src/constants.ts
|
|
1411
|
+
var DEFAULT_LOGO_POSITION = "left";
|
|
1412
|
+
var DEFAULT_ACCEPT_ALL_BUTTON_TEXT = "Accept All";
|
|
1413
|
+
var DEFAULT_ACCEPT_SELECTED_BUTTON_TEXT = "Accept Selected";
|
|
1414
|
+
var DEFAULT_DECLINE_BUTTON_TEXT = "Decline";
|
|
1415
|
+
var ACCEPT_ALL_BUTTON_ID = "accept-all-button-text";
|
|
1416
|
+
var ACCEPT_SELECTED_BUTTON_ID = "confirm-button-text";
|
|
1417
|
+
var DECLINE_BUTTON_ID = "decline-button-text";
|
|
1418
|
+
var SUBMITTING_BUTTON_TEXT = "Submitting...";
|
|
1419
|
+
var LOGO_POSITION_JUSTIFY = {
|
|
1420
|
+
left: "flex-start",
|
|
1421
|
+
center: "center",
|
|
1422
|
+
right: "flex-end"
|
|
1423
|
+
};
|
|
1424
|
+
var REQUIRED_MARKER_GLYPH = "*";
|
|
1425
|
+
var REQUIRED_MARKER_ARIA_LABEL = "required";
|
|
1426
|
+
var REQUIRED_MARKER_COLOR = "red";
|
|
1427
|
+
var REQUIRED_MARKER_GAP = "4px";
|
|
1428
|
+
var REQUIRED_MARKER_STYLE = {
|
|
1429
|
+
color: REQUIRED_MARKER_COLOR,
|
|
1430
|
+
marginLeft: REQUIRED_MARKER_GAP
|
|
1431
|
+
};
|
|
1432
|
+
var DPO_AUDIO_ELEMENT_MAP = [
|
|
1433
|
+
{ audioKey: "grievance_text_audio_url", elementId: "dpo-grievance-text" },
|
|
1434
|
+
{ audioKey: "grievance_anchor_text_audio_url", elementId: "dpo-grievance-link" },
|
|
1435
|
+
{ audioKey: "grievance_email_connector_text_audio_url", elementId: "dpo-grievance-email-connector" },
|
|
1436
|
+
{ audioKey: "grievance_email_audio_url", elementId: "dpo-grievance-email" },
|
|
1437
|
+
{ audioKey: "dp_board_text_audio_url", elementId: "dpo-dp-board-text" },
|
|
1438
|
+
{ audioKey: "dp_board_anchor_text_audio_url", elementId: "dpo-dp-board-link" },
|
|
1439
|
+
{ audioKey: "dpo_text_audio_url", elementId: "dpo-dpo-text" },
|
|
1440
|
+
{ audioKey: "dpo_anchor_text_audio_url", elementId: "dpo-dpo-link" }
|
|
1441
|
+
];
|
|
1442
|
+
|
|
1443
|
+
// src/core/required-marker.ts
|
|
1444
|
+
function createRequiredMarker(label = REQUIRED_MARKER_ARIA_LABEL) {
|
|
1445
|
+
return el(
|
|
1446
|
+
"span",
|
|
1447
|
+
{ style: REQUIRED_MARKER_STYLE, "aria-label": label },
|
|
1448
|
+
REQUIRED_MARKER_GLYPH
|
|
1449
|
+
);
|
|
1450
|
+
}
|
|
1451
|
+
function hasRequiredDataElement(purpose) {
|
|
1452
|
+
return purpose.data_elements.some((element) => element.required);
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1187
1455
|
// src/modal/purposes.ts
|
|
1188
1456
|
function renderModalPurposes(purposes, options) {
|
|
1189
1457
|
var _a, _b;
|
|
@@ -1249,6 +1517,9 @@ function renderModalPurposes(purposes, options) {
|
|
|
1249
1517
|
}
|
|
1250
1518
|
});
|
|
1251
1519
|
titleEl.textContent = getTranslatedText("purposes.name", purpose.name, purpose.uuid);
|
|
1520
|
+
if (hasRequiredDataElement(purpose)) {
|
|
1521
|
+
titleEl.appendChild(createRequiredMarker());
|
|
1522
|
+
}
|
|
1252
1523
|
titleElements.set(purpose.uuid, titleEl);
|
|
1253
1524
|
const descEl = el("h3", {
|
|
1254
1525
|
id: `purpose-description-${purpose.uuid}`,
|
|
@@ -1316,9 +1587,7 @@ function renderModalPurposes(purposes, options) {
|
|
|
1316
1587
|
labelSpan.textContent = getTranslatedText("data_elements.name", dataElement.name, dataElement.uuid);
|
|
1317
1588
|
dataElementLabels.set(combinedId, labelSpan);
|
|
1318
1589
|
if (dataElement.required) {
|
|
1319
|
-
|
|
1320
|
-
asterisk.textContent = "*";
|
|
1321
|
-
labelSpan.appendChild(asterisk);
|
|
1590
|
+
labelSpan.appendChild(createRequiredMarker());
|
|
1322
1591
|
}
|
|
1323
1592
|
let deRightElement;
|
|
1324
1593
|
if (!isAlreadyConsented) {
|
|
@@ -1687,18 +1956,6 @@ var updateConsentState = (updates) => {
|
|
|
1687
1956
|
}
|
|
1688
1957
|
};
|
|
1689
1958
|
|
|
1690
|
-
// src/constants.ts
|
|
1691
|
-
var DPO_AUDIO_ELEMENT_MAP = [
|
|
1692
|
-
{ audioKey: "grievance_text_audio_url", elementId: "dpo-grievance-text" },
|
|
1693
|
-
{ audioKey: "grievance_anchor_text_audio_url", elementId: "dpo-grievance-link" },
|
|
1694
|
-
{ audioKey: "grievance_email_connector_text_audio_url", elementId: "dpo-grievance-email-connector" },
|
|
1695
|
-
{ audioKey: "grievance_email_audio_url", elementId: "dpo-grievance-email" },
|
|
1696
|
-
{ audioKey: "dp_board_text_audio_url", elementId: "dpo-dp-board-text" },
|
|
1697
|
-
{ audioKey: "dp_board_anchor_text_audio_url", elementId: "dpo-dp-board-link" },
|
|
1698
|
-
{ audioKey: "dpo_text_audio_url", elementId: "dpo-dpo-text" },
|
|
1699
|
-
{ audioKey: "dpo_anchor_text_audio_url", elementId: "dpo-dpo-link" }
|
|
1700
|
-
];
|
|
1701
|
-
|
|
1702
1959
|
// src/modal/tts.ts
|
|
1703
1960
|
var TTSManager = class {
|
|
1704
1961
|
/**
|
|
@@ -1746,9 +2003,16 @@ var TTSManager = class {
|
|
|
1746
2003
|
const response = await fetchTTSAudioUrls({
|
|
1747
2004
|
accessToken: params.accessToken,
|
|
1748
2005
|
baseUrl: params.baseUrl,
|
|
2006
|
+
ledgerBaseUrl: params.ledgerBaseUrl,
|
|
1749
2007
|
noticeUuid: params.noticeUuid,
|
|
1750
2008
|
language: params.language,
|
|
1751
|
-
signal: this.abortController.signal
|
|
2009
|
+
signal: this.abortController.signal,
|
|
2010
|
+
token: params.token,
|
|
2011
|
+
sandboxUcic: params.sandboxUcic,
|
|
2012
|
+
sandboxEmail: params.sandboxEmail,
|
|
2013
|
+
sandboxMobile: params.sandboxMobile,
|
|
2014
|
+
organisationUuid: params.organisationUuid,
|
|
2015
|
+
workspaceUuid: params.workspaceUuid
|
|
1752
2016
|
});
|
|
1753
2017
|
this.audioUrls = response.detail;
|
|
1754
2018
|
this.buildSequences(params.content);
|
|
@@ -1802,8 +2066,9 @@ var TTSManager = class {
|
|
|
1802
2066
|
push(audioUrl, mapping.elementId);
|
|
1803
2067
|
}
|
|
1804
2068
|
}
|
|
1805
|
-
push(urls.
|
|
1806
|
-
push(urls.
|
|
2069
|
+
push(urls.accept_all_button_text_audio_url, ACCEPT_ALL_BUTTON_ID);
|
|
2070
|
+
push(urls.confirm_button_text_audio_url, ACCEPT_SELECTED_BUTTON_ID);
|
|
2071
|
+
push(urls.decline_button_text_audio_url, DECLINE_BUTTON_ID);
|
|
1807
2072
|
this.audioSequence = audioSeq;
|
|
1808
2073
|
this.elementIdSequence = elementSeq;
|
|
1809
2074
|
}
|
|
@@ -2694,14 +2959,17 @@ function renderVerificationScreen(options) {
|
|
|
2694
2959
|
|
|
2695
2960
|
// src/modal/colors.ts
|
|
2696
2961
|
function resolveColors(settings, activeConfig) {
|
|
2697
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
2962
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
|
|
2698
2963
|
const primaryColor = (activeConfig == null ? void 0 : activeConfig.primary_color) || "#4f87ff";
|
|
2699
2964
|
const secondaryColor = (activeConfig == null ? void 0 : activeConfig.secondary_color) || "#4f87ff";
|
|
2700
2965
|
return {
|
|
2701
2966
|
acceptBg: ((_b = (_a = settings == null ? void 0 : settings.button) == null ? void 0 : _a.accept) == null ? void 0 : _b.backgroundColor) || primaryColor,
|
|
2702
2967
|
acceptText: ((_d = (_c = settings == null ? void 0 : settings.button) == null ? void 0 : _c.accept) == null ? void 0 : _d.textColor) || "#ffffff",
|
|
2703
|
-
|
|
2704
|
-
|
|
2968
|
+
acceptShortcutBg: ((_f = (_e = settings == null ? void 0 : settings.button) == null ? void 0 : _e.acceptAll) == null ? void 0 : _f.backgroundColor) || "#ffffff",
|
|
2969
|
+
acceptShortcutText: ((_h = (_g = settings == null ? void 0 : settings.button) == null ? void 0 : _g.acceptAll) == null ? void 0 : _h.textColor) || primaryColor,
|
|
2970
|
+
acceptShortcutBorder: primaryColor,
|
|
2971
|
+
declineBg: ((_j = (_i = settings == null ? void 0 : settings.button) == null ? void 0 : _i.decline) == null ? void 0 : _j.backgroundColor) || "#ffffff",
|
|
2972
|
+
declineText: ((_l = (_k = settings == null ? void 0 : settings.button) == null ? void 0 : _k.decline) == null ? void 0 : _l.textColor) || "#000000",
|
|
2705
2973
|
declineBorder: (settings == null ? void 0 : settings.borderColor) || "#d0d5dd",
|
|
2706
2974
|
link: (settings == null ? void 0 : settings.link) || secondaryColor,
|
|
2707
2975
|
heading: (settings == null ? void 0 : settings.headingColor) || "#323B4B",
|
|
@@ -2713,6 +2981,31 @@ function resolveColors(settings, activeConfig) {
|
|
|
2713
2981
|
};
|
|
2714
2982
|
}
|
|
2715
2983
|
|
|
2984
|
+
// src/modal/consentSelection.ts
|
|
2985
|
+
var dataElementKey = (purposeUuid, elementUuid) => `${purposeUuid}-${elementUuid}`;
|
|
2986
|
+
var buildSelectionForMode = (purposes, mode, current) => {
|
|
2987
|
+
if (mode === "selected") {
|
|
2988
|
+
return current;
|
|
2989
|
+
}
|
|
2990
|
+
const selectedPurposes = {};
|
|
2991
|
+
const selectedDataElements = {};
|
|
2992
|
+
purposes.forEach((purpose) => {
|
|
2993
|
+
selectedPurposes[purpose.uuid] = true;
|
|
2994
|
+
purpose.data_elements.forEach((element) => {
|
|
2995
|
+
selectedDataElements[dataElementKey(purpose.uuid, element.uuid)] = element.enabled;
|
|
2996
|
+
});
|
|
2997
|
+
});
|
|
2998
|
+
return { selectedPurposes, selectedDataElements };
|
|
2999
|
+
};
|
|
3000
|
+
|
|
3001
|
+
// src/modal/layout.ts
|
|
3002
|
+
function resolveLogoPosition(raw) {
|
|
3003
|
+
return raw === "center" || raw === "right" ? raw : DEFAULT_LOGO_POSITION;
|
|
3004
|
+
}
|
|
3005
|
+
function logoRowJustify(position) {
|
|
3006
|
+
return LOGO_POSITION_JUSTIFY[position];
|
|
3007
|
+
}
|
|
3008
|
+
|
|
2716
3009
|
// src/modal/index.ts
|
|
2717
3010
|
var ModalController = class {
|
|
2718
3011
|
constructor(options) {
|
|
@@ -2737,6 +3030,7 @@ var ModalController = class {
|
|
|
2737
3030
|
this.visibilityChangeHandler = null;
|
|
2738
3031
|
// DOM refs for targeted updates
|
|
2739
3032
|
this.acceptBtn = null;
|
|
3033
|
+
this.acceptAllBtn = null;
|
|
2740
3034
|
this.declineBtn = null;
|
|
2741
3035
|
this.middleSection = null;
|
|
2742
3036
|
this.errorEl = null;
|
|
@@ -2817,21 +3111,47 @@ var ModalController = class {
|
|
|
2817
3111
|
this.langSelectorRefs = null;
|
|
2818
3112
|
}
|
|
2819
3113
|
// ─── Private ───
|
|
3114
|
+
/**
|
|
3115
|
+
* Sandbox auth options to forward to every API call. When `token` is set the
|
|
3116
|
+
* API layer sends the single `X-Consent-Token` header, carries every supplied
|
|
3117
|
+
* identifier in each request's payload, and reads org/workspace from these
|
|
3118
|
+
* fields instead of decoding the JWT. Empty in the normal JWT flow.
|
|
3119
|
+
*/
|
|
3120
|
+
sandboxParams() {
|
|
3121
|
+
var _a;
|
|
3122
|
+
const legacySubject = (_a = this.options.sandboxSubject) == null ? void 0 : _a.trim();
|
|
3123
|
+
const legacyEmail = (legacySubject == null ? void 0 : legacySubject.includes("@")) ? legacySubject : void 0;
|
|
3124
|
+
const legacyMobile = legacySubject && !legacySubject.includes("@") ? legacySubject : void 0;
|
|
3125
|
+
return {
|
|
3126
|
+
token: this.options.token,
|
|
3127
|
+
// The static token carries no identity; carry every supplied identifier
|
|
3128
|
+
// (ucic/email/mobile) so the server can pick the principal by precedence
|
|
3129
|
+
// (UCIC > email > mobile) and keep the rest as the sandbox contact.
|
|
3130
|
+
// sandboxUcic / sandboxEmail / sandboxMobile stay as fallbacks.
|
|
3131
|
+
sandboxUcic: this.options.ucic || this.options.sandboxUcic,
|
|
3132
|
+
sandboxEmail: this.options.email || this.options.sandboxEmail || legacyEmail,
|
|
3133
|
+
sandboxMobile: this.options.mobile || this.options.sandboxMobile || legacyMobile,
|
|
3134
|
+
organisationUuid: this.options.organisationUuid,
|
|
3135
|
+
workspaceUuid: this.options.workspaceUuid
|
|
3136
|
+
};
|
|
3137
|
+
}
|
|
2820
3138
|
async fetchNotice() {
|
|
2821
3139
|
var _a, _b, _c, _d;
|
|
2822
3140
|
this.abortController = new AbortController();
|
|
2823
3141
|
this.store.setState({ isLoading: true, fetchError: null });
|
|
2824
3142
|
try {
|
|
3143
|
+
const noticeBaseUrl = this.options.ledgerBaseUrl && !this.options.applicationId && !this.options.token ? this.options.ledgerBaseUrl : this.options.baseUrl;
|
|
2825
3144
|
const data = await fetchConsentContent({
|
|
2826
3145
|
noticeId: this.options.noticeId,
|
|
2827
3146
|
accessToken: this.options.accessToken,
|
|
2828
3147
|
refreshToken: this.options.refreshToken,
|
|
2829
|
-
baseUrl:
|
|
3148
|
+
baseUrl: noticeBaseUrl,
|
|
2830
3149
|
language: this.options.language,
|
|
2831
3150
|
specific_uuid: this.options.applicationId,
|
|
2832
3151
|
validate_against: this.options.validateAgainst || "all",
|
|
2833
3152
|
include_fully_consented_data: this.options.includeFullyConsentedData,
|
|
2834
|
-
signal: this.abortController.signal
|
|
3153
|
+
signal: this.abortController.signal,
|
|
3154
|
+
...this.sandboxParams()
|
|
2835
3155
|
});
|
|
2836
3156
|
const activeConfig = data.detail.active_config;
|
|
2837
3157
|
let purposeSelections = data.detail.purpose_selections;
|
|
@@ -2893,10 +3213,11 @@ var ModalController = class {
|
|
|
2893
3213
|
if (this.options.includeFullyConsentedData && needsConsentPurposes.length === 0 && alreadyConsentedPurposes.length > 0) {
|
|
2894
3214
|
isFullyConsentedReviewMode = true;
|
|
2895
3215
|
}
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
3216
|
+
const selectedLanguage = resolveDisplayLanguage(
|
|
3217
|
+
this.options.language,
|
|
3218
|
+
activeConfig.default_language,
|
|
3219
|
+
activeConfig.supported_languages_and_translations
|
|
3220
|
+
);
|
|
2900
3221
|
let isMinorFlow = false;
|
|
2901
3222
|
let showAgeVerification = false;
|
|
2902
3223
|
let restoreState = {};
|
|
@@ -3194,49 +3515,32 @@ var ModalController = class {
|
|
|
3194
3515
|
(_g = this.ttsManager) == null ? void 0 : _g.fetchAudioUrls({
|
|
3195
3516
|
accessToken: this.options.accessToken,
|
|
3196
3517
|
baseUrl: this.options.baseUrl,
|
|
3518
|
+
ledgerBaseUrl: this.options.ledgerBaseUrl,
|
|
3197
3519
|
noticeUuid: activeConfig.notice_uuid,
|
|
3198
3520
|
language: this.getLanguageCode(state.selectedLanguage),
|
|
3199
|
-
content: activeConfig
|
|
3521
|
+
content: activeConfig,
|
|
3522
|
+
...this.sandboxParams()
|
|
3200
3523
|
});
|
|
3201
3524
|
const topRight = el("div", {
|
|
3202
3525
|
style: { display: "flex", alignItems: "center", gap: "8px", flexShrink: 0 }
|
|
3203
3526
|
}, this.ttsButton, this.langSelectorRefs.container);
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
{
|
|
3209
|
-
style: {
|
|
3210
|
-
display: "flex",
|
|
3211
|
-
alignItems: "center",
|
|
3212
|
-
gap: "10px",
|
|
3213
|
-
width: "100%"
|
|
3214
|
-
}
|
|
3215
|
-
},
|
|
3216
|
-
...logoImg ? [logoImg] : [],
|
|
3527
|
+
const logoPosition = resolveLogoPosition(activeConfig.logo_position);
|
|
3528
|
+
const headerRows = [];
|
|
3529
|
+
if (logoImg) {
|
|
3530
|
+
headerRows.push(
|
|
3217
3531
|
el(
|
|
3218
3532
|
"div",
|
|
3219
|
-
{ style: {
|
|
3220
|
-
|
|
3533
|
+
{ style: { ...styles.logoRow, justifyContent: logoRowJustify(logoPosition) } },
|
|
3534
|
+
logoImg
|
|
3221
3535
|
)
|
|
3222
3536
|
);
|
|
3223
|
-
topSection = el(
|
|
3224
|
-
"div",
|
|
3225
|
-
{
|
|
3226
|
-
style: {
|
|
3227
|
-
...styles.topSection,
|
|
3228
|
-
flexDirection: "column",
|
|
3229
|
-
alignItems: "stretch",
|
|
3230
|
-
flexWrap: "nowrap"
|
|
3231
|
-
}
|
|
3232
|
-
},
|
|
3233
|
-
headerRow,
|
|
3234
|
-
this.noticeTitleEl
|
|
3235
|
-
);
|
|
3236
|
-
} else {
|
|
3237
|
-
const topLeft = el("div", { style: styles.topLeft }, logoImg, this.noticeTitleEl);
|
|
3238
|
-
topSection = el("div", { style: styles.topSection }, topLeft, topRight);
|
|
3239
3537
|
}
|
|
3538
|
+
headerRows.push(el("div", { style: styles.headerRow }, this.noticeTitleEl, topRight));
|
|
3539
|
+
const topSection = el(
|
|
3540
|
+
"div",
|
|
3541
|
+
{ style: { ...styles.headerStack, gap: isMobile ? "12px" : "10px" } },
|
|
3542
|
+
...headerRows
|
|
3543
|
+
);
|
|
3240
3544
|
this.middleSection = el("div", { style: styles.middleSection });
|
|
3241
3545
|
const baseTextStyle = { ...styles.privacyText, color: textColor, fontFamily: font, fontSize: isMobile ? "14px" : "16px" };
|
|
3242
3546
|
const noticeTextSpan = el("span", { id: "privacy-notice-text" });
|
|
@@ -3427,24 +3731,57 @@ var ModalController = class {
|
|
|
3427
3731
|
display: "none"
|
|
3428
3732
|
}
|
|
3429
3733
|
});
|
|
3430
|
-
const acceptBtnText = state.isFullyConsentedReviewMode ? this.options.reviewModeButtonText || "Continue" : this.getTranslatedText(
|
|
3734
|
+
const acceptBtnText = state.isFullyConsentedReviewMode ? this.options.reviewModeButtonText || "Continue" : this.getTranslatedText(
|
|
3735
|
+
"confirm_button_text",
|
|
3736
|
+
activeConfig.confirm_button_text || DEFAULT_ACCEPT_SELECTED_BUTTON_TEXT
|
|
3737
|
+
);
|
|
3738
|
+
const gridButtonBox = {
|
|
3739
|
+
fontSize: isMobile ? "14px" : "16px",
|
|
3740
|
+
padding: state.isFullyConsentedReviewMode ? isMobile ? "10px 20px" : "9px 45px" : "9px 20px",
|
|
3741
|
+
width: state.isFullyConsentedReviewMode || isMobile ? "100%" : "auto",
|
|
3742
|
+
whiteSpace: "normal",
|
|
3743
|
+
wordBreak: "break-word"
|
|
3744
|
+
};
|
|
3431
3745
|
const acceptStyle = {
|
|
3432
3746
|
...styles.button,
|
|
3433
|
-
...styles.acceptButton,
|
|
3434
|
-
|
|
3435
|
-
|
|
3747
|
+
...state.isFullyConsentedReviewMode ? { ...styles.acceptButton, backgroundColor: colors.acceptBg, color: colors.acceptText } : {
|
|
3748
|
+
...styles.cancelButton,
|
|
3749
|
+
backgroundColor: "#ffffff",
|
|
3750
|
+
color: colors.acceptShortcutText,
|
|
3751
|
+
borderColor: colors.acceptShortcutBorder
|
|
3752
|
+
},
|
|
3436
3753
|
borderRadius: colors.borderRadius,
|
|
3437
|
-
|
|
3438
|
-
padding: isMobile ? "10px 20px" : "9px 45px"
|
|
3754
|
+
...gridButtonBox
|
|
3439
3755
|
};
|
|
3440
3756
|
this.acceptBtn = el("button", {
|
|
3441
|
-
id:
|
|
3757
|
+
id: ACCEPT_SELECTED_BUTTON_ID,
|
|
3442
3758
|
style: acceptStyle,
|
|
3443
3759
|
type: "button",
|
|
3444
|
-
onClick: () => this.handleAccept()
|
|
3760
|
+
onClick: () => this.handleAccept("selected")
|
|
3445
3761
|
});
|
|
3446
3762
|
this.acceptBtn.textContent = acceptBtnText;
|
|
3447
|
-
const
|
|
3763
|
+
const shortcutStyle = {
|
|
3764
|
+
...styles.button,
|
|
3765
|
+
...styles.acceptButton,
|
|
3766
|
+
backgroundColor: colors.acceptBg,
|
|
3767
|
+
color: colors.acceptText,
|
|
3768
|
+
borderRadius: colors.borderRadius,
|
|
3769
|
+
...gridButtonBox
|
|
3770
|
+
};
|
|
3771
|
+
this.acceptAllBtn = el("button", {
|
|
3772
|
+
id: ACCEPT_ALL_BUTTON_ID,
|
|
3773
|
+
style: shortcutStyle,
|
|
3774
|
+
type: "button",
|
|
3775
|
+
onClick: () => this.handleAccept("all")
|
|
3776
|
+
});
|
|
3777
|
+
this.acceptAllBtn.textContent = this.getTranslatedText(
|
|
3778
|
+
"accept_all_button_text",
|
|
3779
|
+
activeConfig.accept_all_button_text || DEFAULT_ACCEPT_ALL_BUTTON_TEXT
|
|
3780
|
+
);
|
|
3781
|
+
const declineBtnText = this.getTranslatedText(
|
|
3782
|
+
"decline_button_text",
|
|
3783
|
+
activeConfig.decline_button_text || DEFAULT_DECLINE_BUTTON_TEXT
|
|
3784
|
+
);
|
|
3448
3785
|
const declineStyle = {
|
|
3449
3786
|
...styles.button,
|
|
3450
3787
|
...styles.cancelButton,
|
|
@@ -3452,11 +3789,13 @@ var ModalController = class {
|
|
|
3452
3789
|
color: colors.declineText,
|
|
3453
3790
|
borderColor: colors.declineBorder,
|
|
3454
3791
|
borderRadius: colors.borderRadius,
|
|
3455
|
-
|
|
3456
|
-
|
|
3792
|
+
...gridButtonBox,
|
|
3793
|
+
// Pushes everything after decline to the right edge, which is the whole
|
|
3794
|
+
// of the wide layout. Inert in the stack and in review mode.
|
|
3795
|
+
marginRight: state.isFullyConsentedReviewMode || isMobile ? "0" : "auto"
|
|
3457
3796
|
};
|
|
3458
3797
|
this.declineBtn = el("button", {
|
|
3459
|
-
id:
|
|
3798
|
+
id: DECLINE_BUTTON_ID,
|
|
3460
3799
|
style: declineStyle,
|
|
3461
3800
|
type: "button",
|
|
3462
3801
|
onClick: () => this.handleDecline()
|
|
@@ -3464,12 +3803,19 @@ var ModalController = class {
|
|
|
3464
3803
|
if (!state.isFullyConsentedReviewMode) {
|
|
3465
3804
|
this.declineBtn.textContent = declineBtnText;
|
|
3466
3805
|
}
|
|
3467
|
-
const bottomSection = el("div", {
|
|
3468
|
-
|
|
3806
|
+
const bottomSection = el("div", {
|
|
3807
|
+
style: state.isFullyConsentedReviewMode ? styles.bottomSection : isMobile ? styles.actionButtonStack : styles.actionButtonRow
|
|
3808
|
+
});
|
|
3809
|
+
if (state.isFullyConsentedReviewMode) {
|
|
3810
|
+
bottomSection.appendChild(this.acceptBtn);
|
|
3811
|
+
} else if (isMobile) {
|
|
3812
|
+
bottomSection.appendChild(this.acceptAllBtn);
|
|
3469
3813
|
bottomSection.appendChild(this.acceptBtn);
|
|
3470
3814
|
bottomSection.appendChild(this.declineBtn);
|
|
3471
3815
|
} else {
|
|
3816
|
+
bottomSection.appendChild(this.declineBtn);
|
|
3472
3817
|
bottomSection.appendChild(this.acceptBtn);
|
|
3818
|
+
bottomSection.appendChild(this.acceptAllBtn);
|
|
3473
3819
|
}
|
|
3474
3820
|
const contentWrapper = el(
|
|
3475
3821
|
"div",
|
|
@@ -3625,7 +3971,12 @@ var ModalController = class {
|
|
|
3625
3971
|
selectedPurposes: { ...state.selectedPurposes, [purposeUuid]: shouldCheckPurpose }
|
|
3626
3972
|
});
|
|
3627
3973
|
}
|
|
3628
|
-
|
|
3974
|
+
/**
|
|
3975
|
+
* Shared by all three accept buttons. The mode only decides which
|
|
3976
|
+
* purposes/data elements are submitted — everything downstream, including
|
|
3977
|
+
* the host's `onAccept` callback, is identical.
|
|
3978
|
+
*/
|
|
3979
|
+
async handleAccept(mode = "selected") {
|
|
3629
3980
|
var _a, _b, _c;
|
|
3630
3981
|
const state = this.store.getState();
|
|
3631
3982
|
if (state.isSubmitting) return;
|
|
@@ -3644,6 +3995,16 @@ var ModalController = class {
|
|
|
3644
3995
|
if (!content) return;
|
|
3645
3996
|
this.abortController = new AbortController();
|
|
3646
3997
|
this.store.setState({ isSubmitting: true, isSubmittingAccept: true, errorMessage: null });
|
|
3998
|
+
const selection = buildSelectionForMode(content.purposes, mode, {
|
|
3999
|
+
selectedPurposes: state.selectedPurposes,
|
|
4000
|
+
selectedDataElements: state.selectedDataElements
|
|
4001
|
+
});
|
|
4002
|
+
if (mode !== "selected") {
|
|
4003
|
+
this.store.setState({
|
|
4004
|
+
selectedPurposes: selection.selectedPurposes,
|
|
4005
|
+
selectedDataElements: selection.selectedDataElements
|
|
4006
|
+
});
|
|
4007
|
+
}
|
|
3647
4008
|
try {
|
|
3648
4009
|
const purposes = content.purposes.map((purpose) => {
|
|
3649
4010
|
const dataElements = purpose.data_elements.filter((de) => de.enabled).map((de) => ({
|
|
@@ -3651,13 +4012,13 @@ var ModalController = class {
|
|
|
3651
4012
|
name: de.name,
|
|
3652
4013
|
enabled: de.enabled,
|
|
3653
4014
|
required: de.required,
|
|
3654
|
-
selected:
|
|
4015
|
+
selected: selection.selectedDataElements[`${purpose.uuid}-${de.uuid}`] || false
|
|
3655
4016
|
}));
|
|
3656
4017
|
return {
|
|
3657
4018
|
uuid: purpose.uuid,
|
|
3658
4019
|
name: purpose.name,
|
|
3659
4020
|
description: purpose.description,
|
|
3660
|
-
selected:
|
|
4021
|
+
selected: selection.selectedPurposes[purpose.uuid] || false,
|
|
3661
4022
|
data_elements: dataElements
|
|
3662
4023
|
};
|
|
3663
4024
|
});
|
|
@@ -3671,7 +4032,9 @@ var ModalController = class {
|
|
|
3671
4032
|
meta_data: this.options.applicationId ? { specific_uuid: this.options.applicationId } : void 0,
|
|
3672
4033
|
guardianVerificationReference: state.verificationReference || void 0,
|
|
3673
4034
|
selfDeclaredAdult: state.selfDeclaredAdult || void 0,
|
|
3674
|
-
|
|
4035
|
+
language: state.selectedLanguage,
|
|
4036
|
+
signal: this.abortController.signal,
|
|
4037
|
+
...this.sandboxParams()
|
|
3675
4038
|
});
|
|
3676
4039
|
this.options.onAccept();
|
|
3677
4040
|
this.destroy();
|
|
@@ -3717,11 +4080,16 @@ var ModalController = class {
|
|
|
3717
4080
|
}
|
|
3718
4081
|
updateAcceptButtonState(state) {
|
|
3719
4082
|
if (!this.acceptBtn || state.isFullyConsentedReviewMode) return;
|
|
4083
|
+
const submitting = state.isSubmittingAccept || state.isSubmittingDecline;
|
|
3720
4084
|
const allRequiredChecked = this.areAllRequiredChecked(state);
|
|
3721
|
-
const
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
4085
|
+
const setDisabled = (btn, disabled) => {
|
|
4086
|
+
if (!btn) return;
|
|
4087
|
+
btn.disabled = disabled;
|
|
4088
|
+
btn.style.opacity = disabled ? "0.5" : "1";
|
|
4089
|
+
btn.style.cursor = disabled ? "not-allowed" : "pointer";
|
|
4090
|
+
};
|
|
4091
|
+
setDisabled(this.acceptBtn, !allRequiredChecked || submitting);
|
|
4092
|
+
setDisabled(this.acceptAllBtn, submitting);
|
|
3725
4093
|
}
|
|
3726
4094
|
updateCollapseState(state) {
|
|
3727
4095
|
const updateRefs = (refs) => {
|
|
@@ -4094,25 +4462,34 @@ var ModalController = class {
|
|
|
4094
4462
|
}
|
|
4095
4463
|
updateButtonStates(state) {
|
|
4096
4464
|
var _a;
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4465
|
+
const content = (_a = state.content) == null ? void 0 : _a.detail.active_config;
|
|
4466
|
+
const applySubmittingState = (btn, idleLabel) => {
|
|
4467
|
+
if (!btn) return;
|
|
4468
|
+
btn.textContent = state.isSubmittingAccept ? SUBMITTING_BUTTON_TEXT : idleLabel;
|
|
4469
|
+
btn.disabled = state.isSubmittingAccept;
|
|
4470
|
+
btn.style.opacity = state.isSubmittingAccept ? "0.7" : "1";
|
|
4471
|
+
btn.style.cursor = state.isSubmittingAccept ? "not-allowed" : "pointer";
|
|
4472
|
+
};
|
|
4473
|
+
applySubmittingState(
|
|
4474
|
+
this.acceptBtn,
|
|
4475
|
+
state.isFullyConsentedReviewMode ? this.options.reviewModeButtonText || "Continue" : this.getTranslatedText(
|
|
4476
|
+
"confirm_button_text",
|
|
4477
|
+
(content == null ? void 0 : content.confirm_button_text) || DEFAULT_ACCEPT_SELECTED_BUTTON_TEXT
|
|
4478
|
+
)
|
|
4479
|
+
);
|
|
4480
|
+
applySubmittingState(
|
|
4481
|
+
this.acceptAllBtn,
|
|
4482
|
+
this.getTranslatedText(
|
|
4483
|
+
"accept_all_button_text",
|
|
4484
|
+
(content == null ? void 0 : content.accept_all_button_text) || DEFAULT_ACCEPT_ALL_BUTTON_TEXT
|
|
4485
|
+
)
|
|
4486
|
+
);
|
|
4111
4487
|
if (this.declineBtn) {
|
|
4112
4488
|
this.declineBtn.disabled = state.isSubmitting;
|
|
4113
4489
|
this.declineBtn.style.opacity = state.isSubmitting ? "0.7" : "1";
|
|
4114
4490
|
this.declineBtn.style.cursor = state.isSubmitting ? "not-allowed" : "pointer";
|
|
4115
4491
|
}
|
|
4492
|
+
this.updateAcceptButtonState(state);
|
|
4116
4493
|
}
|
|
4117
4494
|
};
|
|
4118
4495
|
|
|
@@ -4313,6 +4690,9 @@ function renderInlinePurposes(purposes, options) {
|
|
|
4313
4690
|
}
|
|
4314
4691
|
});
|
|
4315
4692
|
titleEl.textContent = getTranslatedText("purposes.name", purpose.name, purpose.uuid);
|
|
4693
|
+
if (hasRequiredDataElement(purpose)) {
|
|
4694
|
+
titleEl.appendChild(createRequiredMarker());
|
|
4695
|
+
}
|
|
4316
4696
|
titleElements.set(purpose.uuid, titleEl);
|
|
4317
4697
|
const descEl = el("h3", {
|
|
4318
4698
|
style: {
|
|
@@ -4365,12 +4745,7 @@ function renderInlinePurposes(purposes, options) {
|
|
|
4365
4745
|
labelSpan.textContent = getTranslatedText("data_elements.name", dataElement.name, dataElement.uuid);
|
|
4366
4746
|
dataElementLabels.set(combinedId, labelSpan);
|
|
4367
4747
|
if (dataElement.required) {
|
|
4368
|
-
|
|
4369
|
-
style: { color: "red", marginLeft: "4px" },
|
|
4370
|
-
"aria-label": "required"
|
|
4371
|
-
});
|
|
4372
|
-
asterisk.textContent = "*";
|
|
4373
|
-
labelSpan.appendChild(asterisk);
|
|
4748
|
+
labelSpan.appendChild(createRequiredMarker());
|
|
4374
4749
|
}
|
|
4375
4750
|
const deCheckbox = el("input", {
|
|
4376
4751
|
type: "checkbox",
|
|
@@ -4416,6 +4791,8 @@ var InlineController = class {
|
|
|
4416
4791
|
this.errorEl = null;
|
|
4417
4792
|
this.hasSubmitted = false;
|
|
4418
4793
|
this.hasCheckedLedgerConsent = false;
|
|
4794
|
+
this.fetchedWithToken = false;
|
|
4795
|
+
this.fetchGeneration = 0;
|
|
4419
4796
|
this.unsubscribe = null;
|
|
4420
4797
|
this.abortController = null;
|
|
4421
4798
|
this.options = options;
|
|
@@ -4473,6 +4850,10 @@ var InlineController = class {
|
|
|
4473
4850
|
}
|
|
4474
4851
|
updateToken(accessToken) {
|
|
4475
4852
|
this.options = { ...this.options, accessToken };
|
|
4853
|
+
if (accessToken && !this.fetchedWithToken) {
|
|
4854
|
+
void this.fetchNotice();
|
|
4855
|
+
return;
|
|
4856
|
+
}
|
|
4476
4857
|
if (this.shouldCheckLedgerConsent()) {
|
|
4477
4858
|
void this.checkLedgerConsent();
|
|
4478
4859
|
return;
|
|
@@ -4488,17 +4869,20 @@ var InlineController = class {
|
|
|
4488
4869
|
}
|
|
4489
4870
|
async fetchNotice() {
|
|
4490
4871
|
var _a, _b, _c, _d;
|
|
4491
|
-
|
|
4872
|
+
const generation = ++this.fetchGeneration;
|
|
4873
|
+
this.fetchedWithToken = Boolean(this.options.accessToken);
|
|
4874
|
+
this.store.setState({ isLoading: true, fetchError: null, hasAlreadyConsented: false });
|
|
4492
4875
|
try {
|
|
4493
4876
|
const data = await fetchConsentContentPublic({
|
|
4494
4877
|
org_uuid: this.options.org_uuid,
|
|
4495
4878
|
workspace_uuid: this.options.workspace_uuid,
|
|
4496
4879
|
notice_uuid: this.options.notice_uuid,
|
|
4880
|
+
accessToken: this.options.accessToken,
|
|
4497
4881
|
baseUrl: this.options.baseUrl,
|
|
4498
4882
|
language: this.options.language,
|
|
4499
4883
|
specific_uuid: this.options.applicationId
|
|
4500
4884
|
});
|
|
4501
|
-
if (this.isDestroyed()) {
|
|
4885
|
+
if (this.isDestroyed() || generation !== this.fetchGeneration) {
|
|
4502
4886
|
return;
|
|
4503
4887
|
}
|
|
4504
4888
|
const activeConfig = data.detail.active_config;
|
|
@@ -4516,13 +4900,17 @@ var InlineController = class {
|
|
|
4516
4900
|
this.store.setState({
|
|
4517
4901
|
content: activeConfig,
|
|
4518
4902
|
isLoading: false,
|
|
4519
|
-
selectedLanguage:
|
|
4903
|
+
selectedLanguage: resolveDisplayLanguage(
|
|
4904
|
+
this.options.language,
|
|
4905
|
+
activeConfig.default_language,
|
|
4906
|
+
activeConfig.supported_languages_and_translations
|
|
4907
|
+
),
|
|
4520
4908
|
collapsedPurposes: initialCollapsed,
|
|
4521
4909
|
selectedPurposes: initialPurposes,
|
|
4522
4910
|
selectedDataElements: initialElements
|
|
4523
4911
|
});
|
|
4524
4912
|
} catch (err) {
|
|
4525
|
-
if (this.isDestroyed()) {
|
|
4913
|
+
if (this.isDestroyed() || generation !== this.fetchGeneration) {
|
|
4526
4914
|
return;
|
|
4527
4915
|
}
|
|
4528
4916
|
const error = err;
|
|
@@ -4839,7 +5227,8 @@ var InlineController = class {
|
|
|
4839
5227
|
};
|
|
4840
5228
|
}),
|
|
4841
5229
|
declined: false,
|
|
4842
|
-
meta_data: this.options.applicationId ? { specific_uuid: this.options.applicationId } : void 0
|
|
5230
|
+
meta_data: this.options.applicationId ? { specific_uuid: this.options.applicationId } : void 0,
|
|
5231
|
+
language: state.selectedLanguage
|
|
4843
5232
|
});
|
|
4844
5233
|
(_b = (_a = this.options).onAccept) == null ? void 0 : _b.call(_a);
|
|
4845
5234
|
this.hasSubmitted = false;
|