@redacto.io/consent-sdk-react 3.2.0 → 3.3.0

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