@redacto.io/consent-sdk-react 3.2.0 → 3.3.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/CHANGELOG.md +6 -0
- package/dist/index.js +144 -104
- package/dist/index.mjs +144 -104
- package/package.json +1 -1
- package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +14 -21
- package/src/RedactoNoticeConsent/api/index.ts +62 -52
- package/src/RedactoNoticeConsent/api/types.ts +20 -5
- package/src/RedactoNoticeConsentInline/RedactoNoticeConsentInline.tsx +18 -23
- package/src/RedactoNoticeConsentInline/api/index.ts +65 -27
- package/src/RedactoNoticeConsentInline/api/types.ts +20 -5
- package/src/utils/isLegacyApi.ts +12 -0
package/dist/index.mjs
CHANGED
|
@@ -3,6 +3,21 @@ import { useCallback, useEffect as useEffect2, useMemo, useRef, useState as useS
|
|
|
3
3
|
|
|
4
4
|
// src/RedactoNoticeConsent/api/index.ts
|
|
5
5
|
import { jwtDecode } from "jwt-decode";
|
|
6
|
+
|
|
7
|
+
// src/utils/isLegacyApi.ts
|
|
8
|
+
function isLegacyApi(baseUrl) {
|
|
9
|
+
try {
|
|
10
|
+
return new URL(baseUrl).hostname.endsWith(".redacto.io");
|
|
11
|
+
} catch {
|
|
12
|
+
try {
|
|
13
|
+
return new URL(`https://${baseUrl}`).hostname.endsWith(".redacto.io");
|
|
14
|
+
} catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/RedactoNoticeConsent/api/index.ts
|
|
6
21
|
var BASE_URL = "https://api.redacto.io/consent";
|
|
7
22
|
var apiCache = /* @__PURE__ */ new Map();
|
|
8
23
|
var CACHE_TTL = 5 * 60 * 1e3;
|
|
@@ -27,14 +42,6 @@ function decodeTokenSafely(token) {
|
|
|
27
42
|
return null;
|
|
28
43
|
}
|
|
29
44
|
}
|
|
30
|
-
var storage = {
|
|
31
|
-
getItem: (key) => {
|
|
32
|
-
if (typeof window !== "undefined") {
|
|
33
|
-
return localStorage.getItem(key);
|
|
34
|
-
}
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
45
|
var clearApiCache = () => {
|
|
39
46
|
apiCache.clear();
|
|
40
47
|
};
|
|
@@ -108,7 +115,6 @@ var fetchConsentContent = async ({
|
|
|
108
115
|
}
|
|
109
116
|
return data;
|
|
110
117
|
} catch (error) {
|
|
111
|
-
console.error("Error fetching consent content:", error instanceof Error ? error.message : error);
|
|
112
118
|
throw error;
|
|
113
119
|
}
|
|
114
120
|
};
|
|
@@ -134,42 +140,62 @@ var submitConsentEvent = async ({
|
|
|
134
140
|
if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
|
|
135
141
|
throw new Error("Invalid token: missing organization or workspace UUID");
|
|
136
142
|
}
|
|
137
|
-
const primaryEmail = storage.getItem("userEmail");
|
|
138
|
-
const validatedPurposes = purposes.map((purpose) => {
|
|
139
|
-
if (!purpose.uuid || !purpose.name) {
|
|
140
|
-
throw new Error(`Invalid purpose data: missing uuid or name`);
|
|
141
|
-
}
|
|
142
|
-
return {
|
|
143
|
-
uuid: purpose.uuid,
|
|
144
|
-
name: purpose.name,
|
|
145
|
-
description: purpose.description || "",
|
|
146
|
-
industries: purpose.industries || "",
|
|
147
|
-
selected: Boolean(purpose.selected),
|
|
148
|
-
data_elements: (purpose.data_elements || []).map((element) => {
|
|
149
|
-
if (!element.uuid || !element.name) {
|
|
150
|
-
throw new Error(`Invalid data element: missing uuid or name`);
|
|
151
|
-
}
|
|
152
|
-
return {
|
|
153
|
-
uuid: element.uuid,
|
|
154
|
-
name: element.name,
|
|
155
|
-
enabled: Boolean(element.enabled),
|
|
156
|
-
required: Boolean(element.required),
|
|
157
|
-
selected: element.required ? true : Boolean(element.selected)
|
|
158
|
-
};
|
|
159
|
-
})
|
|
160
|
-
};
|
|
161
|
-
});
|
|
162
|
-
const payload = {
|
|
163
|
-
primary_email: primaryEmail || void 0,
|
|
164
|
-
notice_uuid: noticeUuid,
|
|
165
|
-
source: "WEB",
|
|
166
|
-
declined: Boolean(declined),
|
|
167
|
-
consents: {
|
|
168
|
-
purposes: validatedPurposes
|
|
169
|
-
},
|
|
170
|
-
meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid } : void 0
|
|
171
|
-
};
|
|
172
143
|
const apiBaseUrl = baseUrl || BASE_URL;
|
|
144
|
+
let payload;
|
|
145
|
+
if (isLegacyApi(apiBaseUrl)) {
|
|
146
|
+
const primaryEmail = typeof window !== "undefined" ? localStorage.getItem("userEmail") : null;
|
|
147
|
+
payload = {
|
|
148
|
+
primary_email: primaryEmail || void 0,
|
|
149
|
+
notice_uuid: noticeUuid,
|
|
150
|
+
source: "WEB",
|
|
151
|
+
declined: Boolean(declined),
|
|
152
|
+
consents: {
|
|
153
|
+
purposes: purposes.map((purpose) => {
|
|
154
|
+
if (!purpose.uuid) {
|
|
155
|
+
throw new Error(`Invalid purpose data: missing uuid`);
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
uuid: purpose.uuid,
|
|
159
|
+
name: purpose.name,
|
|
160
|
+
description: purpose.description || "",
|
|
161
|
+
industries: purpose.industries || "",
|
|
162
|
+
selected: Boolean(purpose.selected),
|
|
163
|
+
data_elements: (purpose.data_elements || []).map((element) => ({
|
|
164
|
+
uuid: element.uuid,
|
|
165
|
+
name: element.name,
|
|
166
|
+
enabled: Boolean(element.enabled),
|
|
167
|
+
required: Boolean(element.required),
|
|
168
|
+
selected: element.required ? true : Boolean(element.selected)
|
|
169
|
+
}))
|
|
170
|
+
};
|
|
171
|
+
})
|
|
172
|
+
},
|
|
173
|
+
meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid } : void 0
|
|
174
|
+
};
|
|
175
|
+
} else {
|
|
176
|
+
const validatedPurposes = purposes.map((purpose) => {
|
|
177
|
+
if (!purpose.uuid) {
|
|
178
|
+
throw new Error(`Invalid purpose data: missing uuid`);
|
|
179
|
+
}
|
|
180
|
+
const dataElements = (purpose.data_elements || []).filter((element) => element.enabled).map((element) => ({
|
|
181
|
+
uuid: element.uuid,
|
|
182
|
+
selected: element.required ? true : Boolean(element.selected)
|
|
183
|
+
}));
|
|
184
|
+
return {
|
|
185
|
+
purpose_uuid: purpose.uuid,
|
|
186
|
+
selected: Boolean(purpose.selected),
|
|
187
|
+
data_elements: dataElements.length > 0 ? dataElements : void 0
|
|
188
|
+
};
|
|
189
|
+
});
|
|
190
|
+
payload = {
|
|
191
|
+
notice_uuid: noticeUuid,
|
|
192
|
+
purposes: validatedPurposes,
|
|
193
|
+
select_all_mandatory: false,
|
|
194
|
+
source: "WEB",
|
|
195
|
+
declined: Boolean(declined),
|
|
196
|
+
meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid } : void 0
|
|
197
|
+
};
|
|
198
|
+
}
|
|
173
199
|
const response = await fetch(
|
|
174
200
|
`${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/submit-consent`,
|
|
175
201
|
{
|
|
@@ -202,7 +228,6 @@ var submitConsentEvent = async ({
|
|
|
202
228
|
}
|
|
203
229
|
apiCache.clear();
|
|
204
230
|
} catch (error) {
|
|
205
|
-
console.error("Error submitting consent event:", error instanceof Error ? error.message : error);
|
|
206
231
|
throw error;
|
|
207
232
|
}
|
|
208
233
|
};
|
|
@@ -334,7 +359,6 @@ var fetchTTSAudioUrls = async ({
|
|
|
334
359
|
const data = await response.json();
|
|
335
360
|
return data;
|
|
336
361
|
} catch (error) {
|
|
337
|
-
console.error("Error fetching TTS audio URLs:", error instanceof Error ? error.message : error);
|
|
338
362
|
throw error;
|
|
339
363
|
}
|
|
340
364
|
};
|
|
@@ -1825,14 +1849,15 @@ var RedactoNoticeConsent = ({
|
|
|
1825
1849
|
validate_against: validateAgainst,
|
|
1826
1850
|
signal: (_a2 = abortControllerRef.current) == null ? void 0 : _a2.signal
|
|
1827
1851
|
});
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1852
|
+
const activeConfig = consentContentData.detail.active_config;
|
|
1853
|
+
setContent(activeConfig);
|
|
1854
|
+
if (activeConfig.default_language) {
|
|
1855
|
+
const defaultLang = activeConfig.default_language;
|
|
1831
1856
|
setSelectedLanguage(
|
|
1832
1857
|
defaultLang === "en" || defaultLang === "EN" ? "English" : defaultLang
|
|
1833
1858
|
);
|
|
1834
1859
|
}
|
|
1835
|
-
const initialCollapsedState =
|
|
1860
|
+
const initialCollapsedState = activeConfig.purposes.reduce(
|
|
1836
1861
|
(acc, purpose) => ({
|
|
1837
1862
|
...acc,
|
|
1838
1863
|
[purpose.uuid]: true
|
|
@@ -1843,13 +1868,13 @@ var RedactoNoticeConsent = ({
|
|
|
1843
1868
|
const isReconsentRequired = consentContentData.detail.reconsent_required;
|
|
1844
1869
|
const purposeSelections = consentContentData.detail.purpose_selections;
|
|
1845
1870
|
const newCategorizedPurposes = isReconsentRequired ? {
|
|
1846
|
-
alreadyConsented:
|
|
1871
|
+
alreadyConsented: activeConfig.purposes.filter(
|
|
1847
1872
|
(purpose) => {
|
|
1848
1873
|
const selection = purposeSelections == null ? void 0 : purposeSelections[purpose.uuid];
|
|
1849
1874
|
return selection && (selection.status === "ACTIVE" || selection.status === "EXPIRED") && !selection.needs_reconsent;
|
|
1850
1875
|
}
|
|
1851
1876
|
),
|
|
1852
|
-
needsConsent:
|
|
1877
|
+
needsConsent: activeConfig.purposes.filter(
|
|
1853
1878
|
(purpose) => {
|
|
1854
1879
|
const selection = purposeSelections == null ? void 0 : purposeSelections[purpose.uuid];
|
|
1855
1880
|
return !selection || selection.status !== "ACTIVE" || selection.needs_reconsent;
|
|
@@ -1857,7 +1882,7 @@ var RedactoNoticeConsent = ({
|
|
|
1857
1882
|
)
|
|
1858
1883
|
} : null;
|
|
1859
1884
|
setCategorizedPurposes(newCategorizedPurposes);
|
|
1860
|
-
const initialPurposeState =
|
|
1885
|
+
const initialPurposeState = activeConfig.purposes.reduce(
|
|
1861
1886
|
(acc, purpose) => {
|
|
1862
1887
|
if (isReconsentRequired && (purposeSelections == null ? void 0 : purposeSelections[purpose.uuid])) {
|
|
1863
1888
|
acc[purpose.uuid] = purposeSelections[purpose.uuid].selected;
|
|
@@ -1869,7 +1894,7 @@ var RedactoNoticeConsent = ({
|
|
|
1869
1894
|
{}
|
|
1870
1895
|
);
|
|
1871
1896
|
setSelectedPurposes(initialPurposeState);
|
|
1872
|
-
const initialDataElementState =
|
|
1897
|
+
const initialDataElementState = activeConfig.purposes.reduce(
|
|
1873
1898
|
(acc, purpose) => {
|
|
1874
1899
|
purpose.data_elements.forEach((element) => {
|
|
1875
1900
|
var _a3, _b2;
|
|
@@ -1910,7 +1935,6 @@ var RedactoNoticeConsent = ({
|
|
|
1910
1935
|
if (err instanceof Error && err.name === "AbortError") {
|
|
1911
1936
|
return;
|
|
1912
1937
|
}
|
|
1913
|
-
console.error(err);
|
|
1914
1938
|
const error = err;
|
|
1915
1939
|
setFetchError(error);
|
|
1916
1940
|
if (error.status === 409) {
|
|
@@ -2045,13 +2069,9 @@ var RedactoNoticeConsent = ({
|
|
|
2045
2069
|
if (error instanceof Error && error.name === "AbortError") {
|
|
2046
2070
|
return;
|
|
2047
2071
|
}
|
|
2048
|
-
console.error("Error submitting consent:", error);
|
|
2049
2072
|
const err = error;
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
"An error occurred while submitting your consent. Please try again later."
|
|
2053
|
-
);
|
|
2054
|
-
}
|
|
2073
|
+
const message = err.status === 500 ? "An error occurred while submitting your consent. Please try again later." : err.message || "Failed to submit consent. Please try again.";
|
|
2074
|
+
setErrorMessage(message);
|
|
2055
2075
|
onError == null ? void 0 : onError(error);
|
|
2056
2076
|
} finally {
|
|
2057
2077
|
setIsSubmittingAccept(false);
|
|
@@ -2433,7 +2453,6 @@ var RedactoNoticeConsent = ({
|
|
|
2433
2453
|
setIsTTSAvailable(true);
|
|
2434
2454
|
}).catch((error) => {
|
|
2435
2455
|
if (error instanceof Error && error.name !== "AbortError") {
|
|
2436
|
-
console.warn("Failed to fetch TTS audio URLs:", error.message);
|
|
2437
2456
|
setIsTTSAvailable(false);
|
|
2438
2457
|
setTtsAudioUrls(null);
|
|
2439
2458
|
}
|
|
@@ -3199,7 +3218,6 @@ var fetchConsentContent2 = async ({
|
|
|
3199
3218
|
const data = await response.json();
|
|
3200
3219
|
return data;
|
|
3201
3220
|
} catch (error) {
|
|
3202
|
-
console.error("Error fetching consent content:", error);
|
|
3203
3221
|
throw error;
|
|
3204
3222
|
}
|
|
3205
3223
|
};
|
|
@@ -3215,31 +3233,61 @@ var submitConsentEvent2 = async ({
|
|
|
3215
3233
|
}) => {
|
|
3216
3234
|
var _a, _b;
|
|
3217
3235
|
try {
|
|
3218
|
-
const primaryEmail = localStorage.getItem("userEmail");
|
|
3219
|
-
const payload = {
|
|
3220
|
-
primary_email: primaryEmail || void 0,
|
|
3221
|
-
notice_uuid: noticeUuid,
|
|
3222
|
-
source: "WEB",
|
|
3223
|
-
declined,
|
|
3224
|
-
consents: {
|
|
3225
|
-
purposes: purposes.map((purpose) => ({
|
|
3226
|
-
uuid: purpose.uuid,
|
|
3227
|
-
name: purpose.name,
|
|
3228
|
-
description: purpose.description,
|
|
3229
|
-
industries: purpose.industries || "",
|
|
3230
|
-
selected: purpose.selected,
|
|
3231
|
-
data_elements: purpose.data_elements.map((element) => ({
|
|
3232
|
-
uuid: element.uuid,
|
|
3233
|
-
name: element.name,
|
|
3234
|
-
enabled: element.enabled,
|
|
3235
|
-
required: element.required,
|
|
3236
|
-
selected: element.required ? true : element.selected
|
|
3237
|
-
}))
|
|
3238
|
-
}))
|
|
3239
|
-
},
|
|
3240
|
-
meta_data
|
|
3241
|
-
};
|
|
3242
3236
|
const apiBaseUrl = baseUrl || BASE_URL2;
|
|
3237
|
+
let payload;
|
|
3238
|
+
if (isLegacyApi(apiBaseUrl)) {
|
|
3239
|
+
const primaryEmail = typeof window !== "undefined" ? localStorage.getItem("userEmail") : null;
|
|
3240
|
+
payload = {
|
|
3241
|
+
primary_email: primaryEmail || void 0,
|
|
3242
|
+
notice_uuid: noticeUuid,
|
|
3243
|
+
source: "WEB",
|
|
3244
|
+
declined: Boolean(declined),
|
|
3245
|
+
consents: {
|
|
3246
|
+
purposes: purposes.map((purpose) => {
|
|
3247
|
+
if (!purpose.uuid) {
|
|
3248
|
+
throw new Error(`Invalid purpose data: missing uuid`);
|
|
3249
|
+
}
|
|
3250
|
+
return {
|
|
3251
|
+
uuid: purpose.uuid,
|
|
3252
|
+
name: purpose.name,
|
|
3253
|
+
description: purpose.description || "",
|
|
3254
|
+
industries: purpose.industries || "",
|
|
3255
|
+
selected: Boolean(purpose.selected),
|
|
3256
|
+
data_elements: (purpose.data_elements || []).map((element) => ({
|
|
3257
|
+
uuid: element.uuid,
|
|
3258
|
+
name: element.name,
|
|
3259
|
+
enabled: Boolean(element.enabled),
|
|
3260
|
+
required: Boolean(element.required),
|
|
3261
|
+
selected: element.required ? true : Boolean(element.selected)
|
|
3262
|
+
}))
|
|
3263
|
+
};
|
|
3264
|
+
})
|
|
3265
|
+
},
|
|
3266
|
+
meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid } : void 0
|
|
3267
|
+
};
|
|
3268
|
+
} else {
|
|
3269
|
+
payload = {
|
|
3270
|
+
notice_uuid: noticeUuid,
|
|
3271
|
+
purposes: purposes.map((purpose) => {
|
|
3272
|
+
if (!purpose.uuid) {
|
|
3273
|
+
throw new Error(`Invalid purpose data: missing uuid`);
|
|
3274
|
+
}
|
|
3275
|
+
const dataElements = purpose.data_elements.filter((element) => element.enabled).map((element) => ({
|
|
3276
|
+
uuid: element.uuid,
|
|
3277
|
+
selected: element.required ? true : Boolean(element.selected)
|
|
3278
|
+
}));
|
|
3279
|
+
return {
|
|
3280
|
+
purpose_uuid: purpose.uuid,
|
|
3281
|
+
selected: Boolean(purpose.selected),
|
|
3282
|
+
data_elements: dataElements.length > 0 ? dataElements : void 0
|
|
3283
|
+
};
|
|
3284
|
+
}),
|
|
3285
|
+
select_all_mandatory: false,
|
|
3286
|
+
source: "WEB",
|
|
3287
|
+
declined,
|
|
3288
|
+
meta_data
|
|
3289
|
+
};
|
|
3290
|
+
}
|
|
3243
3291
|
const response = await fetch(
|
|
3244
3292
|
`${apiBaseUrl}/public/organisations/${org_uuid}/workspaces/${workspace_uuid}/submit-consent`,
|
|
3245
3293
|
{
|
|
@@ -3276,7 +3324,6 @@ var submitConsentEvent2 = async ({
|
|
|
3276
3324
|
throw error;
|
|
3277
3325
|
}
|
|
3278
3326
|
} catch (error) {
|
|
3279
|
-
console.error("Error submitting consent event:", error);
|
|
3280
3327
|
throw error;
|
|
3281
3328
|
}
|
|
3282
3329
|
};
|
|
@@ -3794,13 +3841,12 @@ var RedactoConsentInlineComponent = ({
|
|
|
3794
3841
|
language,
|
|
3795
3842
|
specific_uuid: applicationId
|
|
3796
3843
|
});
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
);
|
|
3844
|
+
const activeConfig = consentContentData.detail.active_config;
|
|
3845
|
+
setContent(activeConfig);
|
|
3846
|
+
if (activeConfig.default_language) {
|
|
3847
|
+
setSelectedLanguage(activeConfig.default_language);
|
|
3802
3848
|
}
|
|
3803
|
-
const initialCollapsedState =
|
|
3849
|
+
const initialCollapsedState = activeConfig.purposes.reduce(
|
|
3804
3850
|
(acc, purpose) => ({
|
|
3805
3851
|
...acc,
|
|
3806
3852
|
[purpose.uuid]: true
|
|
@@ -3808,7 +3854,7 @@ var RedactoConsentInlineComponent = ({
|
|
|
3808
3854
|
{}
|
|
3809
3855
|
);
|
|
3810
3856
|
setCollapsedPurposes(initialCollapsedState);
|
|
3811
|
-
const initialDataElementState =
|
|
3857
|
+
const initialDataElementState = activeConfig.purposes.reduce(
|
|
3812
3858
|
(acc, purpose) => {
|
|
3813
3859
|
purpose.data_elements.forEach((element) => {
|
|
3814
3860
|
const combinedId = `${purpose.uuid}-${element.uuid}`;
|
|
@@ -3819,7 +3865,7 @@ var RedactoConsentInlineComponent = ({
|
|
|
3819
3865
|
{}
|
|
3820
3866
|
);
|
|
3821
3867
|
setSelectedDataElements(initialDataElementState);
|
|
3822
|
-
const initialPurposeState =
|
|
3868
|
+
const initialPurposeState = activeConfig.purposes.reduce(
|
|
3823
3869
|
(acc, purpose) => {
|
|
3824
3870
|
return {
|
|
3825
3871
|
...acc,
|
|
@@ -3830,7 +3876,6 @@ var RedactoConsentInlineComponent = ({
|
|
|
3830
3876
|
);
|
|
3831
3877
|
setSelectedPurposes(initialPurposeState);
|
|
3832
3878
|
} catch (err) {
|
|
3833
|
-
console.error(err);
|
|
3834
3879
|
const error = err;
|
|
3835
3880
|
setFetchError(error);
|
|
3836
3881
|
if (error.status === 409) {
|
|
@@ -3945,13 +3990,9 @@ var RedactoConsentInlineComponent = ({
|
|
|
3945
3990
|
onAccept == null ? void 0 : onAccept();
|
|
3946
3991
|
hasSubmittedRef.current = false;
|
|
3947
3992
|
} catch (error) {
|
|
3948
|
-
console.error("Error submitting consent:", error);
|
|
3949
3993
|
const err = error;
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
"An error occurred while submitting your consent. Please try again later."
|
|
3953
|
-
);
|
|
3954
|
-
}
|
|
3994
|
+
const message = err.status === 500 ? "An error occurred while submitting your consent. Please try again later." : err.message || "Failed to submit consent. Please try again.";
|
|
3995
|
+
setErrorMessage(message);
|
|
3955
3996
|
hasSubmittedRef.current = false;
|
|
3956
3997
|
throw error;
|
|
3957
3998
|
} finally {
|
|
@@ -3976,7 +4017,6 @@ var RedactoConsentInlineComponent = ({
|
|
|
3976
4017
|
try {
|
|
3977
4018
|
await handleAcceptWithToken(accessToken);
|
|
3978
4019
|
} catch (err) {
|
|
3979
|
-
console.error("Failed to auto-submit consent:", err);
|
|
3980
4020
|
hasSubmittedRef.current = false;
|
|
3981
4021
|
onError == null ? void 0 : onError(err);
|
|
3982
4022
|
}
|
package/package.json
CHANGED
|
@@ -1163,17 +1163,16 @@ export const RedactoNoticeConsent = ({
|
|
|
1163
1163
|
validate_against: validateAgainst,
|
|
1164
1164
|
signal: abortControllerRef.current?.signal,
|
|
1165
1165
|
});
|
|
1166
|
-
|
|
1167
|
-
|
|
1166
|
+
const activeConfig = consentContentData.detail.active_config;
|
|
1167
|
+
setContent(activeConfig);
|
|
1168
|
+
if (activeConfig.default_language) {
|
|
1168
1169
|
// Normalize "en" to "English" for consistency
|
|
1169
|
-
const defaultLang =
|
|
1170
|
-
consentContentData.detail.active_config.default_language;
|
|
1170
|
+
const defaultLang = activeConfig.default_language;
|
|
1171
1171
|
setSelectedLanguage(
|
|
1172
1172
|
defaultLang === "en" || defaultLang === "EN" ? "English" : defaultLang
|
|
1173
1173
|
);
|
|
1174
1174
|
}
|
|
1175
|
-
const initialCollapsedState =
|
|
1176
|
-
consentContentData.detail.active_config.purposes.reduce(
|
|
1175
|
+
const initialCollapsedState = activeConfig.purposes.reduce(
|
|
1177
1176
|
(acc, purpose) => ({
|
|
1178
1177
|
...acc,
|
|
1179
1178
|
[purpose.uuid]: true,
|
|
@@ -1189,8 +1188,7 @@ export const RedactoNoticeConsent = ({
|
|
|
1189
1188
|
// Categorize purposes for reconsent UI
|
|
1190
1189
|
const newCategorizedPurposes = isReconsentRequired
|
|
1191
1190
|
? {
|
|
1192
|
-
alreadyConsented:
|
|
1193
|
-
consentContentData.detail.active_config.purposes.filter(
|
|
1191
|
+
alreadyConsented: activeConfig.purposes.filter(
|
|
1194
1192
|
(purpose) => {
|
|
1195
1193
|
const selection = purposeSelections?.[purpose.uuid];
|
|
1196
1194
|
return (
|
|
@@ -1201,8 +1199,7 @@ export const RedactoNoticeConsent = ({
|
|
|
1201
1199
|
);
|
|
1202
1200
|
}
|
|
1203
1201
|
),
|
|
1204
|
-
needsConsent:
|
|
1205
|
-
consentContentData.detail.active_config.purposes.filter(
|
|
1202
|
+
needsConsent: activeConfig.purposes.filter(
|
|
1206
1203
|
(purpose) => {
|
|
1207
1204
|
const selection = purposeSelections?.[purpose.uuid];
|
|
1208
1205
|
return (
|
|
@@ -1218,7 +1215,7 @@ export const RedactoNoticeConsent = ({
|
|
|
1218
1215
|
setCategorizedPurposes(newCategorizedPurposes);
|
|
1219
1216
|
|
|
1220
1217
|
const initialPurposeState: Record<string, boolean> =
|
|
1221
|
-
|
|
1218
|
+
activeConfig.purposes.reduce(
|
|
1222
1219
|
(acc: Record<string, boolean>, purpose) => {
|
|
1223
1220
|
if (isReconsentRequired && purposeSelections?.[purpose.uuid]) {
|
|
1224
1221
|
// Pre-populate with existing selection for reconsent
|
|
@@ -1234,7 +1231,7 @@ export const RedactoNoticeConsent = ({
|
|
|
1234
1231
|
setSelectedPurposes(initialPurposeState);
|
|
1235
1232
|
|
|
1236
1233
|
const initialDataElementState: Record<string, boolean> =
|
|
1237
|
-
|
|
1234
|
+
activeConfig.purposes.reduce(
|
|
1238
1235
|
(acc: Record<string, boolean>, purpose) => {
|
|
1239
1236
|
purpose.data_elements.forEach((element) => {
|
|
1240
1237
|
const combinedId = `${purpose.uuid}-${element.uuid}`;
|
|
@@ -1293,7 +1290,6 @@ export const RedactoNoticeConsent = ({
|
|
|
1293
1290
|
return; // Don't update state for cancelled requests
|
|
1294
1291
|
}
|
|
1295
1292
|
|
|
1296
|
-
console.error(err);
|
|
1297
1293
|
const error = err as Error & { status?: number };
|
|
1298
1294
|
setFetchError(error);
|
|
1299
1295
|
if (error.status === 409) {
|
|
@@ -1475,13 +1471,12 @@ export const RedactoNoticeConsent = ({
|
|
|
1475
1471
|
return;
|
|
1476
1472
|
}
|
|
1477
1473
|
|
|
1478
|
-
console.error("Error submitting consent:", error);
|
|
1479
1474
|
const err = error as Error & { status?: number };
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
"An error occurred while submitting your consent. Please try again later."
|
|
1483
|
-
|
|
1484
|
-
|
|
1475
|
+
const message =
|
|
1476
|
+
err.status === 500
|
|
1477
|
+
? "An error occurred while submitting your consent. Please try again later."
|
|
1478
|
+
: err.message || "Failed to submit consent. Please try again.";
|
|
1479
|
+
setErrorMessage(message);
|
|
1485
1480
|
onError?.(error as Error);
|
|
1486
1481
|
} finally {
|
|
1487
1482
|
setIsSubmittingAccept(false);
|
|
@@ -2080,9 +2075,7 @@ export const RedactoNoticeConsent = ({
|
|
|
2080
2075
|
})
|
|
2081
2076
|
.catch((error) => {
|
|
2082
2077
|
// Handle errors gracefully - don't break the UI if TTS API fails
|
|
2083
|
-
// Only log if it's not an AbortError (which is expected when component unmounts or request is cancelled)
|
|
2084
2078
|
if (error instanceof Error && error.name !== "AbortError") {
|
|
2085
|
-
console.warn("Failed to fetch TTS audio URLs:", error.message);
|
|
2086
2079
|
setIsTTSAvailable(false);
|
|
2087
2080
|
setTtsAudioUrls(null);
|
|
2088
2081
|
}
|
|
@@ -5,11 +5,13 @@ import type {
|
|
|
5
5
|
FetchConsentContentParams,
|
|
6
6
|
FetchTTSAudioUrlsParams,
|
|
7
7
|
GuardianInfoPayload,
|
|
8
|
+
LegacyConsentEventPayload,
|
|
8
9
|
RedactoJwtPayload,
|
|
9
10
|
SubmitConsentEventParams,
|
|
10
11
|
SubmitGuardianInfoParams,
|
|
11
12
|
TTSAudioUrlsResponse,
|
|
12
13
|
} from "./types";
|
|
14
|
+
import { isLegacyApi } from "../../utils/isLegacyApi";
|
|
13
15
|
|
|
14
16
|
const BASE_URL = "https://api.redacto.io/consent";
|
|
15
17
|
|
|
@@ -45,16 +47,6 @@ function decodeTokenSafely(token: string): RedactoJwtPayload | null {
|
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
// Abstract localStorage access for better testability
|
|
49
|
-
const storage = {
|
|
50
|
-
getItem: (key: string): string | null => {
|
|
51
|
-
if (typeof window !== 'undefined') {
|
|
52
|
-
return localStorage.getItem(key);
|
|
53
|
-
}
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
|
|
58
50
|
// Function to clear API cache (useful for testing or when switching users)
|
|
59
51
|
export const clearApiCache = (): void => {
|
|
60
52
|
apiCache.clear();
|
|
@@ -146,8 +138,6 @@ export const fetchConsentContent = async ({
|
|
|
146
138
|
|
|
147
139
|
return data;
|
|
148
140
|
} catch (error) {
|
|
149
|
-
// Log error for debugging but don't expose sensitive information
|
|
150
|
-
console.error("Error fetching consent content:", error instanceof Error ? error.message : error);
|
|
151
141
|
throw error;
|
|
152
142
|
}
|
|
153
143
|
};
|
|
@@ -177,48 +167,72 @@ export const submitConsentEvent = async ({
|
|
|
177
167
|
throw new Error("Invalid token: missing organization or workspace UUID");
|
|
178
168
|
}
|
|
179
169
|
|
|
180
|
-
const
|
|
170
|
+
const apiBaseUrl = baseUrl || BASE_URL;
|
|
181
171
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
172
|
+
let payload: ConsentEventPayload | LegacyConsentEventPayload;
|
|
173
|
+
|
|
174
|
+
if (isLegacyApi(apiBaseUrl)) {
|
|
175
|
+
// @deprecated Legacy payload for production (.io) backend
|
|
176
|
+
const primaryEmail = typeof window !== "undefined" ? localStorage.getItem("userEmail") : null;
|
|
177
|
+
payload = {
|
|
178
|
+
primary_email: primaryEmail || undefined,
|
|
179
|
+
notice_uuid: noticeUuid,
|
|
180
|
+
source: "WEB",
|
|
181
|
+
declined: Boolean(declined),
|
|
182
|
+
consents: {
|
|
183
|
+
purposes: purposes.map((purpose) => {
|
|
184
|
+
if (!purpose.uuid) {
|
|
185
|
+
throw new Error(`Invalid purpose data: missing uuid`);
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
uuid: purpose.uuid,
|
|
189
|
+
name: purpose.name,
|
|
190
|
+
description: purpose.description || "",
|
|
191
|
+
industries: purpose.industries || "",
|
|
192
|
+
selected: Boolean(purpose.selected),
|
|
193
|
+
data_elements: (purpose.data_elements || []).map((element) => ({
|
|
194
|
+
uuid: element.uuid,
|
|
195
|
+
name: element.name,
|
|
196
|
+
enabled: Boolean(element.enabled),
|
|
197
|
+
required: Boolean(element.required),
|
|
198
|
+
selected: element.required ? true : Boolean(element.selected),
|
|
199
|
+
})),
|
|
200
|
+
};
|
|
201
|
+
}),
|
|
202
|
+
},
|
|
203
|
+
meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid } : undefined,
|
|
204
|
+
};
|
|
205
|
+
} else {
|
|
206
|
+
// New payload for staging (.tech) / localhost
|
|
207
|
+
const validatedPurposes = purposes.map((purpose) => {
|
|
208
|
+
if (!purpose.uuid) {
|
|
209
|
+
throw new Error(`Invalid purpose data: missing uuid`);
|
|
210
|
+
}
|
|
187
211
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
industries: purpose.industries || "",
|
|
193
|
-
selected: Boolean(purpose.selected),
|
|
194
|
-
data_elements: (purpose.data_elements || []).map((element) => {
|
|
195
|
-
if (!element.uuid || !element.name) {
|
|
196
|
-
throw new Error(`Invalid data element: missing uuid or name`);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return {
|
|
212
|
+
// Only include enabled data elements (disabled elements are not selectable)
|
|
213
|
+
const dataElements = (purpose.data_elements || [])
|
|
214
|
+
.filter((element) => element.enabled)
|
|
215
|
+
.map((element) => ({
|
|
200
216
|
uuid: element.uuid,
|
|
201
|
-
name: element.name,
|
|
202
|
-
enabled: Boolean(element.enabled),
|
|
203
|
-
required: Boolean(element.required),
|
|
204
217
|
selected: element.required ? true : Boolean(element.selected),
|
|
205
|
-
};
|
|
206
|
-
}),
|
|
207
|
-
};
|
|
208
|
-
});
|
|
218
|
+
}));
|
|
209
219
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
purposes: validatedPurposes,
|
|
217
|
-
},
|
|
218
|
-
meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid } : undefined,
|
|
219
|
-
};
|
|
220
|
+
return {
|
|
221
|
+
purpose_uuid: purpose.uuid,
|
|
222
|
+
selected: Boolean(purpose.selected),
|
|
223
|
+
data_elements: dataElements.length > 0 ? dataElements : undefined,
|
|
224
|
+
};
|
|
225
|
+
});
|
|
220
226
|
|
|
221
|
-
|
|
227
|
+
payload = {
|
|
228
|
+
notice_uuid: noticeUuid,
|
|
229
|
+
purposes: validatedPurposes,
|
|
230
|
+
select_all_mandatory: false,
|
|
231
|
+
source: "WEB",
|
|
232
|
+
declined: Boolean(declined),
|
|
233
|
+
meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid } : undefined,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
222
236
|
|
|
223
237
|
const response = await fetch(
|
|
224
238
|
`${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/submit-consent`,
|
|
@@ -257,8 +271,6 @@ export const submitConsentEvent = async ({
|
|
|
257
271
|
apiCache.clear();
|
|
258
272
|
|
|
259
273
|
} catch (error) {
|
|
260
|
-
// Log error for debugging but don't expose sensitive information
|
|
261
|
-
console.error("Error submitting consent event:", error instanceof Error ? error.message : error);
|
|
262
274
|
throw error;
|
|
263
275
|
}
|
|
264
276
|
};
|
|
@@ -414,8 +426,6 @@ export const fetchTTSAudioUrls = async ({
|
|
|
414
426
|
|
|
415
427
|
return data;
|
|
416
428
|
} catch (error) {
|
|
417
|
-
// Log error for debugging but don't expose sensitive information
|
|
418
|
-
console.error("Error fetching TTS audio URLs:", error instanceof Error ? error.message : error);
|
|
419
429
|
throw error;
|
|
420
430
|
}
|
|
421
431
|
};
|