strapi-plugin-bulk-updater 1.4.2 → 1.4.3

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.
@@ -1603,9 +1603,72 @@ const BulkUpdatePreviewModal = ({
1603
1603
  }
1604
1604
  );
1605
1605
  };
1606
+ function parseDynamicZonePath(path) {
1607
+ if (!path) return { cleanPath: "", rootKey: "", subPath: "" };
1608
+ const bracketMatch = path.match(/^([^.[]+)\[([^\]]+)\]\.(.+)$/);
1609
+ if (bracketMatch) {
1610
+ const rootKey = bracketMatch[1];
1611
+ const selector = bracketMatch[2];
1612
+ const subPath = bracketMatch[3];
1613
+ const cleanPath = `${rootKey}.${subPath}`;
1614
+ if (/^\d+$/.test(selector)) {
1615
+ return { cleanPath, rootKey, subPath, targetIndex: parseInt(selector, 10) };
1616
+ }
1617
+ return { cleanPath, rootKey, subPath, targetComponentUid: selector };
1618
+ }
1619
+ const parts = path.split(".");
1620
+ return {
1621
+ cleanPath: path,
1622
+ rootKey: parts[0] || "",
1623
+ subPath: parts.slice(1).join(".")
1624
+ };
1625
+ }
1626
+ function formatComponentName(compUid) {
1627
+ if (!compUid) return "Component";
1628
+ const name = compUid.includes(".") ? compUid.split(".")[1] : compUid;
1629
+ return name.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
1630
+ }
1631
+ function getAllNestedValues(obj, path) {
1632
+ if (!obj || !path) return [];
1633
+ const parsed = parseDynamicZonePath(path);
1634
+ const parts = parsed.cleanPath.split(".");
1635
+ const [head, ...tail] = parts;
1636
+ const rootTarget = obj[head];
1637
+ if (Array.isArray(rootTarget)) {
1638
+ const results = [];
1639
+ rootTarget.forEach((item, idx) => {
1640
+ if (!item || typeof item !== "object") return;
1641
+ if (parsed.targetIndex !== void 0 && idx !== parsed.targetIndex) return;
1642
+ if (parsed.targetComponentUid && item.__component !== parsed.targetComponentUid) return;
1643
+ const subVal = tail.length > 0 ? getNestedValue(item, tail.join(".")) : item;
1644
+ if (subVal !== void 0) {
1645
+ results.push({
1646
+ index: idx,
1647
+ componentUid: item.__component,
1648
+ componentName: formatComponentName(item.__component),
1649
+ value: subVal,
1650
+ path: `${head}[#${idx}${item.__component ? ` ${formatComponentName(item.__component)}` : ""}].${tail.join(".")}`
1651
+ });
1652
+ }
1653
+ });
1654
+ return results;
1655
+ }
1656
+ const singleVal = getNestedValue(obj, parsed.cleanPath);
1657
+ if (singleVal !== void 0) {
1658
+ return [
1659
+ {
1660
+ index: 0,
1661
+ value: singleVal,
1662
+ path: parsed.cleanPath
1663
+ }
1664
+ ];
1665
+ }
1666
+ return [];
1667
+ }
1606
1668
  function getNestedValue(obj, path) {
1607
1669
  if (!obj || !path) return void 0;
1608
- const parts = path.split(".");
1670
+ const parsed = parseDynamicZonePath(path);
1671
+ const parts = parsed.cleanPath.split(".");
1609
1672
  function extract(current, remainingParts) {
1610
1673
  if (current === void 0 || current === null) return void 0;
1611
1674
  if (remainingParts.length === 0) return current;
@@ -1614,7 +1677,10 @@ function getNestedValue(obj, path) {
1614
1677
  if (/^\d+$/.test(head)) {
1615
1678
  return extract(current[parseInt(head, 10)], tail);
1616
1679
  }
1617
- for (const item of current) {
1680
+ for (let idx = 0; idx < current.length; idx++) {
1681
+ const item = current[idx];
1682
+ if (parsed.targetIndex !== void 0 && idx !== parsed.targetIndex) continue;
1683
+ if (parsed.targetComponentUid && item?.__component !== parsed.targetComponentUid) continue;
1618
1684
  const found = extract(item?.[head], tail);
1619
1685
  if (found !== void 0 && found !== null) return found;
1620
1686
  }
@@ -1644,7 +1710,18 @@ function getAvailableColumnsForCollection(col, selectedAttribute, searchAttribut
1644
1710
  if (attr === "name" || attr === "title" || attr === selectedAttribute || searchAttribute && attr === searchAttribute) {
1645
1711
  return;
1646
1712
  }
1647
- const formattedLabel = attr.includes(".") ? attr.split(".").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" > ") : attr.charAt(0).toUpperCase() + attr.slice(1);
1713
+ let formattedLabel = attr;
1714
+ const bracketMatch = attr.match(/^([^.[]+)\[([^\]]+)\]\.(.+)$/);
1715
+ if (bracketMatch) {
1716
+ const root = bracketMatch[1].charAt(0).toUpperCase() + bracketMatch[1].slice(1);
1717
+ const comp = formatComponentName(bracketMatch[2]);
1718
+ const sub = bracketMatch[3].split(".").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" > ");
1719
+ formattedLabel = `${root} [${comp}] > ${sub}`;
1720
+ } else if (attr.includes(".")) {
1721
+ formattedLabel = attr.split(".").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" > ");
1722
+ } else {
1723
+ formattedLabel = attr.charAt(0).toUpperCase() + attr.slice(1);
1724
+ }
1648
1725
  cols.push({
1649
1726
  key: `attr_${attr}`,
1650
1727
  label: formattedLabel,
@@ -1653,7 +1730,16 @@ function getAvailableColumnsForCollection(col, selectedAttribute, searchAttribut
1653
1730
  });
1654
1731
  });
1655
1732
  if (searchAttribute && searchAttribute !== selectedAttribute) {
1656
- const formattedSearchLabel = searchAttribute.includes(".") ? searchAttribute.split(".").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" > ") : searchAttribute;
1733
+ let formattedSearchLabel = searchAttribute;
1734
+ const bracketMatch = searchAttribute.match(/^([^.[]+)\[([^\]]+)\]\.(.+)$/);
1735
+ if (bracketMatch) {
1736
+ const root = bracketMatch[1].charAt(0).toUpperCase() + bracketMatch[1].slice(1);
1737
+ const comp = formatComponentName(bracketMatch[2]);
1738
+ const sub = bracketMatch[3].split(".").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" > ");
1739
+ formattedSearchLabel = `${root} [${comp}] > ${sub}`;
1740
+ } else if (searchAttribute.includes(".")) {
1741
+ formattedSearchLabel = searchAttribute.split(".").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" > ");
1742
+ }
1657
1743
  cols.push({
1658
1744
  key: "search_attr",
1659
1745
  label: `Search: "${formattedSearchLabel}"`,
@@ -1662,7 +1748,17 @@ function getAvailableColumnsForCollection(col, selectedAttribute, searchAttribut
1662
1748
  });
1663
1749
  }
