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