@redacto.io/consent-sdk-react 3.3.0 → 3.5.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 +12 -0
- package/README.md +11 -0
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +321 -155
- package/dist/index.mjs +321 -155
- package/package.json +1 -1
- package/src/RedactoNoticeConsent/RedactoNoticeConsent.test.tsx +123 -1
- package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +924 -652
- package/src/RedactoNoticeConsent/api/index.ts +31 -65
- package/src/RedactoNoticeConsent/api/types.ts +47 -33
- package/src/RedactoNoticeConsent/constants.ts +20 -0
- package/src/RedactoNoticeConsent/styles.ts +3 -6
- package/src/RedactoNoticeConsent/types.ts +11 -5
- package/src/RedactoNoticeConsentInline/RedactoNoticeConsentInline.tsx +12 -3
- package/src/RedactoNoticeConsentInline/api/index.ts +26 -64
- package/src/RedactoNoticeConsentInline/api/types.ts +8 -31
- package/src/RedactoNoticeConsentInline/types.ts +9 -4
- package/src/types/react-jsx.d.ts +26 -0
- package/tests/mocks.ts +53 -7
- package/tsconfig.json +1 -5
- package/src/utils/isLegacyApi.ts +0 -12
|
@@ -5,13 +5,11 @@ import type {
|
|
|
5
5
|
FetchConsentContentParams,
|
|
6
6
|
FetchTTSAudioUrlsParams,
|
|
7
7
|
GuardianInfoPayload,
|
|
8
|
-
LegacyConsentEventPayload,
|
|
9
8
|
RedactoJwtPayload,
|
|
10
9
|
SubmitConsentEventParams,
|
|
11
10
|
SubmitGuardianInfoParams,
|
|
12
11
|
TTSAudioUrlsResponse,
|
|
13
12
|
} from "./types";
|
|
14
|
-
import { isLegacyApi } from "../../utils/isLegacyApi";
|
|
15
13
|
|
|
16
14
|
const BASE_URL = "https://api.redacto.io/consent";
|
|
17
15
|
|
|
@@ -59,6 +57,7 @@ export const fetchConsentContent = async ({
|
|
|
59
57
|
language = "en",
|
|
60
58
|
specific_uuid,
|
|
61
59
|
validate_against = "all",
|
|
60
|
+
include_fully_consented_data = false,
|
|
62
61
|
signal, // AbortSignal for request cancellation
|
|
63
62
|
}: FetchConsentContentParams & { signal?: AbortSignal }): Promise<ConsentContent> => {
|
|
64
63
|
try {
|
|
@@ -80,7 +79,7 @@ export const fetchConsentContent = async ({
|
|
|
80
79
|
const apiBaseUrl = baseUrl || BASE_URL;
|
|
81
80
|
|
|
82
81
|
// Create cache key for this request - include accessToken to ensure different users get different cache
|
|
83
|
-
const cacheKey = `${accessToken}-${noticeId}-${validate_against}-${language}-${specific_uuid || ''}`;
|
|
82
|
+
const cacheKey = `${accessToken}-${noticeId}-${validate_against}-${language}-${specific_uuid || ''}-${include_fully_consented_data}`;
|
|
84
83
|
|
|
85
84
|
// Check cache first (skip for reconsent requests as they need fresh data)
|
|
86
85
|
if (validate_against === "all" && !specific_uuid) {
|
|
@@ -98,6 +97,9 @@ export const fetchConsentContent = async ({
|
|
|
98
97
|
url.searchParams.append("specific_uuid", specific_uuid);
|
|
99
98
|
}
|
|
100
99
|
url.searchParams.append("validate_against", validate_against);
|
|
100
|
+
if (include_fully_consented_data) {
|
|
101
|
+
url.searchParams.append("include_fully_consented_data", "true");
|
|
102
|
+
}
|
|
101
103
|
|
|
102
104
|
const response = await fetch(url.toString(), {
|
|
103
105
|
method: "GET",
|
|
@@ -169,70 +171,34 @@ export const submitConsentEvent = async ({
|
|
|
169
171
|
|
|
170
172
|
const apiBaseUrl = baseUrl || BASE_URL;
|
|
171
173
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
-
}
|
|
174
|
+
const validatedPurposes = purposes.map((purpose) => {
|
|
175
|
+
if (!purpose.uuid) {
|
|
176
|
+
throw new Error(`Invalid purpose data: missing uuid`);
|
|
177
|
+
}
|
|
211
178
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
};
|
|
225
|
-
});
|
|
226
|
-
|
|
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,
|
|
179
|
+
// Only include enabled data elements (disabled elements are not selectable)
|
|
180
|
+
const dataElements = (purpose.data_elements || [])
|
|
181
|
+
.filter((element) => element.enabled)
|
|
182
|
+
.map((element) => ({
|
|
183
|
+
uuid: element.uuid,
|
|
184
|
+
selected: element.required ? true : Boolean(element.selected),
|
|
185
|
+
}));
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
purpose_uuid: purpose.uuid,
|
|
189
|
+
selected: Boolean(purpose.selected),
|
|
190
|
+
data_elements: dataElements.length > 0 ? dataElements : undefined,
|
|
234
191
|
};
|
|
235
|
-
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
const payload: ConsentEventPayload = {
|
|
195
|
+
notice_uuid: noticeUuid,
|
|
196
|
+
purposes: validatedPurposes,
|
|
197
|
+
select_all_mandatory: false,
|
|
198
|
+
source: "WEB",
|
|
199
|
+
declined: Boolean(declined),
|
|
200
|
+
meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid } : undefined,
|
|
201
|
+
};
|
|
236
202
|
|
|
237
203
|
const response = await fetch(
|
|
238
204
|
`${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/submit-consent`,
|
|
@@ -49,7 +49,7 @@ export type ConsentContent = {
|
|
|
49
49
|
decline_button_text: string;
|
|
50
50
|
logo_url: string;
|
|
51
51
|
privacy_policy_url: string;
|
|
52
|
-
|
|
52
|
+
privacy_center_url: string;
|
|
53
53
|
primary_color: string;
|
|
54
54
|
secondary_color: string;
|
|
55
55
|
font_preference: string;
|
|
@@ -75,28 +75,28 @@ export type ConsentContent = {
|
|
|
75
75
|
confirm_button_text: string;
|
|
76
76
|
decline_button_text: string;
|
|
77
77
|
privacy_policy_prefix_text: string;
|
|
78
|
-
vendor_list_prefix_text: string;
|
|
79
78
|
privacy_policy_anchor_text: string;
|
|
80
|
-
|
|
79
|
+
privacy_center_anchor_text?: string;
|
|
81
80
|
purpose_section_heading: string;
|
|
82
81
|
notice_banner_heading: string;
|
|
83
82
|
data_elements: {
|
|
84
83
|
[key: string]: string;
|
|
85
84
|
};
|
|
86
85
|
purposes: {
|
|
87
|
-
[key: string]: string;
|
|
86
|
+
[key: string]: string | { name: string; description: string };
|
|
88
87
|
};
|
|
88
|
+
dpo_info?: DpoInfoTranslation;
|
|
89
89
|
};
|
|
90
90
|
};
|
|
91
91
|
created_at: string;
|
|
92
92
|
updated_at: string;
|
|
93
93
|
deployed_at: string;
|
|
94
94
|
privacy_policy_prefix_text: string;
|
|
95
|
-
vendor_list_prefix_text: string;
|
|
96
95
|
privacy_policy_anchor_text: string;
|
|
97
|
-
|
|
96
|
+
privacy_center_anchor_text?: string;
|
|
98
97
|
purpose_section_heading: string;
|
|
99
98
|
notice_banner_heading: string;
|
|
99
|
+
dpo_info?: DpoInfo;
|
|
100
100
|
};
|
|
101
101
|
notice_type?: string;
|
|
102
102
|
compliance_requirement?: string;
|
|
@@ -154,6 +154,7 @@ export type FetchConsentContentParams = {
|
|
|
154
154
|
language?: string;
|
|
155
155
|
specific_uuid?: string;
|
|
156
156
|
validate_against?: "all" | "required";
|
|
157
|
+
include_fully_consented_data?: boolean;
|
|
157
158
|
};
|
|
158
159
|
|
|
159
160
|
export type ConsentEventPayload = {
|
|
@@ -174,31 +175,6 @@ export type ConsentEventPayload = {
|
|
|
174
175
|
};
|
|
175
176
|
};
|
|
176
177
|
|
|
177
|
-
// @deprecated Remove when production backend supports the new payload format.
|
|
178
|
-
export type LegacyConsentEventPayload = {
|
|
179
|
-
primary_email?: string;
|
|
180
|
-
notice_uuid: string;
|
|
181
|
-
source: string;
|
|
182
|
-
declined: boolean;
|
|
183
|
-
consents: {
|
|
184
|
-
purposes: Array<{
|
|
185
|
-
uuid: string;
|
|
186
|
-
name: string;
|
|
187
|
-
description: string;
|
|
188
|
-
industries?: string;
|
|
189
|
-
selected: boolean;
|
|
190
|
-
data_elements: Array<{
|
|
191
|
-
uuid: string;
|
|
192
|
-
name: string;
|
|
193
|
-
enabled: boolean;
|
|
194
|
-
required: boolean;
|
|
195
|
-
selected: boolean;
|
|
196
|
-
}>;
|
|
197
|
-
}>;
|
|
198
|
-
};
|
|
199
|
-
meta_data?: { specific_uuid: string };
|
|
200
|
-
};
|
|
201
|
-
|
|
202
178
|
export type DataElement = {
|
|
203
179
|
uuid: string;
|
|
204
180
|
name: string;
|
|
@@ -278,6 +254,31 @@ export type PurposeItemProps = {
|
|
|
278
254
|
isAlreadyConsented: boolean;
|
|
279
255
|
};
|
|
280
256
|
|
|
257
|
+
// DPO Info Types
|
|
258
|
+
export type DpoInfo = {
|
|
259
|
+
grievance_text: string;
|
|
260
|
+
grievance_anchor_text: string;
|
|
261
|
+
grievance_url: string;
|
|
262
|
+
grievance_email: string;
|
|
263
|
+
grievance_email_connector_text?: string;
|
|
264
|
+
dp_board_text: string;
|
|
265
|
+
dp_board_anchor_text: string;
|
|
266
|
+
dp_board_url: string;
|
|
267
|
+
dpo_text: string;
|
|
268
|
+
dpo_anchor_text: string;
|
|
269
|
+
dpo_url: string;
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
export type DpoInfoTranslation = {
|
|
273
|
+
grievance_text?: string;
|
|
274
|
+
grievance_anchor_text?: string;
|
|
275
|
+
grievance_email_connector_text?: string;
|
|
276
|
+
dp_board_text?: string;
|
|
277
|
+
dp_board_anchor_text?: string;
|
|
278
|
+
dpo_text?: string;
|
|
279
|
+
dpo_anchor_text?: string;
|
|
280
|
+
};
|
|
281
|
+
|
|
281
282
|
// TTS Audio Types
|
|
282
283
|
export type PurposeAudioUrls = {
|
|
283
284
|
name_audio_url: string;
|
|
@@ -300,16 +301,29 @@ export type TTSAudioUrlsResponse = {
|
|
|
300
301
|
confirm_button_text_audio_url: string;
|
|
301
302
|
decline_button_text_audio_url: string;
|
|
302
303
|
privacy_policy_prefix_text_audio_url: string;
|
|
303
|
-
vendor_list_prefix_text_audio_url: string;
|
|
304
304
|
privacy_policy_anchor_text_audio_url: string;
|
|
305
|
-
|
|
305
|
+
privacy_center_anchor_text_audio_url?: string;
|
|
306
306
|
purpose_section_heading_audio_url: string;
|
|
307
307
|
notice_banner_heading_audio_url: string;
|
|
308
308
|
purposes_audio: Record<string, PurposeAudioUrls>;
|
|
309
309
|
data_elements_audio: Record<string, DataElementAudioUrls>;
|
|
310
|
+
dpo_info_audio?: DpoInfoAudio;
|
|
310
311
|
};
|
|
311
312
|
};
|
|
312
313
|
|
|
314
|
+
export type DpoInfoAudioKey = keyof DpoInfoAudio;
|
|
315
|
+
|
|
316
|
+
export type DpoInfoAudio = {
|
|
317
|
+
grievance_text_audio_url?: string;
|
|
318
|
+
grievance_anchor_text_audio_url?: string;
|
|
319
|
+
dp_board_text_audio_url?: string;
|
|
320
|
+
dp_board_anchor_text_audio_url?: string;
|
|
321
|
+
dpo_text_audio_url?: string;
|
|
322
|
+
dpo_anchor_text_audio_url?: string;
|
|
323
|
+
grievance_email_connector_text_audio_url?: string;
|
|
324
|
+
grievance_email_audio_url?: string;
|
|
325
|
+
};
|
|
326
|
+
|
|
313
327
|
export type FetchTTSAudioUrlsParams = {
|
|
314
328
|
accessToken: string;
|
|
315
329
|
baseUrl?: string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { DpoInfoAudioKey } from "./api/types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Maps DPO info audio URL keys to their corresponding DOM element IDs.
|
|
5
|
+
* Used by both audioSequence and elementIdSequence builders to keep them in sync.
|
|
6
|
+
* Order determines playback sequence: grievance → dp board → dpo.
|
|
7
|
+
*/
|
|
8
|
+
export const DPO_AUDIO_ELEMENT_MAP: ReadonlyArray<{
|
|
9
|
+
audioKey: DpoInfoAudioKey;
|
|
10
|
+
elementId: string;
|
|
11
|
+
}> = [
|
|
12
|
+
{ audioKey: "grievance_text_audio_url", elementId: "dpo-grievance-text" },
|
|
13
|
+
{ audioKey: "grievance_anchor_text_audio_url", elementId: "dpo-grievance-link" },
|
|
14
|
+
{ audioKey: "grievance_email_connector_text_audio_url", elementId: "dpo-grievance-email-connector" },
|
|
15
|
+
{ audioKey: "grievance_email_audio_url", elementId: "dpo-grievance-email" },
|
|
16
|
+
{ audioKey: "dp_board_text_audio_url", elementId: "dpo-dp-board-text" },
|
|
17
|
+
{ audioKey: "dp_board_anchor_text_audio_url", elementId: "dpo-dp-board-link" },
|
|
18
|
+
{ audioKey: "dpo_text_audio_url", elementId: "dpo-dpo-text" },
|
|
19
|
+
{ audioKey: "dpo_anchor_text_audio_url", elementId: "dpo-dpo-link" },
|
|
20
|
+
] as const;
|
|
@@ -54,8 +54,7 @@ export const styles = {
|
|
|
54
54
|
justifyContent: "space-between",
|
|
55
55
|
alignItems: "center",
|
|
56
56
|
paddingBottom: "15px",
|
|
57
|
-
borderBottom: "
|
|
58
|
-
boxShadow: "0 1px 2px rgba(0, 0, 0, 0.03)",
|
|
57
|
+
borderBottom: "none",
|
|
59
58
|
backgroundColor: "#ffffff",
|
|
60
59
|
margin: 0,
|
|
61
60
|
boxSizing: "border-box",
|
|
@@ -114,7 +113,7 @@ export const styles = {
|
|
|
114
113
|
},
|
|
115
114
|
|
|
116
115
|
middleSection: {
|
|
117
|
-
margin: "
|
|
116
|
+
margin: "0px",
|
|
118
117
|
display: "flex",
|
|
119
118
|
flexDirection: "column",
|
|
120
119
|
gap: "12px",
|
|
@@ -130,7 +129,6 @@ export const styles = {
|
|
|
130
129
|
fontSize: "16px",
|
|
131
130
|
fontWeight: 400,
|
|
132
131
|
lineHeight: "150%",
|
|
133
|
-
letterSpacing: "0.2px",
|
|
134
132
|
color: "#344054",
|
|
135
133
|
margin: 0,
|
|
136
134
|
padding: 0,
|
|
@@ -284,8 +282,7 @@ export const styles = {
|
|
|
284
282
|
justifyContent: "space-between",
|
|
285
283
|
gap: "29px",
|
|
286
284
|
paddingTop: "15px",
|
|
287
|
-
borderTop: "
|
|
288
|
-
boxShadow: "0 -1px 2px rgba(0, 0, 0, 0.03)",
|
|
285
|
+
borderTop: "none",
|
|
289
286
|
backgroundColor: "#ffffff",
|
|
290
287
|
margin: 0,
|
|
291
288
|
boxSizing: "border-box",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Settings } from "./api/types";
|
|
1
|
+
import type { Settings, DpoInfoTranslation } from "./api/types";
|
|
2
2
|
|
|
3
3
|
export type Props = Readonly<{
|
|
4
4
|
noticeId: string;
|
|
@@ -13,10 +13,14 @@ export type Props = Readonly<{
|
|
|
13
13
|
onError?: (error: Error) => void;
|
|
14
14
|
applicationId?: string;
|
|
15
15
|
validateAgainst?: "all" | "required";
|
|
16
|
+
includeFullyConsentedData?: boolean;
|
|
17
|
+
reviewModeButtonText?: string;
|
|
16
18
|
}>;
|
|
17
19
|
|
|
20
|
+
export type PurposeTranslation = string | { name: string; description: string };
|
|
21
|
+
|
|
18
22
|
export type PurposeTranslations = {
|
|
19
|
-
[purposeId: string]:
|
|
23
|
+
[purposeId: string]: PurposeTranslation;
|
|
20
24
|
};
|
|
21
25
|
|
|
22
26
|
export type DataElementTranslations = {
|
|
@@ -33,13 +37,15 @@ export type TranslationObject = {
|
|
|
33
37
|
purpose_section_heading?: string;
|
|
34
38
|
notice_banner_heading?: string;
|
|
35
39
|
privacy_policy_prefix_text?: string;
|
|
36
|
-
vendor_list_prefix_text?: string;
|
|
37
40
|
privacy_policy_anchor_text?: string;
|
|
38
|
-
|
|
41
|
+
privacy_center_anchor_text?: string;
|
|
42
|
+
dpo_info?: DpoInfoTranslation;
|
|
39
43
|
[key: string]:
|
|
40
44
|
| string
|
|
41
45
|
| PurposeTranslations
|
|
42
46
|
| DataElementTranslations
|
|
43
|
-
|
|
|
47
|
+
| DpoInfoTranslation
|
|
48
|
+
| Record<string, string | undefined>
|
|
49
|
+
| Record<string, string | { name: string; description: string }>
|
|
44
50
|
| undefined;
|
|
45
51
|
};
|
|
@@ -103,7 +103,7 @@ const RedactoConsentInlineComponent = ({
|
|
|
103
103
|
): string => {
|
|
104
104
|
if (!content) return defaultText;
|
|
105
105
|
|
|
106
|
-
if (selectedLanguage ===
|
|
106
|
+
if (selectedLanguage === "English" || selectedLanguage === "en" || selectedLanguage === "EN") {
|
|
107
107
|
if (
|
|
108
108
|
key === "privacy_policy_anchor_text" &&
|
|
109
109
|
content.privacy_policy_anchor_text
|
|
@@ -127,7 +127,16 @@ const RedactoConsentInlineComponent = ({
|
|
|
127
127
|
|
|
128
128
|
if (itemId) {
|
|
129
129
|
if (key === "purposes.name" || key === "purposes.description") {
|
|
130
|
-
|
|
130
|
+
const purposeData = translationMap.purposes?.[itemId];
|
|
131
|
+
if (purposeData) {
|
|
132
|
+
if (typeof purposeData === "string") {
|
|
133
|
+
return key === "purposes.name" ? purposeData : defaultText;
|
|
134
|
+
}
|
|
135
|
+
return key === "purposes.name"
|
|
136
|
+
? purposeData.name || defaultText
|
|
137
|
+
: purposeData.description || defaultText;
|
|
138
|
+
}
|
|
139
|
+
return defaultText;
|
|
131
140
|
}
|
|
132
141
|
if (key.startsWith("data_elements.")) {
|
|
133
142
|
return translationMap.data_elements?.[itemId] || defaultText;
|
|
@@ -135,7 +144,7 @@ const RedactoConsentInlineComponent = ({
|
|
|
135
144
|
}
|
|
136
145
|
|
|
137
146
|
const value = translationMap[key];
|
|
138
|
-
if (typeof value === "string") {
|
|
147
|
+
if (typeof value === "string" && value) {
|
|
139
148
|
return value;
|
|
140
149
|
}
|
|
141
150
|
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
ConsentContent,
|
|
3
3
|
ConsentEventPayload,
|
|
4
|
-
LegacyConsentEventPayload,
|
|
5
4
|
FetchConsentContentParams,
|
|
6
5
|
SubmitConsentEventParams,
|
|
7
6
|
} from "./types";
|
|
8
|
-
import { isLegacyApi } from "../../utils/isLegacyApi";
|
|
9
7
|
|
|
10
8
|
const BASE_URL = "https://api.redacto.tech/consent";
|
|
11
9
|
|
|
@@ -77,68 +75,32 @@ export const submitConsentEvent = async ({
|
|
|
77
75
|
try {
|
|
78
76
|
const apiBaseUrl = baseUrl || BASE_URL;
|
|
79
77
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
selected: element.required ? true : Boolean(element.selected),
|
|
107
|
-
})),
|
|
108
|
-
};
|
|
109
|
-
}),
|
|
110
|
-
},
|
|
111
|
-
meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid } : undefined,
|
|
112
|
-
};
|
|
113
|
-
} else {
|
|
114
|
-
// New payload for staging (.tech) / localhost
|
|
115
|
-
payload = {
|
|
116
|
-
notice_uuid: noticeUuid,
|
|
117
|
-
purposes: purposes.map((purpose) => {
|
|
118
|
-
if (!purpose.uuid) {
|
|
119
|
-
throw new Error(`Invalid purpose data: missing uuid`);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Only include enabled data elements (disabled elements are not selectable)
|
|
123
|
-
const dataElements = purpose.data_elements
|
|
124
|
-
.filter((element) => element.enabled)
|
|
125
|
-
.map((element) => ({
|
|
126
|
-
uuid: element.uuid,
|
|
127
|
-
selected: element.required ? true : Boolean(element.selected),
|
|
128
|
-
}));
|
|
129
|
-
|
|
130
|
-
return {
|
|
131
|
-
purpose_uuid: purpose.uuid,
|
|
132
|
-
selected: Boolean(purpose.selected),
|
|
133
|
-
data_elements: dataElements.length > 0 ? dataElements : undefined,
|
|
134
|
-
};
|
|
135
|
-
}),
|
|
136
|
-
select_all_mandatory: false,
|
|
137
|
-
source: "WEB",
|
|
138
|
-
declined,
|
|
139
|
-
meta_data,
|
|
140
|
-
};
|
|
141
|
-
}
|
|
78
|
+
const payload: ConsentEventPayload = {
|
|
79
|
+
notice_uuid: noticeUuid,
|
|
80
|
+
purposes: purposes.map((purpose) => {
|
|
81
|
+
if (!purpose.uuid) {
|
|
82
|
+
throw new Error(`Invalid purpose data: missing uuid`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Only include enabled data elements (disabled elements are not selectable)
|
|
86
|
+
const dataElements = purpose.data_elements
|
|
87
|
+
.filter((element) => element.enabled)
|
|
88
|
+
.map((element) => ({
|
|
89
|
+
uuid: element.uuid,
|
|
90
|
+
selected: element.required ? true : Boolean(element.selected),
|
|
91
|
+
}));
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
purpose_uuid: purpose.uuid,
|
|
95
|
+
selected: Boolean(purpose.selected),
|
|
96
|
+
data_elements: dataElements.length > 0 ? dataElements : undefined,
|
|
97
|
+
};
|
|
98
|
+
}),
|
|
99
|
+
select_all_mandatory: false,
|
|
100
|
+
source: "WEB",
|
|
101
|
+
declined,
|
|
102
|
+
meta_data,
|
|
103
|
+
};
|
|
142
104
|
|
|
143
105
|
const response = await fetch(
|
|
144
106
|
`${apiBaseUrl}/public/organisations/${org_uuid}/workspaces/${workspace_uuid}/submit-consent`,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { DpoInfo, DpoInfoTranslation } from "../../RedactoNoticeConsent/api/types";
|
|
2
|
+
|
|
1
3
|
export type RedactoJwtPayload = {
|
|
2
4
|
organisation_uuid: string;
|
|
3
5
|
workspace_uuid: string;
|
|
@@ -33,7 +35,7 @@ export type ConsentContent = {
|
|
|
33
35
|
decline_button_text: string;
|
|
34
36
|
logo_url: string;
|
|
35
37
|
privacy_policy_url: string;
|
|
36
|
-
|
|
38
|
+
privacy_center_url: string;
|
|
37
39
|
primary_color: string;
|
|
38
40
|
secondary_color: string;
|
|
39
41
|
font_preference: string;
|
|
@@ -59,26 +61,26 @@ export type ConsentContent = {
|
|
|
59
61
|
confirm_button_text: string;
|
|
60
62
|
decline_button_text: string;
|
|
61
63
|
privacy_policy_prefix_text: string;
|
|
62
|
-
vendor_list_prefix_text: string;
|
|
63
64
|
privacy_policy_anchor_text: string;
|
|
64
|
-
|
|
65
|
+
privacy_center_anchor_text?: string;
|
|
65
66
|
purpose_section_heading: string;
|
|
66
67
|
data_elements: {
|
|
67
68
|
[key: string]: string;
|
|
68
69
|
};
|
|
69
70
|
purposes: {
|
|
70
|
-
[key: string]: string;
|
|
71
|
+
[key: string]: string | { name: string; description: string };
|
|
71
72
|
};
|
|
73
|
+
dpo_info?: DpoInfoTranslation;
|
|
72
74
|
};
|
|
73
75
|
};
|
|
74
76
|
created_at: string;
|
|
75
77
|
updated_at: string;
|
|
76
78
|
deployed_at: string;
|
|
77
79
|
privacy_policy_prefix_text: string;
|
|
78
|
-
vendor_list_prefix_text: string;
|
|
79
80
|
privacy_policy_anchor_text: string;
|
|
80
|
-
|
|
81
|
+
privacy_center_anchor_text?: string;
|
|
81
82
|
purpose_section_heading: string;
|
|
83
|
+
dpo_info?: DpoInfo;
|
|
82
84
|
};
|
|
83
85
|
created_at: string;
|
|
84
86
|
updated_at: string;
|
|
@@ -149,31 +151,6 @@ export type ConsentEventPayload = {
|
|
|
149
151
|
};
|
|
150
152
|
};
|
|
151
153
|
|
|
152
|
-
// @deprecated Remove when production backend supports the new payload format.
|
|
153
|
-
export type LegacyConsentEventPayload = {
|
|
154
|
-
primary_email?: string;
|
|
155
|
-
notice_uuid: string;
|
|
156
|
-
source: string;
|
|
157
|
-
declined: boolean;
|
|
158
|
-
consents: {
|
|
159
|
-
purposes: Array<{
|
|
160
|
-
uuid: string;
|
|
161
|
-
name: string;
|
|
162
|
-
description: string;
|
|
163
|
-
industries?: string;
|
|
164
|
-
selected: boolean;
|
|
165
|
-
data_elements: Array<{
|
|
166
|
-
uuid: string;
|
|
167
|
-
name: string;
|
|
168
|
-
enabled: boolean;
|
|
169
|
-
required: boolean;
|
|
170
|
-
selected: boolean;
|
|
171
|
-
}>;
|
|
172
|
-
}>;
|
|
173
|
-
};
|
|
174
|
-
meta_data?: { specific_uuid: string };
|
|
175
|
-
};
|
|
176
|
-
|
|
177
154
|
export type DataElement = {
|
|
178
155
|
uuid: string;
|
|
179
156
|
name: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Settings } from "./api/types";
|
|
2
|
+
import type { DpoInfoTranslation } from "../RedactoNoticeConsent/api/types";
|
|
2
3
|
|
|
3
4
|
export type Props = Readonly<{
|
|
4
5
|
org_uuid: string;
|
|
@@ -16,8 +17,10 @@ export type Props = Readonly<{
|
|
|
16
17
|
applicationId?: string;
|
|
17
18
|
}>;
|
|
18
19
|
|
|
20
|
+
export type PurposeTranslation = string | { name: string; description: string };
|
|
21
|
+
|
|
19
22
|
export type PurposeTranslations = {
|
|
20
|
-
[purposeId: string]:
|
|
23
|
+
[purposeId: string]: PurposeTranslation;
|
|
21
24
|
};
|
|
22
25
|
|
|
23
26
|
export type DataElementTranslations = {
|
|
@@ -33,13 +36,15 @@ export type TranslationObject = {
|
|
|
33
36
|
data_elements?: DataElementTranslations;
|
|
34
37
|
purpose_section_heading?: string;
|
|
35
38
|
privacy_policy_prefix_text?: string;
|
|
36
|
-
vendor_list_prefix_text?: string;
|
|
37
39
|
privacy_policy_anchor_text?: string;
|
|
38
|
-
|
|
40
|
+
privacy_center_anchor_text?: string;
|
|
41
|
+
dpo_info?: DpoInfoTranslation;
|
|
39
42
|
[key: string]:
|
|
40
43
|
| string
|
|
41
44
|
| PurposeTranslations
|
|
42
45
|
| DataElementTranslations
|
|
43
|
-
|
|
|
46
|
+
| DpoInfoTranslation
|
|
47
|
+
| Record<string, string | undefined>
|
|
48
|
+
| Record<string, string | { name: string; description: string }>
|
|
44
49
|
| undefined;
|
|
45
50
|
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React 19 JSX compatibility: ensures intrinsic elements (div, button, etc.)
|
|
3
|
+
* and Element type align with React's types to fix "Element is not assignable to ReactNode".
|
|
4
|
+
* @see https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/71395
|
|
5
|
+
*/
|
|
6
|
+
import type React from "react";
|
|
7
|
+
|
|
8
|
+
declare global {
|
|
9
|
+
namespace JSX {
|
|
10
|
+
type Element = React.ReactElement;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
declare module "react/jsx-runtime" {
|
|
15
|
+
namespace JSX {
|
|
16
|
+
type Element = React.ReactElement;
|
|
17
|
+
interface IntrinsicElements extends React.JSX.IntrinsicElements {}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
declare module "react/jsx-dev-runtime" {
|
|
22
|
+
namespace JSX {
|
|
23
|
+
type Element = React.ReactElement;
|
|
24
|
+
interface IntrinsicElements extends React.JSX.IntrinsicElements {}
|
|
25
|
+
}
|
|
26
|
+
}
|