@redacto.io/consent-sdk-react 3.0.0 → 3.2.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 +12 -0
- package/dist/index.js +135 -52
- package/dist/index.mjs +135 -52
- package/package.json +1 -1
- package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +179 -78
- package/src/RedactoNoticeConsentInline/RedactoNoticeConsentInline.tsx +15 -5
- package/.turbo/turbo-build.log +0 -21
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @redacto.io/consent-sdk-react
|
|
2
2
|
|
|
3
|
+
## 3.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- enhance PurposeItem component to track initial data elements
|
|
8
|
+
|
|
9
|
+
## 3.1.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- purpose expiry for null validity
|
|
14
|
+
|
|
3
15
|
## 3.0.0
|
|
4
16
|
|
|
5
17
|
### Major Changes
|
package/dist/index.js
CHANGED
|
@@ -899,7 +899,8 @@ var PurposeItem = ({
|
|
|
899
899
|
onDataElementToggle,
|
|
900
900
|
getTranslatedText,
|
|
901
901
|
isAlreadyConsented,
|
|
902
|
-
selectedLanguage
|
|
902
|
+
selectedLanguage,
|
|
903
|
+
initialDataElementSelections = {}
|
|
903
904
|
}) => {
|
|
904
905
|
const headingStyle = (0, import_react2.useMemo)(
|
|
905
906
|
() => ({
|
|
@@ -1114,46 +1115,68 @@ var PurposeItem = ({
|
|
|
1114
1115
|
)}${dataElement.required ? " (required)" : ""}`
|
|
1115
1116
|
}
|
|
1116
1117
|
),
|
|
1117
|
-
isAlreadyConsented && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
width: "16px",
|
|
1125
|
-
height: "16px",
|
|
1126
|
-
borderRadius: "50%",
|
|
1127
|
-
backgroundColor: "#10B981"
|
|
1128
|
-
},
|
|
1129
|
-
"aria-label": `Already consented to ${getTranslatedText(
|
|
1130
|
-
`data_elements.name`,
|
|
1131
|
-
dataElement.name,
|
|
1132
|
-
dataElement.uuid
|
|
1133
|
-
)}`,
|
|
1134
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1135
|
-
"svg",
|
|
1118
|
+
isAlreadyConsented && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: (() => {
|
|
1119
|
+
const combinedId = `${purpose.uuid}-${dataElement.uuid}`;
|
|
1120
|
+
const isCurrentlySelected = selectedDataElements[combinedId] || false;
|
|
1121
|
+
const wasOriginallySelected = initialDataElementSelections[combinedId] || false;
|
|
1122
|
+
if (isCurrentlySelected && wasOriginallySelected) {
|
|
1123
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1124
|
+
"div",
|
|
1136
1125
|
{
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1126
|
+
style: {
|
|
1127
|
+
display: "flex",
|
|
1128
|
+
alignItems: "center",
|
|
1129
|
+
justifyContent: "center",
|
|
1130
|
+
width: "16px",
|
|
1131
|
+
height: "16px",
|
|
1132
|
+
borderRadius: "50%",
|
|
1133
|
+
backgroundColor: "#10B981"
|
|
1134
|
+
},
|
|
1135
|
+
"aria-label": `Already consented to ${getTranslatedText(
|
|
1136
|
+
`data_elements.name`,
|
|
1137
|
+
dataElement.name,
|
|
1138
|
+
dataElement.uuid
|
|
1139
|
+
)}`,
|
|
1143
1140
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1144
|
-
"
|
|
1141
|
+
"svg",
|
|
1145
1142
|
{
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1143
|
+
width: "10",
|
|
1144
|
+
height: "10",
|
|
1145
|
+
viewBox: "0 0 10 10",
|
|
1146
|
+
fill: "none",
|
|
1147
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1148
|
+
"aria-hidden": "true",
|
|
1149
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1150
|
+
"path",
|
|
1151
|
+
{
|
|
1152
|
+
d: "M3 5L4.5 6.5L7.5 3.5",
|
|
1153
|
+
stroke: "white",
|
|
1154
|
+
strokeWidth: "1.5",
|
|
1155
|
+
strokeLinecap: "round",
|
|
1156
|
+
strokeLinejoin: "round"
|
|
1157
|
+
}
|
|
1158
|
+
)
|
|
1151
1159
|
}
|
|
1152
1160
|
)
|
|
1153
1161
|
}
|
|
1154
|
-
)
|
|
1162
|
+
);
|
|
1163
|
+
} else {
|
|
1164
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1165
|
+
"input",
|
|
1166
|
+
{
|
|
1167
|
+
className: "redacto-checkbox-small",
|
|
1168
|
+
type: "checkbox",
|
|
1169
|
+
checked: isCurrentlySelected,
|
|
1170
|
+
onChange: () => onDataElementToggle(dataElement.uuid, purpose.uuid),
|
|
1171
|
+
"aria-label": `Select ${getTranslatedText(
|
|
1172
|
+
`data_elements.name`,
|
|
1173
|
+
dataElement.name,
|
|
1174
|
+
dataElement.uuid
|
|
1175
|
+
)}${dataElement.required ? " (required)" : ""}`
|
|
1176
|
+
}
|
|
1177
|
+
);
|
|
1155
1178
|
}
|
|
1156
|
-
)
|
|
1179
|
+
})() })
|
|
1157
1180
|
]
|
|
1158
1181
|
},
|
|
1159
1182
|
`${dataElement.uuid}-${selectedLanguage}`
|
|
@@ -1592,10 +1615,13 @@ var RedactoNoticeConsent = ({
|
|
|
1592
1615
|
}
|
|
1593
1616
|
const [isLoading, setIsLoading] = (0, import_react2.useState)(true);
|
|
1594
1617
|
const [isSubmitting, setIsSubmitting] = (0, import_react2.useState)(false);
|
|
1618
|
+
const [isSubmittingAccept, setIsSubmittingAccept] = (0, import_react2.useState)(false);
|
|
1619
|
+
const [isSubmittingDecline, setIsSubmittingDecline] = (0, import_react2.useState)(false);
|
|
1595
1620
|
const [content, setContent] = (0, import_react2.useState)(null);
|
|
1596
1621
|
const [categorizedPurposes, setCategorizedPurposes] = (0, import_react2.useState)(null);
|
|
1597
1622
|
const [selectedPurposes, setSelectedPurposes] = (0, import_react2.useState)({});
|
|
1598
1623
|
const [selectedDataElements, setSelectedDataElements] = (0, import_react2.useState)({});
|
|
1624
|
+
const [initialDataElementSelections, setInitialDataElementSelections] = (0, import_react2.useState)({});
|
|
1599
1625
|
const [collapsedPurposes, setCollapsedPurposes] = (0, import_react2.useState)({});
|
|
1600
1626
|
const [hasAlreadyConsented, setHasAlreadyConsented] = (0, import_react2.useState)(false);
|
|
1601
1627
|
const [isLanguageDropdownOpen, setIsLanguageDropdownOpen] = (0, import_react2.useState)(false);
|
|
@@ -1736,11 +1762,14 @@ var RedactoNoticeConsent = ({
|
|
|
1736
1762
|
setCategorizedPurposes(null);
|
|
1737
1763
|
setSelectedPurposes({});
|
|
1738
1764
|
setSelectedDataElements({});
|
|
1765
|
+
setInitialDataElementSelections({});
|
|
1739
1766
|
setCollapsedPurposes({});
|
|
1740
1767
|
setHasAlreadyConsented(false);
|
|
1741
1768
|
setErrorMessage(null);
|
|
1742
1769
|
setFetchError(null);
|
|
1743
1770
|
setIsLoading(true);
|
|
1771
|
+
setIsSubmittingAccept(false);
|
|
1772
|
+
setIsSubmittingDecline(false);
|
|
1744
1773
|
setShowAgeVerification(false);
|
|
1745
1774
|
setShowGuardianForm(false);
|
|
1746
1775
|
setIsSubmittingGuardian(false);
|
|
@@ -1883,6 +1912,23 @@ var RedactoNoticeConsent = ({
|
|
|
1883
1912
|
{}
|
|
1884
1913
|
);
|
|
1885
1914
|
setSelectedDataElements(initialDataElementState);
|
|
1915
|
+
if (isReconsentRequired && newCategorizedPurposes) {
|
|
1916
|
+
const initialSelections = {};
|
|
1917
|
+
newCategorizedPurposes.alreadyConsented.forEach((purpose) => {
|
|
1918
|
+
purpose.data_elements.forEach((element) => {
|
|
1919
|
+
var _a3, _b2;
|
|
1920
|
+
const combinedId = `${purpose.uuid}-${element.uuid}`;
|
|
1921
|
+
if ((_b2 = (_a3 = purposeSelections == null ? void 0 : purposeSelections[purpose.uuid]) == null ? void 0 : _a3.data_elements) == null ? void 0 : _b2[element.uuid]) {
|
|
1922
|
+
initialSelections[combinedId] = purposeSelections[purpose.uuid].data_elements[element.uuid].selected;
|
|
1923
|
+
} else {
|
|
1924
|
+
initialSelections[combinedId] = false;
|
|
1925
|
+
}
|
|
1926
|
+
});
|
|
1927
|
+
});
|
|
1928
|
+
setInitialDataElementSelections(initialSelections);
|
|
1929
|
+
} else {
|
|
1930
|
+
setInitialDataElementSelections({});
|
|
1931
|
+
}
|
|
1886
1932
|
if (consentContentData.detail.is_minor) {
|
|
1887
1933
|
setShowAgeVerification(true);
|
|
1888
1934
|
}
|
|
@@ -1968,7 +2014,7 @@ var RedactoNoticeConsent = ({
|
|
|
1968
2014
|
(el) => newState[`${purposeUuid}-${el.uuid}`]
|
|
1969
2015
|
);
|
|
1970
2016
|
} else {
|
|
1971
|
-
shouldCheckPurpose = purpose.data_elements.
|
|
2017
|
+
shouldCheckPurpose = purpose.data_elements.some(
|
|
1972
2018
|
(el) => newState[`${purposeUuid}-${el.uuid}`]
|
|
1973
2019
|
);
|
|
1974
2020
|
}
|
|
@@ -1983,6 +2029,7 @@ var RedactoNoticeConsent = ({
|
|
|
1983
2029
|
const handleAccept = async () => {
|
|
1984
2030
|
const submitController = new AbortController();
|
|
1985
2031
|
try {
|
|
2032
|
+
setIsSubmittingAccept(true);
|
|
1986
2033
|
setIsSubmitting(true);
|
|
1987
2034
|
setErrorMessage(null);
|
|
1988
2035
|
if (content) {
|
|
@@ -1990,14 +2037,31 @@ var RedactoNoticeConsent = ({
|
|
|
1990
2037
|
accessToken,
|
|
1991
2038
|
baseUrl,
|
|
1992
2039
|
noticeUuid: content.notice_uuid,
|
|
1993
|
-
purposes: content.purposes.map((purpose) =>
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2040
|
+
purposes: content.purposes.map((purpose) => {
|
|
2041
|
+
const isAlreadyConsented = (categorizedPurposes == null ? void 0 : categorizedPurposes.alreadyConsented.some(
|
|
2042
|
+
(p) => p.uuid === purpose.uuid
|
|
2043
|
+
)) || false;
|
|
2044
|
+
let purposeSelected = selectedPurposes[purpose.uuid] || false;
|
|
2045
|
+
if (isAlreadyConsented) {
|
|
2046
|
+
const wasModified = purpose.data_elements.some((element) => {
|
|
2047
|
+
const combinedId = `${purpose.uuid}-${element.uuid}`;
|
|
2048
|
+
const currentSelection = selectedDataElements[combinedId] || false;
|
|
2049
|
+
const initialSelection = initialDataElementSelections[combinedId] || false;
|
|
2050
|
+
return currentSelection !== initialSelection;
|
|
2051
|
+
});
|
|
2052
|
+
if (wasModified) {
|
|
2053
|
+
purposeSelected = true;
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
return {
|
|
2057
|
+
...purpose,
|
|
2058
|
+
selected: purposeSelected,
|
|
2059
|
+
data_elements: purpose.data_elements.map((element) => ({
|
|
2060
|
+
...element,
|
|
2061
|
+
selected: element.required ? true : selectedDataElements[`${purpose.uuid}-${element.uuid}`] || false
|
|
2062
|
+
}))
|
|
2063
|
+
};
|
|
2064
|
+
}),
|
|
2001
2065
|
declined: false,
|
|
2002
2066
|
meta_data: applicationId ? { specific_uuid: applicationId } : void 0,
|
|
2003
2067
|
signal: submitController.signal
|
|
@@ -2017,11 +2081,19 @@ var RedactoNoticeConsent = ({
|
|
|
2017
2081
|
}
|
|
2018
2082
|
onError == null ? void 0 : onError(error);
|
|
2019
2083
|
} finally {
|
|
2084
|
+
setIsSubmittingAccept(false);
|
|
2020
2085
|
setIsSubmitting(false);
|
|
2021
2086
|
}
|
|
2022
2087
|
};
|
|
2023
2088
|
const handleDecline = (0, import_react2.useCallback)(async () => {
|
|
2024
|
-
|
|
2089
|
+
try {
|
|
2090
|
+
setIsSubmittingDecline(true);
|
|
2091
|
+
setIsSubmitting(true);
|
|
2092
|
+
onDecline == null ? void 0 : onDecline();
|
|
2093
|
+
} finally {
|
|
2094
|
+
setIsSubmittingDecline(false);
|
|
2095
|
+
setIsSubmitting(false);
|
|
2096
|
+
}
|
|
2025
2097
|
}, [onDecline]);
|
|
2026
2098
|
const handleGuardianFormChange = (0, import_react2.useCallback)(
|
|
2027
2099
|
(field, value) => {
|
|
@@ -2995,7 +3067,8 @@ var RedactoNoticeConsent = ({
|
|
|
2995
3067
|
onDataElementToggle: handleDataElementCheckboxChange,
|
|
2996
3068
|
getTranslatedText,
|
|
2997
3069
|
isAlreadyConsented: true,
|
|
2998
|
-
selectedLanguage
|
|
3070
|
+
selectedLanguage,
|
|
3071
|
+
initialDataElementSelections
|
|
2999
3072
|
},
|
|
3000
3073
|
`${purpose.uuid}-${selectedLanguage}`
|
|
3001
3074
|
)),
|
|
@@ -3065,12 +3138,12 @@ var RedactoNoticeConsent = ({
|
|
|
3065
3138
|
opacity: acceptDisabled ? 0.5 : 1
|
|
3066
3139
|
},
|
|
3067
3140
|
onClick: handleAccept,
|
|
3068
|
-
disabled: acceptDisabled,
|
|
3141
|
+
disabled: acceptDisabled || isSubmittingDecline,
|
|
3069
3142
|
"aria-label": getTranslatedText(
|
|
3070
3143
|
"confirm_button_text",
|
|
3071
3144
|
(content == null ? void 0 : content.confirm_button_text) || "Accept"
|
|
3072
3145
|
),
|
|
3073
|
-
children:
|
|
3146
|
+
children: isSubmittingAccept ? "Processing..." : getTranslatedText(
|
|
3074
3147
|
"confirm_button_text",
|
|
3075
3148
|
(content == null ? void 0 : content.confirm_button_text) || "Accept"
|
|
3076
3149
|
)
|
|
@@ -3084,15 +3157,16 @@ var RedactoNoticeConsent = ({
|
|
|
3084
3157
|
...styles.button,
|
|
3085
3158
|
...responsiveStyles.button,
|
|
3086
3159
|
...styles.cancelButton,
|
|
3087
|
-
...declineButtonStyle
|
|
3160
|
+
...declineButtonStyle,
|
|
3161
|
+
opacity: isSubmittingAccept ? 0.5 : 1
|
|
3088
3162
|
},
|
|
3089
3163
|
onClick: handleDecline,
|
|
3090
|
-
disabled:
|
|
3164
|
+
disabled: isSubmittingDecline || isSubmittingAccept,
|
|
3091
3165
|
"aria-label": getTranslatedText(
|
|
3092
3166
|
"decline_button_text",
|
|
3093
3167
|
(content == null ? void 0 : content.decline_button_text) || "Decline"
|
|
3094
3168
|
),
|
|
3095
|
-
children:
|
|
3169
|
+
children: isSubmittingDecline ? "Processing..." : getTranslatedText(
|
|
3096
3170
|
"decline_button_text",
|
|
3097
3171
|
(content == null ? void 0 : content.decline_button_text) || "Decline"
|
|
3098
3172
|
)
|
|
@@ -3843,10 +3917,19 @@ var RedactoConsentInlineComponent = ({
|
|
|
3843
3917
|
const requiredElements = purpose.data_elements.filter(
|
|
3844
3918
|
(el) => el.required
|
|
3845
3919
|
);
|
|
3846
|
-
|
|
3920
|
+
let shouldCheckPurpose;
|
|
3921
|
+
if (requiredElements.length > 0) {
|
|
3922
|
+
shouldCheckPurpose = requiredElements.every(
|
|
3923
|
+
(el) => newState[`${purposeUuid}-${el.uuid}`]
|
|
3924
|
+
);
|
|
3925
|
+
} else {
|
|
3926
|
+
shouldCheckPurpose = purpose.data_elements.some(
|
|
3927
|
+
(el) => newState[`${purposeUuid}-${el.uuid}`]
|
|
3928
|
+
);
|
|
3929
|
+
}
|
|
3847
3930
|
setSelectedPurposes((prevPurposes) => ({
|
|
3848
3931
|
...prevPurposes,
|
|
3849
|
-
[purposeUuid]:
|
|
3932
|
+
[purposeUuid]: shouldCheckPurpose
|
|
3850
3933
|
}));
|
|
3851
3934
|
return newState;
|
|
3852
3935
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -872,7 +872,8 @@ var PurposeItem = ({
|
|
|
872
872
|
onDataElementToggle,
|
|
873
873
|
getTranslatedText,
|
|
874
874
|
isAlreadyConsented,
|
|
875
|
-
selectedLanguage
|
|
875
|
+
selectedLanguage,
|
|
876
|
+
initialDataElementSelections = {}
|
|
876
877
|
}) => {
|
|
877
878
|
const headingStyle = useMemo(
|
|
878
879
|
() => ({
|
|
@@ -1087,46 +1088,68 @@ var PurposeItem = ({
|
|
|
1087
1088
|
)}${dataElement.required ? " (required)" : ""}`
|
|
1088
1089
|
}
|
|
1089
1090
|
),
|
|
1090
|
-
isAlreadyConsented && /* @__PURE__ */ jsx(
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
width: "16px",
|
|
1098
|
-
height: "16px",
|
|
1099
|
-
borderRadius: "50%",
|
|
1100
|
-
backgroundColor: "#10B981"
|
|
1101
|
-
},
|
|
1102
|
-
"aria-label": `Already consented to ${getTranslatedText(
|
|
1103
|
-
`data_elements.name`,
|
|
1104
|
-
dataElement.name,
|
|
1105
|
-
dataElement.uuid
|
|
1106
|
-
)}`,
|
|
1107
|
-
children: /* @__PURE__ */ jsx(
|
|
1108
|
-
"svg",
|
|
1091
|
+
isAlreadyConsented && /* @__PURE__ */ jsx(Fragment, { children: (() => {
|
|
1092
|
+
const combinedId = `${purpose.uuid}-${dataElement.uuid}`;
|
|
1093
|
+
const isCurrentlySelected = selectedDataElements[combinedId] || false;
|
|
1094
|
+
const wasOriginallySelected = initialDataElementSelections[combinedId] || false;
|
|
1095
|
+
if (isCurrentlySelected && wasOriginallySelected) {
|
|
1096
|
+
return /* @__PURE__ */ jsx(
|
|
1097
|
+
"div",
|
|
1109
1098
|
{
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1099
|
+
style: {
|
|
1100
|
+
display: "flex",
|
|
1101
|
+
alignItems: "center",
|
|
1102
|
+
justifyContent: "center",
|
|
1103
|
+
width: "16px",
|
|
1104
|
+
height: "16px",
|
|
1105
|
+
borderRadius: "50%",
|
|
1106
|
+
backgroundColor: "#10B981"
|
|
1107
|
+
},
|
|
1108
|
+
"aria-label": `Already consented to ${getTranslatedText(
|
|
1109
|
+
`data_elements.name`,
|
|
1110
|
+
dataElement.name,
|
|
1111
|
+
dataElement.uuid
|
|
1112
|
+
)}`,
|
|
1116
1113
|
children: /* @__PURE__ */ jsx(
|
|
1117
|
-
"
|
|
1114
|
+
"svg",
|
|
1118
1115
|
{
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1116
|
+
width: "10",
|
|
1117
|
+
height: "10",
|
|
1118
|
+
viewBox: "0 0 10 10",
|
|
1119
|
+
fill: "none",
|
|
1120
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1121
|
+
"aria-hidden": "true",
|
|
1122
|
+
children: /* @__PURE__ */ jsx(
|
|
1123
|
+
"path",
|
|
1124
|
+
{
|
|
1125
|
+
d: "M3 5L4.5 6.5L7.5 3.5",
|
|
1126
|
+
stroke: "white",
|
|
1127
|
+
strokeWidth: "1.5",
|
|
1128
|
+
strokeLinecap: "round",
|
|
1129
|
+
strokeLinejoin: "round"
|
|
1130
|
+
}
|
|
1131
|
+
)
|
|
1124
1132
|
}
|
|
1125
1133
|
)
|
|
1126
1134
|
}
|
|
1127
|
-
)
|
|
1135
|
+
);
|
|
1136
|
+
} else {
|
|
1137
|
+
return /* @__PURE__ */ jsx(
|
|
1138
|
+
"input",
|
|
1139
|
+
{
|
|
1140
|
+
className: "redacto-checkbox-small",
|
|
1141
|
+
type: "checkbox",
|
|
1142
|
+
checked: isCurrentlySelected,
|
|
1143
|
+
onChange: () => onDataElementToggle(dataElement.uuid, purpose.uuid),
|
|
1144
|
+
"aria-label": `Select ${getTranslatedText(
|
|
1145
|
+
`data_elements.name`,
|
|
1146
|
+
dataElement.name,
|
|
1147
|
+
dataElement.uuid
|
|
1148
|
+
)}${dataElement.required ? " (required)" : ""}`
|
|
1149
|
+
}
|
|
1150
|
+
);
|
|
1128
1151
|
}
|
|
1129
|
-
)
|
|
1152
|
+
})() })
|
|
1130
1153
|
]
|
|
1131
1154
|
},
|
|
1132
1155
|
`${dataElement.uuid}-${selectedLanguage}`
|
|
@@ -1565,10 +1588,13 @@ var RedactoNoticeConsent = ({
|
|
|
1565
1588
|
}
|
|
1566
1589
|
const [isLoading, setIsLoading] = useState2(true);
|
|
1567
1590
|
const [isSubmitting, setIsSubmitting] = useState2(false);
|
|
1591
|
+
const [isSubmittingAccept, setIsSubmittingAccept] = useState2(false);
|
|
1592
|
+
const [isSubmittingDecline, setIsSubmittingDecline] = useState2(false);
|
|
1568
1593
|
const [content, setContent] = useState2(null);
|
|
1569
1594
|
const [categorizedPurposes, setCategorizedPurposes] = useState2(null);
|
|
1570
1595
|
const [selectedPurposes, setSelectedPurposes] = useState2({});
|
|
1571
1596
|
const [selectedDataElements, setSelectedDataElements] = useState2({});
|
|
1597
|
+
const [initialDataElementSelections, setInitialDataElementSelections] = useState2({});
|
|
1572
1598
|
const [collapsedPurposes, setCollapsedPurposes] = useState2({});
|
|
1573
1599
|
const [hasAlreadyConsented, setHasAlreadyConsented] = useState2(false);
|
|
1574
1600
|
const [isLanguageDropdownOpen, setIsLanguageDropdownOpen] = useState2(false);
|
|
@@ -1709,11 +1735,14 @@ var RedactoNoticeConsent = ({
|
|
|
1709
1735
|
setCategorizedPurposes(null);
|
|
1710
1736
|
setSelectedPurposes({});
|
|
1711
1737
|
setSelectedDataElements({});
|
|
1738
|
+
setInitialDataElementSelections({});
|
|
1712
1739
|
setCollapsedPurposes({});
|
|
1713
1740
|
setHasAlreadyConsented(false);
|
|
1714
1741
|
setErrorMessage(null);
|
|
1715
1742
|
setFetchError(null);
|
|
1716
1743
|
setIsLoading(true);
|
|
1744
|
+
setIsSubmittingAccept(false);
|
|
1745
|
+
setIsSubmittingDecline(false);
|
|
1717
1746
|
setShowAgeVerification(false);
|
|
1718
1747
|
setShowGuardianForm(false);
|
|
1719
1748
|
setIsSubmittingGuardian(false);
|
|
@@ -1856,6 +1885,23 @@ var RedactoNoticeConsent = ({
|
|
|
1856
1885
|
{}
|
|
1857
1886
|
);
|
|
1858
1887
|
setSelectedDataElements(initialDataElementState);
|
|
1888
|
+
if (isReconsentRequired && newCategorizedPurposes) {
|
|
1889
|
+
const initialSelections = {};
|
|
1890
|
+
newCategorizedPurposes.alreadyConsented.forEach((purpose) => {
|
|
1891
|
+
purpose.data_elements.forEach((element) => {
|
|
1892
|
+
var _a3, _b2;
|
|
1893
|
+
const combinedId = `${purpose.uuid}-${element.uuid}`;
|
|
1894
|
+
if ((_b2 = (_a3 = purposeSelections == null ? void 0 : purposeSelections[purpose.uuid]) == null ? void 0 : _a3.data_elements) == null ? void 0 : _b2[element.uuid]) {
|
|
1895
|
+
initialSelections[combinedId] = purposeSelections[purpose.uuid].data_elements[element.uuid].selected;
|
|
1896
|
+
} else {
|
|
1897
|
+
initialSelections[combinedId] = false;
|
|
1898
|
+
}
|
|
1899
|
+
});
|
|
1900
|
+
});
|
|
1901
|
+
setInitialDataElementSelections(initialSelections);
|
|
1902
|
+
} else {
|
|
1903
|
+
setInitialDataElementSelections({});
|
|
1904
|
+
}
|
|
1859
1905
|
if (consentContentData.detail.is_minor) {
|
|
1860
1906
|
setShowAgeVerification(true);
|
|
1861
1907
|
}
|
|
@@ -1941,7 +1987,7 @@ var RedactoNoticeConsent = ({
|
|
|
1941
1987
|
(el) => newState[`${purposeUuid}-${el.uuid}`]
|
|
1942
1988
|
);
|
|
1943
1989
|
} else {
|
|
1944
|
-
shouldCheckPurpose = purpose.data_elements.
|
|
1990
|
+
shouldCheckPurpose = purpose.data_elements.some(
|
|
1945
1991
|
(el) => newState[`${purposeUuid}-${el.uuid}`]
|
|
1946
1992
|
);
|
|
1947
1993
|
}
|
|
@@ -1956,6 +2002,7 @@ var RedactoNoticeConsent = ({
|
|
|
1956
2002
|
const handleAccept = async () => {
|
|
1957
2003
|
const submitController = new AbortController();
|
|
1958
2004
|
try {
|
|
2005
|
+
setIsSubmittingAccept(true);
|
|
1959
2006
|
setIsSubmitting(true);
|
|
1960
2007
|
setErrorMessage(null);
|
|
1961
2008
|
if (content) {
|
|
@@ -1963,14 +2010,31 @@ var RedactoNoticeConsent = ({
|
|
|
1963
2010
|
accessToken,
|
|
1964
2011
|
baseUrl,
|
|
1965
2012
|
noticeUuid: content.notice_uuid,
|
|
1966
|
-
purposes: content.purposes.map((purpose) =>
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
2013
|
+
purposes: content.purposes.map((purpose) => {
|
|
2014
|
+
const isAlreadyConsented = (categorizedPurposes == null ? void 0 : categorizedPurposes.alreadyConsented.some(
|
|
2015
|
+
(p) => p.uuid === purpose.uuid
|
|
2016
|
+
)) || false;
|
|
2017
|
+
let purposeSelected = selectedPurposes[purpose.uuid] || false;
|
|
2018
|
+
if (isAlreadyConsented) {
|
|
2019
|
+
const wasModified = purpose.data_elements.some((element) => {
|
|
2020
|
+
const combinedId = `${purpose.uuid}-${element.uuid}`;
|
|
2021
|
+
const currentSelection = selectedDataElements[combinedId] || false;
|
|
2022
|
+
const initialSelection = initialDataElementSelections[combinedId] || false;
|
|
2023
|
+
return currentSelection !== initialSelection;
|
|
2024
|
+
});
|
|
2025
|
+
if (wasModified) {
|
|
2026
|
+
purposeSelected = true;
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
return {
|
|
2030
|
+
...purpose,
|
|
2031
|
+
selected: purposeSelected,
|
|
2032
|
+
data_elements: purpose.data_elements.map((element) => ({
|
|
2033
|
+
...element,
|
|
2034
|
+
selected: element.required ? true : selectedDataElements[`${purpose.uuid}-${element.uuid}`] || false
|
|
2035
|
+
}))
|
|
2036
|
+
};
|
|
2037
|
+
}),
|
|
1974
2038
|
declined: false,
|
|
1975
2039
|
meta_data: applicationId ? { specific_uuid: applicationId } : void 0,
|
|
1976
2040
|
signal: submitController.signal
|
|
@@ -1990,11 +2054,19 @@ var RedactoNoticeConsent = ({
|
|
|
1990
2054
|
}
|
|
1991
2055
|
onError == null ? void 0 : onError(error);
|
|
1992
2056
|
} finally {
|
|
2057
|
+
setIsSubmittingAccept(false);
|
|
1993
2058
|
setIsSubmitting(false);
|
|
1994
2059
|
}
|
|
1995
2060
|
};
|
|
1996
2061
|
const handleDecline = useCallback(async () => {
|
|
1997
|
-
|
|
2062
|
+
try {
|
|
2063
|
+
setIsSubmittingDecline(true);
|
|
2064
|
+
setIsSubmitting(true);
|
|
2065
|
+
onDecline == null ? void 0 : onDecline();
|
|
2066
|
+
} finally {
|
|
2067
|
+
setIsSubmittingDecline(false);
|
|
2068
|
+
setIsSubmitting(false);
|
|
2069
|
+
}
|
|
1998
2070
|
}, [onDecline]);
|
|
1999
2071
|
const handleGuardianFormChange = useCallback(
|
|
2000
2072
|
(field, value) => {
|
|
@@ -2968,7 +3040,8 @@ var RedactoNoticeConsent = ({
|
|
|
2968
3040
|
onDataElementToggle: handleDataElementCheckboxChange,
|
|
2969
3041
|
getTranslatedText,
|
|
2970
3042
|
isAlreadyConsented: true,
|
|
2971
|
-
selectedLanguage
|
|
3043
|
+
selectedLanguage,
|
|
3044
|
+
initialDataElementSelections
|
|
2972
3045
|
},
|
|
2973
3046
|
`${purpose.uuid}-${selectedLanguage}`
|
|
2974
3047
|
)),
|
|
@@ -3038,12 +3111,12 @@ var RedactoNoticeConsent = ({
|
|
|
3038
3111
|
opacity: acceptDisabled ? 0.5 : 1
|
|
3039
3112
|
},
|
|
3040
3113
|
onClick: handleAccept,
|
|
3041
|
-
disabled: acceptDisabled,
|
|
3114
|
+
disabled: acceptDisabled || isSubmittingDecline,
|
|
3042
3115
|
"aria-label": getTranslatedText(
|
|
3043
3116
|
"confirm_button_text",
|
|
3044
3117
|
(content == null ? void 0 : content.confirm_button_text) || "Accept"
|
|
3045
3118
|
),
|
|
3046
|
-
children:
|
|
3119
|
+
children: isSubmittingAccept ? "Processing..." : getTranslatedText(
|
|
3047
3120
|
"confirm_button_text",
|
|
3048
3121
|
(content == null ? void 0 : content.confirm_button_text) || "Accept"
|
|
3049
3122
|
)
|
|
@@ -3057,15 +3130,16 @@ var RedactoNoticeConsent = ({
|
|
|
3057
3130
|
...styles.button,
|
|
3058
3131
|
...responsiveStyles.button,
|
|
3059
3132
|
...styles.cancelButton,
|
|
3060
|
-
...declineButtonStyle
|
|
3133
|
+
...declineButtonStyle,
|
|
3134
|
+
opacity: isSubmittingAccept ? 0.5 : 1
|
|
3061
3135
|
},
|
|
3062
3136
|
onClick: handleDecline,
|
|
3063
|
-
disabled:
|
|
3137
|
+
disabled: isSubmittingDecline || isSubmittingAccept,
|
|
3064
3138
|
"aria-label": getTranslatedText(
|
|
3065
3139
|
"decline_button_text",
|
|
3066
3140
|
(content == null ? void 0 : content.decline_button_text) || "Decline"
|
|
3067
3141
|
),
|
|
3068
|
-
children:
|
|
3142
|
+
children: isSubmittingDecline ? "Processing..." : getTranslatedText(
|
|
3069
3143
|
"decline_button_text",
|
|
3070
3144
|
(content == null ? void 0 : content.decline_button_text) || "Decline"
|
|
3071
3145
|
)
|
|
@@ -3816,10 +3890,19 @@ var RedactoConsentInlineComponent = ({
|
|
|
3816
3890
|
const requiredElements = purpose.data_elements.filter(
|
|
3817
3891
|
(el) => el.required
|
|
3818
3892
|
);
|
|
3819
|
-
|
|
3893
|
+
let shouldCheckPurpose;
|
|
3894
|
+
if (requiredElements.length > 0) {
|
|
3895
|
+
shouldCheckPurpose = requiredElements.every(
|
|
3896
|
+
(el) => newState[`${purposeUuid}-${el.uuid}`]
|
|
3897
|
+
);
|
|
3898
|
+
} else {
|
|
3899
|
+
shouldCheckPurpose = purpose.data_elements.some(
|
|
3900
|
+
(el) => newState[`${purposeUuid}-${el.uuid}`]
|
|
3901
|
+
);
|
|
3902
|
+
}
|
|
3820
3903
|
setSelectedPurposes((prevPurposes) => ({
|
|
3821
3904
|
...prevPurposes,
|
|
3822
|
-
[purposeUuid]:
|
|
3905
|
+
[purposeUuid]: shouldCheckPurpose
|
|
3823
3906
|
}));
|
|
3824
3907
|
return newState;
|
|
3825
3908
|
});
|
package/package.json
CHANGED
|
@@ -45,7 +45,10 @@ import { useMediaQuery } from "./useMediaQuery";
|
|
|
45
45
|
* Supports both regular consent and reconsent UI modes.
|
|
46
46
|
*/
|
|
47
47
|
const PurposeItem: React.FC<
|
|
48
|
-
PurposeItemProps & {
|
|
48
|
+
PurposeItemProps & {
|
|
49
|
+
selectedLanguage: string;
|
|
50
|
+
initialDataElementSelections?: Record<string, boolean>;
|
|
51
|
+
}
|
|
49
52
|
> = ({
|
|
50
53
|
purpose,
|
|
51
54
|
selectedPurposes,
|
|
@@ -60,6 +63,7 @@ const PurposeItem: React.FC<
|
|
|
60
63
|
getTranslatedText,
|
|
61
64
|
isAlreadyConsented,
|
|
62
65
|
selectedLanguage,
|
|
66
|
+
initialDataElementSelections = {},
|
|
63
67
|
}) => {
|
|
64
68
|
const headingStyle = useMemo(
|
|
65
69
|
() => ({
|
|
@@ -266,39 +270,71 @@ const PurposeItem: React.FC<
|
|
|
266
270
|
/>
|
|
267
271
|
)}
|
|
268
272
|
{isAlreadyConsented && (
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
273
|
+
<>
|
|
274
|
+
{(() => {
|
|
275
|
+
const combinedId = `${purpose.uuid}-${dataElement.uuid}`;
|
|
276
|
+
const isCurrentlySelected =
|
|
277
|
+
selectedDataElements[combinedId] || false;
|
|
278
|
+
const wasOriginallySelected =
|
|
279
|
+
initialDataElementSelections[combinedId] || false;
|
|
280
|
+
|
|
281
|
+
// Show green tick only if it was originally selected
|
|
282
|
+
// Show checkbox if it's newly selected or unselected
|
|
283
|
+
if (isCurrentlySelected && wasOriginallySelected) {
|
|
284
|
+
return (
|
|
285
|
+
<div
|
|
286
|
+
style={{
|
|
287
|
+
display: "flex",
|
|
288
|
+
alignItems: "center",
|
|
289
|
+
justifyContent: "center",
|
|
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
|
+
);
|
|
335
|
+
}
|
|
336
|
+
})()}
|
|
337
|
+
</>
|
|
302
338
|
)}
|
|
303
339
|
</div>
|
|
304
340
|
))}
|
|
@@ -776,6 +812,8 @@ export const RedactoNoticeConsent = ({
|
|
|
776
812
|
// Loading and submission states
|
|
777
813
|
const [isLoading, setIsLoading] = useState(true);
|
|
778
814
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
815
|
+
const [isSubmittingAccept, setIsSubmittingAccept] = useState(false);
|
|
816
|
+
const [isSubmittingDecline, setIsSubmittingDecline] = useState(false);
|
|
779
817
|
|
|
780
818
|
// Content and consent data
|
|
781
819
|
const [content, setContent] = useState<
|
|
@@ -793,6 +831,9 @@ export const RedactoNoticeConsent = ({
|
|
|
793
831
|
const [selectedDataElements, setSelectedDataElements] = useState<
|
|
794
832
|
Record<string, boolean>
|
|
795
833
|
>({});
|
|
834
|
+
// Track initial data element selections for already-consented purposes
|
|
835
|
+
const [initialDataElementSelections, setInitialDataElementSelections] =
|
|
836
|
+
useState<Record<string, boolean>>({});
|
|
796
837
|
const [collapsedPurposes, setCollapsedPurposes] = useState<
|
|
797
838
|
Record<string, boolean>
|
|
798
839
|
>({});
|
|
@@ -987,11 +1028,14 @@ export const RedactoNoticeConsent = ({
|
|
|
987
1028
|
setCategorizedPurposes(null);
|
|
988
1029
|
setSelectedPurposes({});
|
|
989
1030
|
setSelectedDataElements({});
|
|
1031
|
+
setInitialDataElementSelections({});
|
|
990
1032
|
setCollapsedPurposes({});
|
|
991
1033
|
setHasAlreadyConsented(false);
|
|
992
1034
|
setErrorMessage(null);
|
|
993
1035
|
setFetchError(null);
|
|
994
1036
|
setIsLoading(true);
|
|
1037
|
+
setIsSubmittingAccept(false);
|
|
1038
|
+
setIsSubmittingDecline(false);
|
|
995
1039
|
|
|
996
1040
|
// Clear age verification and guardian form state
|
|
997
1041
|
setShowAgeVerification(false);
|
|
@@ -1213,6 +1257,27 @@ export const RedactoNoticeConsent = ({
|
|
|
1213
1257
|
{}
|
|
1214
1258
|
);
|
|
1215
1259
|
setSelectedDataElements(initialDataElementState);
|
|
1260
|
+
// Store initial selections for already-consented purposes to track modifications
|
|
1261
|
+
if (isReconsentRequired && newCategorizedPurposes) {
|
|
1262
|
+
const initialSelections: Record<string, boolean> = {};
|
|
1263
|
+
newCategorizedPurposes.alreadyConsented.forEach((purpose) => {
|
|
1264
|
+
purpose.data_elements.forEach((element) => {
|
|
1265
|
+
const combinedId = `${purpose.uuid}-${element.uuid}`;
|
|
1266
|
+
if (
|
|
1267
|
+
purposeSelections?.[purpose.uuid]?.data_elements?.[element.uuid]
|
|
1268
|
+
) {
|
|
1269
|
+
initialSelections[combinedId] =
|
|
1270
|
+
purposeSelections[purpose.uuid].data_elements[element.uuid]
|
|
1271
|
+
.selected;
|
|
1272
|
+
} else {
|
|
1273
|
+
initialSelections[combinedId] = false;
|
|
1274
|
+
}
|
|
1275
|
+
});
|
|
1276
|
+
});
|
|
1277
|
+
setInitialDataElementSelections(initialSelections);
|
|
1278
|
+
} else {
|
|
1279
|
+
setInitialDataElementSelections({});
|
|
1280
|
+
}
|
|
1216
1281
|
|
|
1217
1282
|
// Check if age verification is required - set this after all content is loaded
|
|
1218
1283
|
// but before setIsLoading(false) to ensure proper render order
|
|
@@ -1329,8 +1394,8 @@ export const RedactoNoticeConsent = ({
|
|
|
1329
1394
|
(el) => newState[`${purposeUuid}-${el.uuid}`]
|
|
1330
1395
|
);
|
|
1331
1396
|
} else {
|
|
1332
|
-
// No required elements: check purpose
|
|
1333
|
-
shouldCheckPurpose = purpose.data_elements.
|
|
1397
|
+
// No required elements: check purpose when ANY element is selected
|
|
1398
|
+
shouldCheckPurpose = purpose.data_elements.some(
|
|
1334
1399
|
(el) => newState[`${purposeUuid}-${el.uuid}`]
|
|
1335
1400
|
);
|
|
1336
1401
|
}
|
|
@@ -1350,24 +1415,52 @@ export const RedactoNoticeConsent = ({
|
|
|
1350
1415
|
const submitController = new AbortController();
|
|
1351
1416
|
|
|
1352
1417
|
try {
|
|
1353
|
-
|
|
1418
|
+
setIsSubmittingAccept(true);
|
|
1419
|
+
setIsSubmitting(true); // Keep for backward compatibility with acceptDisabled
|
|
1354
1420
|
setErrorMessage(null);
|
|
1355
1421
|
if (content) {
|
|
1356
1422
|
await submitConsentEvent({
|
|
1357
1423
|
accessToken,
|
|
1358
1424
|
baseUrl,
|
|
1359
1425
|
noticeUuid: content.notice_uuid,
|
|
1360
|
-
purposes: content.purposes.map((purpose) =>
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1426
|
+
purposes: content.purposes.map((purpose) => {
|
|
1427
|
+
// Check if this is an already-consented purpose that was modified
|
|
1428
|
+
const isAlreadyConsented =
|
|
1429
|
+
categorizedPurposes?.alreadyConsented.some(
|
|
1430
|
+
(p) => p.uuid === purpose.uuid
|
|
1431
|
+
) || false;
|
|
1432
|
+
|
|
1433
|
+
let purposeSelected = selectedPurposes[purpose.uuid] || false;
|
|
1434
|
+
|
|
1435
|
+
// If it's an already-consented purpose, check if any data elements were modified
|
|
1436
|
+
if (isAlreadyConsented) {
|
|
1437
|
+
const wasModified = purpose.data_elements.some((element) => {
|
|
1438
|
+
const combinedId = `${purpose.uuid}-${element.uuid}`;
|
|
1439
|
+
const currentSelection =
|
|
1440
|
+
selectedDataElements[combinedId] || false;
|
|
1441
|
+
const initialSelection =
|
|
1442
|
+
initialDataElementSelections[combinedId] || false;
|
|
1443
|
+
return currentSelection !== initialSelection;
|
|
1444
|
+
});
|
|
1445
|
+
|
|
1446
|
+
// If modified, treat as new selection (mark purpose as selected)
|
|
1447
|
+
if (wasModified) {
|
|
1448
|
+
purposeSelected = true;
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
return {
|
|
1453
|
+
...purpose,
|
|
1454
|
+
selected: purposeSelected,
|
|
1455
|
+
data_elements: purpose.data_elements.map((element) => ({
|
|
1456
|
+
...element,
|
|
1457
|
+
selected: element.required
|
|
1458
|
+
? true
|
|
1459
|
+
: selectedDataElements[`${purpose.uuid}-${element.uuid}`] ||
|
|
1460
|
+
false,
|
|
1461
|
+
})),
|
|
1462
|
+
};
|
|
1463
|
+
}),
|
|
1371
1464
|
declined: false,
|
|
1372
1465
|
meta_data: applicationId
|
|
1373
1466
|
? { specific_uuid: applicationId }
|
|
@@ -1391,36 +1484,42 @@ export const RedactoNoticeConsent = ({
|
|
|
1391
1484
|
}
|
|
1392
1485
|
onError?.(error as Error);
|
|
1393
1486
|
} finally {
|
|
1394
|
-
|
|
1487
|
+
setIsSubmittingAccept(false);
|
|
1488
|
+
setIsSubmitting(false); // Keep for backward compatibility
|
|
1395
1489
|
}
|
|
1396
1490
|
};
|
|
1397
1491
|
|
|
1398
1492
|
const handleDecline = useCallback(async () => {
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1493
|
+
try {
|
|
1494
|
+
setIsSubmittingDecline(true);
|
|
1495
|
+
setIsSubmitting(true); // Keep for backward compatibility
|
|
1496
|
+
// try {
|
|
1497
|
+
// if (content) {
|
|
1498
|
+
// await submitConsentEvent(
|
|
1499
|
+
// accessToken,
|
|
1500
|
+
// content.notice_uuid,
|
|
1501
|
+
// content.purposes.map((purpose) => ({
|
|
1502
|
+
// ...purpose,
|
|
1503
|
+
// selected: false,
|
|
1504
|
+
// data_elements: purpose.data_elements.map((element) => ({
|
|
1505
|
+
// ...element,
|
|
1506
|
+
// selected: element.required ? true : false,
|
|
1507
|
+
// })),
|
|
1508
|
+
// })),
|
|
1509
|
+
// true
|
|
1510
|
+
// );
|
|
1511
|
+
// }
|
|
1512
|
+
// } catch (error) {
|
|
1513
|
+
// console.error("Error submitting consent:", error);
|
|
1514
|
+
// onError?.(error as Error);
|
|
1515
|
+
// } finally {
|
|
1516
|
+
// setIsSubmitting(false);
|
|
1517
|
+
// }
|
|
1518
|
+
onDecline?.();
|
|
1519
|
+
} finally {
|
|
1520
|
+
setIsSubmittingDecline(false);
|
|
1521
|
+
setIsSubmitting(false); // Keep for backward compatibility
|
|
1522
|
+
}
|
|
1424
1523
|
}, [onDecline]);
|
|
1425
1524
|
|
|
1426
1525
|
// Guardian form handlers
|
|
@@ -2606,6 +2705,7 @@ export const RedactoNoticeConsent = ({
|
|
|
2606
2705
|
getTranslatedText={getTranslatedText}
|
|
2607
2706
|
isAlreadyConsented={true}
|
|
2608
2707
|
selectedLanguage={selectedLanguage}
|
|
2708
|
+
initialDataElementSelections={initialDataElementSelections}
|
|
2609
2709
|
/>
|
|
2610
2710
|
))}
|
|
2611
2711
|
|
|
@@ -2671,13 +2771,13 @@ export const RedactoNoticeConsent = ({
|
|
|
2671
2771
|
opacity: acceptDisabled ? 0.5 : 1,
|
|
2672
2772
|
}}
|
|
2673
2773
|
onClick={handleAccept}
|
|
2674
|
-
disabled={acceptDisabled}
|
|
2774
|
+
disabled={acceptDisabled || isSubmittingDecline}
|
|
2675
2775
|
aria-label={getTranslatedText(
|
|
2676
2776
|
"confirm_button_text",
|
|
2677
2777
|
content?.confirm_button_text || "Accept"
|
|
2678
2778
|
)}
|
|
2679
2779
|
>
|
|
2680
|
-
{
|
|
2780
|
+
{isSubmittingAccept
|
|
2681
2781
|
? "Processing..."
|
|
2682
2782
|
: getTranslatedText(
|
|
2683
2783
|
"confirm_button_text",
|
|
@@ -2691,15 +2791,16 @@ export const RedactoNoticeConsent = ({
|
|
|
2691
2791
|
...responsiveStyles.button,
|
|
2692
2792
|
...styles.cancelButton,
|
|
2693
2793
|
...declineButtonStyle,
|
|
2794
|
+
opacity: isSubmittingAccept ? 0.5 : 1,
|
|
2694
2795
|
}}
|
|
2695
2796
|
onClick={handleDecline}
|
|
2696
|
-
disabled={
|
|
2797
|
+
disabled={isSubmittingDecline || isSubmittingAccept}
|
|
2697
2798
|
aria-label={getTranslatedText(
|
|
2698
2799
|
"decline_button_text",
|
|
2699
2800
|
content?.decline_button_text || "Decline"
|
|
2700
2801
|
)}
|
|
2701
2802
|
>
|
|
2702
|
-
{
|
|
2803
|
+
{isSubmittingDecline
|
|
2703
2804
|
? "Processing..."
|
|
2704
2805
|
: getTranslatedText(
|
|
2705
2806
|
"decline_button_text",
|
|
@@ -274,17 +274,27 @@ const RedactoConsentInlineComponent = ({
|
|
|
274
274
|
};
|
|
275
275
|
|
|
276
276
|
// Check if all required elements are checked
|
|
277
|
-
// Purpose checkbox should only be checked when ALL required data elements are checked
|
|
278
277
|
const requiredElements = purpose.data_elements.filter(
|
|
279
278
|
(el) => el.required
|
|
280
279
|
);
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
280
|
+
|
|
281
|
+
let shouldCheckPurpose: boolean;
|
|
282
|
+
|
|
283
|
+
if (requiredElements.length > 0) {
|
|
284
|
+
// Has required elements: check purpose when all required are selected
|
|
285
|
+
shouldCheckPurpose = requiredElements.every(
|
|
286
|
+
(el) => newState[`${purposeUuid}-${el.uuid}`]
|
|
287
|
+
);
|
|
288
|
+
} else {
|
|
289
|
+
// No required elements: check purpose when ANY element is selected
|
|
290
|
+
shouldCheckPurpose = purpose.data_elements.some(
|
|
291
|
+
(el) => newState[`${purposeUuid}-${el.uuid}`]
|
|
292
|
+
);
|
|
293
|
+
}
|
|
284
294
|
|
|
285
295
|
setSelectedPurposes((prevPurposes) => ({
|
|
286
296
|
...prevPurposes,
|
|
287
|
-
[purposeUuid]:
|
|
297
|
+
[purposeUuid]: shouldCheckPurpose,
|
|
288
298
|
}));
|
|
289
299
|
|
|
290
300
|
return newState;
|
package/.turbo/turbo-build.log
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
> @redacto.io/consent-sdk-react@2.1.0 build /Users/sadanmianredacto/Developer/consent-sdk/packages/consent-sdk-react
|
|
4
|
-
> tsup
|
|
5
|
-
|
|
6
|
-
[34mCLI[39m Building entry: {"index":"src/index.ts"}
|
|
7
|
-
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
8
|
-
[34mCLI[39m tsup v8.5.1
|
|
9
|
-
[34mCLI[39m Using tsup config: /Users/sadanmianredacto/Developer/consent-sdk/packages/consent-sdk-react/tsup.config.ts
|
|
10
|
-
[34mCLI[39m Target: es2019
|
|
11
|
-
[34mCLI[39m Cleaning output folder
|
|
12
|
-
[34mESM[39m Build start
|
|
13
|
-
[34mCJS[39m Build start
|
|
14
|
-
[32mESM[39m [1mdist/index.mjs [22m[32m151.83 KB[39m
|
|
15
|
-
[32mESM[39m ⚡️ Build success in 13ms
|
|
16
|
-
[32mCJS[39m [1mdist/index.js [22m[32m157.63 KB[39m
|
|
17
|
-
[32mCJS[39m ⚡️ Build success in 13ms
|
|
18
|
-
DTS Build start
|
|
19
|
-
DTS ⚡️ Build success in 671ms
|
|
20
|
-
DTS dist/index.d.mts 2.79 KB
|
|
21
|
-
DTS dist/index.d.ts 2.79 KB
|