@redacto.io/consent-sdk-js 0.1.2-beta.0 → 1.0.0-beta.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 +4 -0
- package/dist/redacto.d.ts +4 -0
- package/dist/redacto.global.js +2 -2
- package/dist/redacto.global.js.map +1 -1
- package/dist/redacto.js +153 -8
- package/dist/redacto.mjs +153 -8
- package/package.json +1 -1
package/dist/redacto.js
CHANGED
|
@@ -303,6 +303,7 @@ var fetchConsentContentPublic = async ({
|
|
|
303
303
|
var submitConsentEvent = async ({
|
|
304
304
|
accessToken,
|
|
305
305
|
baseUrl,
|
|
306
|
+
ledgerBaseUrl,
|
|
306
307
|
noticeUuid,
|
|
307
308
|
purposes,
|
|
308
309
|
declined,
|
|
@@ -318,7 +319,7 @@ var submitConsentEvent = async ({
|
|
|
318
319
|
if (!decodedToken) throw new Error("Invalid access token");
|
|
319
320
|
const { organisation_uuid: ORG, workspace_uuid: WS } = decodedToken;
|
|
320
321
|
if (!ORG || !WS) throw new Error("Invalid token: missing organization or workspace UUID");
|
|
321
|
-
const apiBaseUrl = baseUrl || BASE_URL;
|
|
322
|
+
const apiBaseUrl = ledgerBaseUrl || baseUrl || BASE_URL;
|
|
322
323
|
const validatedPurposes = purposes.map((purpose) => {
|
|
323
324
|
if (!purpose.uuid) throw new Error("Invalid purpose data: missing uuid");
|
|
324
325
|
const dataElements = (purpose.data_elements || []).filter((element) => element.enabled).map((element) => ({
|
|
@@ -377,13 +378,14 @@ var submitConsentEventPublic = async ({
|
|
|
377
378
|
workspace_uuid,
|
|
378
379
|
accessToken,
|
|
379
380
|
baseUrl,
|
|
381
|
+
ledgerBaseUrl,
|
|
380
382
|
noticeUuid,
|
|
381
383
|
purposes,
|
|
382
384
|
declined,
|
|
383
385
|
meta_data
|
|
384
386
|
}) => {
|
|
385
387
|
var _a, _b;
|
|
386
|
-
const apiBaseUrl = baseUrl || BASE_URL_PUBLIC;
|
|
388
|
+
const apiBaseUrl = ledgerBaseUrl || baseUrl || BASE_URL_PUBLIC;
|
|
387
389
|
const payload = {
|
|
388
390
|
notice_uuid: noticeUuid,
|
|
389
391
|
purposes: purposes.map((purpose) => {
|
|
@@ -616,6 +618,95 @@ var verifyGuardianStatus = async ({
|
|
|
616
618
|
};
|
|
617
619
|
};
|
|
618
620
|
|
|
621
|
+
// src/api/notice-consent-status.ts
|
|
622
|
+
var fetchNoticeConsentStatusForScope = async ({
|
|
623
|
+
accessToken,
|
|
624
|
+
ledgerBaseUrl,
|
|
625
|
+
organisationUuid,
|
|
626
|
+
workspaceUuid,
|
|
627
|
+
noticeUuid,
|
|
628
|
+
signal
|
|
629
|
+
}) => {
|
|
630
|
+
if (!accessToken || !ledgerBaseUrl || !organisationUuid || !workspaceUuid || !noticeUuid) {
|
|
631
|
+
return null;
|
|
632
|
+
}
|
|
633
|
+
try {
|
|
634
|
+
const response = await fetch(
|
|
635
|
+
`${ledgerBaseUrl}/public/organisations/${organisationUuid}/workspaces/${workspaceUuid}/notices/${noticeUuid}/check-consent`,
|
|
636
|
+
{
|
|
637
|
+
method: "POST",
|
|
638
|
+
headers: {
|
|
639
|
+
Authorization: `Bearer ${accessToken}`,
|
|
640
|
+
"Content-Type": "application/json"
|
|
641
|
+
},
|
|
642
|
+
body: "{}",
|
|
643
|
+
signal
|
|
644
|
+
}
|
|
645
|
+
);
|
|
646
|
+
if (!response.ok) return null;
|
|
647
|
+
const data = await response.json();
|
|
648
|
+
const body = data && typeof data === "object" && "detail" in data && data.detail ? data.detail : data;
|
|
649
|
+
if (!body || !Array.isArray(body.purposes)) return null;
|
|
650
|
+
return body;
|
|
651
|
+
} catch {
|
|
652
|
+
return null;
|
|
653
|
+
}
|
|
654
|
+
};
|
|
655
|
+
var fetchNoticeConsentStatus = async ({
|
|
656
|
+
accessToken,
|
|
657
|
+
ledgerBaseUrl,
|
|
658
|
+
noticeUuid,
|
|
659
|
+
signal
|
|
660
|
+
}) => {
|
|
661
|
+
if (!accessToken || !ledgerBaseUrl || !noticeUuid) return null;
|
|
662
|
+
const decodedToken = decodeTokenSafely(accessToken);
|
|
663
|
+
if (!decodedToken) return null;
|
|
664
|
+
const { organisation_uuid: ORG, workspace_uuid: WS } = decodedToken;
|
|
665
|
+
if (!ORG || !WS) return null;
|
|
666
|
+
return fetchNoticeConsentStatusForScope({
|
|
667
|
+
accessToken,
|
|
668
|
+
ledgerBaseUrl,
|
|
669
|
+
organisationUuid: ORG,
|
|
670
|
+
workspaceUuid: WS,
|
|
671
|
+
noticeUuid,
|
|
672
|
+
signal
|
|
673
|
+
});
|
|
674
|
+
};
|
|
675
|
+
var isNoticeFullyConsented = (status) => status.consented && status.all_mandatory_active;
|
|
676
|
+
var buildLedgerConsentOverlay = (status, configPurposes) => {
|
|
677
|
+
var _a, _b;
|
|
678
|
+
const byUuid = new Map(status.purposes.map((p) => [p.uuid, p]));
|
|
679
|
+
const hasHistory = status.purposes.some((p) => (p.status || "INACTIVE") !== "INACTIVE");
|
|
680
|
+
if (!hasHistory) return null;
|
|
681
|
+
const purpose_selections = {};
|
|
682
|
+
for (const cp of configPurposes) {
|
|
683
|
+
const lp = byUuid.get(cp.uuid);
|
|
684
|
+
const purposeStatus = (lp == null ? void 0 : lp.status) || "INACTIVE";
|
|
685
|
+
const selectedByUuid = new Map(((lp == null ? void 0 : lp.data_elements) || []).map((de) => [de.uuid, de.selected]));
|
|
686
|
+
const data_elements = {};
|
|
687
|
+
for (const de of cp.data_elements) {
|
|
688
|
+
data_elements[de.uuid] = {
|
|
689
|
+
selected: (_a = selectedByUuid.get(de.uuid)) != null ? _a : false,
|
|
690
|
+
enabled: de.enabled,
|
|
691
|
+
required: de.required
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
purpose_selections[cp.uuid] = {
|
|
695
|
+
selected: (_b = lp == null ? void 0 : lp.selected) != null ? _b : false,
|
|
696
|
+
status: purposeStatus,
|
|
697
|
+
needs_reconsent: purposeStatus !== "ACTIVE",
|
|
698
|
+
data_elements
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
const reconsent_required = configPurposes.some(
|
|
702
|
+
(cp) => {
|
|
703
|
+
var _a2;
|
|
704
|
+
return (((_a2 = purpose_selections[cp.uuid]) == null ? void 0 : _a2.status) || "INACTIVE") !== "ACTIVE";
|
|
705
|
+
}
|
|
706
|
+
);
|
|
707
|
+
return { reconsent_required, purpose_selections };
|
|
708
|
+
};
|
|
709
|
+
|
|
619
710
|
// src/styles/inject.ts
|
|
620
711
|
var STYLE_ID = "redacto-consent-checkbox-styles";
|
|
621
712
|
var STYLE_CONTENT = `
|
|
@@ -2757,7 +2848,7 @@ var ModalController = class {
|
|
|
2757
2848
|
}
|
|
2758
2849
|
// ─── Private ───
|
|
2759
2850
|
async fetchNotice() {
|
|
2760
|
-
var _a, _b, _c;
|
|
2851
|
+
var _a, _b, _c, _d;
|
|
2761
2852
|
this.abortController = new AbortController();
|
|
2762
2853
|
this.store.setState({ isLoading: true, fetchError: null });
|
|
2763
2854
|
try {
|
|
@@ -2773,7 +2864,23 @@ var ModalController = class {
|
|
|
2773
2864
|
signal: this.abortController.signal
|
|
2774
2865
|
});
|
|
2775
2866
|
const activeConfig = data.detail.active_config;
|
|
2776
|
-
|
|
2867
|
+
let purposeSelections = data.detail.purpose_selections;
|
|
2868
|
+
let reconsentRequired = data.detail.reconsent_required;
|
|
2869
|
+
let ledgerOverlayApplied = false;
|
|
2870
|
+
if (this.options.ledgerBaseUrl && !this.options.applicationId) {
|
|
2871
|
+
const ledgerStatus = await fetchNoticeConsentStatus({
|
|
2872
|
+
accessToken: this.options.accessToken,
|
|
2873
|
+
ledgerBaseUrl: this.options.ledgerBaseUrl,
|
|
2874
|
+
noticeUuid: this.options.noticeId,
|
|
2875
|
+
signal: this.abortController.signal
|
|
2876
|
+
});
|
|
2877
|
+
if (ledgerStatus) {
|
|
2878
|
+
const overlay = buildLedgerConsentOverlay(ledgerStatus, activeConfig.purposes);
|
|
2879
|
+
reconsentRequired = (_a = overlay == null ? void 0 : overlay.reconsent_required) != null ? _a : false;
|
|
2880
|
+
purposeSelections = overlay == null ? void 0 : overlay.purpose_selections;
|
|
2881
|
+
ledgerOverlayApplied = Boolean(overlay);
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2777
2884
|
const initialCollapsed = {};
|
|
2778
2885
|
const initialPurposes = {};
|
|
2779
2886
|
const initialElements = {};
|
|
@@ -2781,7 +2888,7 @@ var ModalController = class {
|
|
|
2781
2888
|
let needsConsentPurposes = [];
|
|
2782
2889
|
let alreadyConsentedPurposes = [];
|
|
2783
2890
|
let isFullyConsentedReviewMode = false;
|
|
2784
|
-
if (purposeSelections &&
|
|
2891
|
+
if (purposeSelections && (reconsentRequired || ledgerOverlayApplied)) {
|
|
2785
2892
|
for (const purpose of activeConfig.purposes) {
|
|
2786
2893
|
const selection = purposeSelections[purpose.uuid];
|
|
2787
2894
|
initialCollapsed[purpose.uuid] = true;
|
|
@@ -2790,7 +2897,7 @@ var ModalController = class {
|
|
|
2790
2897
|
initialPurposes[purpose.uuid] = true;
|
|
2791
2898
|
for (const de of purpose.data_elements) {
|
|
2792
2899
|
const combinedId = `${purpose.uuid}-${de.uuid}`;
|
|
2793
|
-
const deSelection = (
|
|
2900
|
+
const deSelection = (_b = selection.data_elements) == null ? void 0 : _b[de.uuid];
|
|
2794
2901
|
const wasSelected = (deSelection == null ? void 0 : deSelection.selected) || false;
|
|
2795
2902
|
initialElements[combinedId] = wasSelected;
|
|
2796
2903
|
initialDataElementSelections[combinedId] = wasSelected;
|
|
@@ -2879,7 +2986,7 @@ var ModalController = class {
|
|
|
2879
2986
|
this.options.onAccept();
|
|
2880
2987
|
} else {
|
|
2881
2988
|
this.store.setState({ fetchError: error, isLoading: false });
|
|
2882
|
-
(
|
|
2989
|
+
(_d = (_c = this.options).onError) == null ? void 0 : _d.call(_c, error);
|
|
2883
2990
|
}
|
|
2884
2991
|
}
|
|
2885
2992
|
}
|
|
@@ -3587,6 +3694,7 @@ var ModalController = class {
|
|
|
3587
3694
|
await submitConsentEvent({
|
|
3588
3695
|
accessToken: this.options.accessToken,
|
|
3589
3696
|
baseUrl: this.options.baseUrl,
|
|
3697
|
+
ledgerBaseUrl: this.options.ledgerBaseUrl,
|
|
3590
3698
|
noticeUuid: state.content.detail.uuid,
|
|
3591
3699
|
purposes,
|
|
3592
3700
|
declined: false,
|
|
@@ -4337,6 +4445,7 @@ var InlineController = class {
|
|
|
4337
4445
|
this.purposeRefs = null;
|
|
4338
4446
|
this.errorEl = null;
|
|
4339
4447
|
this.hasSubmitted = false;
|
|
4448
|
+
this.hasCheckedLedgerConsent = false;
|
|
4340
4449
|
this.unsubscribe = null;
|
|
4341
4450
|
this.options = options;
|
|
4342
4451
|
this.store = new Store({
|
|
@@ -4348,6 +4457,7 @@ var InlineController = class {
|
|
|
4348
4457
|
selectedPurposes: {},
|
|
4349
4458
|
selectedDataElements: {},
|
|
4350
4459
|
hasAlreadyConsented: false,
|
|
4460
|
+
isCheckingConsent: false,
|
|
4351
4461
|
fetchError: null,
|
|
4352
4462
|
errorMessage: null
|
|
4353
4463
|
});
|
|
@@ -4390,6 +4500,10 @@ var InlineController = class {
|
|
|
4390
4500
|
}
|
|
4391
4501
|
updateToken(accessToken) {
|
|
4392
4502
|
this.options = { ...this.options, accessToken };
|
|
4503
|
+
if (this.shouldCheckLedgerConsent()) {
|
|
4504
|
+
void this.checkLedgerConsent();
|
|
4505
|
+
return;
|
|
4506
|
+
}
|
|
4393
4507
|
this.tryAutoSubmit();
|
|
4394
4508
|
}
|
|
4395
4509
|
// ─── Private ───
|
|
@@ -4416,6 +4530,7 @@ var InlineController = class {
|
|
|
4416
4530
|
initialElements[`${purpose.uuid}-${de.uuid}`] = false;
|
|
4417
4531
|
}
|
|
4418
4532
|
}
|
|
4533
|
+
void this.checkLedgerConsent();
|
|
4419
4534
|
this.store.setState({
|
|
4420
4535
|
content: activeConfig,
|
|
4421
4536
|
isLoading: false,
|
|
@@ -4435,6 +4550,35 @@ var InlineController = class {
|
|
|
4435
4550
|
}
|
|
4436
4551
|
}
|
|
4437
4552
|
}
|
|
4553
|
+
shouldCheckLedgerConsent() {
|
|
4554
|
+
return Boolean(this.options.ledgerBaseUrl) && !this.options.applicationId && Boolean(this.options.accessToken) && !this.hasCheckedLedgerConsent;
|
|
4555
|
+
}
|
|
4556
|
+
// Ask the Go ledger whether this principal has already fully consented —
|
|
4557
|
+
// the ledger equivalent of the legacy Python 409. Skipped without a ledger
|
|
4558
|
+
// (the 409 path governs) and for the magic-link applicationId flow. Any
|
|
4559
|
+
// failure returns null and falls through to the normal flow.
|
|
4560
|
+
async checkLedgerConsent() {
|
|
4561
|
+
var _a, _b;
|
|
4562
|
+
if (!this.shouldCheckLedgerConsent()) return;
|
|
4563
|
+
this.hasCheckedLedgerConsent = true;
|
|
4564
|
+
this.store.setState({ isCheckingConsent: true });
|
|
4565
|
+
try {
|
|
4566
|
+
const status = await fetchNoticeConsentStatusForScope({
|
|
4567
|
+
accessToken: this.options.accessToken,
|
|
4568
|
+
ledgerBaseUrl: this.options.ledgerBaseUrl,
|
|
4569
|
+
organisationUuid: this.options.org_uuid,
|
|
4570
|
+
workspaceUuid: this.options.workspace_uuid,
|
|
4571
|
+
noticeUuid: this.options.notice_uuid
|
|
4572
|
+
});
|
|
4573
|
+
if (status && isNoticeFullyConsented(status)) {
|
|
4574
|
+
this.store.setState({ hasAlreadyConsented: true });
|
|
4575
|
+
(_b = (_a = this.options).onAccept) == null ? void 0 : _b.call(_a);
|
|
4576
|
+
}
|
|
4577
|
+
} finally {
|
|
4578
|
+
this.store.setState({ isCheckingConsent: false });
|
|
4579
|
+
this.tryAutoSubmit();
|
|
4580
|
+
}
|
|
4581
|
+
}
|
|
4438
4582
|
handleStateChange(state, prevState) {
|
|
4439
4583
|
if (prevState.isLoading && !state.isLoading && state.content) {
|
|
4440
4584
|
this.rebuildUI();
|
|
@@ -4656,7 +4800,7 @@ var InlineController = class {
|
|
|
4656
4800
|
var _a, _b;
|
|
4657
4801
|
const state = this.store.getState();
|
|
4658
4802
|
const accessToken = this.options.accessToken;
|
|
4659
|
-
if (accessToken && state.content && this.areAllRequiredElementsChecked() && !state.isSubmitting && !this.hasSubmitted) {
|
|
4803
|
+
if (accessToken && state.content && this.areAllRequiredElementsChecked() && !state.isSubmitting && !state.isCheckingConsent && !state.hasAlreadyConsented && !this.hasSubmitted) {
|
|
4660
4804
|
this.hasSubmitted = true;
|
|
4661
4805
|
try {
|
|
4662
4806
|
await this.handleSubmit(accessToken);
|
|
@@ -4681,6 +4825,7 @@ var InlineController = class {
|
|
|
4681
4825
|
workspace_uuid: this.options.workspace_uuid,
|
|
4682
4826
|
accessToken: tokenToUse,
|
|
4683
4827
|
baseUrl: this.options.baseUrl,
|
|
4828
|
+
ledgerBaseUrl: this.options.ledgerBaseUrl,
|
|
4684
4829
|
noticeUuid: content.notice_uuid,
|
|
4685
4830
|
purposes: content.purposes.map((purpose) => {
|
|
4686
4831
|
const hasSelectedRequired = purpose.data_elements.some(
|
package/dist/redacto.mjs
CHANGED
|
@@ -273,6 +273,7 @@ var fetchConsentContentPublic = async ({
|
|
|
273
273
|
var submitConsentEvent = async ({
|
|
274
274
|
accessToken,
|
|
275
275
|
baseUrl,
|
|
276
|
+
ledgerBaseUrl,
|
|
276
277
|
noticeUuid,
|
|
277
278
|
purposes,
|
|
278
279
|
declined,
|
|
@@ -288,7 +289,7 @@ var submitConsentEvent = async ({
|
|
|
288
289
|
if (!decodedToken) throw new Error("Invalid access token");
|
|
289
290
|
const { organisation_uuid: ORG, workspace_uuid: WS } = decodedToken;
|
|
290
291
|
if (!ORG || !WS) throw new Error("Invalid token: missing organization or workspace UUID");
|
|
291
|
-
const apiBaseUrl = baseUrl || BASE_URL;
|
|
292
|
+
const apiBaseUrl = ledgerBaseUrl || baseUrl || BASE_URL;
|
|
292
293
|
const validatedPurposes = purposes.map((purpose) => {
|
|
293
294
|
if (!purpose.uuid) throw new Error("Invalid purpose data: missing uuid");
|
|
294
295
|
const dataElements = (purpose.data_elements || []).filter((element) => element.enabled).map((element) => ({
|
|
@@ -347,13 +348,14 @@ var submitConsentEventPublic = async ({
|
|
|
347
348
|
workspace_uuid,
|
|
348
349
|
accessToken,
|
|
349
350
|
baseUrl,
|
|
351
|
+
ledgerBaseUrl,
|
|
350
352
|
noticeUuid,
|
|
351
353
|
purposes,
|
|
352
354
|
declined,
|
|
353
355
|
meta_data
|
|
354
356
|
}) => {
|
|
355
357
|
var _a, _b;
|
|
356
|
-
const apiBaseUrl = baseUrl || BASE_URL_PUBLIC;
|
|
358
|
+
const apiBaseUrl = ledgerBaseUrl || baseUrl || BASE_URL_PUBLIC;
|
|
357
359
|
const payload = {
|
|
358
360
|
notice_uuid: noticeUuid,
|
|
359
361
|
purposes: purposes.map((purpose) => {
|
|
@@ -586,6 +588,95 @@ var verifyGuardianStatus = async ({
|
|
|
586
588
|
};
|
|
587
589
|
};
|
|
588
590
|
|
|
591
|
+
// src/api/notice-consent-status.ts
|
|
592
|
+
var fetchNoticeConsentStatusForScope = async ({
|
|
593
|
+
accessToken,
|
|
594
|
+
ledgerBaseUrl,
|
|
595
|
+
organisationUuid,
|
|
596
|
+
workspaceUuid,
|
|
597
|
+
noticeUuid,
|
|
598
|
+
signal
|
|
599
|
+
}) => {
|
|
600
|
+
if (!accessToken || !ledgerBaseUrl || !organisationUuid || !workspaceUuid || !noticeUuid) {
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
603
|
+
try {
|
|
604
|
+
const response = await fetch(
|
|
605
|
+
`${ledgerBaseUrl}/public/organisations/${organisationUuid}/workspaces/${workspaceUuid}/notices/${noticeUuid}/check-consent`,
|
|
606
|
+
{
|
|
607
|
+
method: "POST",
|
|
608
|
+
headers: {
|
|
609
|
+
Authorization: `Bearer ${accessToken}`,
|
|
610
|
+
"Content-Type": "application/json"
|
|
611
|
+
},
|
|
612
|
+
body: "{}",
|
|
613
|
+
signal
|
|
614
|
+
}
|
|
615
|
+
);
|
|
616
|
+
if (!response.ok) return null;
|
|
617
|
+
const data = await response.json();
|
|
618
|
+
const body = data && typeof data === "object" && "detail" in data && data.detail ? data.detail : data;
|
|
619
|
+
if (!body || !Array.isArray(body.purposes)) return null;
|
|
620
|
+
return body;
|
|
621
|
+
} catch {
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
var fetchNoticeConsentStatus = async ({
|
|
626
|
+
accessToken,
|
|
627
|
+
ledgerBaseUrl,
|
|
628
|
+
noticeUuid,
|
|
629
|
+
signal
|
|
630
|
+
}) => {
|
|
631
|
+
if (!accessToken || !ledgerBaseUrl || !noticeUuid) return null;
|
|
632
|
+
const decodedToken = decodeTokenSafely(accessToken);
|
|
633
|
+
if (!decodedToken) return null;
|
|
634
|
+
const { organisation_uuid: ORG, workspace_uuid: WS } = decodedToken;
|
|
635
|
+
if (!ORG || !WS) return null;
|
|
636
|
+
return fetchNoticeConsentStatusForScope({
|
|
637
|
+
accessToken,
|
|
638
|
+
ledgerBaseUrl,
|
|
639
|
+
organisationUuid: ORG,
|
|
640
|
+
workspaceUuid: WS,
|
|
641
|
+
noticeUuid,
|
|
642
|
+
signal
|
|
643
|
+
});
|
|
644
|
+
};
|
|
645
|
+
var isNoticeFullyConsented = (status) => status.consented && status.all_mandatory_active;
|
|
646
|
+
var buildLedgerConsentOverlay = (status, configPurposes) => {
|
|
647
|
+
var _a, _b;
|
|
648
|
+
const byUuid = new Map(status.purposes.map((p) => [p.uuid, p]));
|
|
649
|
+
const hasHistory = status.purposes.some((p) => (p.status || "INACTIVE") !== "INACTIVE");
|
|
650
|
+
if (!hasHistory) return null;
|
|
651
|
+
const purpose_selections = {};
|
|
652
|
+
for (const cp of configPurposes) {
|
|
653
|
+
const lp = byUuid.get(cp.uuid);
|
|
654
|
+
const purposeStatus = (lp == null ? void 0 : lp.status) || "INACTIVE";
|
|
655
|
+
const selectedByUuid = new Map(((lp == null ? void 0 : lp.data_elements) || []).map((de) => [de.uuid, de.selected]));
|
|
656
|
+
const data_elements = {};
|
|
657
|
+
for (const de of cp.data_elements) {
|
|
658
|
+
data_elements[de.uuid] = {
|
|
659
|
+
selected: (_a = selectedByUuid.get(de.uuid)) != null ? _a : false,
|
|
660
|
+
enabled: de.enabled,
|
|
661
|
+
required: de.required
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
purpose_selections[cp.uuid] = {
|
|
665
|
+
selected: (_b = lp == null ? void 0 : lp.selected) != null ? _b : false,
|
|
666
|
+
status: purposeStatus,
|
|
667
|
+
needs_reconsent: purposeStatus !== "ACTIVE",
|
|
668
|
+
data_elements
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
const reconsent_required = configPurposes.some(
|
|
672
|
+
(cp) => {
|
|
673
|
+
var _a2;
|
|
674
|
+
return (((_a2 = purpose_selections[cp.uuid]) == null ? void 0 : _a2.status) || "INACTIVE") !== "ACTIVE";
|
|
675
|
+
}
|
|
676
|
+
);
|
|
677
|
+
return { reconsent_required, purpose_selections };
|
|
678
|
+
};
|
|
679
|
+
|
|
589
680
|
// src/styles/inject.ts
|
|
590
681
|
var STYLE_ID = "redacto-consent-checkbox-styles";
|
|
591
682
|
var STYLE_CONTENT = `
|
|
@@ -2727,7 +2818,7 @@ var ModalController = class {
|
|
|
2727
2818
|
}
|
|
2728
2819
|
// ─── Private ───
|
|
2729
2820
|
async fetchNotice() {
|
|
2730
|
-
var _a, _b, _c;
|
|
2821
|
+
var _a, _b, _c, _d;
|
|
2731
2822
|
this.abortController = new AbortController();
|
|
2732
2823
|
this.store.setState({ isLoading: true, fetchError: null });
|
|
2733
2824
|
try {
|
|
@@ -2743,7 +2834,23 @@ var ModalController = class {
|
|
|
2743
2834
|
signal: this.abortController.signal
|
|
2744
2835
|
});
|
|
2745
2836
|
const activeConfig = data.detail.active_config;
|
|
2746
|
-
|
|
2837
|
+
let purposeSelections = data.detail.purpose_selections;
|
|
2838
|
+
let reconsentRequired = data.detail.reconsent_required;
|
|
2839
|
+
let ledgerOverlayApplied = false;
|
|
2840
|
+
if (this.options.ledgerBaseUrl && !this.options.applicationId) {
|
|
2841
|
+
const ledgerStatus = await fetchNoticeConsentStatus({
|
|
2842
|
+
accessToken: this.options.accessToken,
|
|
2843
|
+
ledgerBaseUrl: this.options.ledgerBaseUrl,
|
|
2844
|
+
noticeUuid: this.options.noticeId,
|
|
2845
|
+
signal: this.abortController.signal
|
|
2846
|
+
});
|
|
2847
|
+
if (ledgerStatus) {
|
|
2848
|
+
const overlay = buildLedgerConsentOverlay(ledgerStatus, activeConfig.purposes);
|
|
2849
|
+
reconsentRequired = (_a = overlay == null ? void 0 : overlay.reconsent_required) != null ? _a : false;
|
|
2850
|
+
purposeSelections = overlay == null ? void 0 : overlay.purpose_selections;
|
|
2851
|
+
ledgerOverlayApplied = Boolean(overlay);
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2747
2854
|
const initialCollapsed = {};
|
|
2748
2855
|
const initialPurposes = {};
|
|
2749
2856
|
const initialElements = {};
|
|
@@ -2751,7 +2858,7 @@ var ModalController = class {
|
|
|
2751
2858
|
let needsConsentPurposes = [];
|
|
2752
2859
|
let alreadyConsentedPurposes = [];
|
|
2753
2860
|
let isFullyConsentedReviewMode = false;
|
|
2754
|
-
if (purposeSelections &&
|
|
2861
|
+
if (purposeSelections && (reconsentRequired || ledgerOverlayApplied)) {
|
|
2755
2862
|
for (const purpose of activeConfig.purposes) {
|
|
2756
2863
|
const selection = purposeSelections[purpose.uuid];
|
|
2757
2864
|
initialCollapsed[purpose.uuid] = true;
|
|
@@ -2760,7 +2867,7 @@ var ModalController = class {
|
|
|
2760
2867
|
initialPurposes[purpose.uuid] = true;
|
|
2761
2868
|
for (const de of purpose.data_elements) {
|
|
2762
2869
|
const combinedId = `${purpose.uuid}-${de.uuid}`;
|
|
2763
|
-
const deSelection = (
|
|
2870
|
+
const deSelection = (_b = selection.data_elements) == null ? void 0 : _b[de.uuid];
|
|
2764
2871
|
const wasSelected = (deSelection == null ? void 0 : deSelection.selected) || false;
|
|
2765
2872
|
initialElements[combinedId] = wasSelected;
|
|
2766
2873
|
initialDataElementSelections[combinedId] = wasSelected;
|
|
@@ -2849,7 +2956,7 @@ var ModalController = class {
|
|
|
2849
2956
|
this.options.onAccept();
|
|
2850
2957
|
} else {
|
|
2851
2958
|
this.store.setState({ fetchError: error, isLoading: false });
|
|
2852
|
-
(
|
|
2959
|
+
(_d = (_c = this.options).onError) == null ? void 0 : _d.call(_c, error);
|
|
2853
2960
|
}
|
|
2854
2961
|
}
|
|
2855
2962
|
}
|
|
@@ -3557,6 +3664,7 @@ var ModalController = class {
|
|
|
3557
3664
|
await submitConsentEvent({
|
|
3558
3665
|
accessToken: this.options.accessToken,
|
|
3559
3666
|
baseUrl: this.options.baseUrl,
|
|
3667
|
+
ledgerBaseUrl: this.options.ledgerBaseUrl,
|
|
3560
3668
|
noticeUuid: state.content.detail.uuid,
|
|
3561
3669
|
purposes,
|
|
3562
3670
|
declined: false,
|
|
@@ -4307,6 +4415,7 @@ var InlineController = class {
|
|
|
4307
4415
|
this.purposeRefs = null;
|
|
4308
4416
|
this.errorEl = null;
|
|
4309
4417
|
this.hasSubmitted = false;
|
|
4418
|
+
this.hasCheckedLedgerConsent = false;
|
|
4310
4419
|
this.unsubscribe = null;
|
|
4311
4420
|
this.options = options;
|
|
4312
4421
|
this.store = new Store({
|
|
@@ -4318,6 +4427,7 @@ var InlineController = class {
|
|
|
4318
4427
|
selectedPurposes: {},
|
|
4319
4428
|
selectedDataElements: {},
|
|
4320
4429
|
hasAlreadyConsented: false,
|
|
4430
|
+
isCheckingConsent: false,
|
|
4321
4431
|
fetchError: null,
|
|
4322
4432
|
errorMessage: null
|
|
4323
4433
|
});
|
|
@@ -4360,6 +4470,10 @@ var InlineController = class {
|
|
|
4360
4470
|
}
|
|
4361
4471
|
updateToken(accessToken) {
|
|
4362
4472
|
this.options = { ...this.options, accessToken };
|
|
4473
|
+
if (this.shouldCheckLedgerConsent()) {
|
|
4474
|
+
void this.checkLedgerConsent();
|
|
4475
|
+
return;
|
|
4476
|
+
}
|
|
4363
4477
|
this.tryAutoSubmit();
|
|
4364
4478
|
}
|
|
4365
4479
|
// ─── Private ───
|
|
@@ -4386,6 +4500,7 @@ var InlineController = class {
|
|
|
4386
4500
|
initialElements[`${purpose.uuid}-${de.uuid}`] = false;
|
|
4387
4501
|
}
|
|
4388
4502
|
}
|
|
4503
|
+
void this.checkLedgerConsent();
|
|
4389
4504
|
this.store.setState({
|
|
4390
4505
|
content: activeConfig,
|
|
4391
4506
|
isLoading: false,
|
|
@@ -4405,6 +4520,35 @@ var InlineController = class {
|
|
|
4405
4520
|
}
|
|
4406
4521
|
}
|
|
4407
4522
|
}
|
|
4523
|
+
shouldCheckLedgerConsent() {
|
|
4524
|
+
return Boolean(this.options.ledgerBaseUrl) && !this.options.applicationId && Boolean(this.options.accessToken) && !this.hasCheckedLedgerConsent;
|
|
4525
|
+
}
|
|
4526
|
+
// Ask the Go ledger whether this principal has already fully consented —
|
|
4527
|
+
// the ledger equivalent of the legacy Python 409. Skipped without a ledger
|
|
4528
|
+
// (the 409 path governs) and for the magic-link applicationId flow. Any
|
|
4529
|
+
// failure returns null and falls through to the normal flow.
|
|
4530
|
+
async checkLedgerConsent() {
|
|
4531
|
+
var _a, _b;
|
|
4532
|
+
if (!this.shouldCheckLedgerConsent()) return;
|
|
4533
|
+
this.hasCheckedLedgerConsent = true;
|
|
4534
|
+
this.store.setState({ isCheckingConsent: true });
|
|
4535
|
+
try {
|
|
4536
|
+
const status = await fetchNoticeConsentStatusForScope({
|
|
4537
|
+
accessToken: this.options.accessToken,
|
|
4538
|
+
ledgerBaseUrl: this.options.ledgerBaseUrl,
|
|
4539
|
+
organisationUuid: this.options.org_uuid,
|
|
4540
|
+
workspaceUuid: this.options.workspace_uuid,
|
|
4541
|
+
noticeUuid: this.options.notice_uuid
|
|
4542
|
+
});
|
|
4543
|
+
if (status && isNoticeFullyConsented(status)) {
|
|
4544
|
+
this.store.setState({ hasAlreadyConsented: true });
|
|
4545
|
+
(_b = (_a = this.options).onAccept) == null ? void 0 : _b.call(_a);
|
|
4546
|
+
}
|
|
4547
|
+
} finally {
|
|
4548
|
+
this.store.setState({ isCheckingConsent: false });
|
|
4549
|
+
this.tryAutoSubmit();
|
|
4550
|
+
}
|
|
4551
|
+
}
|
|
4408
4552
|
handleStateChange(state, prevState) {
|
|
4409
4553
|
if (prevState.isLoading && !state.isLoading && state.content) {
|
|
4410
4554
|
this.rebuildUI();
|
|
@@ -4626,7 +4770,7 @@ var InlineController = class {
|
|
|
4626
4770
|
var _a, _b;
|
|
4627
4771
|
const state = this.store.getState();
|
|
4628
4772
|
const accessToken = this.options.accessToken;
|
|
4629
|
-
if (accessToken && state.content && this.areAllRequiredElementsChecked() && !state.isSubmitting && !this.hasSubmitted) {
|
|
4773
|
+
if (accessToken && state.content && this.areAllRequiredElementsChecked() && !state.isSubmitting && !state.isCheckingConsent && !state.hasAlreadyConsented && !this.hasSubmitted) {
|
|
4630
4774
|
this.hasSubmitted = true;
|
|
4631
4775
|
try {
|
|
4632
4776
|
await this.handleSubmit(accessToken);
|
|
@@ -4651,6 +4795,7 @@ var InlineController = class {
|
|
|
4651
4795
|
workspace_uuid: this.options.workspace_uuid,
|
|
4652
4796
|
accessToken: tokenToUse,
|
|
4653
4797
|
baseUrl: this.options.baseUrl,
|
|
4798
|
+
ledgerBaseUrl: this.options.ledgerBaseUrl,
|
|
4654
4799
|
noticeUuid: content.notice_uuid,
|
|
4655
4800
|
purposes: content.purposes.map((purpose) => {
|
|
4656
4801
|
const hasSelectedRequired = purpose.data_elements.some(
|