1664
1750
  if (selectedAttribute && selectedAttribute.trim()) {
1665
- const targetLabel = searchAttribute && searchAttribute !== selectedAttribute ? `Update Target: "${selectedAttribute}"` : `Current "${selectedAttribute}"`;
1751
+ let formattedTargetLabel = selectedAttribute;
1752
+ const bracketMatch = selectedAttribute.match(/^([^.[]+)\[([^\]]+)\]\.(.+)$/);
1753
+ if (bracketMatch) {
1754
+ const root = bracketMatch[1].charAt(0).toUpperCase() + bracketMatch[1].slice(1);
1755
+ const comp = formatComponentName(bracketMatch[2]);
1756
+ const sub = bracketMatch[3].split(".").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" > ");
1757
+ formattedTargetLabel = `${root} [${comp}] > ${sub}`;
1758
+ } else if (selectedAttribute.includes(".")) {
1759
+ formattedTargetLabel = selectedAttribute.split(".").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" > ");
1760
+ }
1761
+ const targetLabel = searchAttribute && searchAttribute !== selectedAttribute ? `Update Target: "${formattedTargetLabel}"` : `Current "${formattedTargetLabel}"`;
1666
1762
  cols.push({
1667
1763
  key: "target_attr",
1668
1764
  label: targetLabel,
@@ -1787,7 +1883,7 @@ function getEntryLifecycleStatus(entry, col, pendingBatches = []) {
1787
1883
  textColor: "neutral700"
1788
1884
  };
1789
1885
  }
1790
- function renderCellContent(colDef, entry, col, selectedAttribute, searchAttribute, isMatch, hasAttr, isSelected, onToggleSelect, pendingBatches = []) {
1886
+ function renderCellContent(colDef, entry, col, selectedAttribute, searchAttribute, isMatch, hasAttr, isSelected, onToggleSelect, pendingBatches = [], isDzExpanded = false, onToggleExpandDz, filterQuery = "") {
1791
1887
  switch (colDef.key) {
1792
1888
  case "select":
1793
1889
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -1799,51 +1895,162 @@ function renderCellContent(colDef, entry, col, selectedAttribute, searchAttribut
1799
1895
  );
1800
1896
  case "identifier": {
1801
1897
  const mainText = entry.name || entry.title || `Entry #${entry.id || entry.documentId}`;
1898
+ const dzField = Object.keys(entry).find(
1899
+ (k) => Array.isArray(entry[k]) && entry[k].length > 0 && entry[k].some((item) => item && item.__component)
1900
+ );
1901
+ const dzItemsCount = dzField ? entry[dzField].length : 0;
1802
1902
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
1803
1903
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: mainText }),
1804
- entry.email && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", display: "block", children: entry.email })
1904
+ entry.email && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", display: "block", children: entry.email }),
1905
+ dzItemsCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(
1906
+ designSystem.Button,
1907
+ {
1908
+ variant: "ghost",
1909
+ size: "S",
1910
+ onClick: onToggleExpandDz,
1911
+ style: {
1912
+ padding: "2px 6px",
1913
+ height: "auto",
1914
+ fontSize: "0.72rem",
1915
+ color: isDzExpanded ? "#4945ff" : "#666687",
1916
+ backgroundColor: isDzExpanded ? "rgba(73, 69, 255, 0.08)" : "rgba(0, 0, 0, 0.04)",
1917
+ borderRadius: 4
1918
+ },
1919
+ children: isDzExpanded ? `▲ Hide ${dzField} (${dzItemsCount} blocks)` : `▼ View ${dzField} (${dzItemsCount} blocks)`
1920
+ }
1921
+ ) })
1805
1922
  ] });
