@redacto.io/consent-sdk-react 10.2.0 → 10.2.1-beta.1

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.js CHANGED
@@ -745,7 +745,9 @@ var DPO_AUDIO_ELEMENT_MAP = [
745
745
  { audioKey: "dpo_anchor_text_audio_url", elementId: "dpo-dpo-link" }
746
746
  ];
747
747
  var PRODUCT_SECTION_COPY = {
748
- toggleSelection: (productName, purposeCount) => `Toggle all ${purposeCount} purposes for ${productName}`
748
+ toggleSelection: (productName, purposeCount) => `Toggle all ${purposeCount} purposes for ${productName}`,
749
+ toggleRequired: (productName, purposeCount) => `Toggle the ${purposeCount} required purposes for ${productName}`,
750
+ settled: (productName) => `Already consented to ${productName}`
749
751
  };
750
752
 
751
753
  // src/RedactoNoticeConsent/styles.ts
@@ -1343,6 +1345,24 @@ var purposeCoversProduct = (purpose, productUuid) => {
1343
1345
  return scope.includes(productUuid);
1344
1346
  };
1345
1347
  var isMultiProduct = (products) => Boolean(products && products.length > 1);
1348
+ var serverMarksMandatoryProducts = (products) => Boolean(
1349
+ products && products.length > 0 && products.every((product) => product.mandatory !== void 0)
1350
+ );
1351
+ var gatedProductUuids = (products) => new Set(
1352
+ (products != null ? products : []).filter((product) => product.mandatory === true).map((product) => product.uuid)
1353
+ );
1354
+ var rowProductUuid = (row, products) => {
1355
+ var _a;
1356
+ return (_a = row.productUuid) != null ? _a : (products == null ? void 0 : products.length) === 1 ? products[0].uuid : void 0;
1357
+ };
1358
+ var rowsUnderGate = (rows, products) => {
1359
+ const gated = gatedProductUuids(products);
1360
+ if (gated.size === 0) return [];
1361
+ return rows.filter((row) => {
1362
+ const productUuid = rowProductUuid(row, products);
1363
+ return productUuid !== void 0 && gated.has(productUuid);
1364
+ });
1365
+ };
1346
1366
  var groupPurposesByProduct = (products, purposes, order) => {
1347
1367
  if (!products || products.length === 0) return [];
1348
1368
  return products.map((product) => {
@@ -1442,6 +1462,7 @@ var buildPreselection = (purposes, mode) => {
1442
1462
  return { selectedPurposes, selectedDataElements };
1443
1463
  };
1444
1464
  var hasRequiredDataElement = (purpose) => purpose.data_elements.some((element) => element.required);
1465
+ var isBlockingRequiredElement = (element) => element.required && element.enabled;
1445
1466
 
1446
1467
  // src/RedactoNoticeConsent/layout.ts
1447
1468
  var resolveLogoPosition = (raw) => raw === "center" || raw === "right" ? raw : DEFAULT_LOGO_POSITION;
@@ -2075,6 +2096,16 @@ var CHEVRON_WRAPPER_STYLE = {
2075
2096
  justifyContent: "center",
2076
2097
  marginLeft: "5px"
2077
2098
  };
2099
+ var reviewIndicatorStyle = (size) => ({
2100
+ display: "flex",
2101
+ alignItems: "center",
2102
+ justifyContent: "center",
2103
+ width: `${size}px`,
2104
+ height: `${size}px`,
2105
+ borderRadius: "50%",
2106
+ flexShrink: 0,
2107
+ boxSizing: "border-box"
2108
+ });
2078
2109
  var CollapseChevron = ({ open, color }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: CHEVRON_WRAPPER_STYLE, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2079
2110
  "svg",
2080
2111
  {
@@ -2097,7 +2128,9 @@ var CollapseChevron = ({ open, color }) => /* @__PURE__ */ (0, import_jsx_runtim
2097
2128
  ) });
2098
2129
  var ProductSectionHeader = ({
2099
2130
  product,
2131
+ isRequired,
2100
2132
  purposeCount,
2133
+ requiredOnly,
2101
2134
  allSelected,
2102
2135
  someSelected,
2103
2136
  collapsed,
@@ -2137,7 +2170,7 @@ var ProductSectionHeader = ({
2137
2170
  children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CollapseChevron, { open: !collapsed, color: headingColor })
2138
2171
  }
2139
2172
  ),
2140
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: styles4.optionTextContainer, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2173
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: styles4.optionTextContainer, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2141
2174
  "h2",
2142
2175
  {
2143
2176
  id: `product-name-${product.uuid}`,
@@ -2146,11 +2179,46 @@ var ProductSectionHeader = ({
2146
2179
  ...responsiveStyles.optionTitle,
2147
2180
  color: headingColor
2148
2181
  },
2149
- children: name
2182
+ children: [
2183
+ name,
2184
+ isRequired && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RequiredMarker, {})
2185
+ ]
2150
2186
  }
2151
2187
  ) })
2152
2188
  ] }),
