@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.
@@ -157,9 +157,26 @@ export type FetchConsentContentParams = {
157
157
  };
158
158
 
159
159
  export type ConsentEventPayload = {
160
- org_user_id?: string;
160
+ notice_uuid: string;
161
+ purposes: Array<{
162
+ purpose_uuid: string;
163
+ selected: boolean;
164
+ data_elements?: Array<{
165
+ uuid: string;
166
+ selected: boolean;
167
+ }>;
168
+ }>;
169
+ select_all_mandatory: boolean;
170
+ source?: string;
171
+ declined?: boolean;
172
+ meta_data?: {
173
+ specific_uuid: string;
174
+ };
175
+ };
176
+
177
+ // @deprecated Remove when production backend supports the new payload format.
178
+ export type LegacyConsentEventPayload = {
161
179
  primary_email?: string;
162
- primary_mobile?: string;
163
180
  notice_uuid: string;
164
181
  source: string;
165
182
  declined: boolean;
@@ -179,9 +196,7 @@ export type ConsentEventPayload = {
179
196
  }>;
180
197
  }>;
181
198
  };
182
- meta_data?: {
183
- specific_uuid: string;
184
- };
199
+ meta_data?: { specific_uuid: string };
185
200
  };
186
201
 
187
202
  export type DataElement = {
@@ -154,26 +154,24 @@ const RedactoConsentInlineComponent = ({
154
154
  language,
155
155
  specific_uuid: applicationId,
156
156
  });
157
- setContent(consentContentData.detail.active_config);
158
- if (consentContentData.detail.active_config.default_language) {
159
- setSelectedLanguage(
160
- consentContentData.detail.active_config.default_language
161
- );
157
+ const activeConfig = consentContentData.detail.active_config;
158
+ setContent(activeConfig);
159
+ if (activeConfig.default_language) {
160
+ setSelectedLanguage(activeConfig.default_language);
162
161
  }
163
- const initialCollapsedState =
164
- consentContentData.detail.active_config.purposes.reduce(
165
- (acc, purpose) => ({
166
- ...acc,
167
- [purpose.uuid]: true,
168
- }),
169
- {}
170
- );
162
+ const initialCollapsedState = activeConfig.purposes.reduce(
163
+ (acc, purpose) => ({
164
+ ...acc,
165
+ [purpose.uuid]: true,
166
+ }),
167
+ {}
168
+ );
171
169
  setCollapsedPurposes(initialCollapsedState);
172
170
 
173
171
  // Initialize selected states
174
172
  // All elements start unchecked, but required elements cannot be unchecked once checked
175
173
  const initialDataElementState: Record<string, boolean> =
176
- consentContentData.detail.active_config.purposes.reduce(
174
+ activeConfig.purposes.reduce(
177
175
  (acc: Record<string, boolean>, purpose) => {
178
176
  purpose.data_elements.forEach((element) => {
179
177
  const combinedId = `${purpose.uuid}-${element.uuid}`;
@@ -187,7 +185,7 @@ const RedactoConsentInlineComponent = ({
187
185
 
188
186
  // Initialize purpose states - all start unchecked
189
187
  const initialPurposeState: Record<string, boolean> =
190
- consentContentData.detail.active_config.purposes.reduce(
188
+ activeConfig.purposes.reduce(
191
189
  (acc: Record<string, boolean>, purpose) => {
192
190
  return {
193
191
  ...acc,
@@ -198,7 +196,6 @@ const RedactoConsentInlineComponent = ({
198
196
  );
199
197
  setSelectedPurposes(initialPurposeState);
200
198
  } catch (err: unknown) {
201
- console.error(err);
202
199
  const error = err as Error & { status?: number };
203
200
  setFetchError(error);
204
201
  if (error.status === 409) {
@@ -360,13 +357,12 @@ const RedactoConsentInlineComponent = ({
360
357
  // Reset ref on success to allow re-submission if needed
361
358
  hasSubmittedRef.current = false;
362
359
  } catch (error) {
363
- console.error("Error submitting consent:", error);
364
360
  const err = error as Error & { status?: number };
365
- if (err.status === 500) {
366
- setErrorMessage(
367
- "An error occurred while submitting your consent. Please try again later."
368
- );
369
- }
361
+ const message =
362
+ err.status === 500
363
+ ? "An error occurred while submitting your consent. Please try again later."
364
+ : err.message || "Failed to submit consent. Please try again.";
365
+ setErrorMessage(message);
370
366
  // Reset ref on error to allow retry
371
367
  hasSubmittedRef.current = false;
372
368
  throw error;
@@ -400,7 +396,6 @@ const RedactoConsentInlineComponent = ({
400
396
  try {
401
397
  await handleAcceptWithToken(accessToken);
402
398
  } catch (err) {
403
- console.error("Failed to auto-submit consent:", err);
404
399
  hasSubmittedRef.current = false;
405
400
  onError?.(err as Error);
406
401
  }
@@ -1,9 +1,11 @@
1
1
  import type {
2
2
  ConsentContent,
3
3
  ConsentEventPayload,
4
+ LegacyConsentEventPayload,
4
5
  FetchConsentContentParams,
5
6
  SubmitConsentEventParams,
6
7
  } from "./types";
8
+ import { isLegacyApi } from "../../utils/isLegacyApi";
7
9
 
8
10
  const BASE_URL = "https://api.redacto.tech/consent";
9
11
 
@@ -58,7 +60,6 @@ export const fetchConsentContent = async ({
58
60
  const data = await response.json();
59
61
  return data;
60
62
  } catch (error) {
61
- console.error("Error fetching consent content:", error);
62
63
  throw error;
63
64
  }
64
65
  };
@@ -74,33 +75,71 @@ export const submitConsentEvent = async ({
74
75
  meta_data,
75
76
  }: SubmitConsentEventParams): Promise<void> => {
76
77
  try {
77
- const primaryEmail = localStorage.getItem("userEmail");
78
- const payload: ConsentEventPayload = {
79
- primary_email: primaryEmail || undefined,
80
- notice_uuid: noticeUuid,
81
- source: "WEB",
82
- declined,
83
- consents: {
84
- purposes: purposes.map((purpose) => ({
85
- uuid: purpose.uuid,
86
- name: purpose.name,
87
- description: purpose.description,
88
- industries: purpose.industries || "",
89
- selected: purpose.selected,
90
- data_elements: purpose.data_elements.map((element) => ({
91
- uuid: element.uuid,
92
- name: element.name,
93
- enabled: element.enabled,
94
- required: element.required,
95
- selected: element.required ? true : element.selected,
96
- })),
97
- })),
98
- },
99
- meta_data,
100
- };
101
-
102
78
  const apiBaseUrl = baseUrl || BASE_URL;
103
79
 
80
+ let payload: ConsentEventPayload | LegacyConsentEventPayload;
81
+
82
+ if (isLegacyApi(apiBaseUrl)) {
83
+ // @deprecated Legacy payload for production (.io) backend
84
+ const primaryEmail = typeof window !== "undefined" ? localStorage.getItem("userEmail") : null;
85
+ payload = {
86
+ primary_email: primaryEmail || undefined,
87
+ notice_uuid: noticeUuid,
88
+ source: "WEB",
89
+ declined: Boolean(declined),
90
+ consents: {
91
+ purposes: purposes.map((purpose) => {
92
+ if (!purpose.uuid) {
93
+ throw new Error(`Invalid purpose data: missing uuid`);
94
+ }
95
+ return {
96
+ uuid: purpose.uuid,
97
+ name: purpose.name,
98
+ description: purpose.description || "",
99
+ industries: purpose.industries || "",
100
+ selected: Boolean(purpose.selected),
101
+ data_elements: (purpose.data_elements || []).map((element) => ({
102
+ uuid: element.uuid,
103
+ name: element.name,
104
+ enabled: Boolean(element.enabled),
105
+ required: Boolean(element.required),
106
+ selected: element.required ? true : Boolean(element.selected),
107
+ })),
108
+ };
109
+ }),
110
+ },
111
+ meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid } : undefined,
112
+ };
113
+ } else {
114
+ // New payload for staging (.tech) / localhost
115
+ payload = {
116
+ notice_uuid: noticeUuid,
117
+ purposes: purposes.map((purpose) => {
118
+ if (!purpose.uuid) {
119
+ throw new Error(`Invalid purpose data: missing uuid`);
120
+ }
121
+
122
+ // Only include enabled data elements (disabled elements are not selectable)
123
+ const dataElements = purpose.data_elements
124
+ .filter((element) => element.enabled)
125
+ .map((element) => ({
126
+ uuid: element.uuid,
127
+ selected: element.required ? true : Boolean(element.selected),
128
+ }));
129
+
130
+ return {
131
+ purpose_uuid: purpose.uuid,
132
+ selected: Boolean(purpose.selected),
133
+ data_elements: dataElements.length > 0 ? dataElements : undefined,
134
+ };
135
+ }),
136
+ select_all_mandatory: false,
137
+ source: "WEB",
138
+ declined,
139
+ meta_data,
140
+ };
141
+ }
142
+
104
143
  const response = await fetch(
105
144
  `${apiBaseUrl}/public/organisations/${org_uuid}/workspaces/${workspace_uuid}/submit-consent`,
106
145
  {
@@ -151,7 +190,6 @@ export const submitConsentEvent = async ({
151
190
  throw error;
152
191
  }
153
192
  } catch (error) {
154
- console.error("Error submitting consent event:", error);
155
193
  throw error;
156
194
  }
157
195
  };
@@ -132,9 +132,26 @@ export type FetchConsentContentParams = {
132
132
  };
133
133
 
134
134
  export type ConsentEventPayload = {
135
- org_user_id?: string;
135
+ notice_uuid: string;
136
+ purposes: Array<{
137
+ purpose_uuid: string;
138
+ selected: boolean;
139
+ data_elements?: Array<{
140
+ uuid: string;
141
+ selected: boolean;
142
+ }>;
143
+ }>;
144
+ select_all_mandatory: boolean;
145
+ source?: string;
146
+ declined?: boolean;
147
+ meta_data?: {
148
+ specific_uuid: string;
149
+ };
150
+ };
151
+
152
+ // @deprecated Remove when production backend supports the new payload format.
153
+ export type LegacyConsentEventPayload = {
136
154
  primary_email?: string;
137
- primary_mobile?: string;
138
155
  notice_uuid: string;
139
156
  source: string;
140
157
  declined: boolean;
@@ -154,9 +171,7 @@ export type ConsentEventPayload = {
154
171
  }>;
155
172
  }>;
156
173
  };
157
- meta_data?: {
158
- specific_uuid: string;
159
- };
174
+ meta_data?: { specific_uuid: string };
160
175
  };
161
176
 
162
177
  export type DataElement = {
@@ -0,0 +1,12 @@
1
+ // @deprecated Remove when production backend supports the new payload format.
2
+ export function isLegacyApi(baseUrl: string): boolean {
3
+ try {
4
+ return new URL(baseUrl).hostname.endsWith(".redacto.io");
5
+ } catch {
6
+ try {
7
+ return new URL(`https://${baseUrl}`).hostname.endsWith(".redacto.io");
8
+ } catch {
9
+ return false;
10
+ }
11
+ }
12
+ }