@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.
@@ -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
- 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
- }
174
+ const validatedPurposes = purposes.map((purpose) => {
175
+ if (!purpose.uuid) {
176
+ throw new Error(`Invalid purpose data: missing uuid`);
177
+ }
211
178
 
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) => ({
216
- uuid: element.uuid,
217
- selected: element.required ? true : Boolean(element.selected),
218
- }));
219
-
220
- return {
221
- purpose_uuid: purpose.uuid,
222
- selected: Boolean(purpose.selected),
223
- data_elements: dataElements.length > 0 ? dataElements : undefined,
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
- sub_processors_url: string;
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
- vendors_list_anchor_text: string;
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
- vendors_list_anchor_text: string;
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
- vendors_list_anchor_text_audio_url: string;
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: "1px solid #e5e7eb",
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: "20px 0px",
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: "1px solid #e5e7eb",
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]: 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
- vendors_list_anchor_text?: string;
41
+ privacy_center_anchor_text?: string;
42
+ dpo_info?: DpoInfoTranslation;
39
43
  [key: string]:
40
44
  | string
41
45
  | PurposeTranslations
42
46
  | DataElementTranslations
43
- | Record<string, string>
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 === content.default_language) {
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
- return translationMap.purposes?.[itemId] || defaultText;
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
- let payload: ConsentEventPayload | LegacyConsentEventPayload;
81
-
82
- if (isLegacyApi(apiBaseUrl)) {
83
- // @deprecated Legacy payload for production (.io) backend
84
- const primaryEmail = typeof window !== "undefined" ? localStorage.getItem("userEmail") : null;
85
- payload = {
86
- primary_email: primaryEmail || undefined,
87
- notice_uuid: noticeUuid,
88
- source: "WEB",
89
- declined: Boolean(declined),
90
- consents: {
91
- purposes: purposes.map((purpose) => {
92
- if (!purpose.uuid) {
93
- throw new Error(`Invalid purpose data: missing uuid`);
94
- }
95
- return {
96
- uuid: purpose.uuid,
97
- name: purpose.name,
98
- description: purpose.description || "",
99
- industries: purpose.industries || "",
100
- selected: Boolean(purpose.selected),
101
- data_elements: (purpose.data_elements || []).map((element) => ({
102
- uuid: element.uuid,
103
- name: element.name,
104
- enabled: Boolean(element.enabled),
105
- required: Boolean(element.required),
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
- sub_processors_url: string;
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
- vendors_list_anchor_text: string;
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
- vendors_list_anchor_text: string;
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]: 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
- vendors_list_anchor_text?: string;
40
+ privacy_center_anchor_text?: string;
41
+ dpo_info?: DpoInfoTranslation;
39
42
  [key: string]:
40
43
  | string
41
44
  | PurposeTranslations
42
45
  | DataElementTranslations
43
- | Record<string, string>
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
+ }