2153
- !locked && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2189
+ locked ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2190
+ "div",
2191
+ {
2192
+ role: "img",
2193
+ style: {
2194
+ ...reviewIndicatorStyle(20),
2195
+ backgroundColor: "#10B981",
2196
+ marginLeft: "12px"
2197
+ },
2198
+ "aria-label": PRODUCT_SECTION_COPY.settled(name),
2199
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2200
+ "svg",
2201
+ {
2202
+ width: "12",
2203
+ height: "12",
2204
+ viewBox: "0 0 12 12",
2205
+ fill: "none",
2206
+ xmlns: "http://www.w3.org/2000/svg",
2207
+ "aria-hidden": "true",
2208
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2209
+ "path",
2210
+ {
2211
+ d: "M4 6L5.5 7.5L8.5 4.5",
2212
+ stroke: "white",
2213
+ strokeWidth: "2",
2214
+ strokeLinecap: "round",
2215
+ strokeLinejoin: "round"
2216
+ }
2217
+ )
2218
+ }
2219
+ )
2220
+ }
2221
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2154
2222
  "input",
2155
2223
  {
2156
2224
  ref: checkboxRef,
@@ -2158,7 +2226,7 @@ var ProductSectionHeader = ({
2158
2226
  type: "checkbox",
2159
2227
  checked: allSelected,
2160
2228
  onChange: handleToggleSelect,
2161
- "aria-label": PRODUCT_SECTION_COPY.toggleSelection(name, purposeCount)
2229
+ "aria-label": requiredOnly ? PRODUCT_SECTION_COPY.toggleRequired(name, purposeCount) : PRODUCT_SECTION_COPY.toggleSelection(name, purposeCount)
2162
2230
  }
2163
2231
  )
2164
2232
  ] });
