@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.mjs
CHANGED
|
@@ -186,7 +186,7 @@ function decodeTokenSafely(token) {
|
|
|
186
186
|
// src/api/sandbox.ts
|
|
187
187
|
var SANDBOX_TOKEN_HEADER = "X-Consent-Token";
|
|
188
188
|
var resolveSandboxAuth = (options) => {
|
|
189
|
-
var _a, _b, _c, _d, _e;
|
|
189
|
+
var _a, _b, _c, _d, _e, _f;
|
|
190
190
|
const token = (_a = options.token) == null ? void 0 : _a.trim();
|
|
191
191
|
if (!token) {
|
|
192
192
|
return null;
|
|
@@ -194,16 +194,17 @@ var resolveSandboxAuth = (options) => {
|
|
|
194
194
|
const organisationUuid = (_b = options.organisationUuid) == null ? void 0 : _b.trim();
|
|
195
195
|
const workspaceUuid = (_c = options.workspaceUuid) == null ? void 0 : _c.trim();
|
|
196
196
|
const ucic = (_d = options.sandboxUcic) == null ? void 0 : _d.trim();
|
|
197
|
-
const
|
|
197
|
+
const email = (_e = options.sandboxEmail) == null ? void 0 : _e.trim();
|
|
198
|
+
const mobile = (_f = options.sandboxMobile) == null ? void 0 : _f.trim();
|
|
198
199
|
if (!organisationUuid || !workspaceUuid) {
|
|
199
200
|
throw new Error(
|
|
200
201
|
"Sandbox mode requires organisationUuid and workspaceUuid"
|
|
201
202
|
);
|
|
202
203
|
}
|
|
203
|
-
if (!ucic && !
|
|
204
|
+
if (!ucic && !email && !mobile) {
|
|
204
205
|
throw new Error("Sandbox mode requires a UCIC, email, or mobile");
|
|
205
206
|
}
|
|
206
|
-
return { token, ucic,
|
|
207
|
+
return { token, ucic, email, mobile, organisationUuid, workspaceUuid };
|
|
207
208
|
};
|
|
208
209
|
var resolveRequestAuth = (accessToken, options) => {
|
|
209
210
|
const sandbox = resolveSandboxAuth(options);
|
|
@@ -235,10 +236,17 @@ var buildAuthHeaders = (accessToken, sandbox) => {
|
|
|
235
236
|
return { Authorization: `Bearer ${accessToken}` };
|
|
236
237
|
};
|
|
237
238
|
var sandboxBodyIdentity = (sandbox) => {
|
|
239
|
+
const payload = {};
|
|
238
240
|
if (sandbox.ucic) {
|
|
239
|
-
|
|
241
|
+
payload.org_user_id = sandbox.ucic;
|
|
240
242
|
}
|
|
241
|
-
|
|
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;
|
|
242
250
|
};
|
|
243
251
|
var sandboxQueryIdentity = (sandbox) => sandboxBodyIdentity(sandbox);
|
|
244
252
|
|
|
@@ -324,7 +332,7 @@ var fetchConsentContent = async ({
|
|
|
324
332
|
sandboxParams
|
|
325
333
|
);
|
|
326
334
|
const apiBaseUrl = baseUrl || BASE_URL;
|
|
327
|
-
const cacheIdentity = sandbox ? `sandbox:${sandbox.organisationUuid}:${sandbox.workspaceUuid}:${sandbox.ucic || sandbox.
|
|
335
|
+
const cacheIdentity = sandbox ? `sandbox:${sandbox.organisationUuid}:${sandbox.workspaceUuid}:${sandbox.ucic || sandbox.email || sandbox.mobile}` : accessToken;
|
|
328
336
|
const cacheKey = `${cacheIdentity}-${noticeId}-${validate_against}-${language}-${specific_uuid || ""}-${include_fully_consented_data}`;
|
|
329
337
|
if (validate_against === "all" && !specific_uuid) {
|
|
330
338
|
const cached = getCachedData(cacheKey);
|
|
@@ -336,7 +344,7 @@ var fetchConsentContent = async ({
|
|
|
336
344
|
if (include_fully_consented_data) url.searchParams.append("include_fully_consented_data", "true");
|
|
337
345
|
if (sandbox) {
|
|
338
346
|
for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
|
|
339
|
-
url.searchParams.append(key, value);
|
|
347
|
+
if (value) url.searchParams.append(key, value);
|
|
340
348
|
}
|
|
341
349
|
}
|
|
342
350
|
const response = await fetch(url.toString(), {
|
|
@@ -572,7 +580,7 @@ var fetchTTSAudioUrls = async ({
|
|
|
572
580
|
);
|
|
573
581
|
if (sandbox) {
|
|
574
582
|
for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
|
|
575
|
-
url.searchParams.append(key, value);
|
|
583
|
+
if (value) url.searchParams.append(key, value);
|
|
576
584
|
}
|
|
577
585
|
}
|
|
578
586
|
const response = await fetch(url.toString(), {
|
|
@@ -1287,6 +1295,32 @@ var styles = {
|
|
|
1287
1295
|
margin: 0,
|
|
1288
1296
|
boxSizing: "border-box"
|
|
1289
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
|
+
},
|
|
1290
1324
|
button: {
|
|
1291
1325
|
width: "100%",
|
|
1292
1326
|
borderRadius: "8px",
|
|
@@ -1373,6 +1407,51 @@ var styles = {
|
|
|
1373
1407
|
}
|
|
1374
1408
|
};
|
|
1375
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
|
+
|
|
1376
1455
|
// src/modal/purposes.ts
|
|
1377
1456
|
function renderModalPurposes(purposes, options) {
|
|
1378
1457
|
var _a, _b;
|
|
@@ -1438,6 +1517,9 @@ function renderModalPurposes(purposes, options) {
|
|
|
1438
1517
|
}
|
|
1439
1518
|
});
|
|
1440
1519
|
titleEl.textContent = getTranslatedText("purposes.name", purpose.name, purpose.uuid);
|
|
1520
|
+
if (hasRequiredDataElement(purpose)) {
|
|
1521
|
+
titleEl.appendChild(createRequiredMarker());
|
|
1522
|
+
}
|
|
1441
1523
|
titleElements.set(purpose.uuid, titleEl);
|
|
1442
1524
|
const descEl = el("h3", {
|
|
1443
1525
|
id: `purpose-description-${purpose.uuid}`,
|
|
@@ -1505,9 +1587,7 @@ function renderModalPurposes(purposes, options) {
|
|
|
1505
1587
|
labelSpan.textContent = getTranslatedText("data_elements.name", dataElement.name, dataElement.uuid);
|
|
1506
1588
|
dataElementLabels.set(combinedId, labelSpan);
|
|
1507
1589
|
if (dataElement.required) {
|
|
1508
|
-
|
|
1509
|
-
asterisk.textContent = "*";
|
|
1510
|
-
labelSpan.appendChild(asterisk);
|
|
1590
|
+
labelSpan.appendChild(createRequiredMarker());
|
|
1511
1591
|
}
|
|
1512
1592
|
let deRightElement;
|
|
1513
1593
|
if (!isAlreadyConsented) {
|
|
@@ -1876,24 +1956,6 @@ var updateConsentState = (updates) => {
|
|
|
1876
1956
|
}
|
|
1877
1957
|
};
|
|
1878
1958
|
|
|
1879
|
-
// src/constants.ts
|
|
1880
|
-
var DEFAULT_LOGO_POSITION = "left";
|
|
1881
|
-
var LOGO_POSITION_JUSTIFY = {
|
|
1882
|
-
left: "flex-start",
|
|
1883
|
-
center: "center",
|
|
1884
|
-
right: "flex-end"
|
|
1885
|
-
};
|
|
1886
|
-
var DPO_AUDIO_ELEMENT_MAP = [
|
|
1887
|
-
{ audioKey: "grievance_text_audio_url", elementId: "dpo-grievance-text" },
|
|
1888
|
-
{ audioKey: "grievance_anchor_text_audio_url", elementId: "dpo-grievance-link" },
|
|
1889
|
-
{ audioKey: "grievance_email_connector_text_audio_url", elementId: "dpo-grievance-email-connector" },
|
|
1890
|
-
{ audioKey: "grievance_email_audio_url", elementId: "dpo-grievance-email" },
|
|
1891
|
-
{ audioKey: "dp_board_text_audio_url", elementId: "dpo-dp-board-text" },
|
|
1892
|
-
{ audioKey: "dp_board_anchor_text_audio_url", elementId: "dpo-dp-board-link" },
|
|
1893
|
-
{ audioKey: "dpo_text_audio_url", elementId: "dpo-dpo-text" },
|
|
1894
|
-
{ audioKey: "dpo_anchor_text_audio_url", elementId: "dpo-dpo-link" }
|
|
1895
|
-
];
|
|
1896
|
-
|
|
1897
1959
|
// src/modal/tts.ts
|
|
1898
1960
|
var TTSManager = class {
|
|
1899
1961
|
/**
|
|
@@ -1947,7 +2009,8 @@ var TTSManager = class {
|
|
|
1947
2009
|
signal: this.abortController.signal,
|
|
1948
2010
|
token: params.token,
|
|
1949
2011
|
sandboxUcic: params.sandboxUcic,
|
|
1950
|
-
|
|
2012
|
+
sandboxEmail: params.sandboxEmail,
|
|
2013
|
+
sandboxMobile: params.sandboxMobile,
|
|
1951
2014
|
organisationUuid: params.organisationUuid,
|
|
1952
2015
|
workspaceUuid: params.workspaceUuid
|
|
1953
2016
|
});
|
|
@@ -2003,8 +2066,9 @@ var TTSManager = class {
|
|
|
2003
2066
|
push(audioUrl, mapping.elementId);
|
|
2004
2067
|
}
|
|
2005
2068
|
}
|
|
2006
|
-
push(urls.
|
|
2007
|
-
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);
|
|
2008
2072
|
this.audioSequence = audioSeq;
|
|
2009
2073
|
this.elementIdSequence = elementSeq;
|
|
2010
2074
|
}
|
|
@@ -2895,14 +2959,17 @@ function renderVerificationScreen(options) {
|
|
|
2895
2959
|
|
|
2896
2960
|
// src/modal/colors.ts
|
|
2897
2961
|
function resolveColors(settings, activeConfig) {
|
|
2898
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
2962
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
|
|
2899
2963
|
const primaryColor = (activeConfig == null ? void 0 : activeConfig.primary_color) || "#4f87ff";
|
|
2900
2964
|
const secondaryColor = (activeConfig == null ? void 0 : activeConfig.secondary_color) || "#4f87ff";
|
|
2901
2965
|
return {
|
|
2902
2966
|
acceptBg: ((_b = (_a = settings == null ? void 0 : settings.button) == null ? void 0 : _a.accept) == null ? void 0 : _b.backgroundColor) || primaryColor,
|
|
2903
2967
|
acceptText: ((_d = (_c = settings == null ? void 0 : settings.button) == null ? void 0 : _c.accept) == null ? void 0 : _d.textColor) || "#ffffff",
|
|
2904
|
-
|
|
2905
|
-
|
|
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",
|
|
2906
2973
|
declineBorder: (settings == null ? void 0 : settings.borderColor) || "#d0d5dd",
|
|
2907
2974
|
link: (settings == null ? void 0 : settings.link) || secondaryColor,
|
|
2908
2975
|
heading: (settings == null ? void 0 : settings.headingColor) || "#323B4B",
|
|
@@ -2914,6 +2981,23 @@ function resolveColors(settings, activeConfig) {
|
|
|
2914
2981
|
};
|
|
2915
2982
|
}
|
|
2916
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
|
+
|
|
2917
3001
|
// src/modal/layout.ts
|
|
2918
3002
|
function resolveLogoPosition(raw) {
|
|
2919
3003
|
return raw === "center" || raw === "right" ? raw : DEFAULT_LOGO_POSITION;
|
|
@@ -2946,6 +3030,7 @@ var ModalController = class {
|
|
|
2946
3030
|
this.visibilityChangeHandler = null;
|
|
2947
3031
|
// DOM refs for targeted updates
|
|
2948
3032
|
this.acceptBtn = null;
|
|
3033
|
+
this.acceptAllBtn = null;
|
|
2949
3034
|
this.declineBtn = null;
|
|
2950
3035
|
this.middleSection = null;
|
|
2951
3036
|
this.errorEl = null;
|
|
@@ -3028,18 +3113,24 @@ var ModalController = class {
|
|
|
3028
3113
|
// ─── Private ───
|
|
3029
3114
|
/**
|
|
3030
3115
|
* Sandbox auth options to forward to every API call. When `token` is set the
|
|
3031
|
-
* API layer sends the single `X-Consent-Token` header,
|
|
3032
|
-
*
|
|
3033
|
-
* instead of decoding the JWT. Empty in the normal JWT flow.
|
|
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.
|
|
3034
3119
|
*/
|
|
3035
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;
|
|
3036
3125
|
return {
|
|
3037
3126
|
token: this.options.token,
|
|
3038
|
-
// The static token carries no identity;
|
|
3039
|
-
//
|
|
3040
|
-
//
|
|
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.
|
|
3041
3131
|
sandboxUcic: this.options.ucic || this.options.sandboxUcic,
|
|
3042
|
-
|
|
3132
|
+
sandboxEmail: this.options.email || this.options.sandboxEmail || legacyEmail,
|
|
3133
|
+
sandboxMobile: this.options.mobile || this.options.sandboxMobile || legacyMobile,
|
|
3043
3134
|
organisationUuid: this.options.organisationUuid,
|
|
3044
3135
|
workspaceUuid: this.options.workspaceUuid
|
|
3045
3136
|
};
|
|
@@ -3640,24 +3731,57 @@ var ModalController = class {
|
|
|
3640
3731
|
display: "none"
|
|
3641
3732
|
}
|
|
3642
3733
|
});
|
|
3643
|
-
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
|
+
};
|
|
3644
3745
|
const acceptStyle = {
|
|
3645
3746
|
...styles.button,
|
|
3646
|
-
...styles.acceptButton,
|
|
3647
|
-
|
|
3648
|
-
|
|
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
|
+
},
|
|
3649
3753
|
borderRadius: colors.borderRadius,
|
|
3650
|
-
|
|
3651
|
-
padding: isMobile ? "10px 20px" : "9px 45px"
|
|
3754
|
+
...gridButtonBox
|
|
3652
3755
|
};
|
|
3653
3756
|
this.acceptBtn = el("button", {
|
|
3654
|
-
id:
|
|
3757
|
+
id: ACCEPT_SELECTED_BUTTON_ID,
|
|
3655
3758
|
style: acceptStyle,
|
|
3656
3759
|
type: "button",
|
|
3657
|
-
onClick: () => this.handleAccept()
|
|
3760
|
+
onClick: () => this.handleAccept("selected")
|
|
3658
3761
|
});
|
|
3659
3762
|
this.acceptBtn.textContent = acceptBtnText;
|
|
3660
|
-
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
|
+
);
|
|
3661
3785
|
const declineStyle = {
|
|
3662
3786
|
...styles.button,
|
|
3663
3787
|
...styles.cancelButton,
|
|
@@ -3665,11 +3789,13 @@ var ModalController = class {
|
|
|
3665
3789
|
color: colors.declineText,
|
|
3666
3790
|
borderColor: colors.declineBorder,
|
|
3667
3791
|
borderRadius: colors.borderRadius,
|
|
3668
|
-
|
|
3669
|
-
|
|
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"
|
|
3670
3796
|
};
|
|
3671
3797
|
this.declineBtn = el("button", {
|
|
3672
|
-
id:
|
|
3798
|
+
id: DECLINE_BUTTON_ID,
|
|
3673
3799
|
style: declineStyle,
|
|
3674
3800
|
type: "button",
|
|
3675
3801
|
onClick: () => this.handleDecline()
|
|
@@ -3677,12 +3803,19 @@ var ModalController = class {
|
|
|
3677
3803
|
if (!state.isFullyConsentedReviewMode) {
|
|
3678
3804
|
this.declineBtn.textContent = declineBtnText;
|
|
3679
3805
|
}
|
|
3680
|
-
const bottomSection = el("div", {
|
|
3681
|
-
|
|
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);
|
|
3682
3813
|
bottomSection.appendChild(this.acceptBtn);
|
|
3683
3814
|
bottomSection.appendChild(this.declineBtn);
|
|
3684
3815
|
} else {
|
|
3816
|
+
bottomSection.appendChild(this.declineBtn);
|
|
3685
3817
|
bottomSection.appendChild(this.acceptBtn);
|
|
3818
|
+
bottomSection.appendChild(this.acceptAllBtn);
|
|
3686
3819
|
}
|
|
3687
3820
|
const contentWrapper = el(
|
|
3688
3821
|
"div",
|
|
@@ -3838,7 +3971,12 @@ var ModalController = class {
|
|
|
3838
3971
|
selectedPurposes: { ...state.selectedPurposes, [purposeUuid]: shouldCheckPurpose }
|
|
3839
3972
|
});
|
|
3840
3973
|
}
|
|
3841
|
-
|
|
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") {
|
|
3842
3980
|
var _a, _b, _c;
|
|
3843
3981
|
const state = this.store.getState();
|
|
3844
3982
|
if (state.isSubmitting) return;
|
|
@@ -3857,6 +3995,16 @@ var ModalController = class {
|
|
|
3857
3995
|
if (!content) return;
|
|
3858
3996
|
this.abortController = new AbortController();
|
|
3859
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
|
+
}
|
|
3860
4008
|
try {
|
|
3861
4009
|
const purposes = content.purposes.map((purpose) => {
|
|
3862
4010
|
const dataElements = purpose.data_elements.filter((de) => de.enabled).map((de) => ({
|
|
@@ -3864,13 +4012,13 @@ var ModalController = class {
|
|
|
3864
4012
|
name: de.name,
|
|
3865
4013
|
enabled: de.enabled,
|
|
3866
4014
|
required: de.required,
|
|
3867
|
-
selected:
|
|
4015
|
+
selected: selection.selectedDataElements[`${purpose.uuid}-${de.uuid}`] || false
|
|
3868
4016
|
}));
|
|
3869
4017
|
return {
|
|
3870
4018
|
uuid: purpose.uuid,
|
|
3871
4019
|
name: purpose.name,
|
|
3872
4020
|
description: purpose.description,
|
|
3873
|
-
selected:
|
|
4021
|
+
selected: selection.selectedPurposes[purpose.uuid] || false,
|
|
3874
4022
|
data_elements: dataElements
|
|
3875
4023
|
};
|
|
3876
4024
|
});
|
|
@@ -3932,11 +4080,16 @@ var ModalController = class {
|
|
|
3932
4080
|
}
|
|
3933
4081
|
updateAcceptButtonState(state) {
|
|
3934
4082
|
if (!this.acceptBtn || state.isFullyConsentedReviewMode) return;
|
|
4083
|
+
const submitting = state.isSubmittingAccept || state.isSubmittingDecline;
|
|
3935
4084
|
const allRequiredChecked = this.areAllRequiredChecked(state);
|
|
3936
|
-
const
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
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);
|
|
3940
4093
|
}
|
|
3941
4094
|
updateCollapseState(state) {
|
|
3942
4095
|
const updateRefs = (refs) => {
|
|
@@ -4309,25 +4462,34 @@ var ModalController = class {
|
|
|
4309
4462
|
}
|
|
4310
4463
|
updateButtonStates(state) {
|
|
4311
4464
|
var _a;
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
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
|
+
);
|
|
4326
4487
|
if (this.declineBtn) {
|
|
4327
4488
|
this.declineBtn.disabled = state.isSubmitting;
|
|
4328
4489
|
this.declineBtn.style.opacity = state.isSubmitting ? "0.7" : "1";
|
|
4329
4490
|
this.declineBtn.style.cursor = state.isSubmitting ? "not-allowed" : "pointer";
|
|
4330
4491
|
}
|
|
4492
|
+
this.updateAcceptButtonState(state);
|
|
4331
4493
|
}
|
|
4332
4494
|
};
|
|
4333
4495
|
|
|
@@ -4528,6 +4690,9 @@ function renderInlinePurposes(purposes, options) {
|
|
|
4528
4690
|
}
|
|
4529
4691
|
});
|
|
4530
4692
|
titleEl.textContent = getTranslatedText("purposes.name", purpose.name, purpose.uuid);
|
|
4693
|
+
if (hasRequiredDataElement(purpose)) {
|
|
4694
|
+
titleEl.appendChild(createRequiredMarker());
|
|
4695
|
+
}
|
|
4531
4696
|
titleElements.set(purpose.uuid, titleEl);
|
|
4532
4697
|
const descEl = el("h3", {
|
|
4533
4698
|
style: {
|
|
@@ -4580,12 +4745,7 @@ function renderInlinePurposes(purposes, options) {
|
|
|
4580
4745
|
labelSpan.textContent = getTranslatedText("data_elements.name", dataElement.name, dataElement.uuid);
|
|
4581
4746
|
dataElementLabels.set(combinedId, labelSpan);
|
|
4582
4747
|
if (dataElement.required) {
|
|
4583
|
-
|
|
4584
|
-
style: { color: "red", marginLeft: "4px" },
|
|
4585
|
-
"aria-label": "required"
|
|
4586
|
-
});
|
|
4587
|
-
asterisk.textContent = "*";
|
|
4588
|
-
labelSpan.appendChild(asterisk);
|
|
4748
|
+
labelSpan.appendChild(createRequiredMarker());
|
|
4589
4749
|
}
|
|
4590
4750
|
const deCheckbox = el("input", {
|
|
4591
4751
|
type: "checkbox",
|