@redacto.io/consent-sdk-react 0.0.1 → 0.0.3

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.
@@ -7,14 +7,14 @@ import type {
7
7
  SubmitConsentEventParams,
8
8
  } from "./types";
9
9
 
10
- const BASE_URL = "https://api.redacto.tech";
11
- const BACKEND_URL = "http://localhost:3000";
10
+ const BASE_URL = "https://api.redacto.tech/consent";
12
11
 
13
12
  export const fetchConsentContent = async ({
14
13
  noticeId,
15
14
  accessToken,
16
- refreshToken,
15
+ baseUrl,
17
16
  language = "en",
17
+ specific_uuid,
18
18
  }: FetchConsentContentParams): Promise<ConsentContent> => {
19
19
  try {
20
20
  const decodedToken = jwtDecode(accessToken) as RedactoJwtPayload;
@@ -22,46 +22,31 @@ export const fetchConsentContent = async ({
22
22
  const ORGANISATION_UUID = decodedToken?.organisation_uuid;
23
23
  const WORKSPACE_UUID = decodedToken?.workspace_uuid;
24
24
 
25
- const response = await fetch(
26
- `${BASE_URL}/consent/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/notices/${noticeId}`,
27
- {
28
- method: "GET",
29
- headers: {
30
- Authorization: `Bearer ${accessToken}`,
31
- "Accept-Language": language,
32
- "Content-Type": "application/json",
33
- },
34
- }
25
+ const apiBaseUrl = baseUrl || BASE_URL;
26
+
27
+ const url = new URL(
28
+ `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/notices/${noticeId}`
35
29
  );
36
30
 
31
+ if (specific_uuid) {
32
+ url.searchParams.append("specific_uuid", specific_uuid);
33
+ }
34
+
35
+ const response = await fetch(url.toString(), {
36
+ method: "GET",
37
+ headers: {
38
+ Authorization: `Bearer ${accessToken}`,
39
+ "Accept-Language": language,
40
+ "Content-Type": "application/json",
41
+ },
42
+ });
43
+
37
44
  if (response.status === 401) {
38
- try {
39
- const response = await fetch(
40
- `${BACKEND_URL}/api/consent/refresh-token`,
41
- {
42
- method: "POST",
43
- headers: {
44
- "Content-Type": "application/json",
45
- },
46
- body: JSON.stringify({
47
- refreshToken,
48
- }),
49
- }
50
- );
51
-
52
- if (!response.ok) {
53
- throw new Error("Failed to get consent tokens");
54
- }
55
-
56
- const data = await response.json();
57
-
58
- localStorage.setItem("accessToken", data.token);
59
- localStorage.setItem("refreshToken", data.refresh_token);
60
- localStorage.setItem("tokenExpiresAt", data.expires_at);
61
- } catch (error) {
62
- console.error("Failed to setup consent:", error);
63
- throw error;
64
- }
45
+ const error = new Error("Unauthorized") as Error & {
46
+ status?: number;
47
+ };
48
+ error.status = 401;
49
+ throw error;
65
50
  }
66
51
 
67
52
  if (response.status === 409) {
@@ -88,9 +73,11 @@ export const fetchConsentContent = async ({
88
73
 
89
74
  export const submitConsentEvent = async ({
90
75
  accessToken,
76
+ baseUrl,
91
77
  noticeUuid,
92
78
  purposes,
93
79
  declined,
80
+ meta_data,
94
81
  }: SubmitConsentEventParams): Promise<void> => {
95
82
  try {
96
83
  const primaryEmail = localStorage.getItem("userEmail");
@@ -108,7 +95,7 @@ export const submitConsentEvent = async ({
108
95
  uuid: purpose.uuid,
109
96
  name: purpose.name,
110
97
  description: purpose.description,
111
- industries: purpose.industries,
98
+ industries: purpose.industries || "",
112
99
  selected: purpose.selected,
113
100
  data_elements: purpose.data_elements.map((element) => ({
114
101
  uuid: element.uuid,
@@ -119,10 +106,13 @@ export const submitConsentEvent = async ({
119
106
  })),
120
107
  })),
121
108
  },
109
+ meta_data,
122
110
  };
123
111
 
112
+ const apiBaseUrl = baseUrl || BASE_URL;
113
+
124
114
  const response = await fetch(
125
- `${BASE_URL}/consent/public/consent-events/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/by-token`,
115
+ `${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/by-token`,
126
116
  {
127
117
  method: "POST",
128
118
  headers: {
@@ -41,12 +41,12 @@ export type ConsentContent = {
41
41
  uuid: string;
42
42
  name: string;
43
43
  description: string;
44
- industries: string;
44
+ industries?: string;
45
45
  data_elements: {
46
46
  uuid: string;
47
47
  name: string;
48
48
  description: string | null;
49
- industries: string | null;
49
+ industries?: string | null;
50
50
  enabled: boolean;
51
51
  required: boolean;
52
52
  }[];
@@ -114,9 +114,7 @@ export type Settings = {
114
114
  selectedTextColor?: string;
115
115
  };
116
116
  };
117
- link: {
118
- color: string;
119
- };
117
+ link: string;
120
118
  borderRadius?: string;
121
119
  backgroundColor?: string;
122
120
  headingColor?: string;
@@ -128,7 +126,9 @@ export type FetchConsentContentParams = {
128
126
  noticeId: string;
129
127
  accessToken: string;
130
128
  refreshToken: string;
129
+ baseUrl?: string;
131
130
  language?: string;
131
+ specific_uuid?: string;
132
132
  };
133
133
 
134
134
  export type ConsentEventPayload = {
@@ -143,7 +143,7 @@ export type ConsentEventPayload = {
143
143
  uuid: string;
144
144
  name: string;
145
145
  description: string;
146
- industries: string;
146
+ industries?: string;
147
147
  selected: boolean;
148
148
  data_elements: Array<{
149
149
  uuid: string;
@@ -154,6 +154,9 @@ export type ConsentEventPayload = {
154
154
  }>;
155
155
  }>;
156
156
  };
157
+ meta_data?: {
158
+ specific_uuid: string;
159
+ };
157
160
  };
158
161
 
159
162
  export type DataElement = {
@@ -168,14 +171,18 @@ export type Purpose = {
168
171
  uuid: string;
169
172
  name: string;
170
173
  description: string;
171
- industries: string;
174
+ industries?: string;
172
175
  selected: boolean;
173
176
  data_elements: Array<DataElement>;
174
177
  };
175
178
 
176
179
  export type SubmitConsentEventParams = {
177
180
  accessToken: string;
181
+ baseUrl?: string;
178
182
  noticeUuid: string;
179
183
  purposes: Array<Purpose>;
180
184
  declined: boolean;
185
+ meta_data?: {
186
+ specific_uuid: string;
187
+ };
181
188
  };
@@ -0,0 +1,102 @@
1
+ // CSS injection utility for the consent SDK
2
+ let stylesInjected = false;
3
+
4
+ export const injectCheckboxStyles = () => {
5
+ if (stylesInjected || typeof document === 'undefined') {
6
+ return;
7
+ }
8
+
9
+ const STYLE_ID = 'redacto-consent-checkbox-styles';
10
+
11
+ // Check if styles are already injected
12
+ if (document.getElementById(STYLE_ID)) {
13
+ stylesInjected = true;
14
+ return;
15
+ }
16
+
17
+ const style = document.createElement('style');
18
+ style.id = STYLE_ID;
19
+ style.textContent = `
20
+ /* Redacto Consent SDK Checkbox Styles */
21
+ .redacto-checkbox-large {
22
+ appearance: none !important;
23
+ -webkit-appearance: none !important;
24
+ -moz-appearance: none !important;
25
+ height: 20px !important;
26
+ width: 20px !important;
27
+ border: 2px solid #d0d5dd !important;
28
+ border-radius: 5px !important;
29
+ background-color: transparent !important;
30
+ cursor: pointer !important;
31
+ position: relative !important;
32
+ box-sizing: border-box !important;
33
+ margin: 0 !important;
34
+ padding: 4px !important;
35
+ }
36
+
37
+ .redacto-checkbox-small {
38
+ appearance: none !important;
39
+ -webkit-appearance: none !important;
40
+ -moz-appearance: none !important;
41
+ height: 16px !important;
42
+ width: 16px !important;
43
+ border: 2px solid #d0d5dd !important;
44
+ border-radius: 5px !important;
45
+ background-color: transparent !important;
46
+ cursor: pointer !important;
47
+ position: relative !important;
48
+ box-sizing: border-box !important;
49
+ margin: 0 !important;
50
+ padding: 4px !important;
51
+ }
52
+
53
+ .redacto-checkbox-large:checked {
54
+ background-color: #4f87ff !important;
55
+ border-color: #4f87ff !important;
56
+ }
57
+
58
+ .redacto-checkbox-small:checked {
59
+ background-color: #4f87ff !important;
60
+ border-color: #4f87ff !important;
61
+ }
62
+
63
+ .redacto-checkbox-large:checked::after {
64
+ content: '' !important;
65
+ position: absolute !important;
66
+ left: 50% !important;
67
+ top: 50% !important;
68
+ transform: translate(-50%, -50%) rotate(45deg) !important;
69
+ width: 4px !important;
70
+ height: 8px !important;
71
+ border: solid white !important;
72
+ border-width: 0 2px 2px 0 !important;
73
+ box-sizing: border-box !important;
74
+ }
75
+
76
+ .redacto-checkbox-small:checked::after {
77
+ content: '' !important;
78
+ position: absolute !important;
79
+ left: 50% !important;
80
+ top: 50% !important;
81
+ transform: translate(-50%, -50%) rotate(45deg) !important;
82
+ width: 3px !important;
83
+ height: 6px !important;
84
+ border: solid white !important;
85
+ border-width: 0 1.5px 1.5px 0 !important;
86
+ box-sizing: border-box !important;
87
+ }
88
+
89
+ .redacto-checkbox-large:focus {
90
+ outline: 2px solid #4f87ff !important;
91
+ outline-offset: 2px !important;
92
+ }
93
+
94
+ .redacto-checkbox-small:focus {
95
+ outline: 2px solid #4f87ff !important;
96
+ outline-offset: 2px !important;
97
+ }
98
+ `;
99
+
100
+ document.head.appendChild(style);
101
+ stylesInjected = true;
102
+ };
@@ -12,6 +12,9 @@ export const styles = {
12
12
  zIndex: 999,
13
13
  animation: "overlayFadeIn 0.2s cubic-bezier(0.16, 1, 0.3, 1)",
14
14
  pointerEvents: "all",
15
+ margin: 0,
16
+ padding: 0,
17
+ boxSizing: "border-box",
15
18
  },
16
19
 
17
20
  modal: {
@@ -28,6 +31,11 @@ export const styles = {
28
31
  flexDirection: "column",
29
32
  maxHeight: "90vh",
30
33
  zIndex: 1000,
34
+ margin: 0,
35
+ padding: 0,
36
+ boxSizing: "border-box",
37
+ textAlign: "left",
38
+ fontFamily: "inherit",
31
39
  },
32
40
 
33
41
  content: {
@@ -37,6 +45,8 @@ export const styles = {
37
45
  flexGrow: 1,
38
46
  overflow: "hidden",
39
47
  minHeight: 0,
48
+ boxSizing: "border-box",
49
+ textAlign: "left",
40
50
  },
41
51
 
42
52
  topSection: {
@@ -47,18 +57,27 @@ export const styles = {
47
57
  borderBottom: "1px solid #e5e7eb",
48
58
  boxShadow: "0 1px 2px rgba(0, 0, 0, 0.03)",
49
59
  backgroundColor: "#ffffff",
60
+ margin: 0,
61
+ boxSizing: "border-box",
62
+ textAlign: "left",
50
63
  },
51
64
 
52
65
  topLeft: {
53
66
  display: "flex",
54
67
  alignItems: "center",
55
68
  gap: "10px",
69
+ margin: 0,
70
+ padding: 0,
71
+ boxSizing: "border-box",
56
72
  },
57
73
 
58
74
  logo: {
59
75
  height: "32px",
60
76
  width: "auto",
61
77
  objectFit: "contain",
78
+ margin: 0,
79
+ padding: 0,
80
+ display: "block",
62
81
  },
63
82
 
64
83
  title: {
@@ -68,6 +87,9 @@ export const styles = {
68
87
  letterSpacing: "0.2px",
69
88
  verticalAlign: "middle",
70
89
  color: "#101828",
90
+ margin: 0,
91
+ padding: 0,
92
+ textAlign: "left",
71
93
  },
72
94
 
73
95
  topRight: {
@@ -84,6 +106,9 @@ export const styles = {
84
106
  verticalAlign: "middle",
85
107
  color: "#344054",
86
108
  gap: "5px",
109
+ margin: 0,
110
+ boxSizing: "border-box",
111
+ textAlign: "left",
87
112
  cursor: "pointer",
88
113
  backgroundColor: "#ffffff",
89
114
  },
@@ -97,6 +122,8 @@ export const styles = {
97
122
  overflowY: "auto",
98
123
  minHeight: 0,
99
124
  paddingRight: "15px",
125
+ boxSizing: "border-box",
126
+ textAlign: "left",
100
127
  },
101
128
 
102
129
  privacyText: {
@@ -105,11 +132,17 @@ export const styles = {
105
132
  lineHeight: "150%",
106
133
  letterSpacing: "0.2px",
107
134
  color: "#344054",
135
+ margin: 0,
136
+ padding: 0,
137
+ textAlign: "left",
108
138
  },
109
139
 
110
140
  link: {
111
141
  color: "#4f87ff",
112
142
  textDecoration: "none",
143
+ margin: 0,
144
+ padding: 0,
145
+ boxSizing: "border-box",
113
146
  },
114
147
 
115
148
  subTitle: {
@@ -118,18 +151,27 @@ export const styles = {
118
151
  lineHeight: "150%",
119
152
  letterSpacing: "0.2px",
120
153
  color: "#101828",
154
+ margin: 0,
155
+ padding: 0,
156
+ textAlign: "left",
121
157
  },
122
158
 
123
159
  optionsContainer: {
124
160
  display: "flex",
125
161
  flexDirection: "column",
126
162
  gap: "14px",
163
+ margin: 0,
164
+ padding: 0,
165
+ boxSizing: "border-box",
127
166
  },
128
167
 
129
168
  optionItem: {
130
169
  display: "flex",
131
170
  justifyContent: "space-between",
132
171
  alignItems: "center",
172
+ margin: 0,
173
+ padding: 0,
174
+ boxSizing: "border-box",
133
175
  },
134
176
 
135
177
  optionLeft: {
@@ -137,11 +179,17 @@ export const styles = {
137
179
  gap: "12px",
138
180
  alignItems: "center",
139
181
  cursor: "pointer",
182
+ margin: 0,
183
+ padding: 0,
184
+ boxSizing: "border-box",
140
185
  },
141
186
 
142
187
  optionTextContainer: {
143
188
  display: "flex",
144
189
  flexDirection: "column",
190
+ margin: 0,
191
+ padding: 0,
192
+ boxSizing: "border-box",
145
193
  },
146
194
 
147
195
  optionTitle: {
@@ -150,6 +198,9 @@ export const styles = {
150
198
  lineHeight: "150%",
151
199
  letterSpacing: "0.2px",
152
200
  color: "#101828",
201
+ margin: 0,
202
+ padding: 0,
203
+ textAlign: "left",
153
204
  },
154
205
 
155
206
  optionDescription: {
@@ -159,6 +210,9 @@ export const styles = {
159
210
  letterSpacing: "0.2px",
160
211
  color: "#475467",
161
212
  verticalAlign: "middle",
213
+ margin: 0,
214
+ padding: 0,
215
+ textAlign: "left",
162
216
  },
163
217
 
164
218
  checkboxLarge: {
@@ -167,12 +221,24 @@ export const styles = {
167
221
  padding: "4px",
168
222
  borderRadius: "5px",
169
223
  border: "2px solid #d0d5dd",
224
+ margin: 0,
225
+ boxSizing: "border-box",
226
+ appearance: "none",
227
+ WebkitAppearance: "none",
228
+ MozAppearance: "none",
229
+ cursor: "pointer",
230
+ position: "relative",
231
+ backgroundColor: "transparent",
170
232
  },
171
233
 
172
234
  dataElementsContainer: {
173
235
  marginLeft: "27px",
174
236
  display: "flex",
175
237
  flexDirection: "column",
238
+ margin: 0,
239
+ padding: 0,
240
+ boxSizing: "border-box",
241
+ paddingLeft: "22px",
176
242
  },
177
243
 
178
244
  dataElementItem: {
@@ -180,6 +246,10 @@ export const styles = {
180
246
  alignItems: "center",
181
247
  gap: "10px",
182
248
  justifyContent: "space-between",
249
+ margin: 0,
250
+ padding: 0,
251
+ boxSizing: "border-box",
252
+ minHeight: "24px",
183
253
  },
184
254
 
185
255
  dataElementText: {
@@ -188,6 +258,9 @@ export const styles = {
188
258
  lineHeight: "150%",
189
259
  letterSpacing: "0.2px",
190
260
  color: "#344054",
261
+ margin: 0,
262
+ padding: 0,
263
+ textAlign: "left",
191
264
  },
192
265
 
193
266
  checkboxSmall: {
@@ -196,6 +269,14 @@ export const styles = {
196
269
  padding: "4px",
197
270
  borderRadius: "5px",
198
271
  border: "2px solid #d0d5dd",
272
+ margin: 0,
273
+ boxSizing: "border-box",
274
+ appearance: "none",
275
+ WebkitAppearance: "none",
276
+ MozAppearance: "none",
277
+ cursor: "pointer",
278
+ position: "relative",
279
+ backgroundColor: "transparent",
199
280
  },
200
281
 
201
282
  bottomSection: {
@@ -206,6 +287,8 @@ export const styles = {
206
287
  borderTop: "1px solid #e5e7eb",
207
288
  boxShadow: "0 -1px 2px rgba(0, 0, 0, 0.03)",
208
289
  backgroundColor: "#ffffff",
290
+ margin: 0,
291
+ boxSizing: "border-box",
209
292
  },
210
293
 
211
294
  button: {
@@ -217,6 +300,9 @@ export const styles = {
217
300
  lineHeight: "150%",
218
301
  letterSpacing: "0.2px",
219
302
  cursor: "pointer",
303
+ margin: 0,
304
+ boxSizing: "border-box",
305
+ textAlign: "center",
220
306
  },
221
307
 
222
308
  acceptButton: {
@@ -233,6 +319,9 @@ export const styles = {
233
319
 
234
320
  languageSelectorContainer: {
235
321
  position: "relative",
322
+ margin: 0,
323
+ padding: 0,
324
+ boxSizing: "border-box",
236
325
  },
237
326
 
238
327
  languageDropdown: {
@@ -246,6 +335,9 @@ export const styles = {
246
335
  zIndex: 10,
247
336
  width: "max-content",
248
337
  marginTop: "4px",
338
+ padding: 0,
339
+ boxSizing: "border-box",
340
+ textAlign: "left",
249
341
  },
250
342
 
251
343
  languageItem: {
@@ -255,6 +347,9 @@ export const styles = {
255
347
  color: "#344054",
256
348
  backgroundColor: "#ffffff",
257
349
  borderBottom: "1px solid #e5e7eb",
350
+ margin: 0,
351
+ boxSizing: "border-box",
352
+ textAlign: "left",
258
353
  },
259
354
 
260
355
  selectedLanguageItem: {
@@ -269,6 +364,8 @@ export const styles = {
269
364
  justifyContent: "center",
270
365
  padding: "2rem",
271
366
  minHeight: "200px",
367
+ margin: 0,
368
+ boxSizing: "border-box",
272
369
  },
273
370
 
274
371
  loadingSpinner: {
@@ -279,55 +376,7 @@ export const styles = {
279
376
  borderRadius: "50%",
280
377
  animation: "spin 1s linear infinite",
281
378
  marginBottom: "1rem",
282
- },
283
-
284
- // Media query styles
285
- "@media (max-width: 768px)": {
286
- modal: {
287
- width: "90%",
288
- },
289
- content: {
290
- margin: "20px",
291
- },
292
- title: {
293
- fontSize: "16px",
294
- },
295
- subTitle: {
296
- fontSize: "14px",
297
- },
298
- privacyText: {
299
- fontSize: "14px",
300
- },
301
- optionTitle: {
302
- fontSize: "14px",
303
- },
304
- optionDescription: {
305
- fontSize: "11px",
306
- },
307
- dataElementText: {
308
- fontSize: "12px",
309
- },
310
- topRight: {
311
- fontSize: "11px",
312
- padding: "3px 6px",
313
- height: "auto",
314
- width: "auto",
315
- },
316
- languageItem: {
317
- fontSize: "11px",
318
- padding: "6px 10px",
319
- },
320
- bottomSection: {
321
- flexDirection: "column",
322
- gap: "12px",
323
- },
324
- button: {
325
- fontSize: "14px",
326
- padding: "10px 20px",
327
- },
328
- middleSection: {
329
- paddingRight: "10px",
330
- },
379
+ boxSizing: "border-box",
331
380
  },
332
381
  } as const;
333
382
 
@@ -4,12 +4,14 @@ export type Props = Readonly<{
4
4
  noticeId: string;
5
5
  accessToken: string;
6
6
  refreshToken: string;
7
+ baseUrl?: string;
7
8
  settings?: Partial<Settings>;
8
9
  language?: string;
9
10
  blockUI?: boolean;
10
11
  onAccept: () => void;
11
12
  onDecline: () => void;
12
13
  onError?: (error: Error) => void;
14
+ applicationId?: string;
13
15
  }>;
14
16
 
15
17
  export type PurposeTranslations = {
package/tests/mocks.ts CHANGED
@@ -95,7 +95,7 @@ export const defaultProps = {
95
95
  accessToken: "mock-token",
96
96
  refreshToken: "mock-refresh-token",
97
97
  language: "en",
98
- blockUI: false,
98
+ blockUI: true,
99
99
  onAccept: vi.fn(),
100
100
  onDecline: vi.fn(),
101
101
  onError: vi.fn(),
@@ -109,6 +109,6 @@ export const defaultProps = {
109
109
  decline: { backgroundColor: "#ffffff", textColor: "#000000" },
110
110
  language: { backgroundColor: "#ffffff", textColor: "#344054" },
111
111
  },
112
- link: { color: "#4f87ff" },
112
+ link: "#4f87ff",
113
113
  },
114
114
  } as const;