@@ -2166,6 +2234,7 @@ var ProductSectionHeader = ({
2166
2234
  var PurposeItem = ({
2167
2235
  purpose,
2168
2236
  productUuid,
2237
+ productIsRequired,
2169
2238
  selectedPurposes,
2170
2239
  collapsedPurposes,
2171
2240
  selectedDataElements,
@@ -2235,7 +2304,7 @@ var PurposeItem = ({
2235
2304
  },
2236
2305
  children: [
2237
2306
  getTranslatedText(`purposes.name`, purpose.name, purpose.uuid),
2238
- hasRequiredDataElement(purpose) && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RequiredMarker, {})
2307
+ productIsRequired !== false && hasRequiredDataElement(purpose) && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RequiredMarker, {})
2239
2308
  ]
2240
2309
  },
2241
2310
  `purpose-name-${purpose.uuid}-${selectedLanguage}`
@@ -2278,13 +2347,9 @@ var PurposeItem = ({
2278
2347
  isAlreadyConsented && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2279
2348
  "div",
2280
2349
  {
2350
+ role: "img",
2281
2351
  style: {
2282
- display: "flex",
2283
- alignItems: "center",
2284
- justifyContent: "center",
2285
- width: "20px",
2286
- height: "20px",
2287
- borderRadius: "50%",
2352
+ ...reviewIndicatorStyle(20),
2288
2353
  backgroundColor: "#10B981",
2289
2354
  marginLeft: "12px"
2290
2355
  },
@@ -2372,13 +2437,9 @@ var PurposeItem = ({
2372
2437
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2373
2438
  "div",
2374
2439
  {
2440
+ role: "img",
2375
2441
  style: {
2376
- display: "flex",
2377
- alignItems: "center",
2378
- justifyContent: "center",
2379
- width: "16px",
2380
- height: "16px",
2381
- borderRadius: "50%",
2442
+ ...reviewIndicatorStyle(16),
2382
2443
  backgroundColor: "#10B981"
2383
2444
  },
2384
2445
  "aria-label": `Consented to ${getTranslatedText(
@@ -2413,13 +2474,9 @@ var PurposeItem = ({
2413
2474
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2414
2475
  "div",
2415
2476
  {
2477
+ role: "img",
2416
2478
  style: {
2417
- display: "flex",
2418
- alignItems: "center",
2419
- justifyContent: "center",
2420
- width: "16px",
2421
- height: "16px",
2422
- borderRadius: "50%",
2479
+ ...reviewIndicatorStyle(16),
2423
2480
  border: "2px solid #d0d5dd",
2424
2481
  backgroundColor: "transparent"
2425
2482
  },
@@ -3349,7 +3406,8 @@ var RedactoNoticeConsent = ({
3349
3406
  digilockerCallbackUrl = "/digilocker-callback",
3350
3407
  // Default callback path
3351
3408
  includeFullyConsentedData = false,
3352
- reviewModeButtonText = "Continue"
3409
+ reviewModeButtonText = "Continue",
3410
+ defaultOpenProducts
3353
3411
  }) => {
3354
3412
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
3355
3413
  const sandboxParams = (0, import_react3.useMemo)(
@@ -3421,6 +3479,8 @@ var RedactoNoticeConsent = ({
3421
3479
  const [collapsedProducts, setCollapsedProducts] = (0, import_react3.useState)({});
3422
3480
  const [collapsedPurposes, setCollapsedPurposes] = (0, import_react3.useState)({});
3423
3481
  const [hasAlreadyConsented, setHasAlreadyConsented] = (0, import_react3.useState)(false);
3482
+ const [consentedProducts, setConsentedProducts] = (0, import_react3.useState)({});
3483
+ const [consentedPurposes, setConsentedPurposes] = (0, import_react3.useState)({});
3424
3484
  const [isFullyConsentedReviewMode, setIsFullyConsentedReviewMode] = (0, import_react3.useState)(false);
3425
3485
  const [isLanguageDropdownOpen, setIsLanguageDropdownOpen] = (0, import_react3.useState)(false);
3426
3486
  const [selectedLanguage, setSelectedLanguage] = (0, import_react3.useState)(language);
@@ -3604,42 +3664,67 @@ var RedactoNoticeConsent = ({
3604
3664
  },
3605
3665
  [content]
3606
3666
  );
3607
- const areAllRequiredElementsChecked = (0, import_react3.useMemo)(() => {
3608
- var _a2;
3609
- if (!content) return false;
3610
- if (purposeRows.length === 0) return true;
3611
- const isSatisfied = ({
3612
- purpose,
3613
- productUuid
3614
- }) => {
3615
- const key = productPurposeKey(productUuid, purpose.uuid);
3616
- const requiredElements = purpose.data_elements.filter(
3617
- (element) => element.required && element.enabled
3618
- );
3619
- return requiredElements.every(
3620
- (element) => selectedDataElements[`${key}-${element.uuid}`]
3621
- );
3622
- };
3623
- const rowsByProduct = /* @__PURE__ */ new Map();
3624
- for (const row of purposeRows) {
3625
- const groupKey = (_a2 = row.productUuid) != null ? _a2 : "";
3626
- const group = rowsByProduct.get(groupKey);
3627
- if (group) {
3628
- group.push(row);
3629
- } else {
3630
- rowsByProduct.set(groupKey, [row]);
3667
+ const productMandatoryIsAuthoritative = (0, import_react3.useMemo)(
3668
+ () => serverMarksMandatoryProducts(content == null ? void 0 : content.products),
3669
+ [content]
3670
+ );
3671
+ const gatedProducts = (0, import_react3.useMemo)(
3672
+ () => gatedProductUuids(content == null ? void 0 : content.products),
3673
+ [content]
3674
+ );
3675
+ const productIsRequiredFor = (0, import_react3.useCallback)(
3676
+ (productUuid) => {
3677
+ var _a2;
3678
+ if (!productMandatoryIsAuthoritative) return void 0;
3679
+ const resolved = productUuid != null ? productUuid : ((_a2 = content == null ? void 0 : content.products) == null ? void 0 : _a2.length) === 1 ? content.products[0].uuid : void 0;
3680
+ return resolved !== void 0 && gatedProducts.has(resolved);
3681
+ },
3682
+ [productMandatoryIsAuthoritative, gatedProducts, content]
3683
+ );
3684
+ const confirmSatisfiedWith = (0, import_react3.useCallback)(
3685
+ (selection) => {
3686
+ var _a2;
3687
+ if (!content) return false;
3688
+ if (purposeRows.length === 0) return true;
3689
+ const isSatisfied = ({
3690
+ purpose,
3691
+ productUuid
3692
+ }) => {
3693
+ const key = productPurposeKey(productUuid, purpose.uuid);
3694
+ const requiredElements = purpose.data_elements.filter(
3695
+ isBlockingRequiredElement
3696
+ );
3697
+ return requiredElements.every(
3698
+ (element) => selection[`${key}-${element.uuid}`]
3699
+ );
3700
+ };
3701
+ if (serverMarksMandatoryProducts(content.products)) {
3702
+ return rowsUnderGate(purposeRows, content.products).every(isSatisfied);
3631
3703
  }
3632
- }
3633
- const groups = [...rowsByProduct.values()];
3634
- const everyProductHasMandatory = groups.every(
3635
- (rows) => rows.some(
3636
- ({ purpose }) => purpose.data_elements.some(
3637
- (element) => element.required && element.enabled
3704
+ const rowsByProduct = /* @__PURE__ */ new Map();
3705
+ for (const row of purposeRows) {
3706
+ const groupKey = (_a2 = row.productUuid) != null ? _a2 : "";
3707
+ const group = rowsByProduct.get(groupKey);
3708
+ if (group) {
3709
+ group.push(row);
3710
+ } else {
3711
+ rowsByProduct.set(groupKey, [row]);
3712
+ }
3713
+ }
3714
+ const groups = [...rowsByProduct.values()];
3715
+ const everyProductHasMandatory = groups.every(
3716
+ (rows) => rows.some(
3717
+ ({ purpose }) => purpose.data_elements.some(isBlockingRequiredElement)
3638
3718
  )
3639
- )
3640
- );
3641
- return everyProductHasMandatory ? groups.some((rows) => rows.every(isSatisfied)) : purposeRows.every(isSatisfied);
3642
- }, [content, purposeRows, selectedDataElements]);
3719
+ );
3720
+ return everyProductHasMandatory ? groups.some((rows) => rows.every(isSatisfied)) : purposeRows.every(isSatisfied);
3721
+ },
3722
+ [content, purposeRows]
3723
+ );
3724
+ const areAllRequiredElementsChecked = (0, import_react3.useMemo)(
3725
+ () => confirmSatisfiedWith(selectedDataElements),
3726
+ [confirmSatisfiedWith, selectedDataElements]
3727
+ );
3643
3728
  const acceptDisabled = isSubmitting || !areAllRequiredElementsChecked;
3644
3729
  (0, import_react3.useEffect)(() => {
3645
3730
  var _a2, _b2;
@@ -3652,6 +3737,7 @@ var RedactoNoticeConsent = ({
3652
3737
  setInitialDataElementSelections({});
3653
3738
  setCollapsedPurposes({});
3654
3739
  setCollapsedProducts({});
3740
+ setConsentedProducts({});
3655
3741
  setHasAlreadyConsented(false);
3656
3742
  setErrorMessage(null);
3657
3743
  setFetchError(null);
@@ -3835,15 +3921,18 @@ var RedactoNoticeConsent = ({
3835
3921
  {}
3836
3922
  );
3837
3923
  setCollapsedPurposes(initialCollapsedState);
3924
+ const openByDefault = new Set(defaultOpenProducts != null ? defaultOpenProducts : []);
3925
+ const groups = groupPurposesByProduct(
3926
+ activeConfig.products,
3927
+ activeConfig.purposes,
3928
+ activeConfig.product_purpose_order
3929
+ );
3930
+ const soleSection = groups.length === 1;
3838
3931
  setCollapsedProducts(
3839
- groupPurposesByProduct(
3840
- activeConfig.products,
3841
- activeConfig.purposes,
3842
- activeConfig.product_purpose_order
3843
- ).reduce(
3844
- (acc, group, index) => ({
3932
+ groups.reduce(
3933
+ (acc, group) => ({
3845
3934
  ...acc,
3846
- [group.product.uuid]: index > 0
3935
+ [group.product.uuid]: !soleSection && !openByDefault.has(group.product.uuid)
3847
3936
  }),
3848
3937
  {}
3849
3938
  )
@@ -3870,9 +3959,10 @@ var RedactoNoticeConsent = ({
3870
3959
  activeConfig.purposes,
3871
3960
  activeConfig.product_purpose_order
3872
3961
  );
3962
+ const productPurposeSelections = consentContentData.detail.product_purpose_selections;
3873
3963
  const recordedFor = (purposeUuid, productUuid) => {
3874
- var _a3, _b2;
3875
- const perProduct = productUuid ? (_b2 = (_a3 = activeConfig.product_purpose_selections) == null ? void 0 : _a3[productUuid]) == null ? void 0 : _b2[purposeUuid] : void 0;
3964
+ var _a3;
3965
+ const perProduct = productUuid ? (_a3 = productPurposeSelections == null ? void 0 : productPurposeSelections[productUuid]) == null ? void 0 : _a3[purposeUuid] : void 0;
3876
3966
  return perProduct != null ? perProduct : purposeSelections == null ? void 0 : purposeSelections[purposeUuid];
3877
3967
  };
3878
3968
  const initialPurposeState = seedRows.reduce(
@@ -3912,55 +4002,63 @@ var RedactoNoticeConsent = ({
3912
4002
  {}
3913
4003
  );
3914
4004
  setSelectedDataElements(initialDataElementState);
4005
+ const rowIsRecorded = ({
4006
+ purpose,
4007
+ productUuid
4008
+ }) => {
4009
+ var _a3;
4010
+ return ((_a3 = recordedFor(purpose.uuid, productUuid)) == null ? void 0 : _a3.selected) === true;
4011
+ };
4012
+ const recordedElementSelections = () => {
4013
+ const selections = {};
4014
+ seedRows.filter(rowIsRecorded).forEach(({ purpose, productUuid }) => {
4015
+ const rowKey = productPurposeKey(productUuid, purpose.uuid);
4016
+ purpose.data_elements.forEach((element) => {
4017
+ var _a3, _b2, _c2, _d2;
4018
+ selections[`${rowKey}-${element.uuid}`] = (_d2 = (_c2 = (_b2 = (_a3 = recordedFor(purpose.uuid, productUuid)) == null ? void 0 : _a3.data_elements) == null ? void 0 : _b2[element.uuid]) == null ? void 0 : _c2.selected) != null ? _d2 : false;
4019
+ });
4020
+ });
4021
+ return selections;
4022
+ };
4023
+ if (purposeSelections || productPurposeSelections) {
4024
+ const settled = {};
4025
+ const settledRows = {};
4026
+ seedRows.forEach((row) => {
4027
+ var _a3;
4028
+ const recorded = rowIsRecorded(row);
4029
+ if (!row.productUuid) {
4030
+ return;
4031
+ }
4032
+ settledRows[productPurposeKey(row.productUuid, row.purpose.uuid)] = recorded;
4033
+ settled[row.productUuid] = ((_a3 = settled[row.productUuid]) != null ? _a3 : true) && recorded;
4034
+ });
4035
+ setConsentedProducts(settled);
4036
+ setConsentedPurposes(settledRows);
4037
+ }
3915
4038
  if (isReconsentRequired && newCategorizedPurposes) {
3916
4039
  setIsFullyConsentedReviewMode(false);
3917
4040
  const initialSelections = {};
3918
4041
  const alreadyConsentedUuids = new Set(
3919
4042
  newCategorizedPurposes.alreadyConsented.map((p) => p.uuid)
3920
4043
  );
3921
- seedRows.filter(({ purpose }) => alreadyConsentedUuids.has(purpose.uuid)).forEach(({ purpose, productUuid }) => {
4044
+ seedRows.filter(
4045
+ (row) => alreadyConsentedUuids.has(row.purpose.uuid) || rowIsRecorded(row)
4046
+ ).forEach(({ purpose, productUuid }) => {
3922
4047
  const rowKey = productPurposeKey(productUuid, purpose.uuid);
3923
4048
  purpose.data_elements.forEach((element) => {
3924
- var _a3, _b2;
4049
+ var _a3, _b2, _c2, _d2;
3925
4050
  const combinedId = `${rowKey}-${element.uuid}`;
3926
- if ((_b2 = (_a3 = purposeSelections == null ? void 0 : purposeSelections[purpose.uuid]) == null ? void 0 : _a3.data_elements) == null ? void 0 : _b2[element.uuid]) {
3927
- initialSelections[combinedId] = purposeSelections[purpose.uuid].data_elements[element.uuid].selected;
3928
- } else {
3929
- initialSelections[combinedId] = false;
3930
- }
4051
+ initialSelections[combinedId] = (_d2 = (_c2 = (_b2 = (_a3 = recordedFor(purpose.uuid, productUuid)) == null ? void 0 : _a3.data_elements) == null ? void 0 : _b2[element.uuid]) == null ? void 0 : _c2.selected) != null ? _d2 : false;
3931
4052
  });
3932
4053
  });
3933
4054
  setInitialDataElementSelections(initialSelections);
3934
- } else if (includeFullyConsentedData && purposeSelections) {
3935
- const allPurposesSelected = seedRows.length > 0 && seedRows.every(
3936
- ({ purpose, productUuid }) => {
3937
- var _a3;
3938
- return ((_a3 = recordedFor(purpose.uuid, productUuid)) == null ? void 0 : _a3.selected) === true;
3939
- }
3940
- );
4055
+ } else if (includeFullyConsentedData && (purposeSelections || productPurposeSelections)) {
4056
+ const allPurposesSelected = seedRows.length > 0 && seedRows.every(rowIsRecorded);
3941
4057
  setIsFullyConsentedReviewMode(allPurposesSelected);
3942
- if (allPurposesSelected) {
3943
- const initialSelections = {};
3944
- seedRows.forEach(({ purpose, productUuid }) => {
3945
- const rowKey = productPurposeKey(productUuid, purpose.uuid);
3946
- purpose.data_elements.forEach((element) => {
3947
- var _a3, _b2;
3948
- const combinedId = `${rowKey}-${element.uuid}`;
3949
- if ((_b2 = (_a3 = purposeSelections[purpose.uuid]) == null ? void 0 : _a3.data_elements) == null ? void 0 : _b2[element.uuid]) {
3950
- initialSelections[combinedId] = purposeSelections[purpose.uuid].data_elements[element.uuid].selected;
3951
- } else {
3952
- initialSelections[combinedId] = false;
3953
- }
3954
- });
3955
- });
3956
- setInitialDataElementSelections(initialSelections);
3957
- } else {
3958
- setIsFullyConsentedReviewMode(false);
3959
- setInitialDataElementSelections({});
3960
- }
4058
+ setInitialDataElementSelections(recordedElementSelections());
3961
4059
  } else {
3962
4060
  setIsFullyConsentedReviewMode(false);
3963
- setInitialDataElementSelections({});
4061
+ setInitialDataElementSelections(recordedElementSelections());
3964
4062
  }
3965
4063
  if (consentContentData.detail.is_minor) {
3966
4064
  setIsMinorFlow(true);
@@ -4042,6 +4140,36 @@ var RedactoNoticeConsent = ({
4042
4140
  [productUuid]: !prev[productUuid]
4043
4141
  }));
4044
4142
  };
4143
+ const toggleSection = (sectionPurposes, keyProductUuid) => {
4144
+ const nextState = !sectionPurposes.every(
4145
+ (purpose) => selectedPurposes[productPurposeKey(keyProductUuid, purpose.uuid)]
4146
+ );
4147
+ setSelectedPurposes((prev) => {
4148
+ const updated = { ...prev };
4149
+ sectionPurposes.forEach((purpose) => {
4150
+ updated[productPurposeKey(keyProductUuid, purpose.uuid)] = nextState;
4151
+ });
4152
+ return updated;
4153
+ });
4154
+ const elementOverrides = {};
4155
+ sectionPurposes.forEach((purpose) => {
4156
+ const key = productPurposeKey(keyProductUuid, purpose.uuid);
4157
+ purpose.data_elements.forEach((element) => {
4158
+ elementOverrides[`${key}-${element.uuid}`] = nextState;
4159
+ });
4160
+ });
4161
+ setSelectedDataElements((prev) => ({ ...prev, ...elementOverrides }));
4162
+ };
4163
+ const productBoxPurposes = (0, import_react3.useCallback)(
4164
+ (product, purposes) => {
4165
+ if (productIsRequiredFor(product) === false) {
4166
+ return purposes;
4167
+ }
4168
+ const required = purposes.filter(hasRequiredDataElement);
4169
+ return required.length > 0 ? required : purposes;
4170
+ },
4171
+ [productIsRequiredFor]
4172
+ );
4045
4173
  const handleProductCheckboxChange = (productUuid) => {
4046
4174
  if (!content) {
4047
4175
  return;
@@ -4052,29 +4180,48 @@ var RedactoNoticeConsent = ({
4052
4180
  if (!group) {
4053
4181
  return;
4054
4182
  }
4055
- const nextState = !group.purposes.every(
4056
- (purpose) => selectedPurposes[productPurposeKey(productUuid, purpose.uuid)]
4183
+ const editable = productBoxPurposes(productUuid, group.purposes).filter(
4184
+ (purpose) => !consentedPurposes[productPurposeKey(productUuid, purpose.uuid)]
4057
4185
  );
4058
- setSelectedPurposes((prev) => {
4059
- const updated = { ...prev };
4060
- group.purposes.forEach((purpose) => {
4061
- updated[productPurposeKey(productUuid, purpose.uuid)] = nextState;
4062
- });
4063
- return updated;
4064
- });
4065
- setSelectedDataElements((prev) => {
4066
- const updated = { ...prev };
4067
- group.purposes.forEach((purpose) => {
4068
- const key = productPurposeKey(productUuid, purpose.uuid);
4069
- purpose.data_elements.forEach((element) => {
4070
- updated[`${key}-${element.uuid}`] = nextState;
4071
- });
4072
- });
4073
- return updated;
4074
- });
4075
- if (!nextState) {
4076
- setCollapsedProducts((prev) => ({ ...prev, [productUuid]: false }));
4186
+ if (editable.length === 0) {
4187
+ return;
4077
4188
  }
4189
+ toggleSection(editable, productUuid);
4190
+ };
4191
+ const soleProductSection = (rows) => {
4192
+ var _a2, _b2;
4193
+ const products = content == null ? void 0 : content.products;
4194
+ if (!products || products.length !== 1) {
4195
+ return rows;
4196
+ }
4197
+ const product = products[0];
4198
+ const purposes = (_a2 = content == null ? void 0 : content.purposes) != null ? _a2 : [];
4199
+ const boxPurposes = productBoxPurposes(product.uuid, purposes);
4200
+ const selectedCount = boxPurposes.filter(
4201
+ (purpose) => selectedPurposes[productPurposeKey(void 0, purpose.uuid)]
4202
+ ).length;
4203
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
4204
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4205
+ ProductSectionHeader,
4206
+ {
4207
+ product,
4208
+ isRequired: productIsRequiredFor(product.uuid),
4209
+ purposeCount: boxPurposes.length,
4210
+ requiredOnly: boxPurposes.length < purposes.length,
4211
+ allSelected: boxPurposes.length > 0 && selectedCount === boxPurposes.length,
4212
+ someSelected: selectedCount > 0,
4213
+ collapsed: collapsedProducts[product.uuid] || false,
4214
+ locked: (_b2 = consentedProducts[product.uuid]) != null ? _b2 : isFullyConsentedReviewMode,
4215
+ settings,
4216
+ styles,
4217
+ responsiveStyles,
4218
+ onToggleCollapse: () => toggleProductCollapse(product.uuid),
4219
+ onToggleSelect: () => toggleSection(boxPurposes, void 0),
4220
+ getTranslatedText
4221
+ }
4222
+ ),
4223
+ !collapsedProducts[product.uuid] && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: styles.sectionBody, children: rows })
4224
+ ] });
4078
4225
  };
4079
4226
  const handleDataElementCheckboxChange = (elementUuid, purposeUuid, productUuid) => {
4080
4227
  if (!content) return;
@@ -5660,50 +5807,65 @@ var RedactoNoticeConsent = ({
5660
5807
  },
5661
5808
  `purpose-section-heading-${selectedLanguage}`
5662
5809
  ),
5663
- categorizedPurposes && !isMultiProduct(content == null ? void 0 : content.products) ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles.optionsContainer, children: [
5664
- categorizedPurposes.alreadyConsented.map((purpose) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5665
- PurposeItem,
5666
- {
5667
- purpose,
5668
- selectedPurposes,
5669
- collapsedPurposes,
5670
- selectedDataElements,
5671
- settings,
5672
- styles,
5673
- responsiveStyles,
5674
- onPurposeToggle: handlePurposeCheckboxChange,
5675
- onPurposeCollapse: togglePurposeCollapse,
5676
- onDataElementToggle: handleDataElementCheckboxChange,
5677
- getTranslatedText,
5678
- isAlreadyConsented: true,
5679
- selectedLanguage,
5680
- initialDataElementSelections
5681
- },
5682
- `${purpose.uuid}-${selectedLanguage}`
5683
- )),
5684
- categorizedPurposes.needsConsent.map((purpose) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5685
- PurposeItem,
5686
- {
5687
- purpose,
5688
- selectedPurposes,
5689
- collapsedPurposes,
5690
- selectedDataElements,
5691
- settings,
5692
- styles,
5693
- responsiveStyles,
5694
- onPurposeToggle: handlePurposeCheckboxChange,
5695
- onPurposeCollapse: togglePurposeCollapse,
5696
- onDataElementToggle: handleDataElementCheckboxChange,
5697
- getTranslatedText,
5698
- isAlreadyConsented: false,
5699
- selectedLanguage
5700
- },
5701
- `${purpose.uuid}-${selectedLanguage}`
5702
- ))
5703
- ] }) : (
5810
+ categorizedPurposes && !isMultiProduct(content == null ? void 0 : content.products) ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: styles.optionsContainer, children: soleProductSection(
5811
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
5812
+ categorizedPurposes.alreadyConsented.map(
5813
+ (purpose) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5814
+ PurposeItem,
5815
+ {
5816
+ purpose,
5817
+ productIsRequired: productIsRequiredFor(
5818
+ void 0
5819
+ ),
5820
+ selectedPurposes,
5821
+ collapsedPurposes,
5822
+ selectedDataElements,
5823
+ settings,
5824
+ styles,
5825
+ responsiveStyles,
5826
+ onPurposeToggle: handlePurposeCheckboxChange,
5827
+ onPurposeCollapse: togglePurposeCollapse,
5828
+ onDataElementToggle: handleDataElementCheckboxChange,
5829
+ getTranslatedText,
5830
+ isAlreadyConsented: true,
5831
+ selectedLanguage,
5832
+ initialDataElementSelections
5833
+ },
5834
+ `${purpose.uuid}-${selectedLanguage}`
5835
+ )
5836
+ ),
5837
+ categorizedPurposes.needsConsent.map((purpose) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5838
+ PurposeItem,
5839
+ {
5840
+ purpose,
5841
+ productIsRequired: productIsRequiredFor(
5842
+ void 0
5843
+ ),
5844
+ selectedPurposes,
5845
+ collapsedPurposes,
5846
+ selectedDataElements,
5847
+ settings,
5848
+ styles,
5849
+ responsiveStyles,
5850
+ onPurposeToggle: handlePurposeCheckboxChange,
5851
+ onPurposeCollapse: togglePurposeCollapse,
5852
+ onDataElementToggle: handleDataElementCheckboxChange,
5853
+ getTranslatedText,
5854
+ isAlreadyConsented: false,
5855
+ selectedLanguage
5856
+ },
5857
+ `${purpose.uuid}-${selectedLanguage}`
5858
+ ))
5859
+ ] })
5860
+ ) }) : (
5704
5861
  /* Regular consent UI */
5705
5862
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: styles.optionsContainer, children: isMultiProduct(content == null ? void 0 : content.products) ? productGroups.map((group) => {
5706
- const selectedInGroup = group.purposes.filter(
5863
+ var _a2;
5864
+ const boxPurposes = productBoxPurposes(
5865
+ group.product.uuid,
5866
+ group.purposes
5867
+ );
5868
+ const selectedInGroup = boxPurposes.filter(
5707
5869
  (purpose) => selectedPurposes[productPurposeKey(
5708
5870
  group.product.uuid,
5709
5871
  purpose.uuid
@@ -5714,11 +5876,15 @@ var RedactoNoticeConsent = ({
5714
5876
  ProductSectionHeader,
5715
5877
  {
5716
5878
  product: group.product,
5717
- purposeCount: group.purposes.length,
5718
- allSelected: selectedInGroup === group.purposes.length,
5879
+ isRequired: productIsRequiredFor(
5880
+ group.product.uuid
5881
+ ),
5882
+ purposeCount: boxPurposes.length,
5883
+ requiredOnly: boxPurposes.length < group.purposes.length,
5884
+ allSelected: selectedInGroup === boxPurposes.length,
5719
5885
  someSelected: selectedInGroup > 0,
5720
5886
  collapsed: collapsedProducts[group.product.uuid] || false,
5721
- locked: isFullyConsentedReviewMode,
5887
+ locked: (_a2 = consentedProducts[group.product.uuid]) != null ? _a2 : false,
5722
5888
  settings,
5723
5889
  styles,
5724
5890
  responsiveStyles,
@@ -5733,50 +5899,64 @@ var RedactoNoticeConsent = ({
5733
5899
  id: `product-${group.product.uuid}`,
5734
5900
  style: styles.sectionBody,
5735
5901
  hidden: collapsedProducts[group.product.uuid] || false,
5736
- children: group.purposes.map((purpose) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5737
- PurposeItem,
5738
- {
5739
- purpose,
5740
- productUuid: group.product.uuid,
5741
- selectedPurposes,
5742
- collapsedPurposes,
5743
- selectedDataElements,
5744
- settings,
5745
- styles,
5746
- responsiveStyles,
5747
- onPurposeToggle: handlePurposeCheckboxChange,
5748
- onPurposeCollapse: togglePurposeCollapse,
5749
- onDataElementToggle: handleDataElementCheckboxChange,
5750
- getTranslatedText,
5751
- isAlreadyConsented: isFullyConsentedReviewMode,
5752
- selectedLanguage,
5753
- initialDataElementSelections
5754
- },
5755
- `${group.product.uuid}-${purpose.uuid}-${selectedLanguage}`
5756
- ))
5902
+ children: group.purposes.map((purpose) => {
5903
+ var _a3;
5904
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5905
+ PurposeItem,
5906
+ {
5907
+ purpose,
5908
+ productUuid: group.product.uuid,
5909
+ productIsRequired: productIsRequiredFor(
5910
+ group.product.uuid
5911
+ ),
5912
+ selectedPurposes,
5913
+ collapsedPurposes,
5914
+ selectedDataElements,
5915
+ settings,
5916
+ styles,
5917
+ responsiveStyles,
5918
+ onPurposeToggle: handlePurposeCheckboxChange,
5919
+ onPurposeCollapse: togglePurposeCollapse,
5920
+ onDataElementToggle: handleDataElementCheckboxChange,
5921
+ getTranslatedText,
5922
+ isAlreadyConsented: (_a3 = consentedPurposes[productPurposeKey(
5923
+ group.product.uuid,
5924
+ purpose.uuid
5925
+ )]) != null ? _a3 : false,
5926
+ selectedLanguage,
5927
+ initialDataElementSelections
5928
+ },
5929
+ `${group.product.uuid}-${purpose.uuid}-${selectedLanguage}`
5930
+ );
5931
+ })
5757
5932
  }
5758
5933
  )
5759
5934
  ] }, group.product.uuid);
5760
- }) : (_y = content == null ? void 0 : content.purposes) == null ? void 0 : _y.map((purpose) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5761
- PurposeItem,
5762
- {
5763
- purpose,
5764
- selectedPurposes,
5765
- collapsedPurposes,
5766
- selectedDataElements,
5767
- settings,
5768
- styles,
5769
- responsiveStyles,
5770
- onPurposeToggle: handlePurposeCheckboxChange,
5771
- onPurposeCollapse: togglePurposeCollapse,
5772
- onDataElementToggle: handleDataElementCheckboxChange,
5773
- getTranslatedText,
5774
- isAlreadyConsented: isFullyConsentedReviewMode,
5775
- selectedLanguage,
5776
- initialDataElementSelections
5777
- },
5778
- `${purpose.uuid}-${selectedLanguage}`
5779
- )) })
5935
+ }) : soleProductSection(
5936
+ (_y = content == null ? void 0 : content.purposes) == null ? void 0 : _y.map((purpose) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5937
+ PurposeItem,
5938
+ {
5939
+ purpose,
5940
+ productIsRequired: productIsRequiredFor(
5941
+ void 0
5942
+ ),
5943
+ selectedPurposes,
5944
+ collapsedPurposes,
5945
+ selectedDataElements,
5946
+ settings,
5947
+ styles,
5948
+ responsiveStyles,
5949
+ onPurposeToggle: handlePurposeCheckboxChange,
5950
+ onPurposeCollapse: togglePurposeCollapse,
5951
+ onDataElementToggle: handleDataElementCheckboxChange,
5952
+ getTranslatedText,
5953
+ isAlreadyConsented: isFullyConsentedReviewMode,
5954
+ selectedLanguage,
5955
+ initialDataElementSelections
5956
+ },
5957
+ `${purpose.uuid}-${selectedLanguage}`
5958
+ ))
5959
+ ) })
5780
5960
  ),
5781
5961
  ((content == null ? void 0 : content.privacy_center_url) || (content == null ? void 0 : content.dpo_info)) && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5782
5962
  "div",