1806
1923
  }
1807
1924
  case "search_attr": {
1808
- const val = getNestedValue(entry, searchAttribute);
1809
- const displayStr = formatDisplayValue(val);
1810
- const isUnset = displayStr === "(empty / not set)" || displayStr === "(empty / unlinked)";
1811
- return /* @__PURE__ */ jsxRuntime.jsx(
1812
- designSystem.Typography,
1813
- {
1814
- textColor: isUnset ? "neutral500" : "primary700",
1815
- fontWeight: "semiBold",
1816
- style: {
1817
- fontStyle: isUnset ? "italic" : "normal",
1818
- display: "inline-block",
1819
- maxWidth: 260,
1820
- overflow: "hidden",
1821
- textOverflow: "ellipsis",
1822
- whiteSpace: "nowrap"
1925
+ const matches = getAllNestedValues(entry, searchAttribute);
1926
+ if (matches.length === 0) {
1927
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral500", style: { fontStyle: "italic" }, children: "(empty / not set)" });
1928
+ }
1929
+ if (matches.length === 1 && !matches[0].componentUid) {
1930
+ const displayStr = formatDisplayValue(matches[0].value);
1931
+ const isUnset = displayStr === "(empty / not set)" || displayStr === "(empty / unlinked)";
1932
+ return /* @__PURE__ */ jsxRuntime.jsx(
1933
+ designSystem.Typography,
1934
+ {
1935
+ textColor: isUnset ? "neutral500" : "primary700",
1936
+ fontWeight: "semiBold",
1937
+ style: {
1938
+ fontStyle: isUnset ? "italic" : "normal",
1939
+ display: "inline-block",
1940
+ maxWidth: 260,
1941
+ overflow: "hidden",
1942
+ textOverflow: "ellipsis",
1943
+ whiteSpace: "nowrap"
1944
+ },
1945
+ children: displayStr
1946
+ }
1947
+ );
1948
+ }
1949
+ const qLower = (filterQuery || "").trim().toLowerCase();
1950
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 1, alignItems: "flex-start", style: { maxWidth: 320 }, children: matches.map((m) => {
1951
+ const displayStr = formatDisplayValue(m.value);
1952
+ const isMatchItem = qLower && displayStr.toLowerCase().includes(qLower);
1953
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1954
+ designSystem.Flex,
1955
+ {
1956
+ gap: 1,
1957
+ alignItems: "center",
1958
+ style: {
1959
+ backgroundColor: isMatchItem ? "rgba(46, 213, 115, 0.15)" : "rgba(0, 0, 0, 0.03)",
1960
+ padding: "2px 6px",
1961
+ borderRadius: 4,
1962
+ border: isMatchItem ? "1px solid #2ed573" : "1px solid transparent"
1963
+ },
1964
+ children: [
1965
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { size: "S", backgroundColor: isMatchItem ? "success100" : "primary100", textColor: isMatchItem ? "success700" : "primary700", children: [
1966
+ "#",
1967
+ m.index,
1968
+ " ",
1969
+ m.componentName
1970
+ ] }),
1971
+ /* @__PURE__ */ jsxRuntime.jsx(
1972
+ designSystem.Typography,
1973
+ {
1974
+ variant: "pi",
1975
+ fontWeight: isMatchItem ? "bold" : "normal",
1976
+ textColor: isMatchItem ? "success700" : "neutral800",
1977
+ style: {
1978
+ maxWidth: 200,
1979
+ overflow: "hidden",
1980
+ textOverflow: "ellipsis",
1981
+ whiteSpace: "nowrap"
1982
+ },
1983
+ children: displayStr
1984
+ }
1985
+ )
1986
+ ]
1823
1987
  },
1824
- children: displayStr
1825
- }
1826
- );
1988
+ m.index
1989
+ );
1990
+ }) });
1827
1991
  }
