@redacto.io/consent-sdk-react 3.3.0 → 3.4.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/README.md +11 -0
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +279 -32
- package/dist/index.mjs +279 -32
- 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 +5 -1
- package/src/RedactoNoticeConsent/api/types.ts +47 -8
- 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/types.ts +8 -6
- 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
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* collection and reconsent flows with visual distinction between already-consented
|
|
6
6
|
* and needs-consent purposes.
|
|
7
7
|
*/
|
|
8
|
-
|
|
8
|
+
// @ts-nocheck - React 19 @types/react has Element/ReactNode assignability issues; tsc passes
|
|
9
9
|
// React imports
|
|
10
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
10
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
11
11
|
|
|
12
12
|
// API imports
|
|
13
13
|
import {
|
|
@@ -29,6 +29,9 @@ import type { Props, TranslationObject } from "./types";
|
|
|
29
29
|
// Asset imports
|
|
30
30
|
import logo from "./assets/redacto-logo.png";
|
|
31
31
|
|
|
32
|
+
// Constants
|
|
33
|
+
import { DPO_AUDIO_ELEMENT_MAP } from "./constants";
|
|
34
|
+
|
|
32
35
|
// Utility imports
|
|
33
36
|
import { injectCheckboxStyles } from "./injectStyles";
|
|
34
37
|
import { styles } from "./styles";
|
|
@@ -65,284 +68,287 @@ const PurposeItem: React.FC<
|
|
|
65
68
|
selectedLanguage,
|
|
66
69
|
initialDataElementSelections = {},
|
|
67
70
|
}) => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
const headingStyle = useMemo(
|
|
72
|
+
() => ({
|
|
73
|
+
color: settings?.headingColor || "#323B4B",
|
|
74
|
+
fontFamily: settings?.font || "inherit",
|
|
75
|
+
}),
|
|
76
|
+
[settings]
|
|
77
|
+
);
|
|
75
78
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
const textStyle = useMemo(
|
|
80
|
+
() => ({
|
|
81
|
+
color: settings?.textColor || "#344054",
|
|
82
|
+
fontFamily: settings?.font || "inherit",
|
|
83
|
+
}),
|
|
84
|
+
[settings]
|
|
85
|
+
);
|
|
83
86
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
<div
|
|
88
|
-
style={styles.optionLeft}
|
|
89
|
-
onClick={() => onPurposeCollapse(purpose.uuid)}
|
|
90
|
-
role="button"
|
|
91
|
-
tabIndex={0}
|
|
92
|
-
aria-expanded={!collapsedPurposes[purpose.uuid]}
|
|
93
|
-
aria-controls={`purpose-${purpose.uuid}`}
|
|
94
|
-
aria-label={`${collapsedPurposes[purpose.uuid] ? "Expand" : "Collapse"} ${getTranslatedText(
|
|
95
|
-
`purposes.name`,
|
|
96
|
-
purpose.name,
|
|
97
|
-
purpose.uuid
|
|
98
|
-
)} details`}
|
|
99
|
-
onKeyDown={(e) => {
|
|
100
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
101
|
-
e.preventDefault();
|
|
102
|
-
onPurposeCollapse(purpose.uuid);
|
|
103
|
-
}
|
|
104
|
-
}}
|
|
105
|
-
>
|
|
87
|
+
return (
|
|
88
|
+
<div key={purpose.uuid}>
|
|
89
|
+
<div style={styles.optionItem}>
|
|
106
90
|
<div
|
|
107
|
-
style={
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
91
|
+
style={styles.optionLeft}
|
|
92
|
+
onClick={() => onPurposeCollapse(purpose.uuid)}
|
|
93
|
+
role="button"
|
|
94
|
+
tabIndex={0}
|
|
95
|
+
aria-expanded={!collapsedPurposes[purpose.uuid]}
|
|
96
|
+
aria-controls={`purpose-${purpose.uuid}`}
|
|
97
|
+
aria-label={`${collapsedPurposes[purpose.uuid] ? "Expand" : "Collapse"} ${getTranslatedText(
|
|
98
|
+
`purposes.name`,
|
|
99
|
+
purpose.name,
|
|
100
|
+
purpose.uuid
|
|
101
|
+
)} details`}
|
|
102
|
+
onKeyDown={(e) => {
|
|
103
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
104
|
+
e.preventDefault();
|
|
105
|
+
onPurposeCollapse(purpose.uuid);
|
|
106
|
+
}
|
|
112
107
|
}}
|
|
113
108
|
>
|
|
114
|
-
<
|
|
115
|
-
width="7"
|
|
116
|
-
height="12"
|
|
117
|
-
viewBox="0 0 7 12"
|
|
118
|
-
fill="none"
|
|
119
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
120
|
-
stroke={settings?.headingColor || "#323B4B"}
|
|
121
|
-
strokeWidth="2"
|
|
122
|
-
strokeLinecap="round"
|
|
123
|
-
strokeLinejoin="round"
|
|
109
|
+
<div
|
|
124
110
|
style={{
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
111
|
+
display: "flex",
|
|
112
|
+
alignItems: "center",
|
|
113
|
+
justifyContent: "center",
|
|
114
|
+
marginLeft: "5px",
|
|
129
115
|
}}
|
|
130
|
-
aria-hidden="true"
|
|
131
116
|
>
|
|
132
|
-
<
|
|
133
|
-
|
|
117
|
+
<svg
|
|
118
|
+
width="7"
|
|
119
|
+
height="12"
|
|
120
|
+
viewBox="0 0 7 12"
|
|
121
|
+
fill="none"
|
|
122
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
123
|
+
stroke={settings?.headingColor || "#323B4B"}
|
|
124
|
+
strokeWidth="2"
|
|
125
|
+
strokeLinecap={"round" as const}
|
|
126
|
+
strokeLinejoin={"round" as const}
|
|
127
|
+
style={{
|
|
128
|
+
transform: !collapsedPurposes[purpose.uuid]
|
|
129
|
+
? "rotate(90deg)"
|
|
130
|
+
: "rotate(0deg)",
|
|
131
|
+
transition: "transform 0.3s ease",
|
|
132
|
+
}}
|
|
133
|
+
aria-hidden="true"
|
|
134
|
+
>
|
|
135
|
+
<path d="M1 1L6 6L1 11" />
|
|
136
|
+
</svg>
|
|
137
|
+
</div>
|
|
138
|
+
<div style={styles.optionTextContainer}>
|
|
139
|
+
<h2
|
|
140
|
+
key={`purpose-name-${purpose.uuid}-${selectedLanguage}`}
|
|
141
|
+
id={`purpose-name-${purpose.uuid}`}
|
|
142
|
+
style={{
|
|
143
|
+
...styles.optionTitle,
|
|
144
|
+
...responsiveStyles.optionTitle,
|
|
145
|
+
...headingStyle,
|
|
146
|
+
}}
|
|
147
|
+
>
|
|
148
|
+
{getTranslatedText(`purposes.name`, purpose.name, purpose.uuid)}
|
|
149
|
+
</h2>
|
|
150
|
+
<h3
|
|
151
|
+
key={`purpose-description-${purpose.uuid}-${selectedLanguage}`}
|
|
152
|
+
id={`purpose-description-${purpose.uuid}`}
|
|
153
|
+
style={{
|
|
154
|
+
...styles.optionDescription,
|
|
155
|
+
...responsiveStyles.optionDescription,
|
|
156
|
+
...textStyle,
|
|
157
|
+
}}
|
|
158
|
+
>
|
|
159
|
+
{getTranslatedText(
|
|
160
|
+
`purposes.description`,
|
|
161
|
+
purpose.description,
|
|
162
|
+
purpose.uuid
|
|
163
|
+
)}
|
|
164
|
+
</h3>
|
|
165
|
+
</div>
|
|
134
166
|
</div>
|
|
135
|
-
|
|
136
|
-
<
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
167
|
+
{!isAlreadyConsented && (
|
|
168
|
+
<input
|
|
169
|
+
className="redacto-checkbox-large"
|
|
170
|
+
type="checkbox"
|
|
171
|
+
checked={selectedPurposes[purpose.uuid] || false}
|
|
172
|
+
onChange={() => onPurposeToggle(purpose.uuid)}
|
|
173
|
+
aria-label={`Select all data elements for ${getTranslatedText(
|
|
174
|
+
`purposes.name`,
|
|
175
|
+
purpose.name,
|
|
176
|
+
purpose.uuid
|
|
177
|
+
)}`}
|
|
178
|
+
/>
|
|
179
|
+
)}
|
|
180
|
+
{isAlreadyConsented && (
|
|
181
|
+
<div
|
|
150
182
|
style={{
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
183
|
+
display: "flex",
|
|
184
|
+
alignItems: "center",
|
|
185
|
+
justifyContent: "center",
|
|
186
|
+
width: "20px",
|
|
187
|
+
height: "20px",
|
|
188
|
+
borderRadius: "50%",
|
|
189
|
+
backgroundColor: "#10B981",
|
|
190
|
+
marginLeft: "12px",
|
|
154
191
|
}}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
purpose.description,
|
|
192
|
+
aria-label={`Already consented to ${getTranslatedText(
|
|
193
|
+
`purposes.name`,
|
|
194
|
+
purpose.name,
|
|
159
195
|
purpose.uuid
|
|
160
|
-
)}
|
|
161
|
-
</h3>
|
|
162
|
-
</div>
|
|
163
|
-
</div>
|
|
164
|
-
{!isAlreadyConsented && (
|
|
165
|
-
<input
|
|
166
|
-
className="redacto-checkbox-large"
|
|
167
|
-
type="checkbox"
|
|
168
|
-
checked={selectedPurposes[purpose.uuid] || false}
|
|
169
|
-
onChange={() => onPurposeToggle(purpose.uuid)}
|
|
170
|
-
aria-label={`Select all data elements for ${getTranslatedText(
|
|
171
|
-
`purposes.name`,
|
|
172
|
-
purpose.name,
|
|
173
|
-
purpose.uuid
|
|
174
|
-
)}`}
|
|
175
|
-
/>
|
|
176
|
-
)}
|
|
177
|
-
{isAlreadyConsented && (
|
|
178
|
-
<div
|
|
179
|
-
style={{
|
|
180
|
-
display: "flex",
|
|
181
|
-
alignItems: "center",
|
|
182
|
-
justifyContent: "center",
|
|
183
|
-
width: "20px",
|
|
184
|
-
height: "20px",
|
|
185
|
-
borderRadius: "50%",
|
|
186
|
-
backgroundColor: "#10B981",
|
|
187
|
-
marginLeft: "12px",
|
|
188
|
-
}}
|
|
189
|
-
aria-label={`Already consented to ${getTranslatedText(
|
|
190
|
-
`purposes.name`,
|
|
191
|
-
purpose.name,
|
|
192
|
-
purpose.uuid
|
|
193
|
-
)}`}
|
|
194
|
-
>
|
|
195
|
-
<svg
|
|
196
|
-
width="12"
|
|
197
|
-
height="12"
|
|
198
|
-
viewBox="0 0 12 12"
|
|
199
|
-
fill="none"
|
|
200
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
201
|
-
aria-hidden="true"
|
|
196
|
+
)}`}
|
|
202
197
|
>
|
|
203
|
-
<
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
</svg>
|
|
211
|
-
</div>
|
|
212
|
-
)}
|
|
213
|
-
</div>
|
|
214
|
-
{!collapsedPurposes[purpose.uuid] &&
|
|
215
|
-
purpose.data_elements &&
|
|
216
|
-
purpose.data_elements.length > 0 && (
|
|
217
|
-
<div
|
|
218
|
-
key={`purpose-${purpose.uuid}-container-${selectedLanguage}`}
|
|
219
|
-
id={`purpose-${purpose.uuid}`}
|
|
220
|
-
style={styles.dataElementsContainer}
|
|
221
|
-
>
|
|
222
|
-
{purpose.data_elements.map((dataElement: any) => (
|
|
223
|
-
<div
|
|
224
|
-
key={`${dataElement.uuid}-${selectedLanguage}`}
|
|
225
|
-
style={styles.dataElementItem}
|
|
198
|
+
<svg
|
|
199
|
+
width="12"
|
|
200
|
+
height="12"
|
|
201
|
+
viewBox="0 0 12 12"
|
|
202
|
+
fill="none"
|
|
203
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
204
|
+
aria-hidden="true"
|
|
226
205
|
>
|
|
227
|
-
<
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
206
|
+
<path
|
|
207
|
+
d="M4 6L5.5 7.5L8.5 4.5"
|
|
208
|
+
stroke="white"
|
|
209
|
+
strokeWidth="2"
|
|
210
|
+
strokeLinecap="round"
|
|
211
|
+
strokeLinejoin="round"
|
|
212
|
+
/>
|
|
213
|
+
</svg>
|
|
214
|
+
</div>
|
|
215
|
+
)}
|
|
216
|
+
</div>
|
|
217
|
+
{!collapsedPurposes[purpose.uuid] &&
|
|
218
|
+
purpose.data_elements &&
|
|
219
|
+
purpose.data_elements.length > 0 && (
|
|
220
|
+
<div
|
|
221
|
+
key={`purpose-${purpose.uuid}-container-${selectedLanguage}`}
|
|
222
|
+
id={`purpose-${purpose.uuid}`}
|
|
223
|
+
style={styles.dataElementsContainer}
|
|
224
|
+
>
|
|
225
|
+
{purpose.data_elements.map((dataElement: any) => (
|
|
226
|
+
<div
|
|
227
|
+
key={`${dataElement.uuid}-${selectedLanguage}`}
|
|
228
|
+
style={styles.dataElementItem}
|
|
235
229
|
>
|
|
236
|
-
|
|
237
|
-
`
|
|
238
|
-
dataElement.
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}}
|
|
247
|
-
aria-label="required"
|
|
248
|
-
>
|
|
249
|
-
*
|
|
250
|
-
</span>
|
|
251
|
-
)}
|
|
252
|
-
</span>
|
|
253
|
-
{!isAlreadyConsented && (
|
|
254
|
-
<input
|
|
255
|
-
className="redacto-checkbox-small"
|
|
256
|
-
type="checkbox"
|
|
257
|
-
checked={
|
|
258
|
-
selectedDataElements[
|
|
259
|
-
`${purpose.uuid}-${dataElement.uuid}`
|
|
260
|
-
] || false
|
|
261
|
-
}
|
|
262
|
-
onChange={() =>
|
|
263
|
-
onDataElementToggle(dataElement.uuid, purpose.uuid)
|
|
264
|
-
}
|
|
265
|
-
aria-label={`Select ${getTranslatedText(
|
|
230
|
+
<span
|
|
231
|
+
key={`data-element-${purpose.uuid}-${dataElement.uuid}-${selectedLanguage}`}
|
|
232
|
+
id={`data-element-${purpose.uuid}-${dataElement.uuid}`}
|
|
233
|
+
style={{
|
|
234
|
+
...styles.dataElementText,
|
|
235
|
+
...responsiveStyles.dataElementText,
|
|
236
|
+
...textStyle,
|
|
237
|
+
}}
|
|
238
|
+
>
|
|
239
|
+
{getTranslatedText(
|
|
266
240
|
`data_elements.name`,
|
|
267
241
|
dataElement.name,
|
|
268
242
|
dataElement.uuid
|
|
269
|
-
)}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
width: "16px",
|
|
291
|
-
height: "16px",
|
|
292
|
-
borderRadius: "50%",
|
|
293
|
-
backgroundColor: "#10B981",
|
|
294
|
-
}}
|
|
295
|
-
aria-label={`Already consented to ${getTranslatedText(
|
|
296
|
-
`data_elements.name`,
|
|
297
|
-
dataElement.name,
|
|
298
|
-
dataElement.uuid
|
|
299
|
-
)}`}
|
|
300
|
-
>
|
|
301
|
-
<svg
|
|
302
|
-
width="10"
|
|
303
|
-
height="10"
|
|
304
|
-
viewBox="0 0 10 10"
|
|
305
|
-
fill="none"
|
|
306
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
307
|
-
aria-hidden="true"
|
|
308
|
-
>
|
|
309
|
-
<path
|
|
310
|
-
d="M3 5L4.5 6.5L7.5 3.5"
|
|
311
|
-
stroke="white"
|
|
312
|
-
strokeWidth="1.5"
|
|
313
|
-
strokeLinecap="round"
|
|
314
|
-
strokeLinejoin="round"
|
|
315
|
-
/>
|
|
316
|
-
</svg>
|
|
317
|
-
</div>
|
|
318
|
-
);
|
|
319
|
-
} else {
|
|
320
|
-
return (
|
|
321
|
-
<input
|
|
322
|
-
className="redacto-checkbox-small"
|
|
323
|
-
type="checkbox"
|
|
324
|
-
checked={isCurrentlySelected}
|
|
325
|
-
onChange={() =>
|
|
326
|
-
onDataElementToggle(dataElement.uuid, purpose.uuid)
|
|
327
|
-
}
|
|
328
|
-
aria-label={`Select ${getTranslatedText(
|
|
329
|
-
`data_elements.name`,
|
|
330
|
-
dataElement.name,
|
|
331
|
-
dataElement.uuid
|
|
332
|
-
)}${dataElement.required ? " (required)" : ""}`}
|
|
333
|
-
/>
|
|
334
|
-
);
|
|
243
|
+
)}
|
|
244
|
+
{dataElement.required && (
|
|
245
|
+
<span
|
|
246
|
+
style={{
|
|
247
|
+
color: "red",
|
|
248
|
+
marginLeft: "4px",
|
|
249
|
+
}}
|
|
250
|
+
aria-label="required"
|
|
251
|
+
>
|
|
252
|
+
*
|
|
253
|
+
</span>
|
|
254
|
+
)}
|
|
255
|
+
</span>
|
|
256
|
+
{!isAlreadyConsented && (
|
|
257
|
+
<input
|
|
258
|
+
className="redacto-checkbox-small"
|
|
259
|
+
type="checkbox"
|
|
260
|
+
checked={
|
|
261
|
+
selectedDataElements[
|
|
262
|
+
`${purpose.uuid}-${dataElement.uuid}`
|
|
263
|
+
] || false
|
|
335
264
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
265
|
+
onChange={() =>
|
|
266
|
+
onDataElementToggle(dataElement.uuid, purpose.uuid)
|
|
267
|
+
}
|
|
268
|
+
aria-label={`Select ${getTranslatedText(
|
|
269
|
+
`data_elements.name`,
|
|
270
|
+
dataElement.name,
|
|
271
|
+
dataElement.uuid
|
|
272
|
+
)}${dataElement.required ? " (required)" : ""}`}
|
|
273
|
+
/>
|
|
274
|
+
)}
|
|
275
|
+
{isAlreadyConsented && (
|
|
276
|
+
<>
|
|
277
|
+
{(() => {
|
|
278
|
+
const combinedId = `${purpose.uuid}-${dataElement.uuid}`;
|
|
279
|
+
const wasOriginallySelected =
|
|
280
|
+
initialDataElementSelections[combinedId] || false;
|
|
281
|
+
|
|
282
|
+
// Review mode: read-only display of what user consented to
|
|
283
|
+
// Green tick = consented, empty circle = not consented (no editing)
|
|
284
|
+
if (wasOriginallySelected) {
|
|
285
|
+
return (
|
|
286
|
+
<div
|
|
287
|
+
style={{
|
|
288
|
+
display: "flex",
|
|
289
|
+
alignItems: "center",
|
|
290
|
+
justifyContent: "center",
|
|
291
|
+
width: "16px",
|
|
292
|
+
height: "16px",
|
|
293
|
+
borderRadius: "50%",
|
|
294
|
+
backgroundColor: "#10B981",
|
|
295
|
+
}}
|
|
296
|
+
aria-label={`Consented to ${getTranslatedText(
|
|
297
|
+
`data_elements.name`,
|
|
298
|
+
dataElement.name,
|
|
299
|
+
dataElement.uuid
|
|
300
|
+
)}`}
|
|
301
|
+
>
|
|
302
|
+
<svg
|
|
303
|
+
width="10"
|
|
304
|
+
height="10"
|
|
305
|
+
viewBox="0 0 10 10"
|
|
306
|
+
fill="none"
|
|
307
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
308
|
+
aria-hidden="true"
|
|
309
|
+
>
|
|
310
|
+
<path
|
|
311
|
+
d="M3 5L4.5 6.5L7.5 3.5"
|
|
312
|
+
stroke="white"
|
|
313
|
+
strokeWidth="1.5"
|
|
314
|
+
strokeLinecap={"round" as const}
|
|
315
|
+
strokeLinejoin={"round" as const}
|
|
316
|
+
/>
|
|
317
|
+
</svg>
|
|
318
|
+
</div>
|
|
319
|
+
);
|
|
320
|
+
} else {
|
|
321
|
+
// Not consented: show empty circle (read-only, no checkbox)
|
|
322
|
+
return (
|
|
323
|
+
<div
|
|
324
|
+
style={{
|
|
325
|
+
display: "flex",
|
|
326
|
+
alignItems: "center",
|
|
327
|
+
justifyContent: "center",
|
|
328
|
+
width: "16px",
|
|
329
|
+
height: "16px",
|
|
330
|
+
borderRadius: "50%",
|
|
331
|
+
border: "2px solid #d0d5dd",
|
|
332
|
+
backgroundColor: "transparent",
|
|
333
|
+
}}
|
|
334
|
+
aria-label={`Not consented to ${getTranslatedText(
|
|
335
|
+
`data_elements.name`,
|
|
336
|
+
dataElement.name,
|
|
337
|
+
dataElement.uuid
|
|
338
|
+
)}`}
|
|
339
|
+
/>
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
})()}
|
|
343
|
+
</>
|
|
344
|
+
)}
|
|
345
|
+
</div>
|
|
346
|
+
))}
|
|
347
|
+
</div>
|
|
348
|
+
)}
|
|
349
|
+
</div>
|
|
350
|
+
);
|
|
351
|
+
};
|
|
346
352
|
|
|
347
353
|
/**
|
|
348
354
|
* GuardianForm Component
|
|
@@ -384,220 +390,220 @@ const GuardianForm: React.FC<{
|
|
|
384
390
|
declineButtonStyle,
|
|
385
391
|
logoUrl,
|
|
386
392
|
}) => {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
393
|
+
const inputStyle = {
|
|
394
|
+
width: "100%",
|
|
395
|
+
padding: "10px 12px",
|
|
396
|
+
border: `1px solid ${settings?.borderColor || "#d0d5dd"}`,
|
|
397
|
+
borderRadius: settings?.borderRadius || "8px",
|
|
398
|
+
fontSize: "13px",
|
|
399
|
+
fontFamily: settings?.font || "inherit",
|
|
400
|
+
color: settings?.textColor || "#344054",
|
|
401
|
+
backgroundColor: settings?.backgroundColor || "#ffffff",
|
|
402
|
+
marginBottom: "4px",
|
|
403
|
+
boxSizing: "border-box" as const,
|
|
404
|
+
};
|
|
399
405
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
+
const errorStyle = {
|
|
407
|
+
color: "#DC2626",
|
|
408
|
+
fontSize: "12px",
|
|
409
|
+
marginTop: "4px",
|
|
410
|
+
marginBottom: "16px",
|
|
411
|
+
};
|
|
406
412
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
413
|
+
const labelStyle = {
|
|
414
|
+
...componentTextStyle,
|
|
415
|
+
fontSize: "13px",
|
|
416
|
+
fontWeight: "500",
|
|
417
|
+
marginBottom: "6px",
|
|
418
|
+
display: "block",
|
|
419
|
+
};
|
|
414
420
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
421
|
+
return (
|
|
422
|
+
<>
|
|
423
|
+
<div style={{ ...styles.topSection, ...componentTextStyle }}>
|
|
424
|
+
<div style={styles.topLeft}>
|
|
425
|
+
<img style={styles.logo} src={logoUrl} alt="Redacto Logo" />
|
|
426
|
+
<h2
|
|
427
|
+
id="guardian-form-title"
|
|
428
|
+
style={{
|
|
429
|
+
...styles.title,
|
|
430
|
+
...responsiveStyles.title,
|
|
431
|
+
...componentHeadingStyle,
|
|
432
|
+
fontSize: "16px",
|
|
433
|
+
fontWeight: 600,
|
|
434
|
+
}}
|
|
435
|
+
>
|
|
436
|
+
Guardian Information Required
|
|
437
|
+
</h2>
|
|
438
|
+
</div>
|
|
432
439
|
</div>
|
|
433
|
-
</div>
|
|
434
|
-
|
|
435
|
-
<div
|
|
436
|
-
style={{
|
|
437
|
-
...styles.middleSection,
|
|
438
|
-
...responsiveStyles.middleSection,
|
|
439
|
-
...componentTextStyle,
|
|
440
|
-
}}
|
|
441
|
-
>
|
|
442
|
-
<p
|
|
443
|
-
style={{
|
|
444
|
-
...styles.privacyText,
|
|
445
|
-
...responsiveStyles.privacyText,
|
|
446
|
-
...componentTextStyle,
|
|
447
|
-
fontSize: "14px",
|
|
448
|
-
lineHeight: "1.4",
|
|
449
|
-
}}
|
|
450
|
-
>
|
|
451
|
-
As you are under the age of consent, we need to collect information
|
|
452
|
-
about your legal guardian to proceed.
|
|
453
|
-
</p>
|
|
454
440
|
|
|
455
441
|
<div
|
|
456
442
|
style={{
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
marginTop: "16px",
|
|
443
|
+
...styles.middleSection,
|
|
444
|
+
...responsiveStyles.middleSection,
|
|
445
|
+
...componentTextStyle,
|
|
461
446
|
}}
|
|
462
447
|
>
|
|
463
|
-
<
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
style={{
|
|
476
|
-
...inputStyle,
|
|
477
|
-
borderColor: errors.guardianName
|
|
478
|
-
? "#DC2626"
|
|
479
|
-
: inputStyle.border,
|
|
480
|
-
}}
|
|
481
|
-
placeholder="Enter guardian's full name"
|
|
482
|
-
/>
|
|
483
|
-
{errors.guardianName && (
|
|
484
|
-
<div style={errorStyle}>{errors.guardianName}</div>
|
|
485
|
-
)}
|
|
486
|
-
</div>
|
|
487
|
-
|
|
488
|
-
<div>
|
|
489
|
-
<label
|
|
490
|
-
htmlFor="guardianContact"
|
|
491
|
-
style={{ ...labelStyle, marginBottom: "8px", display: "block" }}
|
|
492
|
-
>
|
|
493
|
-
Contact of Guardian *
|
|
494
|
-
</label>
|
|
495
|
-
<input
|
|
496
|
-
id="guardianContact"
|
|
497
|
-
type="text"
|
|
498
|
-
value={formData.guardianContact}
|
|
499
|
-
onChange={(e) => onChange("guardianContact", e.target.value)}
|
|
500
|
-
style={{
|
|
501
|
-
...inputStyle,
|
|
502
|
-
borderColor: errors.guardianContact
|
|
503
|
-
? "#DC2626"
|
|
504
|
-
: inputStyle.border,
|
|
505
|
-
}}
|
|
506
|
-
placeholder="Enter guardian's email or phone number"
|
|
507
|
-
/>
|
|
508
|
-
{errors.guardianContact && (
|
|
509
|
-
<div style={errorStyle}>{errors.guardianContact}</div>
|
|
510
|
-
)}
|
|
511
|
-
</div>
|
|
512
|
-
|
|
513
|
-
<div>
|
|
514
|
-
<label
|
|
515
|
-
htmlFor="guardianRelationship"
|
|
516
|
-
style={{ ...labelStyle, marginBottom: "8px", display: "block" }}
|
|
517
|
-
>
|
|
518
|
-
Relationship to Guardian *
|
|
519
|
-
</label>
|
|
520
|
-
<input
|
|
521
|
-
id="guardianRelationship"
|
|
522
|
-
type="text"
|
|
523
|
-
value={formData.guardianRelationship}
|
|
524
|
-
onChange={(e) => onChange("guardianRelationship", e.target.value)}
|
|
525
|
-
style={{
|
|
526
|
-
...inputStyle,
|
|
527
|
-
borderColor: errors.guardianRelationship
|
|
528
|
-
? "#DC2626"
|
|
529
|
-
: inputStyle.border,
|
|
530
|
-
}}
|
|
531
|
-
placeholder="e.g., Parent, Legal Guardian, etc."
|
|
532
|
-
/>
|
|
533
|
-
{errors.guardianRelationship && (
|
|
534
|
-
<div style={errorStyle}>{errors.guardianRelationship}</div>
|
|
535
|
-
)}
|
|
536
|
-
</div>
|
|
537
|
-
</div>
|
|
448
|
+
<p
|
|
449
|
+
style={{
|
|
450
|
+
...styles.privacyText,
|
|
451
|
+
...responsiveStyles.privacyText,
|
|
452
|
+
...componentTextStyle,
|
|
453
|
+
fontSize: "14px",
|
|
454
|
+
lineHeight: "1.4",
|
|
455
|
+
}}
|
|
456
|
+
>
|
|
457
|
+
As you are under the age of consent, we need to collect information
|
|
458
|
+
about your legal guardian to proceed.
|
|
459
|
+
</p>
|
|
538
460
|
|
|
539
|
-
{errors.general && (
|
|
540
461
|
<div
|
|
541
|
-
role="alert"
|
|
542
462
|
style={{
|
|
543
|
-
|
|
544
|
-
|
|
463
|
+
display: "flex",
|
|
464
|
+
flexDirection: "column",
|
|
465
|
+
gap: "12px",
|
|
545
466
|
marginTop: "16px",
|
|
546
|
-
padding: "12px",
|
|
547
|
-
borderRadius: "6px",
|
|
548
|
-
border: "1px solid #FCA5A5",
|
|
549
|
-
backgroundColor: "#FEF2F2",
|
|
550
|
-
textAlign: "center",
|
|
551
467
|
}}
|
|
552
468
|
>
|
|
553
|
-
|
|
469
|
+
<div>
|
|
470
|
+
<label
|
|
471
|
+
htmlFor="guardianName"
|
|
472
|
+
style={{ ...labelStyle, marginBottom: "8px", display: "block" }}
|
|
473
|
+
>
|
|
474
|
+
Name of Guardian *
|
|
475
|
+
</label>
|
|
476
|
+
<input
|
|
477
|
+
id="guardianName"
|
|
478
|
+
type="text"
|
|
479
|
+
value={formData.guardianName}
|
|
480
|
+
onChange={(e) => onChange("guardianName", e.target.value)}
|
|
481
|
+
style={{
|
|
482
|
+
...inputStyle,
|
|
483
|
+
borderColor: errors.guardianName
|
|
484
|
+
? "#DC2626"
|
|
485
|
+
: inputStyle.border,
|
|
486
|
+
}}
|
|
487
|
+
placeholder="Enter guardian's full name"
|
|
488
|
+
/>
|
|
489
|
+
{errors.guardianName && (
|
|
490
|
+
<div style={errorStyle}>{errors.guardianName}</div>
|
|
491
|
+
)}
|
|
492
|
+
</div>
|
|
493
|
+
|
|
494
|
+
<div>
|
|
495
|
+
<label
|
|
496
|
+
htmlFor="guardianContact"
|
|
497
|
+
style={{ ...labelStyle, marginBottom: "8px", display: "block" }}
|
|
498
|
+
>
|
|
499
|
+
Contact of Guardian *
|
|
500
|
+
</label>
|
|
501
|
+
<input
|
|
502
|
+
id="guardianContact"
|
|
503
|
+
type="text"
|
|
504
|
+
value={formData.guardianContact}
|
|
505
|
+
onChange={(e) => onChange("guardianContact", e.target.value)}
|
|
506
|
+
style={{
|
|
507
|
+
...inputStyle,
|
|
508
|
+
borderColor: errors.guardianContact
|
|
509
|
+
? "#DC2626"
|
|
510
|
+
: inputStyle.border,
|
|
511
|
+
}}
|
|
512
|
+
placeholder="Enter guardian's email or phone number"
|
|
513
|
+
/>
|
|
514
|
+
{errors.guardianContact && (
|
|
515
|
+
<div style={errorStyle}>{errors.guardianContact}</div>
|
|
516
|
+
)}
|
|
517
|
+
</div>
|
|
518
|
+
|
|
519
|
+
<div>
|
|
520
|
+
<label
|
|
521
|
+
htmlFor="guardianRelationship"
|
|
522
|
+
style={{ ...labelStyle, marginBottom: "8px", display: "block" }}
|
|
523
|
+
>
|
|
524
|
+
Relationship to Guardian *
|
|
525
|
+
</label>
|
|
526
|
+
<input
|
|
527
|
+
id="guardianRelationship"
|
|
528
|
+
type="text"
|
|
529
|
+
value={formData.guardianRelationship}
|
|
530
|
+
onChange={(e) => onChange("guardianRelationship", e.target.value)}
|
|
531
|
+
style={{
|
|
532
|
+
...inputStyle,
|
|
533
|
+
borderColor: errors.guardianRelationship
|
|
534
|
+
? "#DC2626"
|
|
535
|
+
: inputStyle.border,
|
|
536
|
+
}}
|
|
537
|
+
placeholder="e.g., Parent, Legal Guardian, etc."
|
|
538
|
+
/>
|
|
539
|
+
{errors.guardianRelationship && (
|
|
540
|
+
<div style={errorStyle}>{errors.guardianRelationship}</div>
|
|
541
|
+
)}
|
|
542
|
+
</div>
|
|
554
543
|
</div>
|
|
555
|
-
)}
|
|
556
|
-
</div>
|
|
557
544
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
? "Submitting guardian information"
|
|
579
|
-
: "Submit guardian information and proceed"
|
|
580
|
-
}
|
|
581
|
-
>
|
|
582
|
-
{isSubmitting ? "Submitting..." : "Next"}
|
|
583
|
-
</button>
|
|
584
|
-
<button
|
|
545
|
+
{errors.general && (
|
|
546
|
+
<div
|
|
547
|
+
role="alert"
|
|
548
|
+
style={{
|
|
549
|
+
color: "#DC2626",
|
|
550
|
+
fontSize: "14px",
|
|
551
|
+
marginTop: "16px",
|
|
552
|
+
padding: "12px",
|
|
553
|
+
borderRadius: "6px",
|
|
554
|
+
border: "1px solid #FCA5A5",
|
|
555
|
+
backgroundColor: "#FEF2F2",
|
|
556
|
+
textAlign: "center",
|
|
557
|
+
}}
|
|
558
|
+
>
|
|
559
|
+
{errors.general}
|
|
560
|
+
</div>
|
|
561
|
+
)}
|
|
562
|
+
</div>
|
|
563
|
+
|
|
564
|
+
<div
|
|
585
565
|
style={{
|
|
586
|
-
...styles.
|
|
587
|
-
...responsiveStyles.
|
|
588
|
-
...
|
|
589
|
-
|
|
566
|
+
...styles.bottomSection,
|
|
567
|
+
...responsiveStyles.bottomSection,
|
|
568
|
+
...componentTextStyle,
|
|
569
|
+
paddingTop: "12px",
|
|
590
570
|
}}
|
|
591
|
-
onClick={onDecline}
|
|
592
|
-
type="button"
|
|
593
|
-
aria-label="Cancel guardian information form"
|
|
594
571
|
>
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
572
|
+
<button
|
|
573
|
+
style={{
|
|
574
|
+
...styles.button,
|
|
575
|
+
...responsiveStyles.button,
|
|
576
|
+
...styles.acceptButton,
|
|
577
|
+
...acceptButtonStyle,
|
|
578
|
+
}}
|
|
579
|
+
onClick={onNext}
|
|
580
|
+
disabled={isSubmitting}
|
|
581
|
+
type="button"
|
|
582
|
+
aria-label={
|
|
583
|
+
isSubmitting
|
|
584
|
+
? "Submitting guardian information"
|
|
585
|
+
: "Submit guardian information and proceed"
|
|
586
|
+
}
|
|
587
|
+
>
|
|
588
|
+
{isSubmitting ? "Submitting..." : "Next"}
|
|
589
|
+
</button>
|
|
590
|
+
<button
|
|
591
|
+
style={{
|
|
592
|
+
...styles.button,
|
|
593
|
+
...responsiveStyles.button,
|
|
594
|
+
...styles.cancelButton,
|
|
595
|
+
...declineButtonStyle,
|
|
596
|
+
}}
|
|
597
|
+
onClick={onDecline}
|
|
598
|
+
type="button"
|
|
599
|
+
aria-label="Cancel guardian information form"
|
|
600
|
+
>
|
|
601
|
+
Cancel
|
|
602
|
+
</button>
|
|
603
|
+
</div>
|
|
604
|
+
</>
|
|
605
|
+
);
|
|
606
|
+
};
|
|
601
607
|
|
|
602
608
|
/**
|
|
603
609
|
* AgeVerification Component
|
|
@@ -629,122 +635,122 @@ const AgeVerification: React.FC<{
|
|
|
629
635
|
logoUrl,
|
|
630
636
|
isMobile,
|
|
631
637
|
}) => {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
638
|
+
return (
|
|
639
|
+
<>
|
|
640
|
+
<div style={{ ...styles.topSection, ...componentTextStyle }}>
|
|
641
|
+
<div style={styles.topLeft}>
|
|
642
|
+
<img style={styles.logo} src={logoUrl} alt="Redacto Logo" />
|
|
643
|
+
<h2
|
|
644
|
+
id="age-verification-title"
|
|
645
|
+
style={{
|
|
646
|
+
...styles.title,
|
|
647
|
+
...responsiveStyles.title,
|
|
648
|
+
...componentHeadingStyle,
|
|
649
|
+
}}
|
|
650
|
+
>
|
|
651
|
+
Age Verification Required
|
|
652
|
+
</h2>
|
|
653
|
+
</div>
|
|
654
|
+
<button
|
|
655
|
+
onClick={onClose}
|
|
639
656
|
style={{
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
657
|
+
background: "none",
|
|
658
|
+
border: "none",
|
|
659
|
+
color: componentHeadingStyle?.color || "#323B4B",
|
|
660
|
+
cursor: "pointer",
|
|
661
|
+
padding: isMobile ? "2px" : "4px",
|
|
662
|
+
display: "flex",
|
|
663
|
+
alignItems: "center",
|
|
664
|
+
justifyContent: "center",
|
|
643
665
|
}}
|
|
666
|
+
aria-label="Close modal"
|
|
644
667
|
>
|
|
645
|
-
|
|
646
|
-
|
|
668
|
+
<svg
|
|
669
|
+
width={isMobile ? "18" : "20"}
|
|
670
|
+
height={isMobile ? "18" : "20"}
|
|
671
|
+
viewBox="0 0 20 20"
|
|
672
|
+
fill="none"
|
|
673
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
674
|
+
aria-hidden="true"
|
|
675
|
+
>
|
|
676
|
+
<path
|
|
677
|
+
d="M15 5L5 15M5 5L15 15"
|
|
678
|
+
stroke="currentColor"
|
|
679
|
+
strokeWidth="2"
|
|
680
|
+
strokeLinecap="round"
|
|
681
|
+
strokeLinejoin="round"
|
|
682
|
+
/>
|
|
683
|
+
</svg>
|
|
684
|
+
</button>
|
|
647
685
|
</div>
|
|
648
|
-
<button
|
|
649
|
-
onClick={onClose}
|
|
650
|
-
style={{
|
|
651
|
-
background: "none",
|
|
652
|
-
border: "none",
|
|
653
|
-
color: componentHeadingStyle?.color || "#323B4B",
|
|
654
|
-
cursor: "pointer",
|
|
655
|
-
padding: isMobile ? "2px" : "4px",
|
|
656
|
-
display: "flex",
|
|
657
|
-
alignItems: "center",
|
|
658
|
-
justifyContent: "center",
|
|
659
|
-
}}
|
|
660
|
-
aria-label="Close modal"
|
|
661
|
-
>
|
|
662
|
-
<svg
|
|
663
|
-
width={isMobile ? "18" : "20"}
|
|
664
|
-
height={isMobile ? "18" : "20"}
|
|
665
|
-
viewBox="0 0 20 20"
|
|
666
|
-
fill="none"
|
|
667
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
668
|
-
aria-hidden="true"
|
|
669
|
-
>
|
|
670
|
-
<path
|
|
671
|
-
d="M15 5L5 15M5 5L15 15"
|
|
672
|
-
stroke="currentColor"
|
|
673
|
-
strokeWidth="2"
|
|
674
|
-
strokeLinecap="round"
|
|
675
|
-
strokeLinejoin="round"
|
|
676
|
-
/>
|
|
677
|
-
</svg>
|
|
678
|
-
</button>
|
|
679
|
-
</div>
|
|
680
686
|
|
|
681
|
-
|
|
682
|
-
style={{
|
|
683
|
-
...styles.middleSection,
|
|
684
|
-
...responsiveStyles.middleSection,
|
|
685
|
-
...componentTextStyle,
|
|
686
|
-
}}
|
|
687
|
-
>
|
|
688
|
-
<p
|
|
687
|
+
<div
|
|
689
688
|
style={{
|
|
690
|
-
...styles.
|
|
691
|
-
...responsiveStyles.
|
|
689
|
+
...styles.middleSection,
|
|
690
|
+
...responsiveStyles.middleSection,
|
|
692
691
|
...componentTextStyle,
|
|
693
|
-
marginBottom: "16px",
|
|
694
692
|
}}
|
|
695
693
|
>
|
|
696
|
-
|
|
697
|
-
|
|
694
|
+
<p
|
|
695
|
+
style={{
|
|
696
|
+
...styles.privacyText,
|
|
697
|
+
...responsiveStyles.privacyText,
|
|
698
|
+
...componentTextStyle,
|
|
699
|
+
marginBottom: "16px",
|
|
700
|
+
}}
|
|
701
|
+
>
|
|
702
|
+
To proceed with this consent form, we need to verify your age.
|
|
703
|
+
</p>
|
|
698
704
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
705
|
+
<p
|
|
706
|
+
style={{
|
|
707
|
+
...styles.subTitle,
|
|
708
|
+
...componentTextStyle,
|
|
709
|
+
marginBottom: "24px",
|
|
710
|
+
textAlign: "left",
|
|
711
|
+
}}
|
|
712
|
+
>
|
|
713
|
+
Are you 18 years of age or older?
|
|
714
|
+
</p>
|
|
715
|
+
</div>
|
|
710
716
|
|
|
711
|
-
|
|
712
|
-
style={{
|
|
713
|
-
...styles.bottomSection,
|
|
714
|
-
...responsiveStyles.bottomSection,
|
|
715
|
-
...componentTextStyle,
|
|
716
|
-
}}
|
|
717
|
-
>
|
|
718
|
-
<button
|
|
719
|
-
style={{
|
|
720
|
-
...styles.button,
|
|
721
|
-
...responsiveStyles.button,
|
|
722
|
-
...styles.acceptButton,
|
|
723
|
-
...acceptButtonStyle,
|
|
724
|
-
}}
|
|
725
|
-
onClick={onYes}
|
|
726
|
-
type="button"
|
|
727
|
-
aria-label="Confirm that I am 18 years of age or older"
|
|
728
|
-
>
|
|
729
|
-
Yes, I am 18 or older
|
|
730
|
-
</button>
|
|
731
|
-
<button
|
|
717
|
+
<div
|
|
732
718
|
style={{
|
|
733
|
-
...styles.
|
|
734
|
-
...responsiveStyles.
|
|
735
|
-
...
|
|
736
|
-
...declineButtonStyle,
|
|
719
|
+
...styles.bottomSection,
|
|
720
|
+
...responsiveStyles.bottomSection,
|
|
721
|
+
...componentTextStyle,
|
|
737
722
|
}}
|
|
738
|
-
onClick={onNo}
|
|
739
|
-
type="button"
|
|
740
|
-
aria-label="Indicate that I am under 18 years of age"
|
|
741
723
|
>
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
724
|
+
<button
|
|
725
|
+
style={{
|
|
726
|
+
...styles.button,
|
|
727
|
+
...responsiveStyles.button,
|
|
728
|
+
...styles.acceptButton,
|
|
729
|
+
...acceptButtonStyle,
|
|
730
|
+
}}
|
|
731
|
+
onClick={onYes}
|
|
732
|
+
type="button"
|
|
733
|
+
aria-label="Confirm that I am 18 years of age or older"
|
|
734
|
+
>
|
|
735
|
+
Yes, I am 18 or older
|
|
736
|
+
</button>
|
|
737
|
+
<button
|
|
738
|
+
style={{
|
|
739
|
+
...styles.button,
|
|
740
|
+
...responsiveStyles.button,
|
|
741
|
+
...styles.cancelButton,
|
|
742
|
+
...declineButtonStyle,
|
|
743
|
+
}}
|
|
744
|
+
onClick={onNo}
|
|
745
|
+
type="button"
|
|
746
|
+
aria-label="Indicate that I am under 18 years of age"
|
|
747
|
+
>
|
|
748
|
+
No, I am under 18
|
|
749
|
+
</button>
|
|
750
|
+
</div>
|
|
751
|
+
</>
|
|
752
|
+
);
|
|
753
|
+
};
|
|
748
754
|
|
|
749
755
|
// =============================================================================
|
|
750
756
|
// MAIN COMPONENT
|
|
@@ -770,6 +776,8 @@ export const RedactoNoticeConsent = ({
|
|
|
770
776
|
settings,
|
|
771
777
|
applicationId,
|
|
772
778
|
validateAgainst = "all",
|
|
779
|
+
includeFullyConsentedData = false,
|
|
780
|
+
reviewModeButtonText = "Continue",
|
|
773
781
|
}: Props) => {
|
|
774
782
|
// =============================================================================
|
|
775
783
|
// PROP VALIDATION
|
|
@@ -838,6 +846,8 @@ export const RedactoNoticeConsent = ({
|
|
|
838
846
|
Record<string, boolean>
|
|
839
847
|
>({});
|
|
840
848
|
const [hasAlreadyConsented, setHasAlreadyConsented] = useState(false);
|
|
849
|
+
const [isFullyConsentedReviewMode, setIsFullyConsentedReviewMode] =
|
|
850
|
+
useState(false);
|
|
841
851
|
|
|
842
852
|
// UI states
|
|
843
853
|
const [isLanguageDropdownOpen, setIsLanguageDropdownOpen] = useState(false);
|
|
@@ -1058,6 +1068,12 @@ export const RedactoNoticeConsent = ({
|
|
|
1058
1068
|
}
|
|
1059
1069
|
}, [hasAlreadyConsented, onAccept]);
|
|
1060
1070
|
|
|
1071
|
+
const isDefaultLanguage = (lang: string): boolean => {
|
|
1072
|
+
if (!content) return true;
|
|
1073
|
+
if (lang === "English" || lang === "en" || lang === "EN") return true;
|
|
1074
|
+
return false;
|
|
1075
|
+
};
|
|
1076
|
+
|
|
1061
1077
|
const getTranslatedText = (
|
|
1062
1078
|
key: string,
|
|
1063
1079
|
defaultText: string,
|
|
@@ -1065,8 +1081,8 @@ export const RedactoNoticeConsent = ({
|
|
|
1065
1081
|
): string => {
|
|
1066
1082
|
if (!content) return defaultText;
|
|
1067
1083
|
|
|
1068
|
-
// If
|
|
1069
|
-
if (selectedLanguage
|
|
1084
|
+
// If default language is selected, use the base content from active_config
|
|
1085
|
+
if (isDefaultLanguage(selectedLanguage)) {
|
|
1070
1086
|
// Check for specific keys in content object
|
|
1071
1087
|
if (
|
|
1072
1088
|
key === "privacy_policy_anchor_text" &&
|
|
@@ -1110,7 +1126,16 @@ export const RedactoNoticeConsent = ({
|
|
|
1110
1126
|
|
|
1111
1127
|
if (itemId) {
|
|
1112
1128
|
if (key === "purposes.name" || key === "purposes.description") {
|
|
1113
|
-
|
|
1129
|
+
const purposeData = translationMap.purposes?.[itemId];
|
|
1130
|
+
if (purposeData) {
|
|
1131
|
+
if (typeof purposeData === "string") {
|
|
1132
|
+
return key === "purposes.name" ? purposeData : defaultText;
|
|
1133
|
+
}
|
|
1134
|
+
return key === "purposes.name"
|
|
1135
|
+
? purposeData.name || defaultText
|
|
1136
|
+
: purposeData.description || defaultText;
|
|
1137
|
+
}
|
|
1138
|
+
return defaultText;
|
|
1114
1139
|
}
|
|
1115
1140
|
if (key.startsWith("data_elements.")) {
|
|
1116
1141
|
return translationMap.data_elements?.[itemId] || defaultText;
|
|
@@ -1118,13 +1143,37 @@ export const RedactoNoticeConsent = ({
|
|
|
1118
1143
|
}
|
|
1119
1144
|
|
|
1120
1145
|
const value = translationMap[key];
|
|
1121
|
-
if (typeof value === "string") {
|
|
1146
|
+
if (typeof value === "string" && value) {
|
|
1122
1147
|
return value;
|
|
1123
1148
|
}
|
|
1124
1149
|
|
|
1125
1150
|
return defaultText;
|
|
1126
1151
|
};
|
|
1127
1152
|
|
|
1153
|
+
const getTranslatedDpoText = (
|
|
1154
|
+
key: string,
|
|
1155
|
+
defaultText: string
|
|
1156
|
+
): string => {
|
|
1157
|
+
if (!content?.dpo_info) return defaultText;
|
|
1158
|
+
|
|
1159
|
+
if (isDefaultLanguage(selectedLanguage)) {
|
|
1160
|
+
return (content.dpo_info as Record<string, string>)[key] || defaultText;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
if (!content.supported_languages_and_translations) return defaultText;
|
|
1164
|
+
|
|
1165
|
+
const translationMap = content.supported_languages_and_translations[
|
|
1166
|
+
selectedLanguage
|
|
1167
|
+
] as TranslationObject | undefined;
|
|
1168
|
+
|
|
1169
|
+
if (translationMap?.dpo_info) {
|
|
1170
|
+
const translated = (translationMap.dpo_info as Record<string, string | undefined>)[key];
|
|
1171
|
+
if (translated) return translated;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
return (content.dpo_info as Record<string, string>)[key] || defaultText;
|
|
1175
|
+
};
|
|
1176
|
+
|
|
1128
1177
|
// =============================================================================
|
|
1129
1178
|
// EVENT HANDLERS
|
|
1130
1179
|
// =============================================================================
|
|
@@ -1161,6 +1210,7 @@ export const RedactoNoticeConsent = ({
|
|
|
1161
1210
|
language,
|
|
1162
1211
|
specific_uuid: applicationId,
|
|
1163
1212
|
validate_against: validateAgainst,
|
|
1213
|
+
include_fully_consented_data: includeFullyConsentedData,
|
|
1164
1214
|
signal: abortControllerRef.current?.signal,
|
|
1165
1215
|
});
|
|
1166
1216
|
const activeConfig = consentContentData.detail.active_config;
|
|
@@ -1173,12 +1223,12 @@ export const RedactoNoticeConsent = ({
|
|
|
1173
1223
|
);
|
|
1174
1224
|
}
|
|
1175
1225
|
const initialCollapsedState = activeConfig.purposes.reduce(
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1226
|
+
(acc, purpose) => ({
|
|
1227
|
+
...acc,
|
|
1228
|
+
[purpose.uuid]: true,
|
|
1229
|
+
}),
|
|
1230
|
+
{}
|
|
1231
|
+
);
|
|
1182
1232
|
setCollapsedPurposes(initialCollapsedState);
|
|
1183
1233
|
|
|
1184
1234
|
// Initialize selected states - handle reconsent pre-population
|
|
@@ -1188,28 +1238,28 @@ export const RedactoNoticeConsent = ({
|
|
|
1188
1238
|
// Categorize purposes for reconsent UI
|
|
1189
1239
|
const newCategorizedPurposes = isReconsentRequired
|
|
1190
1240
|
? {
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1241
|
+
alreadyConsented: activeConfig.purposes.filter(
|
|
1242
|
+
(purpose) => {
|
|
1243
|
+
const selection = purposeSelections?.[purpose.uuid];
|
|
1244
|
+
return (
|
|
1245
|
+
selection &&
|
|
1246
|
+
(selection.status === "ACTIVE" ||
|
|
1247
|
+
selection.status === "EXPIRED") &&
|
|
1248
|
+
!selection.needs_reconsent
|
|
1249
|
+
);
|
|
1250
|
+
}
|
|
1251
|
+
),
|
|
1252
|
+
needsConsent: activeConfig.purposes.filter(
|
|
1253
|
+
(purpose) => {
|
|
1254
|
+
const selection = purposeSelections?.[purpose.uuid];
|
|
1255
|
+
return (
|
|
1256
|
+
!selection ||
|
|
1257
|
+
selection.status !== "ACTIVE" ||
|
|
1258
|
+
selection.needs_reconsent
|
|
1259
|
+
);
|
|
1260
|
+
}
|
|
1261
|
+
),
|
|
1262
|
+
}
|
|
1213
1263
|
: null;
|
|
1214
1264
|
|
|
1215
1265
|
setCategorizedPurposes(newCategorizedPurposes);
|
|
@@ -1217,8 +1267,8 @@ export const RedactoNoticeConsent = ({
|
|
|
1217
1267
|
const initialPurposeState: Record<string, boolean> =
|
|
1218
1268
|
activeConfig.purposes.reduce(
|
|
1219
1269
|
(acc: Record<string, boolean>, purpose) => {
|
|
1220
|
-
if (
|
|
1221
|
-
// Pre-populate with existing selection
|
|
1270
|
+
if (purposeSelections?.[purpose.uuid]) {
|
|
1271
|
+
// Pre-populate with existing selection (reconsent or magic link flow)
|
|
1222
1272
|
acc[purpose.uuid] = purposeSelections[purpose.uuid].selected;
|
|
1223
1273
|
} else {
|
|
1224
1274
|
// Default to false for new consent
|
|
@@ -1236,10 +1286,9 @@ export const RedactoNoticeConsent = ({
|
|
|
1236
1286
|
purpose.data_elements.forEach((element) => {
|
|
1237
1287
|
const combinedId = `${purpose.uuid}-${element.uuid}`;
|
|
1238
1288
|
if (
|
|
1239
|
-
isReconsentRequired &&
|
|
1240
1289
|
purposeSelections?.[purpose.uuid]?.data_elements?.[element.uuid]
|
|
1241
1290
|
) {
|
|
1242
|
-
// Pre-populate with existing selection
|
|
1291
|
+
// Pre-populate with existing selection (reconsent or magic link flow)
|
|
1243
1292
|
acc[combinedId] =
|
|
1244
1293
|
purposeSelections[purpose.uuid].data_elements[
|
|
1245
1294
|
element.uuid
|
|
@@ -1256,6 +1305,7 @@ export const RedactoNoticeConsent = ({
|
|
|
1256
1305
|
setSelectedDataElements(initialDataElementState);
|
|
1257
1306
|
// Store initial selections for already-consented purposes to track modifications
|
|
1258
1307
|
if (isReconsentRequired && newCategorizedPurposes) {
|
|
1308
|
+
setIsFullyConsentedReviewMode(false);
|
|
1259
1309
|
const initialSelections: Record<string, boolean> = {};
|
|
1260
1310
|
newCategorizedPurposes.alreadyConsented.forEach((purpose) => {
|
|
1261
1311
|
purpose.data_elements.forEach((element) => {
|
|
@@ -1272,7 +1322,39 @@ export const RedactoNoticeConsent = ({
|
|
|
1272
1322
|
});
|
|
1273
1323
|
});
|
|
1274
1324
|
setInitialDataElementSelections(initialSelections);
|
|
1325
|
+
} else if (includeFullyConsentedData && purposeSelections) {
|
|
1326
|
+
// Fully consented review mode: all purposes show green ticks, need initialDataElementSelections
|
|
1327
|
+
const allPurposesSelected =
|
|
1328
|
+
activeConfig.purposes.length > 0 &&
|
|
1329
|
+
activeConfig.purposes.every(
|
|
1330
|
+
(p) => purposeSelections[p.uuid]?.selected === true
|
|
1331
|
+
);
|
|
1332
|
+
setIsFullyConsentedReviewMode(allPurposesSelected);
|
|
1333
|
+
if (allPurposesSelected) {
|
|
1334
|
+
const initialSelections: Record<string, boolean> = {};
|
|
1335
|
+
activeConfig.purposes.forEach(
|
|
1336
|
+
(purpose) => {
|
|
1337
|
+
purpose.data_elements.forEach((element) => {
|
|
1338
|
+
const combinedId = `${purpose.uuid}-${element.uuid}`;
|
|
1339
|
+
if (
|
|
1340
|
+
purposeSelections[purpose.uuid]?.data_elements?.[element.uuid]
|
|
1341
|
+
) {
|
|
1342
|
+
initialSelections[combinedId] =
|
|
1343
|
+
purposeSelections[purpose.uuid].data_elements[element.uuid]
|
|
1344
|
+
.selected;
|
|
1345
|
+
} else {
|
|
1346
|
+
initialSelections[combinedId] = false;
|
|
1347
|
+
}
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
);
|
|
1351
|
+
setInitialDataElementSelections(initialSelections);
|
|
1352
|
+
} else {
|
|
1353
|
+
setIsFullyConsentedReviewMode(false);
|
|
1354
|
+
setInitialDataElementSelections({});
|
|
1355
|
+
}
|
|
1275
1356
|
} else {
|
|
1357
|
+
setIsFullyConsentedReviewMode(false);
|
|
1276
1358
|
setInitialDataElementSelections({});
|
|
1277
1359
|
}
|
|
1278
1360
|
|
|
@@ -1315,6 +1397,7 @@ export const RedactoNoticeConsent = ({
|
|
|
1315
1397
|
language,
|
|
1316
1398
|
applicationId,
|
|
1317
1399
|
validateAgainst,
|
|
1400
|
+
includeFullyConsentedData,
|
|
1318
1401
|
// Removed onError and isRefreshingToken from dependencies to prevent duplicate API calls
|
|
1319
1402
|
// onError is only called on errors, not needed for initial data fetching
|
|
1320
1403
|
]);
|
|
@@ -1453,7 +1536,7 @@ export const RedactoNoticeConsent = ({
|
|
|
1453
1536
|
selected: element.required
|
|
1454
1537
|
? true
|
|
1455
1538
|
: selectedDataElements[`${purpose.uuid}-${element.uuid}`] ||
|
|
1456
|
-
|
|
1539
|
+
false,
|
|
1457
1540
|
})),
|
|
1458
1541
|
};
|
|
1459
1542
|
}),
|
|
@@ -1711,9 +1794,9 @@ export const RedactoNoticeConsent = ({
|
|
|
1711
1794
|
// Determine which purposes to include based on UI mode
|
|
1712
1795
|
const purposesToProcess = categorizedPurposes
|
|
1713
1796
|
? [
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1797
|
+
...categorizedPurposes.alreadyConsented,
|
|
1798
|
+
...categorizedPurposes.needsConsent,
|
|
1799
|
+
]
|
|
1717
1800
|
: content.purposes || [];
|
|
1718
1801
|
|
|
1719
1802
|
purposesToProcess.forEach((purpose) => {
|
|
@@ -1743,12 +1826,32 @@ export const RedactoNoticeConsent = ({
|
|
|
1743
1826
|
}
|
|
1744
1827
|
});
|
|
1745
1828
|
|
|
1746
|
-
// 7.
|
|
1829
|
+
// 7. Additional text audio
|
|
1830
|
+
if (content.additional_text && ttsAudioUrls.additional_text_audio_url) {
|
|
1831
|
+
sequence.push(ttsAudioUrls.additional_text_audio_url);
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
// 8. Privacy center anchor text audio
|
|
1835
|
+
if (content.privacy_center_url && ttsAudioUrls.privacy_center_anchor_text_audio_url) {
|
|
1836
|
+
sequence.push(ttsAudioUrls.privacy_center_anchor_text_audio_url);
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
// 9. DPO info audio
|
|
1840
|
+
if (content.dpo_info && ttsAudioUrls.dpo_info_audio) {
|
|
1841
|
+
const dpoAudio = ttsAudioUrls.dpo_info_audio;
|
|
1842
|
+
DPO_AUDIO_ELEMENT_MAP.forEach(({ audioKey }) => {
|
|
1843
|
+
if (dpoAudio[audioKey]) {
|
|
1844
|
+
sequence.push(dpoAudio[audioKey]);
|
|
1845
|
+
}
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
// 10. Confirm button text audio
|
|
1747
1850
|
if (ttsAudioUrls.confirm_button_text_audio_url) {
|
|
1748
1851
|
sequence.push(ttsAudioUrls.confirm_button_text_audio_url);
|
|
1749
1852
|
}
|
|
1750
1853
|
|
|
1751
|
-
//
|
|
1854
|
+
// 11. Decline button text audio
|
|
1752
1855
|
if (ttsAudioUrls.decline_button_text_audio_url) {
|
|
1753
1856
|
sequence.push(ttsAudioUrls.decline_button_text_audio_url);
|
|
1754
1857
|
}
|
|
@@ -1790,9 +1893,9 @@ export const RedactoNoticeConsent = ({
|
|
|
1790
1893
|
// 6. Purposes and data elements
|
|
1791
1894
|
const purposesToProcess = categorizedPurposes
|
|
1792
1895
|
? [
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1896
|
+
...categorizedPurposes.alreadyConsented,
|
|
1897
|
+
...categorizedPurposes.needsConsent,
|
|
1898
|
+
]
|
|
1796
1899
|
: content.purposes || [];
|
|
1797
1900
|
|
|
1798
1901
|
purposesToProcess.forEach((purpose) => {
|
|
@@ -1818,12 +1921,32 @@ export const RedactoNoticeConsent = ({
|
|
|
1818
1921
|
}
|
|
1819
1922
|
});
|
|
1820
1923
|
|
|
1821
|
-
// 7.
|
|
1924
|
+
// 7. Additional text audio
|
|
1925
|
+
if (content.additional_text && ttsAudioUrls.additional_text_audio_url) {
|
|
1926
|
+
elementIds.push("additional-text");
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
// 8. Privacy center anchor text audio
|
|
1930
|
+
if (content.privacy_center_url && ttsAudioUrls.privacy_center_anchor_text_audio_url) {
|
|
1931
|
+
elementIds.push("privacy-center-link");
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
// 9. DPO info audio
|
|
1935
|
+
if (content.dpo_info && ttsAudioUrls.dpo_info_audio) {
|
|
1936
|
+
const dpoAudio = ttsAudioUrls.dpo_info_audio;
|
|
1937
|
+
DPO_AUDIO_ELEMENT_MAP.forEach(({ audioKey, elementId }) => {
|
|
1938
|
+
if (dpoAudio[audioKey]) {
|
|
1939
|
+
elementIds.push(elementId);
|
|
1940
|
+
}
|
|
1941
|
+
});
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
// 10. Confirm button text audio
|
|
1822
1945
|
if (ttsAudioUrls.confirm_button_text_audio_url) {
|
|
1823
1946
|
elementIds.push("confirm-button-text");
|
|
1824
1947
|
}
|
|
1825
1948
|
|
|
1826
|
-
//
|
|
1949
|
+
// 11. Decline button text audio
|
|
1827
1950
|
if (ttsAudioUrls.decline_button_text_audio_url) {
|
|
1828
1951
|
elementIds.push("decline-button-text");
|
|
1829
1952
|
}
|
|
@@ -1945,9 +2068,9 @@ export const RedactoNoticeConsent = ({
|
|
|
1945
2068
|
// Find which purpose we're on (rough estimate)
|
|
1946
2069
|
const purposesToProcess = categorizedPurposes
|
|
1947
2070
|
? [
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
2071
|
+
...categorizedPurposes.alreadyConsented,
|
|
2072
|
+
...categorizedPurposes.needsConsent,
|
|
2073
|
+
]
|
|
1951
2074
|
: content.purposes || [];
|
|
1952
2075
|
|
|
1953
2076
|
let audioOffset = headerAudioCount;
|
|
@@ -2016,9 +2139,9 @@ export const RedactoNoticeConsent = ({
|
|
|
2016
2139
|
rect.top >= 0 &&
|
|
2017
2140
|
rect.left >= 0 &&
|
|
2018
2141
|
rect.bottom <=
|
|
2019
|
-
|
|
2142
|
+
(window.innerHeight || document.documentElement.clientHeight) &&
|
|
2020
2143
|
rect.right <=
|
|
2021
|
-
|
|
2144
|
+
(window.innerWidth || document.documentElement.clientWidth);
|
|
2022
2145
|
|
|
2023
2146
|
if (!isVisible) {
|
|
2024
2147
|
element.scrollIntoView({
|
|
@@ -2658,7 +2781,7 @@ export const RedactoNoticeConsent = ({
|
|
|
2658
2781
|
{getTranslatedText(
|
|
2659
2782
|
"privacy_policy_anchor_text",
|
|
2660
2783
|
content?.privacy_policy_anchor_text ||
|
|
2661
|
-
|
|
2784
|
+
"Privacy Policy"
|
|
2662
2785
|
)}
|
|
2663
2786
|
</a>
|
|
2664
2787
|
</span>
|
|
@@ -2739,12 +2862,142 @@ export const RedactoNoticeConsent = ({
|
|
|
2739
2862
|
onPurposeCollapse={togglePurposeCollapse}
|
|
2740
2863
|
onDataElementToggle={handleDataElementCheckboxChange}
|
|
2741
2864
|
getTranslatedText={getTranslatedText}
|
|
2742
|
-
isAlreadyConsented={
|
|
2865
|
+
isAlreadyConsented={isFullyConsentedReviewMode}
|
|
2743
2866
|
selectedLanguage={selectedLanguage}
|
|
2867
|
+
initialDataElementSelections={initialDataElementSelections}
|
|
2744
2868
|
/>
|
|
2745
2869
|
))}
|
|
2746
2870
|
</div>
|
|
2747
2871
|
)}
|
|
2872
|
+
|
|
2873
|
+
{(content?.privacy_center_url || content?.dpo_info) && (
|
|
2874
|
+
<div
|
|
2875
|
+
style={{
|
|
2876
|
+
...styles.privacyText,
|
|
2877
|
+
...responsiveStyles.privacyText,
|
|
2878
|
+
...componentTextStyle,
|
|
2879
|
+
fontSize: isMobile ? "13px" : "14px",
|
|
2880
|
+
lineHeight: "1.4",
|
|
2881
|
+
borderTop: "1px solid rgba(152, 162, 179, 0.3)",
|
|
2882
|
+
paddingTop: "12px",
|
|
2883
|
+
marginTop: "12px",
|
|
2884
|
+
}}
|
|
2885
|
+
>
|
|
2886
|
+
<p style={{ margin: 0, opacity: 0.6 }}>
|
|
2887
|
+
{content.privacy_center_url && (
|
|
2888
|
+
<>
|
|
2889
|
+
{content.additional_text && (
|
|
2890
|
+
<span id="additional-text">
|
|
2891
|
+
{getTranslatedText(
|
|
2892
|
+
"additional_text",
|
|
2893
|
+
content.additional_text
|
|
2894
|
+
)}
|
|
2895
|
+
</span>
|
|
2896
|
+
)}{" "}
|
|
2897
|
+
<a
|
|
2898
|
+
id="privacy-center-link"
|
|
2899
|
+
style={{ ...styles.link, ...linkStyle }}
|
|
2900
|
+
href={content.privacy_center_url}
|
|
2901
|
+
target="_blank"
|
|
2902
|
+
rel="noopener noreferrer"
|
|
2903
|
+
>
|
|
2904
|
+
{getTranslatedText(
|
|
2905
|
+
"privacy_center_anchor_text",
|
|
2906
|
+
content.privacy_center_anchor_text || "Privacy Center"
|
|
2907
|
+
)}
|
|
2908
|
+
</a>
|
|
2909
|
+
.
|
|
2910
|
+
</>
|
|
2911
|
+
)}
|
|
2912
|
+
{content.dpo_info && (
|
|
2913
|
+
<>
|
|
2914
|
+
{content.privacy_center_url && <br />}
|
|
2915
|
+
<span id="dpo-grievance-text">
|
|
2916
|
+
{getTranslatedDpoText(
|
|
2917
|
+
"grievance_text",
|
|
2918
|
+
content.dpo_info.grievance_text || ""
|
|
2919
|
+
)}
|
|
2920
|
+
</span>{" "}
|
|
2921
|
+
{content.dpo_info.grievance_url && (
|
|
2922
|
+
<a
|
|
2923
|
+
id="dpo-grievance-link"
|
|
2924
|
+
style={{ ...styles.link, ...linkStyle }}
|
|
2925
|
+
href={content.dpo_info.grievance_url}
|
|
2926
|
+
target="_blank"
|
|
2927
|
+
rel="noopener noreferrer"
|
|
2928
|
+
>
|
|
2929
|
+
{getTranslatedDpoText(
|
|
2930
|
+
"grievance_anchor_text",
|
|
2931
|
+
content.dpo_info.grievance_anchor_text || "click here"
|
|
2932
|
+
)}
|
|
2933
|
+
</a>
|
|
2934
|
+
)}
|
|
2935
|
+
{content.dpo_info.grievance_email && (
|
|
2936
|
+
<>
|
|
2937
|
+
{" "}
|
|
2938
|
+
<span id="dpo-grievance-email-connector">
|
|
2939
|
+
{getTranslatedDpoText(
|
|
2940
|
+
"grievance_email_connector_text",
|
|
2941
|
+
content.dpo_info.grievance_email_connector_text || "or email to"
|
|
2942
|
+
)}
|
|
2943
|
+
</span>
|
|
2944
|
+
{" "}
|
|
2945
|
+
<a
|
|
2946
|
+
id="dpo-grievance-email"
|
|
2947
|
+
style={{ ...styles.link, ...linkStyle }}
|
|
2948
|
+
href={`mailto:${content.dpo_info.grievance_email}`}
|
|
2949
|
+
>
|
|
2950
|
+
{content.dpo_info.grievance_email}
|
|
2951
|
+
</a>
|
|
2952
|
+
</>
|
|
2953
|
+
)}
|
|
2954
|
+
<br />
|
|
2955
|
+
<span id="dpo-dp-board-text">
|
|
2956
|
+
{getTranslatedDpoText(
|
|
2957
|
+
"dp_board_text",
|
|
2958
|
+
content.dpo_info.dp_board_text || ""
|
|
2959
|
+
)}
|
|
2960
|
+
</span>{" "}
|
|
2961
|
+
{content.dpo_info.dp_board_url && (
|
|
2962
|
+
<a
|
|
2963
|
+
id="dpo-dp-board-link"
|
|
2964
|
+
style={{ ...styles.link, ...linkStyle }}
|
|
2965
|
+
href={content.dpo_info.dp_board_url}
|
|
2966
|
+
target="_blank"
|
|
2967
|
+
rel="noopener noreferrer"
|
|
2968
|
+
>
|
|
2969
|
+
{getTranslatedDpoText(
|
|
2970
|
+
"dp_board_anchor_text",
|
|
2971
|
+
content.dpo_info.dp_board_anchor_text || "here"
|
|
2972
|
+
)}
|
|
2973
|
+
</a>
|
|
2974
|
+
)}
|
|
2975
|
+
<br />
|
|
2976
|
+
<span id="dpo-dpo-text">
|
|
2977
|
+
{getTranslatedDpoText(
|
|
2978
|
+
"dpo_text",
|
|
2979
|
+
content.dpo_info.dpo_text || ""
|
|
2980
|
+
)}
|
|
2981
|
+
</span>{" "}
|
|
2982
|
+
{content.dpo_info.dpo_url && (
|
|
2983
|
+
<a
|
|
2984
|
+
id="dpo-dpo-link"
|
|
2985
|
+
style={{ ...styles.link, ...linkStyle }}
|
|
2986
|
+
href={content.dpo_info.dpo_url}
|
|
2987
|
+
target="_blank"
|
|
2988
|
+
rel="noopener noreferrer"
|
|
2989
|
+
>
|
|
2990
|
+
{getTranslatedDpoText(
|
|
2991
|
+
"dpo_anchor_text",
|
|
2992
|
+
content.dpo_info.dpo_anchor_text || "Click here"
|
|
2993
|
+
)}
|
|
2994
|
+
</a>
|
|
2995
|
+
)}
|
|
2996
|
+
</>
|
|
2997
|
+
)}
|
|
2998
|
+
</p>
|
|
2999
|
+
</div>
|
|
3000
|
+
)}
|
|
2748
3001
|
</div>
|
|
2749
3002
|
<div
|
|
2750
3003
|
style={{
|
|
@@ -2753,53 +3006,72 @@ export const RedactoNoticeConsent = ({
|
|
|
2753
3006
|
...sectionStyle,
|
|
2754
3007
|
}}
|
|
2755
3008
|
>
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
3009
|
+
{isFullyConsentedReviewMode ? (
|
|
3010
|
+
<button
|
|
3011
|
+
ref={lastFocusableRef}
|
|
3012
|
+
id="review-mode-continue-button"
|
|
3013
|
+
style={{
|
|
3014
|
+
...styles.button,
|
|
3015
|
+
...responsiveStyles.button,
|
|
3016
|
+
...styles.acceptButton,
|
|
3017
|
+
...acceptButtonStyle,
|
|
3018
|
+
}}
|
|
3019
|
+
onClick={() => onAccept?.()}
|
|
3020
|
+
aria-label={reviewModeButtonText}
|
|
3021
|
+
>
|
|
3022
|
+
{reviewModeButtonText}
|
|
3023
|
+
</button>
|
|
3024
|
+
) : (
|
|
3025
|
+
<>
|
|
3026
|
+
<button
|
|
3027
|
+
ref={lastFocusableRef}
|
|
3028
|
+
id="confirm-button-text"
|
|
3029
|
+
style={{
|
|
3030
|
+
...styles.button,
|
|
3031
|
+
...responsiveStyles.button,
|
|
3032
|
+
...styles.acceptButton,
|
|
3033
|
+
...acceptButtonStyle,
|
|
3034
|
+
opacity: acceptDisabled ? 0.5 : 1,
|
|
3035
|
+
}}
|
|
3036
|
+
onClick={handleAccept}
|
|
3037
|
+
disabled={acceptDisabled || isSubmittingDecline}
|
|
3038
|
+
aria-label={getTranslatedText(
|
|
2776
3039
|
"confirm_button_text",
|
|
2777
3040
|
content?.confirm_button_text || "Accept"
|
|
2778
3041
|
)}
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
3042
|
+
>
|
|
3043
|
+
{isSubmittingAccept
|
|
3044
|
+
? "Processing..."
|
|
3045
|
+
: getTranslatedText(
|
|
3046
|
+
"confirm_button_text",
|
|
3047
|
+
content?.confirm_button_text || "Accept"
|
|
3048
|
+
)}
|
|
3049
|
+
</button>
|
|
3050
|
+
<button
|
|
3051
|
+
id="decline-button-text"
|
|
3052
|
+
style={{
|
|
3053
|
+
...styles.button,
|
|
3054
|
+
...responsiveStyles.button,
|
|
3055
|
+
...styles.cancelButton,
|
|
3056
|
+
...declineButtonStyle,
|
|
3057
|
+
opacity: isSubmittingAccept ? 0.5 : 1,
|
|
3058
|
+
}}
|
|
3059
|
+
onClick={handleDecline}
|
|
3060
|
+
disabled={isSubmittingDecline || isSubmittingAccept}
|
|
3061
|
+
aria-label={getTranslatedText(
|
|
2799
3062
|
"decline_button_text",
|
|
2800
3063
|
content?.decline_button_text || "Decline"
|
|
2801
3064
|
)}
|
|
2802
|
-
|
|
3065
|
+
>
|
|
3066
|
+
{isSubmittingDecline
|
|
3067
|
+
? "Processing..."
|
|
3068
|
+
: getTranslatedText(
|
|
3069
|
+
"decline_button_text",
|
|
3070
|
+
content?.decline_button_text || "Decline"
|
|
3071
|
+
)}
|
|
3072
|
+
</button>
|
|
3073
|
+
</>
|
|
3074
|
+
)}
|
|
2803
3075
|
</div>
|
|
2804
3076
|
</div>
|
|
2805
3077
|
)}
|