@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.mjs CHANGED
@@ -621,7 +621,9 @@ var DPO_AUDIO_ELEMENT_MAP = [
621
621
  { audioKey: "dpo_anchor_text_audio_url", elementId: "dpo-dpo-link" }
622
622
  ];
623
623
  var PRODUCT_SECTION_COPY = {
624
- toggleSelection: (productName, purposeCount) => `Toggle all ${purposeCount} purposes for ${productName}`
624
+ toggleSelection: (productName, purposeCount) => `Toggle all ${purposeCount} purposes for ${productName}`,
625
+ toggleRequired: (productName, purposeCount) => `Toggle the ${purposeCount} required purposes for ${productName}`,
626
+ settled: (productName) => `Already consented to ${productName}`
625
627
  };
626
628
 
627
629
  // src/RedactoNoticeConsent/styles.ts
@@ -1219,6 +1221,24 @@ var purposeCoversProduct = (purpose, productUuid) => {
1219
1221
  return scope.includes(productUuid);
1220
1222
  };
1221
1223
  var isMultiProduct = (products) => Boolean(products && products.length > 1);
1224
+ var serverMarksMandatoryProducts = (products) => Boolean(
1225
+ products && products.length > 0 && products.every((product) => product.mandatory !== void 0)
1226
+ );
1227
+ var gatedProductUuids = (products) => new Set(
1228
+ (products != null ? products : []).filter((product) => product.mandatory === true).map((product) => product.uuid)
1229
+ );
1230
+ var rowProductUuid = (row, products) => {
1231
+ var _a;
1232
+ return (_a = row.productUuid) != null ? _a : (products == null ? void 0 : products.length) === 1 ? products[0].uuid : void 0;
1233
+ };
1234
+ var rowsUnderGate = (rows, products) => {
1235
+ const gated = gatedProductUuids(products);
1236
+ if (gated.size === 0) return [];
1237
+ return rows.filter((row) => {
1238
+ const productUuid = rowProductUuid(row, products);
1239
+ return productUuid !== void 0 && gated.has(productUuid);
1240
+ });
1241
+ };
1222
1242
  var groupPurposesByProduct = (products, purposes, order) => {
1223
1243
  if (!products || products.length === 0) return [];
1224
1244
  return products.map((product) => {
@@ -1318,6 +1338,7 @@ var buildPreselection = (purposes, mode) => {
1318
1338
  return { selectedPurposes, selectedDataElements };
1319
1339
  };
1320
1340
  var hasRequiredDataElement = (purpose) => purpose.data_elements.some((element) => element.required);
1341
+ var isBlockingRequiredElement = (element) => element.required && element.enabled;
1321
1342
 
1322
1343
  // src/RedactoNoticeConsent/layout.ts
1323
1344
  var resolveLogoPosition = (raw) => raw === "center" || raw === "right" ? raw : DEFAULT_LOGO_POSITION;
@@ -1750,6 +1771,16 @@ var CHEVRON_WRAPPER_STYLE = {
1750
1771
  justifyContent: "center",
1751
1772
  marginLeft: "5px"
1752
1773
  };
1774
+ var reviewIndicatorStyle = (size) => ({
1775
+ display: "flex",
1776
+ alignItems: "center",
1777
+ justifyContent: "center",
1778
+ width: `${size}px`,
1779
+ height: `${size}px`,
1780
+ borderRadius: "50%",
1781
+ flexShrink: 0,
1782
+ boxSizing: "border-box"
1783
+ });
1753
1784
  var CollapseChevron = ({ open, color }) => /* @__PURE__ */ jsx3("div", { style: CHEVRON_WRAPPER_STYLE, children: /* @__PURE__ */ jsx3(
1754
1785
  "svg",
1755
1786
  {
@@ -1772,7 +1803,9 @@ var CollapseChevron = ({ open, color }) => /* @__PURE__ */ jsx3("div", { style:
1772
1803
  ) });
1773
1804
  var ProductSectionHeader = ({
1774
1805
  product,
1806
+ isRequired,
1775
1807
  purposeCount,
1808
+ requiredOnly,
1776
1809
  allSelected,
1777
1810
  someSelected,
1778
1811
  collapsed,
@@ -1812,7 +1845,7 @@ var ProductSectionHeader = ({
1812
1845
  children: /* @__PURE__ */ jsx3(CollapseChevron, { open: !collapsed, color: headingColor })
1813
1846
  }
1814
1847
  ),
1815
- /* @__PURE__ */ jsx3("div", { style: styles4.optionTextContainer, children: /* @__PURE__ */ jsx3(
1848
+ /* @__PURE__ */ jsx3("div", { style: styles4.optionTextContainer, children: /* @__PURE__ */ jsxs2(
1816
1849
  "h2",
1817
1850
  {
1818
1851
  id: `product-name-${product.uuid}`,
@@ -1821,11 +1854,46 @@ var ProductSectionHeader = ({
1821
1854
  ...responsiveStyles.optionTitle,
1822
1855
  color: headingColor
1823
1856
  },
1824
- children: name
1857
+ children: [
1858
+ name,
1859
+ isRequired && /* @__PURE__ */ jsx3(RequiredMarker, {})
1860
+ ]
1825
1861
  }
1826
1862
  ) })
1827
1863
  ] }),
1828
- !locked && /* @__PURE__ */ jsx3(
1864
+ locked ? /* @__PURE__ */ jsx3(
1865
+ "div",
1866
+ {
1867
+ role: "img",
1868
+ style: {
1869
+ ...reviewIndicatorStyle(20),
1870
+ backgroundColor: "#10B981",
1871
+ marginLeft: "12px"
1872
+ },
1873
+ "aria-label": PRODUCT_SECTION_COPY.settled(name),
1874
+ children: /* @__PURE__ */ jsx3(
1875
+ "svg",
1876
+ {
1877
+ width: "12",
1878
+ height: "12",
1879
+ viewBox: "0 0 12 12",
1880
+ fill: "none",
1881
+ xmlns: "http://www.w3.org/2000/svg",
1882
+ "aria-hidden": "true",
1883
+ children: /* @__PURE__ */ jsx3(
1884
+ "path",
1885
+ {
1886
+ d: "M4 6L5.5 7.5L8.5 4.5",
1887
+ stroke: "white",
1888
+ strokeWidth: "2",
1889
+ strokeLinecap: "round",
1890
+ strokeLinejoin: "round"
1891
+ }
1892
+ )
1893
+ }
1894
+ )
1895
+ }
1896
+ ) : /* @__PURE__ */ jsx3(
1829
1897
  "input",
1830
1898
  {
1831
1899
  ref: checkboxRef,
@@ -1833,7 +1901,7 @@ var ProductSectionHeader = ({
1833
1901
  type: "checkbox",
1834
1902
  checked: allSelected,
1835
1903
  onChange: handleToggleSelect,
1836
- "aria-label": PRODUCT_SECTION_COPY.toggleSelection(name, purposeCount)
1904
+ "aria-label": requiredOnly ? PRODUCT_SECTION_COPY.toggleRequired(name, purposeCount) : PRODUCT_SECTION_COPY.toggleSelection(name, purposeCount)
1837
1905
  }
1838
1906
  )
1839
1907
  ] });
@@ -1841,6 +1909,7 @@ var ProductSectionHeader = ({
1841
1909
  var PurposeItem = ({
1842
1910
  purpose,
1843
1911
  productUuid,
1912
+ productIsRequired,
1844
1913
  selectedPurposes,
1845
1914
  collapsedPurposes,
1846
1915
  selectedDataElements,
@@ -1910,7 +1979,7 @@ var PurposeItem = ({
1910
1979
  },
1911
1980
  children: [
1912
1981
  getTranslatedText(`purposes.name`, purpose.name, purpose.uuid),
1913
- hasRequiredDataElement(purpose) && /* @__PURE__ */ jsx3(RequiredMarker, {})
1982
+ productIsRequired !== false && hasRequiredDataElement(purpose) && /* @__PURE__ */ jsx3(RequiredMarker, {})
1914
1983
  ]
1915
1984
  },
1916
1985
  `purpose-name-${purpose.uuid}-${selectedLanguage}`
@@ -1953,13 +2022,9 @@ var PurposeItem = ({
1953
2022
  isAlreadyConsented && /* @__PURE__ */ jsx3(
1954
2023
  "div",
1955
2024
  {
2025
+ role: "img",
1956
2026
  style: {
1957
- display: "flex",
1958
- alignItems: "center",
1959
- justifyContent: "center",
1960
- width: "20px",
1961
- height: "20px",
1962
- borderRadius: "50%",
2027
+ ...reviewIndicatorStyle(20),
1963
2028
  backgroundColor: "#10B981",
1964
2029
  marginLeft: "12px"
1965
2030
  },
@@ -2047,13 +2112,9 @@ var PurposeItem = ({
2047
2112
  return /* @__PURE__ */ jsx3(
2048
2113
  "div",
2049
2114
  {
2115
+ role: "img",
2050
2116
  style: {
2051
- display: "flex",
2052
- alignItems: "center",
2053
- justifyContent: "center",
2054
- width: "16px",
2055
- height: "16px",
2056
- borderRadius: "50%",
2117
+ ...reviewIndicatorStyle(16),
2057
2118
  backgroundColor: "#10B981"
2058
2119
  },
2059
2120
  "aria-label": `Consented to ${getTranslatedText(
@@ -2088,13 +2149,9 @@ var PurposeItem = ({
2088
2149
  return /* @__PURE__ */ jsx3(
2089
2150
  "div",
2090
2151
  {
2152
+ role: "img",
2091
2153
  style: {
2092
- display: "flex",
2093
- alignItems: "center",
2094
- justifyContent: "center",
2095
- width: "16px",
2096
- height: "16px",
2097
- borderRadius: "50%",
2154
+ ...reviewIndicatorStyle(16),
2098
2155
  border: "2px solid #d0d5dd",
2099
2156
  backgroundColor: "transparent"
2100
2157
  },
@@ -3024,7 +3081,8 @@ var RedactoNoticeConsent = ({
3024
3081
  digilockerCallbackUrl = "/digilocker-callback",
3025
3082
  // Default callback path
3026
3083
  includeFullyConsentedData = false,
3027
- reviewModeButtonText = "Continue"
3084
+ reviewModeButtonText = "Continue",
3085
+ defaultOpenProducts
3028
3086
  }) => {
3029
3087
  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;
3030
3088
  const sandboxParams = useMemo2(
@@ -3096,6 +3154,8 @@ var RedactoNoticeConsent = ({
3096
3154
  const [collapsedProducts, setCollapsedProducts] = useState({});
3097
3155
  const [collapsedPurposes, setCollapsedPurposes] = useState({});
3098
3156
  const [hasAlreadyConsented, setHasAlreadyConsented] = useState(false);
3157
+ const [consentedProducts, setConsentedProducts] = useState({});
3158
+ const [consentedPurposes, setConsentedPurposes] = useState({});
3099
3159
  const [isFullyConsentedReviewMode, setIsFullyConsentedReviewMode] = useState(false);
3100
3160
  const [isLanguageDropdownOpen, setIsLanguageDropdownOpen] = useState(false);
3101
3161
  const [selectedLanguage, setSelectedLanguage] = useState(language);
@@ -3279,42 +3339,67 @@ var RedactoNoticeConsent = ({
3279
3339
  },
3280
3340
  [content]
3281
3341
  );
3282
- const areAllRequiredElementsChecked = useMemo2(() => {
3283
- var _a2;
3284
- if (!content) return false;
3285
- if (purposeRows.length === 0) return true;
3286
- const isSatisfied = ({
3287
- purpose,
3288
- productUuid
3289
- }) => {
3290
- const key = productPurposeKey(productUuid, purpose.uuid);
3291
- const requiredElements = purpose.data_elements.filter(
3292
- (element) => element.required && element.enabled
3293
- );
3294
- return requiredElements.every(
3295
- (element) => selectedDataElements[`${key}-${element.uuid}`]
3296
- );
3297
- };
3298
- const rowsByProduct = /* @__PURE__ */ new Map();
3299
- for (const row of purposeRows) {
3300
- const groupKey = (_a2 = row.productUuid) != null ? _a2 : "";
3301
- const group = rowsByProduct.get(groupKey);
3302
- if (group) {
3303
- group.push(row);
3304
- } else {
3305
- rowsByProduct.set(groupKey, [row]);
3342
+ const productMandatoryIsAuthoritative = useMemo2(
3343
+ () => serverMarksMandatoryProducts(content == null ? void 0 : content.products),
3344
+ [content]
3345
+ );
3346
+ const gatedProducts = useMemo2(
3347
+ () => gatedProductUuids(content == null ? void 0 : content.products),
3348
+ [content]
3349
+ );
3350
+ const productIsRequiredFor = useCallback(
3351
+ (productUuid) => {
3352
+ var _a2;
3353
+ if (!productMandatoryIsAuthoritative) return void 0;
3354
+ const resolved = productUuid != null ? productUuid : ((_a2 = content == null ? void 0 : content.products) == null ? void 0 : _a2.length) === 1 ? content.products[0].uuid : void 0;
3355
+ return resolved !== void 0 && gatedProducts.has(resolved);
3356
+ },
3357
+ [productMandatoryIsAuthoritative, gatedProducts, content]
3358
+ );
3359
+ const confirmSatisfiedWith = useCallback(
3360
+ (selection) => {
3361
+ var _a2;
3362
+ if (!content) return false;
3363
+ if (purposeRows.length === 0) return true;
3364
+ const isSatisfied = ({
3365
+ purpose,
3366
+ productUuid
3367
+ }) => {
3368
+ const key = productPurposeKey(productUuid, purpose.uuid);
3369
+ const requiredElements = purpose.data_elements.filter(
3370
+ isBlockingRequiredElement
3371
+ );
3372
+ return requiredElements.every(
3373
+ (element) => selection[`${key}-${element.uuid}`]
3374
+ );
3375
+ };
3376
+ if (serverMarksMandatoryProducts(content.products)) {
3377
+ return rowsUnderGate(purposeRows, content.products).every(isSatisfied);
3306
3378
  }
3307
- }
3308
- const groups = [...rowsByProduct.values()];
3309
- const everyProductHasMandatory = groups.every(
3310
- (rows) => rows.some(
3311
- ({ purpose }) => purpose.data_elements.some(
3312
- (element) => element.required && element.enabled
3379
+ const rowsByProduct = /* @__PURE__ */ new Map();
3380
+ for (const row of purposeRows) {
3381
+ const groupKey = (_a2 = row.productUuid) != null ? _a2 : "";
3382
+ const group = rowsByProduct.get(groupKey);
3383
+ if (group) {
3384
+ group.push(row);
3385
+ } else {
3386
+ rowsByProduct.set(groupKey, [row]);
3387
+ }
3388
+ }
3389
+ const groups = [...rowsByProduct.values()];
3390
+ const everyProductHasMandatory = groups.every(
3391
+ (rows) => rows.some(
3392
+ ({ purpose }) => purpose.data_elements.some(isBlockingRequiredElement)
3313
3393
  )
3314
- )
3315
- );
3316
- return everyProductHasMandatory ? groups.some((rows) => rows.every(isSatisfied)) : purposeRows.every(isSatisfied);
3317
- }, [content, purposeRows, selectedDataElements]);
3394
+ );
3395
+ return everyProductHasMandatory ? groups.some((rows) => rows.every(isSatisfied)) : purposeRows.every(isSatisfied);
3396
+ },
3397
+ [content, purposeRows]
3398
+ );
3399
+ const areAllRequiredElementsChecked = useMemo2(
3400
+ () => confirmSatisfiedWith(selectedDataElements),
3401
+ [confirmSatisfiedWith, selectedDataElements]
3402
+ );
3318
3403
  const acceptDisabled = isSubmitting || !areAllRequiredElementsChecked;
3319
3404
  useEffect2(() => {
3320
3405
  var _a2, _b2;
@@ -3327,6 +3412,7 @@ var RedactoNoticeConsent = ({
3327
3412
  setInitialDataElementSelections({});
3328
3413
  setCollapsedPurposes({});
3329
3414
  setCollapsedProducts({});
3415
+ setConsentedProducts({});
3330
3416
  setHasAlreadyConsented(false);
3331
3417
  setErrorMessage(null);
3332
3418
  setFetchError(null);
@@ -3510,15 +3596,18 @@ var RedactoNoticeConsent = ({
3510
3596
  {}
3511
3597
  );
3512
3598
  setCollapsedPurposes(initialCollapsedState);
3599
+ const openByDefault = new Set(defaultOpenProducts != null ? defaultOpenProducts : []);
3600
+ const groups = groupPurposesByProduct(
3601
+ activeConfig.products,
3602
+ activeConfig.purposes,
3603
+ activeConfig.product_purpose_order
3604
+ );
3605
+ const soleSection = groups.length === 1;
3513
3606
  setCollapsedProducts(
3514
- groupPurposesByProduct(
3515
- activeConfig.products,
3516
- activeConfig.purposes,
3517
- activeConfig.product_purpose_order
3518
- ).reduce(
3519
- (acc, group, index) => ({
3607
+ groups.reduce(
3608
+ (acc, group) => ({
3520
3609
  ...acc,
3521
- [group.product.uuid]: index > 0
3610
+ [group.product.uuid]: !soleSection && !openByDefault.has(group.product.uuid)
3522
3611
  }),
3523
3612
  {}
3524
3613
  )
@@ -3545,9 +3634,10 @@ var RedactoNoticeConsent = ({
3545
3634
  activeConfig.purposes,
3546
3635
  activeConfig.product_purpose_order
3547
3636
  );
3637
+ const productPurposeSelections = consentContentData.detail.product_purpose_selections;
3548
3638
  const recordedFor = (purposeUuid, productUuid) => {
3549
- var _a3, _b2;
3550
- const perProduct = productUuid ? (_b2 = (_a3 = activeConfig.product_purpose_selections) == null ? void 0 : _a3[productUuid]) == null ? void 0 : _b2[purposeUuid] : void 0;
3639
+ var _a3;
3640
+ const perProduct = productUuid ? (_a3 = productPurposeSelections == null ? void 0 : productPurposeSelections[productUuid]) == null ? void 0 : _a3[purposeUuid] : void 0;
3551
3641
  return perProduct != null ? perProduct : purposeSelections == null ? void 0 : purposeSelections[purposeUuid];
3552
3642
  };
3553
3643
  const initialPurposeState = seedRows.reduce(
@@ -3587,55 +3677,63 @@ var RedactoNoticeConsent = ({
3587
3677
  {}
3588
3678
  );
3589
3679
  setSelectedDataElements(initialDataElementState);
3680
+ const rowIsRecorded = ({
3681
+ purpose,
3682
+ productUuid
3683
+ }) => {
3684
+ var _a3;
3685
+ return ((_a3 = recordedFor(purpose.uuid, productUuid)) == null ? void 0 : _a3.selected) === true;
3686
+ };
3687
+ const recordedElementSelections = () => {
3688
+ const selections = {};
3689
+ seedRows.filter(rowIsRecorded).forEach(({ purpose, productUuid }) => {
3690
+ const rowKey = productPurposeKey(productUuid, purpose.uuid);
3691
+ purpose.data_elements.forEach((element) => {
3692
+ var _a3, _b2, _c2, _d2;
3693
+ 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;
3694
+ });
3695
+ });
3696
+ return selections;
3697
+ };
3698
+ if (purposeSelections || productPurposeSelections) {
3699
+ const settled = {};
3700
+ const settledRows = {};
3701
+ seedRows.forEach((row) => {
3702
+ var _a3;
3703
+ const recorded = rowIsRecorded(row);
3704
+ if (!row.productUuid) {
3705
+ return;
3706
+ }
3707
+ settledRows[productPurposeKey(row.productUuid, row.purpose.uuid)] = recorded;
3708
+ settled[row.productUuid] = ((_a3 = settled[row.productUuid]) != null ? _a3 : true) && recorded;
3709
+ });
3710
+ setConsentedProducts(settled);
3711
+ setConsentedPurposes(settledRows);
3712
+ }
3590
3713
  if (isReconsentRequired && newCategorizedPurposes) {
3591
3714
  setIsFullyConsentedReviewMode(false);
3592
3715
  const initialSelections = {};
3593
3716
  const alreadyConsentedUuids = new Set(
3594
3717
  newCategorizedPurposes.alreadyConsented.map((p) => p.uuid)
3595
3718
  );
3596
- seedRows.filter(({ purpose }) => alreadyConsentedUuids.has(purpose.uuid)).forEach(({ purpose, productUuid }) => {
3719
+ seedRows.filter(
3720
+ (row) => alreadyConsentedUuids.has(row.purpose.uuid) || rowIsRecorded(row)
3721
+ ).forEach(({ purpose, productUuid }) => {
3597
3722
  const rowKey = productPurposeKey(productUuid, purpose.uuid);
3598
3723
  purpose.data_elements.forEach((element) => {
3599
- var _a3, _b2;
3724
+ var _a3, _b2, _c2, _d2;
3600
3725
  const combinedId = `${rowKey}-${element.uuid}`;
3601
- if ((_b2 = (_a3 = purposeSelections == null ? void 0 : purposeSelections[purpose.uuid]) == null ? void 0 : _a3.data_elements) == null ? void 0 : _b2[element.uuid]) {
3602
- initialSelections[combinedId] = purposeSelections[purpose.uuid].data_elements[element.uuid].selected;
3603
- } else {
3604
- initialSelections[combinedId] = false;
3605
- }
3726
+ 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;
3606
3727
  });
3607
3728
  });
3608
3729
  setInitialDataElementSelections(initialSelections);
3609
- } else if (includeFullyConsentedData && purposeSelections) {
3610
- const allPurposesSelected = seedRows.length > 0 && seedRows.every(
3611
- ({ purpose, productUuid }) => {
3612
- var _a3;
3613
- return ((_a3 = recordedFor(purpose.uuid, productUuid)) == null ? void 0 : _a3.selected) === true;
3614
- }
3615
- );
3730
+ } else if (includeFullyConsentedData && (purposeSelections || productPurposeSelections)) {
3731
+ const allPurposesSelected = seedRows.length > 0 && seedRows.every(rowIsRecorded);
3616
3732
  setIsFullyConsentedReviewMode(allPurposesSelected);
3617
- if (allPurposesSelected) {
3618
- const initialSelections = {};
3619
- seedRows.forEach(({ purpose, productUuid }) => {
3620
- const rowKey = productPurposeKey(productUuid, purpose.uuid);
3621
- purpose.data_elements.forEach((element) => {
3622
- var _a3, _b2;
3623
- const combinedId = `${rowKey}-${element.uuid}`;
3624
- if ((_b2 = (_a3 = purposeSelections[purpose.uuid]) == null ? void 0 : _a3.data_elements) == null ? void 0 : _b2[element.uuid]) {
3625
- initialSelections[combinedId] = purposeSelections[purpose.uuid].data_elements[element.uuid].selected;
3626
- } else {
3627
- initialSelections[combinedId] = false;
3628
- }
3629
- });
3630
- });
3631
- setInitialDataElementSelections(initialSelections);
3632
- } else {
3633
- setIsFullyConsentedReviewMode(false);
3634
- setInitialDataElementSelections({});
3635
- }
3733
+ setInitialDataElementSelections(recordedElementSelections());
3636
3734
  } else {
3637
3735
  setIsFullyConsentedReviewMode(false);
3638
- setInitialDataElementSelections({});
3736
+ setInitialDataElementSelections(recordedElementSelections());
3639
3737
  }
3640
3738
  if (consentContentData.detail.is_minor) {
3641
3739
  setIsMinorFlow(true);
@@ -3717,6 +3815,36 @@ var RedactoNoticeConsent = ({
3717
3815
  [productUuid]: !prev[productUuid]
3718
3816
  }));
3719
3817
  };
3818
+ const toggleSection = (sectionPurposes, keyProductUuid) => {
3819
+ const nextState = !sectionPurposes.every(
3820
+ (purpose) => selectedPurposes[productPurposeKey(keyProductUuid, purpose.uuid)]
3821
+ );
3822
+ setSelectedPurposes((prev) => {
3823
+ const updated = { ...prev };
3824
+ sectionPurposes.forEach((purpose) => {
3825
+ updated[productPurposeKey(keyProductUuid, purpose.uuid)] = nextState;
3826
+ });
3827
+ return updated;
3828
+ });
3829
+ const elementOverrides = {};
3830
+ sectionPurposes.forEach((purpose) => {
3831
+ const key = productPurposeKey(keyProductUuid, purpose.uuid);
3832
+ purpose.data_elements.forEach((element) => {
3833
+ elementOverrides[`${key}-${element.uuid}`] = nextState;
3834
+ });
3835
+ });
3836
+ setSelectedDataElements((prev) => ({ ...prev, ...elementOverrides }));
3837
+ };
3838
+ const productBoxPurposes = useCallback(
3839
+ (product, purposes) => {
3840
+ if (productIsRequiredFor(product) === false) {
3841
+ return purposes;
3842
+ }
3843
+ const required = purposes.filter(hasRequiredDataElement);
3844
+ return required.length > 0 ? required : purposes;
3845
+ },
3846
+ [productIsRequiredFor]
3847
+ );
3720
3848
  const handleProductCheckboxChange = (productUuid) => {
3721
3849
  if (!content) {
3722
3850
  return;
@@ -3727,29 +3855,48 @@ var RedactoNoticeConsent = ({
3727
3855
  if (!group) {
3728
3856
  return;
3729
3857
  }
3730
- const nextState = !group.purposes.every(
3731
- (purpose) => selectedPurposes[productPurposeKey(productUuid, purpose.uuid)]
3858
+ const editable = productBoxPurposes(productUuid, group.purposes).filter(
3859
+ (purpose) => !consentedPurposes[productPurposeKey(productUuid, purpose.uuid)]
3732
3860
  );
3733
- setSelectedPurposes((prev) => {
3734
- const updated = { ...prev };
3735
- group.purposes.forEach((purpose) => {
3736
- updated[productPurposeKey(productUuid, purpose.uuid)] = nextState;
3737
- });
3738
- return updated;
3739
- });
3740
- setSelectedDataElements((prev) => {
3741
- const updated = { ...prev };
3742
- group.purposes.forEach((purpose) => {
3743
- const key = productPurposeKey(productUuid, purpose.uuid);
3744
- purpose.data_elements.forEach((element) => {
3745
- updated[`${key}-${element.uuid}`] = nextState;
3746
- });
3747
- });
3748
- return updated;
3749
- });
3750
- if (!nextState) {
3751
- setCollapsedProducts((prev) => ({ ...prev, [productUuid]: false }));
3861
+ if (editable.length === 0) {
3862
+ return;
3752
3863
  }
3864
+ toggleSection(editable, productUuid);
3865
+ };
3866
+ const soleProductSection = (rows) => {
3867
+ var _a2, _b2;
3868
+ const products = content == null ? void 0 : content.products;
3869
+ if (!products || products.length !== 1) {
3870
+ return rows;
3871
+ }
3872
+ const product = products[0];
3873
+ const purposes = (_a2 = content == null ? void 0 : content.purposes) != null ? _a2 : [];
3874
+ const boxPurposes = productBoxPurposes(product.uuid, purposes);
3875
+ const selectedCount = boxPurposes.filter(
3876
+ (purpose) => selectedPurposes[productPurposeKey(void 0, purpose.uuid)]
3877
+ ).length;
3878
+ return /* @__PURE__ */ jsxs2("div", { children: [
3879
+ /* @__PURE__ */ jsx3(
3880
+ ProductSectionHeader,
3881
+ {
3882
+ product,
3883
+ isRequired: productIsRequiredFor(product.uuid),
3884
+ purposeCount: boxPurposes.length,
3885
+ requiredOnly: boxPurposes.length < purposes.length,
3886
+ allSelected: boxPurposes.length > 0 && selectedCount === boxPurposes.length,
3887
+ someSelected: selectedCount > 0,
3888
+ collapsed: collapsedProducts[product.uuid] || false,
3889
+ locked: (_b2 = consentedProducts[product.uuid]) != null ? _b2 : isFullyConsentedReviewMode,
3890
+ settings,
3891
+ styles,
3892
+ responsiveStyles,
3893
+ onToggleCollapse: () => toggleProductCollapse(product.uuid),
3894
+ onToggleSelect: () => toggleSection(boxPurposes, void 0),
3895
+ getTranslatedText
3896
+ }
3897
+ ),
3898
+ !collapsedProducts[product.uuid] && /* @__PURE__ */ jsx3("div", { style: styles.sectionBody, children: rows })
3899
+ ] });
3753
3900
  };
3754
3901
  const handleDataElementCheckboxChange = (elementUuid, purposeUuid, productUuid) => {
3755
3902
  if (!content) return;
@@ -5335,50 +5482,65 @@ var RedactoNoticeConsent = ({
5335
5482
  },
5336
5483
  `purpose-section-heading-${selectedLanguage}`
5337
5484
  ),
5338
- categorizedPurposes && !isMultiProduct(content == null ? void 0 : content.products) ? /* @__PURE__ */ jsxs2("div", { style: styles.optionsContainer, children: [
5339
- categorizedPurposes.alreadyConsented.map((purpose) => /* @__PURE__ */ jsx3(
5340
- PurposeItem,
5341
- {
5342
- purpose,
5343
- selectedPurposes,
5344
- collapsedPurposes,
5345
- selectedDataElements,
5346
- settings,
5347
- styles,
5348
- responsiveStyles,
5349
- onPurposeToggle: handlePurposeCheckboxChange,
5350
- onPurposeCollapse: togglePurposeCollapse,
5351
- onDataElementToggle: handleDataElementCheckboxChange,
5352
- getTranslatedText,
5353
- isAlreadyConsented: true,
5354
- selectedLanguage,
5355
- initialDataElementSelections
5356
- },
5357
- `${purpose.uuid}-${selectedLanguage}`
5358
- )),
5359
- categorizedPurposes.needsConsent.map((purpose) => /* @__PURE__ */ jsx3(
5360
- PurposeItem,
5361
- {
5362
- purpose,
5363
- selectedPurposes,
5364
- collapsedPurposes,
5365
- selectedDataElements,
5366
- settings,
5367
- styles,
5368
- responsiveStyles,
5369
- onPurposeToggle: handlePurposeCheckboxChange,
5370
- onPurposeCollapse: togglePurposeCollapse,
5371
- onDataElementToggle: handleDataElementCheckboxChange,
5372
- getTranslatedText,
5373
- isAlreadyConsented: false,
5374
- selectedLanguage
5375
- },
5376
- `${purpose.uuid}-${selectedLanguage}`
5377
- ))
5378
- ] }) : (
5485
+ categorizedPurposes && !isMultiProduct(content == null ? void 0 : content.products) ? /* @__PURE__ */ jsx3("div", { style: styles.optionsContainer, children: soleProductSection(
5486
+ /* @__PURE__ */ jsxs2(Fragment2, { children: [
5487
+ categorizedPurposes.alreadyConsented.map(
5488
+ (purpose) => /* @__PURE__ */ jsx3(
5489
+ PurposeItem,
5490
+ {
5491
+ purpose,
5492
+ productIsRequired: productIsRequiredFor(
5493
+ void 0
5494
+ ),
5495
+ selectedPurposes,
5496
+ collapsedPurposes,
5497
+ selectedDataElements,
5498
+ settings,
5499
+ styles,
5500
+ responsiveStyles,
5501
+ onPurposeToggle: handlePurposeCheckboxChange,
5502
+ onPurposeCollapse: togglePurposeCollapse,
5503
+ onDataElementToggle: handleDataElementCheckboxChange,
5504
+ getTranslatedText,
5505
+ isAlreadyConsented: true,
5506
+ selectedLanguage,
5507
+ initialDataElementSelections
5508
+ },
5509
+ `${purpose.uuid}-${selectedLanguage}`
5510
+ )
5511
+ ),
5512
+ categorizedPurposes.needsConsent.map((purpose) => /* @__PURE__ */ jsx3(
5513
+ PurposeItem,
5514
+ {
5515
+ purpose,
5516
+ productIsRequired: productIsRequiredFor(
5517
+ void 0
5518
+ ),
5519
+ selectedPurposes,
5520
+ collapsedPurposes,
5521
+ selectedDataElements,
5522
+ settings,
5523
+ styles,
5524
+ responsiveStyles,
5525
+ onPurposeToggle: handlePurposeCheckboxChange,
5526
+ onPurposeCollapse: togglePurposeCollapse,
5527
+ onDataElementToggle: handleDataElementCheckboxChange,
5528
+ getTranslatedText,
5529
+ isAlreadyConsented: false,
5530
+ selectedLanguage
5531
+ },
5532
+ `${purpose.uuid}-${selectedLanguage}`
5533
+ ))
5534
+ ] })
5535
+ ) }) : (
5379
5536
  /* Regular consent UI */
5380
5537
  /* @__PURE__ */ jsx3("div", { style: styles.optionsContainer, children: isMultiProduct(content == null ? void 0 : content.products) ? productGroups.map((group) => {
5381
- const selectedInGroup = group.purposes.filter(
5538
+ var _a2;
5539
+ const boxPurposes = productBoxPurposes(
5540
+ group.product.uuid,
5541
+ group.purposes
5542
+ );
5543
+ const selectedInGroup = boxPurposes.filter(
5382
5544
  (purpose) => selectedPurposes[productPurposeKey(
5383
5545
  group.product.uuid,
5384
5546
  purpose.uuid
@@ -5389,11 +5551,15 @@ var RedactoNoticeConsent = ({
5389
5551
  ProductSectionHeader,
5390
5552
  {
5391
5553
  product: group.product,
5392
- purposeCount: group.purposes.length,
5393
- allSelected: selectedInGroup === group.purposes.length,
5554
+ isRequired: productIsRequiredFor(
5555
+ group.product.uuid
5556
+ ),
5557
+ purposeCount: boxPurposes.length,
5558
+ requiredOnly: boxPurposes.length < group.purposes.length,
5559
+ allSelected: selectedInGroup === boxPurposes.length,
5394
5560
  someSelected: selectedInGroup > 0,
5395
5561
  collapsed: collapsedProducts[group.product.uuid] || false,
5396
- locked: isFullyConsentedReviewMode,
5562
+ locked: (_a2 = consentedProducts[group.product.uuid]) != null ? _a2 : false,
5397
5563
  settings,
5398
5564
  styles,
5399
5565
  responsiveStyles,
@@ -5408,50 +5574,64 @@ var RedactoNoticeConsent = ({
5408
5574
  id: `product-${group.product.uuid}`,
5409
5575
  style: styles.sectionBody,
5410
5576
  hidden: collapsedProducts[group.product.uuid] || false,
5411
- children: group.purposes.map((purpose) => /* @__PURE__ */ jsx3(
5412
- PurposeItem,
5413
- {
5414
- purpose,
5415
- productUuid: group.product.uuid,
5416
- selectedPurposes,
5417
- collapsedPurposes,
5418
- selectedDataElements,
5419
- settings,
5420
- styles,
5421
- responsiveStyles,
5422
- onPurposeToggle: handlePurposeCheckboxChange,
5423
- onPurposeCollapse: togglePurposeCollapse,
5424
- onDataElementToggle: handleDataElementCheckboxChange,
5425
- getTranslatedText,
5426
- isAlreadyConsented: isFullyConsentedReviewMode,
5427
- selectedLanguage,
5428
- initialDataElementSelections
5429
- },
5430
- `${group.product.uuid}-${purpose.uuid}-${selectedLanguage}`
5431
- ))
5577
+ children: group.purposes.map((purpose) => {
5578
+ var _a3;
5579
+ return /* @__PURE__ */ jsx3(
5580
+ PurposeItem,
5581
+ {
5582
+ purpose,
5583
+ productUuid: group.product.uuid,
5584
+ productIsRequired: productIsRequiredFor(
5585
+ group.product.uuid
5586
+ ),
5587
+ selectedPurposes,
5588
+ collapsedPurposes,
5589
+ selectedDataElements,
5590
+ settings,
5591
+ styles,
5592
+ responsiveStyles,
5593
+ onPurposeToggle: handlePurposeCheckboxChange,
5594
+ onPurposeCollapse: togglePurposeCollapse,
5595
+ onDataElementToggle: handleDataElementCheckboxChange,
5596
+ getTranslatedText,
5597
+ isAlreadyConsented: (_a3 = consentedPurposes[productPurposeKey(
5598
+ group.product.uuid,
5599
+ purpose.uuid
5600
+ )]) != null ? _a3 : false,
5601
+ selectedLanguage,
5602
+ initialDataElementSelections
5603
+ },
5604
+ `${group.product.uuid}-${purpose.uuid}-${selectedLanguage}`
5605
+ );
5606
+ })
5432
5607
  }
5433
5608
  )
5434
5609
  ] }, group.product.uuid);
5435
- }) : (_y = content == null ? void 0 : content.purposes) == null ? void 0 : _y.map((purpose) => /* @__PURE__ */ jsx3(
5436
- PurposeItem,
5437
- {
5438
- purpose,
5439
- selectedPurposes,
5440
- collapsedPurposes,
5441
- selectedDataElements,
5442
- settings,
5443
- styles,
5444
- responsiveStyles,
5445
- onPurposeToggle: handlePurposeCheckboxChange,
5446
- onPurposeCollapse: togglePurposeCollapse,
5447
- onDataElementToggle: handleDataElementCheckboxChange,
5448
- getTranslatedText,
5449
- isAlreadyConsented: isFullyConsentedReviewMode,
5450
- selectedLanguage,
5451
- initialDataElementSelections
5452
- },
5453
- `${purpose.uuid}-${selectedLanguage}`
5454
- )) })
5610
+ }) : soleProductSection(
5611
+ (_y = content == null ? void 0 : content.purposes) == null ? void 0 : _y.map((purpose) => /* @__PURE__ */ jsx3(
5612
+ PurposeItem,
5613
+ {
5614
+ purpose,
5615
+ productIsRequired: productIsRequiredFor(
5616
+ void 0
5617
+ ),
5618
+ selectedPurposes,
5619
+ collapsedPurposes,
5620
+ selectedDataElements,
5621
+ settings,
5622
+ styles,
5623
+ responsiveStyles,
5624
+ onPurposeToggle: handlePurposeCheckboxChange,
5625
+ onPurposeCollapse: togglePurposeCollapse,
5626
+ onDataElementToggle: handleDataElementCheckboxChange,
5627
+ getTranslatedText,
5628
+ isAlreadyConsented: isFullyConsentedReviewMode,
5629
+ selectedLanguage,
5630
+ initialDataElementSelections
5631
+ },
5632
+ `${purpose.uuid}-${selectedLanguage}`
5633
+ ))
5634
+ ) })
5455
5635
  ),
5456
5636
  ((content == null ? void 0 : content.privacy_center_url) || (content == null ? void 0 : content.dpo_info)) && /* @__PURE__ */ jsx3(
5457
5637
  "div",