1828
1992
  case "target_attr": {
1829
- const val = getNestedValue(entry, selectedAttribute);
1830
- const displayStr = formatDisplayValue(val);
1831
- const isUnset = displayStr === "(empty / not set)" || displayStr === "(empty / unlinked)";
1832
- return /* @__PURE__ */ jsxRuntime.jsx(
1833
- designSystem.Typography,
1834
- {
1835
- textColor: isUnset ? "neutral500" : "neutral800",
1836
- style: {
1837
- fontStyle: isUnset ? "italic" : "normal",
1838
- display: "inline-block",
1839
- maxWidth: 260,
1840
- overflow: "hidden",
1841
- textOverflow: "ellipsis",
1842
- whiteSpace: "nowrap"
1993
+ const matches = getAllNestedValues(entry, selectedAttribute);
1994
+ if (matches.length === 0) {
1995
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral500", style: { fontStyle: "italic" }, children: "(empty / not set)" });
1996
+ }
1997
+ if (matches.length === 1 && !matches[0].componentUid) {
1998
+ const displayStr = formatDisplayValue(matches[0].value);
1999
+ const isUnset = displayStr === "(empty / not set)" || displayStr === "(empty / unlinked)";
2000
+ return /* @__PURE__ */ jsxRuntime.jsx(
2001
+ designSystem.Typography,
2002
+ {
2003
+ textColor: isUnset ? "neutral500" : "neutral800",
2004
+ style: {
2005
+ fontStyle: isUnset ? "italic" : "normal",
2006
+ display: "inline-block",
2007
+ maxWidth: 260,
2008
+ overflow: "hidden",
2009
+ textOverflow: "ellipsis",
2010
+ whiteSpace: "nowrap"
2011
+ },
2012
+ children: displayStr
2013
+ }
2014
+ );
2015
+ }
2016
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 1, alignItems: "flex-start", style: { maxWidth: 320 }, children: matches.map((m) => {
2017
+ const displayStr = formatDisplayValue(m.value);
2018
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2019
+ designSystem.Flex,
2020
+ {
2021
+ gap: 1,
2022
+ alignItems: "center",
2023
+ style: {
2024
+ backgroundColor: "rgba(0, 0, 0, 0.03)",
2025
+ padding: "2px 6px",
2026
+ borderRadius: 4
2027
+ },
2028
+ children: [
2029
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { size: "S", backgroundColor: "neutral200", textColor: "neutral800", children: [
2030
+ "#",
2031
+ m.index,
2032
+ " ",
2033
+ m.componentName
2034
+ ] }),
2035
+ /* @__PURE__ */ jsxRuntime.jsx(
2036
+ designSystem.Typography,
2037
+ {
2038
+ variant: "pi",
2039
+ textColor: "neutral800",
2040
+ style: {
2041
+ maxWidth: 200,
2042
+ overflow: "hidden",
2043
+ textOverflow: "ellipsis",
2044
+ whiteSpace: "nowrap"
2045
+ },
2046
+ children: displayStr
2047
+ }
2048
+ )
2049
+ ]
1843
2050
  },
1844
- children: displayStr
1845
- }
1846
- );
2051
+ m.index
2052
+ );
2053
+ }) });
1847
2054
  }
