@redacto.io/consent-sdk-js 1.0.0 → 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 +19 -4
- package/dist/redacto.d.ts +19 -4
- package/dist/redacto.global.js +2 -2
- package/dist/redacto.global.js.map +1 -1
- package/dist/redacto.js +244 -84
- package/dist/redacto.mjs +244 -84
- package/package.json +1 -1
package/dist/redacto.js
CHANGED
|
@@ -216,7 +216,7 @@ function decodeTokenSafely(token) {
|
|
|
216
216
|
// src/api/sandbox.ts
|
|
217
217
|
var SANDBOX_TOKEN_HEADER = "X-Consent-Token";
|
|
218
218
|
var resolveSandboxAuth = (options) => {
|
|
219
|
-
var _a, _b, _c, _d, _e;
|
|
219
|
+
var _a, _b, _c, _d, _e, _f;
|
|
220
220
|
const token = (_a = options.token) == null ? void 0 : _a.trim();
|
|
221
221
|
if (!token) {
|
|
222
222
|
return null;
|
|
@@ -224,16 +224,17 @@ var resolveSandboxAuth = (options) => {
|
|
|
224
224
|
const organisationUuid = (_b = options.organisationUuid) == null ? void 0 : _b.trim();
|
|
225
225
|
const workspaceUuid = (_c = options.workspaceUuid) == null ? void 0 : _c.trim();
|
|
226
226
|
const ucic = (_d = options.sandboxUcic) == null ? void 0 : _d.trim();
|
|
227
|
-
const
|
|
227
|
+
const email = (_e = options.sandboxEmail) == null ? void 0 : _e.trim();
|
|
228
|
+
const mobile = (_f = options.sandboxMobile) == null ? void 0 : _f.trim();
|
|
228
229
|
if (!organisationUuid || !workspaceUuid) {
|
|
229
230
|
throw new Error(
|
|
230
231
|
"Sandbox mode requires organisationUuid and workspaceUuid"
|
|
231
232
|
);
|
|
232
233
|
}
|
|
233
|
-
if (!ucic && !
|
|
234
|
+
if (!ucic && !email && !mobile) {
|
|
234
235
|
throw new Error("Sandbox mode requires a UCIC, email, or mobile");
|
|
235
236
|
}
|
|
236
|
-
return { token, ucic,
|
|
237
|
+
return { token, ucic, email, mobile, organisationUuid, workspaceUuid };
|
|
237
238
|
};
|
|
238
239
|
var resolveRequestAuth = (accessToken, options) => {
|
|
239
240
|
const sandbox = resolveSandboxAuth(options);
|
|
@@ -265,10 +266,17 @@ var buildAuthHeaders = (accessToken, sandbox) => {
|
|
|
265
266
|
return { Authorization: `Bearer ${accessToken}` };
|
|
266
267
|
};
|
|
267
268
|
var sandboxBodyIdentity = (sandbox) => {
|
|
269
|
+
const payload = {};
|
|
268
270
|
if (sandbox.ucic) {
|
|
269
|
-
|
|
271
|
+
payload.org_user_id = sandbox.ucic;
|
|
270
272
|
}
|
|
271
|
-
|
|
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;
|
|
272
280
|
};
|
|
273
281
|
var sandboxQueryIdentity = (sandbox) => sandboxBodyIdentity(sandbox);
|
|
274
282
|
|
|
@@ -354,7 +362,7 @@ var fetchConsentContent = async ({
|
|
|
354
362
|
sandboxParams
|
|
355
363
|
);
|
|
356
364
|
const apiBaseUrl = baseUrl || BASE_URL;
|
|
357
|
-
const cacheIdentity = sandbox ? `sandbox:${sandbox.organisationUuid}:${sandbox.workspaceUuid}:${sandbox.ucic || sandbox.
|
|
365
|
+
const cacheIdentity = sandbox ? `sandbox:${sandbox.organisationUuid}:${sandbox.workspaceUuid}:${sandbox.ucic || sandbox.email || sandbox.mobile}` : accessToken;
|
|
358
366
|
const cacheKey = `${cacheIdentity}-${noticeId}-${validate_against}-${language}-${specific_uuid || ""}-${include_fully_consented_data}`;
|
|
359
367
|
if (validate_against === "all" && !specific_uuid) {
|
|
360
368
|
const cached = getCachedData(cacheKey);
|
|
@@ -366,7 +374,7 @@ var fetchConsentContent = async ({
|
|
|
366
374
|
if (include_fully_consented_data) url.searchParams.append("include_fully_consented_data", "true");
|
|
367
375
|
if (sandbox) {
|
|
368
376
|
for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
|
|
369
|
-
url.searchParams.append(key, value);
|
|
377
|
+
if (value) url.searchParams.append(key, value);
|
|
370
378
|
}
|
|
371
379
|
}
|
|
372
380
|
const response = await fetch(url.toString(), {
|
|
@@ -602,7 +610,7 @@ var fetchTTSAudioUrls = async ({
|
|
|
602
610
|
);
|
|
603
611
|
if (sandbox) {
|
|
604
612
|
for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
|
|
605
|
-
url.searchParams.append(key, value);
|
|
613
|
+
if (value) url.searchParams.append(key, value);
|
|
606
614
|
}
|
|
607
615
|
}
|
|
608
616
|
const response = await fetch(url.toString(), {
|
|
@@ -1317,6 +1325,32 @@ var styles = {
|
|
|
1317
1325
|
margin: 0,
|
|
1318
1326
|
boxSizing: "border-box"
|
|
1319
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
|
+
},
|
|
1320
1354
|
button: {
|
|
1321
1355
|
width: "100%",
|
|
1322
1356
|
borderRadius: "8px",
|
|
@@ -1403,6 +1437,51 @@ var styles = {
|
|
|
1403
1437
|
}
|
|
1404
1438
|
};
|
|
1405
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
|
+
|
|
1406
1485
|
// src/modal/purposes.ts
|
|
1407
1486
|
function renderModalPurposes(purposes, options) {
|
|
1408
1487
|
var _a, _b;
|
|
@@ -1468,6 +1547,9 @@ function renderModalPurposes(purposes, options) {
|
|
|
1468
1547
|
}
|
|
1469
1548
|
});
|
|
1470
1549
|
titleEl.textContent = getTranslatedText("purposes.name", purpose.name, purpose.uuid);
|
|
1550
|
+
if (hasRequiredDataElement(purpose)) {
|
|
1551
|
+
titleEl.appendChild(createRequiredMarker());
|
|
1552
|
+
}
|
|
1471
1553
|
titleElements.set(purpose.uuid, titleEl);
|
|
1472
1554
|
const descEl = el("h3", {
|
|
1473
1555
|
id: `purpose-description-${purpose.uuid}`,
|
|
@@ -1535,9 +1617,7 @@ function renderModalPurposes(purposes, options) {
|
|
|
1535
1617
|
labelSpan.textContent = getTranslatedText("data_elements.name", dataElement.name, dataElement.uuid);
|
|
1536
1618
|
dataElementLabels.set(combinedId, labelSpan);
|
|
1537
1619
|
if (dataElement.required) {
|
|
1538
|
-
|
|
1539
|
-
asterisk.textContent = "*";
|
|
1540
|
-
labelSpan.appendChild(asterisk);
|
|
1620
|
+
labelSpan.appendChild(createRequiredMarker());
|
|
1541
1621
|
}
|
|
1542
1622
|
let deRightElement;
|
|
1543
1623
|
if (!isAlreadyConsented) {
|
|
@@ -1906,24 +1986,6 @@ var updateConsentState = (updates) => {
|
|
|
1906
1986
|
}
|
|
1907
1987
|
};
|
|
1908
1988
|
|
|
1909
|
-
// src/constants.ts
|
|
1910
|
-
var DEFAULT_LOGO_POSITION = "left";
|
|
1911
|
-
var LOGO_POSITION_JUSTIFY = {
|
|
1912
|
-
left: "flex-start",
|
|
1913
|
-
center: "center",
|
|
1914
|
-
right: "flex-end"
|
|
1915
|
-
};
|
|
1916
|
-
var DPO_AUDIO_ELEMENT_MAP = [
|
|
1917
|
-
{ audioKey: "grievance_text_audio_url", elementId: "dpo-grievance-text" },
|
|
1918
|
-
{ audioKey: "grievance_anchor_text_audio_url", elementId: "dpo-grievance-link" },
|
|
1919
|
-
{ audioKey: "grievance_email_connector_text_audio_url", elementId: "dpo-grievance-email-connector" },
|
|
1920
|
-
{ audioKey: "grievance_email_audio_url", elementId: "dpo-grievance-email" },
|
|
1921
|
-
{ audioKey: "dp_board_text_audio_url", elementId: "dpo-dp-board-text" },
|
|
1922
|
-
{ audioKey: "dp_board_anchor_text_audio_url", elementId: "dpo-dp-board-link" },
|
|
1923
|
-
{ audioKey: "dpo_text_audio_url", elementId: "dpo-dpo-text" },
|
|
1924
|
-
{ audioKey: "dpo_anchor_text_audio_url", elementId: "dpo-dpo-link" }
|
|
1925
|
-
];
|
|
1926
|
-
|
|
1927
1989
|
// src/modal/tts.ts
|
|
1928
1990
|
var TTSManager = class {
|
|
1929
1991
|
/**
|
|
@@ -1977,7 +2039,8 @@ var TTSManager = class {
|
|
|
1977
2039
|
signal: this.abortController.signal,
|
|
1978
2040
|
token: params.token,
|
|
1979
2041
|
sandboxUcic: params.sandboxUcic,
|
|
1980
|
-
|
|
2042
|
+
sandboxEmail: params.sandboxEmail,
|
|
2043
|
+
sandboxMobile: params.sandboxMobile,
|
|
1981
2044
|
organisationUuid: params.organisationUuid,
|
|
1982
2045
|
workspaceUuid: params.workspaceUuid
|
|
1983
2046
|
});
|
|
@@ -2033,8 +2096,9 @@ var TTSManager = class {
|
|
|
2033
2096
|
push(audioUrl, mapping.elementId);
|
|
2034
2097
|
}
|
|
2035
2098
|
}
|
|
2036
|
-
push(urls.
|
|
2037
|
-
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);
|
|
2038
2102
|
this.audioSequence = audioSeq;
|
|
2039
2103
|
this.elementIdSequence = elementSeq;
|
|
2040
2104
|
}
|
|
@@ -2925,14 +2989,17 @@ function renderVerificationScreen(options) {
|
|
|
2925
2989
|
|
|
2926
2990
|
// src/modal/colors.ts
|
|
2927
2991
|
function resolveColors(settings, activeConfig) {
|
|
2928
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
2992
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
|
|
2929
2993
|
const primaryColor = (activeConfig == null ? void 0 : activeConfig.primary_color) || "#4f87ff";
|
|
2930
2994
|
const secondaryColor = (activeConfig == null ? void 0 : activeConfig.secondary_color) || "#4f87ff";
|
|
2931
2995
|
return {
|
|
2932
2996
|
acceptBg: ((_b = (_a = settings == null ? void 0 : settings.button) == null ? void 0 : _a.accept) == null ? void 0 : _b.backgroundColor) || primaryColor,
|
|
2933
2997
|
acceptText: ((_d = (_c = settings == null ? void 0 : settings.button) == null ? void 0 : _c.accept) == null ? void 0 : _d.textColor) || "#ffffff",
|
|
2934
|
-
|
|
2935
|
-
|
|
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",
|
|
2936
3003
|
declineBorder: (settings == null ? void 0 : settings.borderColor) || "#d0d5dd",
|
|
2937
3004
|
link: (settings == null ? void 0 : settings.link) || secondaryColor,
|
|
2938
3005
|
heading: (settings == null ? void 0 : settings.headingColor) || "#323B4B",
|
|
@@ -2944,6 +3011,23 @@ function resolveColors(settings, activeConfig) {
|
|
|
2944
3011
|
};
|
|
2945
3012
|
}
|
|
2946
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
|
+
|
|
2947
3031
|
// src/modal/layout.ts
|
|
2948
3032
|
function resolveLogoPosition(raw) {
|
|
2949
3033
|
return raw === "center" || raw === "right" ? raw : DEFAULT_LOGO_POSITION;
|
|
@@ -2976,6 +3060,7 @@ var ModalController = class {
|
|
|
2976
3060
|
this.visibilityChangeHandler = null;
|
|
2977
3061
|
// DOM refs for targeted updates
|
|
2978
3062
|
this.acceptBtn = null;
|
|
3063
|
+
this.acceptAllBtn = null;
|
|
2979
3064
|
this.declineBtn = null;
|
|
2980
3065
|
this.middleSection = null;
|
|
2981
3066
|
this.errorEl = null;
|
|
@@ -3058,18 +3143,24 @@ var ModalController = class {
|
|
|
3058
3143
|
// ─── Private ───
|
|
3059
3144
|
/**
|
|
3060
3145
|
* Sandbox auth options to forward to every API call. When `token` is set the
|
|
3061
|
-
* API layer sends the single `X-Consent-Token` header,
|
|
3062
|
-
*
|
|
3063
|
-
* instead of decoding the JWT. Empty in the normal JWT flow.
|
|
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.
|
|
3064
3149
|
*/
|
|
3065
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;
|
|
3066
3155
|
return {
|
|
3067
3156
|
token: this.options.token,
|
|
3068
|
-
// The static token carries no identity;
|
|
3069
|
-
//
|
|
3070
|
-
//
|
|
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.
|
|
3071
3161
|
sandboxUcic: this.options.ucic || this.options.sandboxUcic,
|
|
3072
|
-
|
|
3162
|
+
sandboxEmail: this.options.email || this.options.sandboxEmail || legacyEmail,
|
|
3163
|
+
sandboxMobile: this.options.mobile || this.options.sandboxMobile || legacyMobile,
|
|
3073
3164
|
organisationUuid: this.options.organisationUuid,
|
|
3074
3165
|
workspaceUuid: this.options.workspaceUuid
|
|
3075
3166
|
};
|
|
@@ -3670,24 +3761,57 @@ var ModalController = class {
|
|
|
3670
3761
|
display: "none"
|
|
3671
3762
|
}
|
|
3672
3763
|
});
|
|
3673
|
-
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
|
+
};
|
|
3674
3775
|
const acceptStyle = {
|
|
3675
3776
|
...styles.button,
|
|
3676
|
-
...styles.acceptButton,
|
|
3677
|
-
|
|
3678
|
-
|
|
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
|
+
},
|
|
3679
3783
|
borderRadius: colors.borderRadius,
|
|
3680
|
-
|
|
3681
|
-
padding: isMobile ? "10px 20px" : "9px 45px"
|
|
3784
|
+
...gridButtonBox
|
|
3682
3785
|
};
|
|
3683
3786
|
this.acceptBtn = el("button", {
|
|
3684
|
-
id:
|
|
3787
|
+
id: ACCEPT_SELECTED_BUTTON_ID,
|
|
3685
3788
|
style: acceptStyle,
|
|
3686
3789
|
type: "button",
|
|
3687
|
-
onClick: () => this.handleAccept()
|
|
3790
|
+
onClick: () => this.handleAccept("selected")
|
|
3688
3791
|
});
|
|
3689
3792
|
this.acceptBtn.textContent = acceptBtnText;
|
|
3690
|
-
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
|
+
);
|
|
3691
3815
|
const declineStyle = {
|
|
3692
3816
|
...styles.button,
|
|
3693
3817
|
...styles.cancelButton,
|
|
@@ -3695,11 +3819,13 @@ var ModalController = class {
|
|
|
3695
3819
|
color: colors.declineText,
|
|
3696
3820
|
borderColor: colors.declineBorder,
|
|
3697
3821
|
borderRadius: colors.borderRadius,
|
|
3698
|
-
|
|
3699
|
-
|
|
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"
|
|
3700
3826
|
};
|
|
3701
3827
|
this.declineBtn = el("button", {
|
|
3702
|
-
id:
|
|
3828
|
+
id: DECLINE_BUTTON_ID,
|
|
3703
3829
|
style: declineStyle,
|
|
3704
3830
|
type: "button",
|
|
3705
3831
|
onClick: () => this.handleDecline()
|
|
@@ -3707,12 +3833,19 @@ var ModalController = class {
|
|
|
3707
3833
|
if (!state.isFullyConsentedReviewMode) {
|
|
3708
3834
|
this.declineBtn.textContent = declineBtnText;
|
|
3709
3835
|
}
|
|
3710
|
-
const bottomSection = el("div", {
|
|
3711
|
-
|
|
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);
|
|
3712
3843
|
bottomSection.appendChild(this.acceptBtn);
|
|
3713
3844
|
bottomSection.appendChild(this.declineBtn);
|
|
3714
3845
|
} else {
|
|
3846
|
+
bottomSection.appendChild(this.declineBtn);
|
|
3715
3847
|
bottomSection.appendChild(this.acceptBtn);
|
|
3848
|
+
bottomSection.appendChild(this.acceptAllBtn);
|
|
3716
3849
|
}
|
|
3717
3850
|
const contentWrapper = el(
|
|
3718
3851
|
"div",
|
|
@@ -3868,7 +4001,12 @@ var ModalController = class {
|
|
|
3868
4001
|
selectedPurposes: { ...state.selectedPurposes, [purposeUuid]: shouldCheckPurpose }
|
|
3869
4002
|
});
|
|
3870
4003
|
}
|
|
3871
|
-
|
|
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") {
|
|
3872
4010
|
var _a, _b, _c;
|
|
3873
4011
|
const state = this.store.getState();
|
|
3874
4012
|
if (state.isSubmitting) return;
|
|
@@ -3887,6 +4025,16 @@ var ModalController = class {
|
|
|
3887
4025
|
if (!content) return;
|
|
3888
4026
|
this.abortController = new AbortController();
|
|
3889
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
|
+
}
|
|
3890
4038
|
try {
|
|
3891
4039
|
const purposes = content.purposes.map((purpose) => {
|
|
3892
4040
|
const dataElements = purpose.data_elements.filter((de) => de.enabled).map((de) => ({
|
|
@@ -3894,13 +4042,13 @@ var ModalController = class {
|
|
|
3894
4042
|
name: de.name,
|
|
3895
4043
|
enabled: de.enabled,
|
|
3896
4044
|
required: de.required,
|
|
3897
|
-
selected:
|
|
4045
|
+
selected: selection.selectedDataElements[`${purpose.uuid}-${de.uuid}`] || false
|
|
3898
4046
|
}));
|
|
3899
4047
|
return {
|
|
3900
4048
|
uuid: purpose.uuid,
|
|
3901
4049
|
name: purpose.name,
|
|
3902
4050
|
description: purpose.description,
|
|
3903
|
-
selected:
|
|
4051
|
+
selected: selection.selectedPurposes[purpose.uuid] || false,
|
|
3904
4052
|
data_elements: dataElements
|
|
3905
4053
|
};
|
|
3906
4054
|
});
|
|
@@ -3962,11 +4110,16 @@ var ModalController = class {
|
|
|
3962
4110
|
}
|
|
3963
4111
|
updateAcceptButtonState(state) {
|
|
3964
4112
|
if (!this.acceptBtn || state.isFullyConsentedReviewMode) return;
|
|
4113
|
+
const submitting = state.isSubmittingAccept || state.isSubmittingDecline;
|
|
3965
4114
|
const allRequiredChecked = this.areAllRequiredChecked(state);
|
|
3966
|
-
const
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
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);
|
|
3970
4123
|
}
|
|
3971
4124
|
updateCollapseState(state) {
|
|
3972
4125
|
const updateRefs = (refs) => {
|
|
@@ -4339,25 +4492,34 @@ var ModalController = class {
|
|
|
4339
4492
|
}
|
|
4340
4493
|
updateButtonStates(state) {
|
|
4341
4494
|
var _a;
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
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
|
+
);
|
|
4356
4517
|
if (this.declineBtn) {
|
|
4357
4518
|
this.declineBtn.disabled = state.isSubmitting;
|
|
4358
4519
|
this.declineBtn.style.opacity = state.isSubmitting ? "0.7" : "1";
|
|
4359
4520
|
this.declineBtn.style.cursor = state.isSubmitting ? "not-allowed" : "pointer";
|
|
4360
4521
|
}
|
|
4522
|
+
this.updateAcceptButtonState(state);
|
|
4361
4523
|
}
|
|
4362
4524
|
};
|
|
4363
4525
|
|
|
@@ -4558,6 +4720,9 @@ function renderInlinePurposes(purposes, options) {
|
|
|
4558
4720
|
}
|
|
4559
4721
|
});
|
|
4560
4722
|
titleEl.textContent = getTranslatedText("purposes.name", purpose.name, purpose.uuid);
|
|
4723
|
+
if (hasRequiredDataElement(purpose)) {
|
|
4724
|
+
titleEl.appendChild(createRequiredMarker());
|
|
4725
|
+
}
|
|
4561
4726
|
titleElements.set(purpose.uuid, titleEl);
|
|
4562
4727
|
const descEl = el("h3", {
|
|
4563
4728
|
style: {
|
|
@@ -4610,12 +4775,7 @@ function renderInlinePurposes(purposes, options) {
|
|
|
4610
4775
|
labelSpan.textContent = getTranslatedText("data_elements.name", dataElement.name, dataElement.uuid);
|
|
4611
4776
|
dataElementLabels.set(combinedId, labelSpan);
|
|
4612
4777
|
if (dataElement.required) {
|
|
4613
|
-
|
|
4614
|
-
style: { color: "red", marginLeft: "4px" },
|
|
4615
|
-
"aria-label": "required"
|
|
4616
|
-
});
|
|
4617
|
-
asterisk.textContent = "*";
|
|
4618
|
-
labelSpan.appendChild(asterisk);
|
|
4778
|
+
labelSpan.appendChild(createRequiredMarker());
|
|
4619
4779
|
}
|
|
4620
4780
|
const deCheckbox = el("input", {
|
|
4621
4781
|
type: "checkbox",
|