1848
2055
  case "status": {
1849
2056
  if (selectedAttribute && !hasAttr) {
@@ -1940,6 +2147,8 @@ const BulkUpdater = () => {
1940
2147
  const [selectedAttribute, setSelectedAttribute] = React.useState(getInitialAttribute);
1941
2148
  const [attributeValue, setAttributeValue] = React.useState("");
1942
2149
  const [selectedEntries, setSelectedEntries] = React.useState({});
2150
+ const [expandedDzEntries, setExpandedDzEntries] = React.useState({});
2151
+ const [selectedDzBlocks, setSelectedDzBlocks] = React.useState({});
1943
2152
  const [relationsMeta, setRelationsMeta] = React.useState({});
1944
2153
  const [relationOptions, setRelationOptions] = React.useState([]);
1945
2154
  const [relationOptionsLoading, setRelationOptionsLoading] = React.useState(false);
@@ -2482,40 +2691,78 @@ const BulkUpdater = () => {
2482
2691
  };
2483
2692
  const toggleEntry = (uid, documentId) => {
2484
2693
  const key = `${uid}${KEY_SEP}${documentId}`;
2694
+ const nextVal = !selectedEntries[key];
2485
2695
  setSelectedEntries((prev) => ({
2486
2696
  ...prev,
2487
- [key]: !prev[key]
2697
+ [key]: nextVal
2488
2698
  }));
2699
+ if (!nextVal) {
2700
+ setSelectedDzBlocks((prev) => {
2701
+ const copy = { ...prev };
2702
+ delete copy[key];
2703
+ return copy;
2704
+ });
2705
+ }
2706
+ };
2707
+ const toggleDzBlock = (uid, documentId, blockIndex, allApplicableIndexes) => {
2708
+ const key = `${uid}${KEY_SEP}${documentId}`;
2709
+ setSelectedDzBlocks((prev) => {
2710
+ const currentSelected = prev[key] !== void 0 ? prev[key] : [...allApplicableIndexes];
2711
+ let nextSelected;
2712
+ if (currentSelected.includes(blockIndex)) {
2713
+ nextSelected = currentSelected.filter((idx) => idx !== blockIndex);
2714
+ } else {
2715
+ nextSelected = [...currentSelected, blockIndex];
2716
+ }
2717
+ if (nextSelected.length > 0) {
2718
+ setSelectedEntries((e) => ({ ...e, [key]: true }));
2719
+ } else {
2720
+ setSelectedEntries((e) => ({ ...e, [key]: false }));
2721
+ }
2722
+ return {
2723
+ ...prev,
2724
+ [key]: nextSelected
2725
+ };
2726
+ });
2489
2727
  };
2490
2728
  const entryMatchesFilter = (entry, attr) => {
2729
+ const matches = getAllNestedValues(entry, attr);
2491
2730
  if (useRegex && regexPattern.trim()) {
2492
2731
  try {
2493
2732
  const regex = new RegExp(regexPattern, "i");
2494
- const targetSearchVal = formatDisplayValue(getNestedValue(entry, attr));
2495
- if (!regex.test(targetSearchVal)) {
2733
+ const anyRegexMatch = matches.some((m) => regex.test(formatDisplayValue(m.value)));
2734
+ if (!anyRegexMatch) {
2496
2735
  return false;
2497
2736
  }
2498
2737
  } catch {
2499
2738
  }
2500
2739
  }
2501
2740
  if (filterOperator === "none") return true;
2502
- const val = getNestedValue(entry, attr);
2503
- const displayStr = formatDisplayValue(val);
2504
- const isValEmpty = val === void 0 || val === null || displayStr.trim() === "" || displayStr.trim().toLowerCase() === "(not set)" || displayStr.trim().toLowerCase() === "(empty / not set)" || displayStr.trim().toLowerCase() === "(empty / unlinked)";
2505
- switch (filterOperator) {
2506
- case "empty":
2507
- return isValEmpty;
2508
- case "not_empty":
2509
- return !isValEmpty;
2510
- case "equals":
2511
- return !isValEmpty && displayStr.trim().toLowerCase() === filterQuery.trim().toLowerCase();
2512
- case "contains":
2513
- return !isValEmpty && displayStr.toLowerCase().includes(filterQuery.trim().toLowerCase());
2514
- case "starts_with":
2515
- return !isValEmpty && displayStr.trim().toLowerCase().startsWith(filterQuery.trim().toLowerCase());
2516
- default:
2517
- return true;
2741
+ if (matches.length === 0) {
2742
+ return filterOperator === "empty";
2743
+ }
2744
+ const checkMatch = (val) => {
2745
+ const displayStr = formatDisplayValue(val);
2746
+ const isValEmpty = val === void 0 || val === null || displayStr.trim() === "" || displayStr.trim().toLowerCase() === "(not set)" || displayStr.trim().toLowerCase() === "(empty / not set)" || displayStr.trim().toLowerCase() === "(empty / unlinked)";
2747
+ switch (filterOperator) {
2748
+ case "empty":
2749
+ return isValEmpty;
2750
+ case "not_empty":
2751
+ return !isValEmpty;
2752
+ case "equals":
2753
+ return !isValEmpty && displayStr.trim().toLowerCase() === filterQuery.trim().toLowerCase();
2754
+ case "contains":
2755
+ return !isValEmpty && displayStr.toLowerCase().includes(filterQuery.trim().toLowerCase());
2756
+ case "starts_with":
2757
+ return !isValEmpty && displayStr.trim().toLowerCase().startsWith(filterQuery.trim().toLowerCase());
2758
+ default:
2759
+ return true;
2760
+ }
2761
+ };
2762
+ if (filterOperator === "empty") {
2763
+ return matches.every((m) => checkMatch(m.value));
2518
2764
  }
2765
+ return matches.some((m) => checkMatch(m.value));
2519
2766
  };
2520
2767
  const getVisibleEntries = (col) => {
2521
2768
  const pageState = collectionPages[col.uid];
@@ -2698,7 +2945,11 @@ const BulkUpdater = () => {
2698
2945
  if (selectedEntries[key]) {
2699
2946
  const parts = key.split(KEY_SEP);
2700
2947
  if (parts.length === 2) {
2701
- targets.push({ uid: parts[0], documentId: parts[1] });
2948
+ const targetObj = { uid: parts[0], documentId: parts[1] };
2949
+ if (selectedDzBlocks[key] && selectedDzBlocks[key].length > 0) {
2950
+ targetObj.dynamicZoneIndexes = selectedDzBlocks[key];
2951
+ }
2952
+ targets.push(targetObj);
2702
2953
  }
2703
2954
  }
2704
2955
  });
@@ -2741,6 +2992,7 @@ const BulkUpdater = () => {
2741
2992
  setIsSubmittingBatch(false);
2742
2993
  if (res.data && res.data.success) {
2743
2994
  setSelectedEntries({});
2995
+ setSelectedDzBlocks({});
2744
2996
  const finalBatchId = res.data.batchId;
2745
2997
  setDashboardBatchId(finalBatchId);
2746
2998
  if (res.data.pending) {
@@ -3928,28 +4180,123 @@ const BulkUpdater = () => {
3928
4180
  const key = `${col.uid}${KEY_SEP}${entry.documentId}`;
3929
4181
  const isSelected = Boolean(selectedEntries[key]);
3930
4182
  const isMatch = isFilterActive;
3931
- return /* @__PURE__ */ jsxRuntime.jsx(
3932
- designSystem.Tr,
3933
- {
3934
- style: {
3935
- backgroundColor: isSelected ? "rgba(73, 69, 255, 0.14)" : isMatch ? "rgba(46, 213, 115, 0.08)" : void 0,
3936
- boxShadow: isSelected ? "inset 3px 0 0 0 #4945ff" : isMatch ? "inset 3px 0 0 0 #2ed573" : void 0
3937
- },
3938
- children: activeColumns.map((colDef) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: renderCellContent(
3939
- colDef,
3940
- entry,
3941
- col,
3942
- selectedAttribute,
3943
- searchAttribute,
3944
- isMatch,
3945
- hasAttr,
3946
- isSelected,
3947
- () => toggleEntry(col.uid, entry.documentId),
3948
- pendingBatches
3949
- ) }, colDef.key))
3950
- },
3951
- key
4183
+ const isDzExpanded = Boolean(expandedDzEntries[key]);
4184
+ const onToggleExpandDz = () => setExpandedDzEntries((prev) => ({
4185
+ ...prev,
4186
+ [key]: !prev[key]
4187
+ }));
4188
+ const dzFields = Object.keys(entry).filter(
4189
+ (k) => Array.isArray(entry[k]) && entry[k].length > 0 && entry[k].some((item) => item && item.__component)
3952
4190
  );
4191
+ return /* @__PURE__ */ jsxRuntime.jsxs(React__default.default.Fragment, { children: [
4192
+ /* @__PURE__ */ jsxRuntime.jsx(
4193
+ designSystem.Tr,
4194
+ {
4195
+ style: {
4196
+ backgroundColor: isSelected ? "rgba(73, 69, 255, 0.14)" : isMatch ? "rgba(46, 213, 115, 0.08)" : void 0,
4197
+ boxShadow: isSelected ? "inset 3px 0 0 0 #4945ff" : isMatch ? "inset 3px 0 0 0 #2ed573" : void 0
4198
+ },
4199
+ children: activeColumns.map((colDef) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: renderCellContent(
4200
+ colDef,
4201
+ entry,
4202
+ col,
4203
+ selectedAttribute,
4204
+ searchAttribute,
4205
+ isMatch,
4206
+ hasAttr,
4207
+ isSelected,
4208
+ () => toggleEntry(col.uid, entry.documentId),
4209
+ pendingBatches,
4210
+ isDzExpanded,
4211
+ onToggleExpandDz,
4212
+ filterQuery
4213
+ ) }, colDef.key))
4214
+ }
4215
+ ),
4216
+ isDzExpanded && dzFields.map((dzKey) => {
4217
+ const blocks = entry[dzKey] || [];
4218
+ const targetParsed = parseDynamicZonePath(selectedAttribute);
4219
+ const allApplicableIndexes = blocks.map((_, i) => i).filter((i) => {
4220
+ if (!targetParsed.subPath) return true;
4221
+ return getNestedValue(blocks[i], targetParsed.subPath) !== void 0;
4222
+ });
4223
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { colSpan: activeColumns.length, style: { padding: "12px 16px", backgroundColor: "rgba(73, 69, 255, 0.02)", borderBottom: "2px solid rgba(73, 69, 255, 0.15)" }, children: /* @__PURE__ */ jsxRuntime.jsxs(
4224
+ designSystem.Box,
4225
+ {
4226
+ padding: 3,
4227
+ background: "neutral0",
4228
+ hasRadius: true,
4229
+ style: { border: "1px solid rgba(73, 69, 255, 0.25)", boxShadow: "0 2px 8px rgba(0,0,0,0.05)" },
4230
+ children: [
4231
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 2, children: [
4232
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
4233
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "sigma", textColor: "primary700", children: [
4234
+ 'DYNAMIC ZONE: "',
4235
+ dzKey,
4236
+ '" (',
4237
+ blocks.length,
4238
+ " blocks)"
4239
+ ] }),
4240
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: "Selective Block Targeting" })
4241
+ ] }),
4242
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: "Selectively choose which blocks to update for this entry" })
4243
+ ] }),
4244
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: 5, rowCount: blocks.length + 1, style: { width: "100%", background: "#fff" }, children: [
4245
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
4246
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { width: 64 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Update?" }) }),
4247
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { width: 60 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Pos" }) }),
4248
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { width: 220 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Component" }) }),
4249
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "sigma", children: [
4250
+ "Target Field Value (",
4251
+ selectedAttribute || "selected",
4252
+ ")"
4253
+ ] }) }),
4254
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Block Summary" }) })
4255
+ ] }) }),
4256
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: blocks.map((block, bIdx) => {
4257
+ const compUid = block.__component;
4258
+ const compLabel = formatComponentName(compUid);
4259
+ const blockVal = targetParsed.subPath ? getNestedValue(block, targetParsed.subPath) : void 0;
4260
+ const hasTargetAttr = blockVal !== void 0;
4261
+ const isBlockChecked = hasTargetAttr && isSelected && (selectedDzBlocks[key] !== void 0 ? selectedDzBlocks[key].includes(bIdx) : true);
4262
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4263
+ designSystem.Tr,
4264
+ {
4265
+ style: {
4266
+ backgroundColor: isBlockChecked ? "rgba(73, 69, 255, 0.08)" : void 0
4267
+ },
4268
+ children: [
4269
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(
4270
+ designSystem.Checkbox,
4271
+ {
4272
+ checked: isBlockChecked,
4273
+ disabled: !hasTargetAttr,
4274
+ onCheckedChange: () => {
4275
+ toggleDzBlock(col.uid, entry.documentId, bIdx, allApplicableIndexes);
4276
+ }
4277
+ }
4278
+ ) }),
4279
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: [
4280
+ "#",
4281
+ bIdx
4282
+ ] }) }),
4283
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Td, { children: [
4284
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "neutral150", textColor: "neutral800", children: compLabel }),
4285
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", display: "block", style: { fontSize: "0.68rem", marginTop: 2 }, children: compUid })
4286
+ ] }),
4287
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: hasTargetAttr ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "semiBold", textColor: "primary700", children: formatDisplayValue(blockVal) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "neutral100", textColor: "neutral500", children: "Does not contain attribute" }) }),
4288
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { maxWidth: 300, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: block.title || block.name || block.heading || block.body || Object.keys(block).filter((k) => !k.startsWith("_") && k !== "id").slice(0, 3).map((k) => `${k}: ${formatDisplayValue(block[k])}`).join(", ") || "-" }) })
4289
+ ]
4290
+ },
4291
+ bIdx
4292
+ );
4293
+ }) })
4294
+ ] })
4295
+ ]
4296
+ }
4297
+ ) }) }, `${key}-dz-${dzKey}`);
4298
+ })
4299
+ ] }, key);
3953
4300
  }) })
3954
4301
  ] }) }),
3955
4302
  !col.isSingleType && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginTop: 4, wrap: "wrap", gap: 3, children: [
@@ -5169,8 +5516,11 @@ const BulkUpdater = () => {
5169
5516
  );
5170
5517
  };
5171
5518
  exports.default = BulkUpdater;
5519
+ exports.formatComponentName = formatComponentName;
5172
5520
  exports.formatDisplayValue = formatDisplayValue;
5521
+ exports.getAllNestedValues = getAllNestedValues;
5173
5522
  exports.getAvailableColumnsForCollection = getAvailableColumnsForCollection;
5174
5523
  exports.getEntryLifecycleStatus = getEntryLifecycleStatus;
5175
5524
  exports.getNestedValue = getNestedValue;
5176
- //# sourceMappingURL=BulkUpdater-L2FdlJfh.js.map
5525
+ exports.parseDynamicZonePath = parseDynamicZonePath;
5526
+ //# sourceMappingURL=BulkUpdater-mSvmiLwc.js.map