strapi-plugin-bulk-updater 1.2.13 → 1.3.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.
@@ -1454,7 +1454,7 @@ function getNestedValue(obj, path) {
1454
1454
  if (!path.includes(".")) return obj[path];
1455
1455
  return path.split(".").reduce((acc, part) => acc && typeof acc === "object" ? acc[part] : void 0, obj);
1456
1456
  }
1457
- function getAvailableColumnsForCollection(col, selectedAttribute) {
1457
+ function getAvailableColumnsForCollection(col, selectedAttribute, searchAttribute) {
1458
1458
  const cols = [
1459
1459
  {
1460
1460
  key: "select",
@@ -1471,7 +1471,7 @@ function getAvailableColumnsForCollection(col, selectedAttribute) {
1471
1471
  ];
1472
1472
  const candidateAttrs = col.attributes || [];
1473
1473
  candidateAttrs.forEach((attr) => {
1474
- if (attr === "name" || attr === "title" || attr === selectedAttribute) {
1474
+ if (attr === "name" || attr === "title" || attr === selectedAttribute || searchAttribute && attr === searchAttribute) {
1475
1475
  return;
1476
1476
  }
1477
1477
  const formattedLabel = attr.includes(".") ? attr.split(".").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" > ") : attr.charAt(0).toUpperCase() + attr.slice(1);
@@ -1482,9 +1482,19 @@ function getAvailableColumnsForCollection(col, selectedAttribute) {
1482
1482
  defaultVisible: ["department", "email", "location"].includes(attr)
1483
1483
  });
1484
1484
  });
1485
+ if (searchAttribute && searchAttribute !== selectedAttribute) {
1486
+ const formattedSearchLabel = searchAttribute.includes(".") ? searchAttribute.split(".").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" > ") : searchAttribute;
1487
+ cols.push({
1488
+ key: "search_attr",
1489
+ label: `Search: "${formattedSearchLabel}"`,
1490
+ sortable: !col.isSingleType && col.attributes.includes(searchAttribute),
1491
+ defaultVisible: true
1492
+ });
1493
+ }
1494
+ const targetLabel = searchAttribute && searchAttribute !== selectedAttribute ? `Update Target: "${selectedAttribute}"` : `Current "${selectedAttribute}"`;
1485
1495
  cols.push({
1486
1496
  key: "target_attr",
1487
- label: `Current "${selectedAttribute}"`,
1497
+ label: targetLabel,
1488
1498
  sortable: !col.isSingleType && col.attributes.includes(selectedAttribute),
1489
1499
  defaultVisible: true
1490
1500
  });
@@ -1496,17 +1506,19 @@ function getAvailableColumnsForCollection(col, selectedAttribute) {
1496
1506
  });
1497
1507
  return cols;
1498
1508
  }
1499
- function renderHeaderCell(colDef, col, currentSort, selectedAttribute, onSort) {
1509
+ function renderHeaderCell(colDef, col, currentSort, selectedAttribute, searchAttribute, onSort) {
1500
1510
  if (colDef.key === "select") {
1501
1511
  return /* @__PURE__ */ jsx(Th, { style: { width: 48 }, children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Select" }) }, "select");
1502
1512
  }
1503
- const isCurrentSorted = colDef.key === "identifier" && (currentSort.field === "name" || currentSort.field === "title") || colDef.key === "target_attr" && currentSort.field === selectedAttribute || colDef.key.startsWith("attr_") && currentSort.field === colDef.key.replace("attr_", "");
1513
+ const isCurrentSorted = colDef.key === "identifier" && (currentSort.field === "name" || currentSort.field === "title") || colDef.key === "search_attr" && currentSort.field === searchAttribute || colDef.key === "target_attr" && currentSort.field === selectedAttribute || colDef.key.startsWith("attr_") && currentSort.field === colDef.key.replace("attr_", "");
1504
1514
  const sortArrow = isCurrentSorted ? currentSort.order === "asc" ? "▲" : "▼" : "⇅";
1505
1515
  const handleClick = () => {
1506
1516
  if (!colDef.sortable) return;
1507
1517
  if (colDef.key === "identifier") {
1508
1518
  const field = col.attributes.includes("name") ? "name" : col.attributes.includes("title") ? "title" : "createdAt";
1509
1519
  onSort(col.uid, field);
1520
+ } else if (colDef.key === "search_attr") {
1521
+ onSort(col.uid, searchAttribute);
1510
1522
  } else if (colDef.key === "target_attr") {
1511
1523
  onSort(col.uid, selectedAttribute);
1512
1524
  } else if (colDef.key.startsWith("attr_")) {
@@ -1542,7 +1554,7 @@ function formatDisplayValue(val) {
1542
1554
  }
1543
1555
  return String(val);
1544
1556
  }
1545
- function renderCellContent(colDef, entry, col, selectedAttribute, isMatch, hasAttr, isSelected, onToggleSelect) {
1557
+ function renderCellContent(colDef, entry, col, selectedAttribute, searchAttribute, isMatch, hasAttr, isSelected, onToggleSelect) {
1546
1558
  switch (colDef.key) {
1547
1559
  case "select":
1548
1560
  return /* @__PURE__ */ jsx(
@@ -1559,6 +1571,27 @@ function renderCellContent(colDef, entry, col, selectedAttribute, isMatch, hasAt
1559
1571
  entry.email && /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", display: "block", children: entry.email })
1560
1572
  ] });
1561
1573
  }
1574
+ case "search_attr": {
1575
+ const val = getNestedValue(entry, searchAttribute);
1576
+ const displayStr = formatDisplayValue(val);
1577
+ const isUnset = displayStr === "(empty / not set)" || displayStr === "(empty / unlinked)";
1578
+ return /* @__PURE__ */ jsx(
1579
+ Typography,
1580
+ {
1581
+ textColor: isUnset ? "neutral500" : "primary700",
1582
+ fontWeight: "semiBold",
1583
+ style: {
1584
+ fontStyle: isUnset ? "italic" : "normal",
1585
+ display: "inline-block",
1586
+ maxWidth: 260,
1587
+ overflow: "hidden",
1588
+ textOverflow: "ellipsis",
1589
+ whiteSpace: "nowrap"
1590
+ },
1591
+ children: displayStr
1592
+ }
1593
+ );
1594
+ }
1562
1595
  case "target_attr": {
1563
1596
  const val = getNestedValue(entry, selectedAttribute);
1564
1597
  const displayStr = formatDisplayValue(val);
@@ -1683,10 +1716,34 @@ const BulkUpdater = () => {
1683
1716
  };
1684
1717
  const [useRegex, setUseRegex] = useState(false);
1685
1718
  const [regexPattern, setRegexPattern] = useState("");
1719
+ const DEPTH_STORAGE_KEY = "strapi_bulk_updater_component_depth";
1720
+ const [componentDepth, setComponentDepth] = useState(() => {
1721
+ try {
1722
+ const stored = localStorage.getItem(DEPTH_STORAGE_KEY);
1723
+ if (stored) {
1724
+ const val = Number(stored);
1725
+ if (val >= 1 && val <= 5) return val;
1726
+ }
1727
+ } catch {
1728
+ }
1729
+ return 3;
1730
+ });
1731
+ const handleComponentDepthChange = (depth) => {
1732
+ const capped = Math.min(5, Math.max(1, depth));
1733
+ setComponentDepth(capped);
1734
+ try {
1735
+ localStorage.setItem(DEPTH_STORAGE_KEY, String(capped));
1736
+ } catch {
1737
+ }
1738
+ };
1739
+ const [searchAttribute, setSearchAttribute] = useState("");
1740
+ const [searchAttributeInput, setSearchAttributeInput] = useState("");
1741
+ const [attributesMeta, setAttributesMeta] = useState({});
1742
+ const effectiveSearchAttribute = searchAttribute || selectedAttribute;
1686
1743
  const [filterOperator, setFilterOperator] = useState("none");
1687
1744
  const [filterQuery, setFilterQuery] = useState("");
1688
1745
  const [entrySearch, setEntrySearch] = useState("");
1689
- const isFilterActive = filterOperator !== "none" && (["empty", "not_empty"].includes(filterOperator) || Boolean(filterQuery.trim())) || Boolean(entrySearch.trim());
1746
+ const isFilterActive = Boolean(searchAttribute && searchAttribute !== selectedAttribute) || filterOperator !== "none" && (["empty", "not_empty"].includes(filterOperator) || Boolean(filterQuery.trim())) || Boolean(entrySearch.trim());
1690
1747
  const [lastResults, setLastResults] = useState(null);
1691
1748
  const [updateSummary, setUpdateSummary] = useState(null);
1692
1749
  const [collectionPages, setCollectionPages] = useState({});
@@ -1766,6 +1823,9 @@ const BulkUpdater = () => {
1766
1823
  if (data.relationsMeta) {
1767
1824
  setRelationsMeta(data.relationsMeta);
1768
1825
  }
1826
+ if (data.attributesMeta) {
1827
+ setAttributesMeta(data.attributesMeta);
1828
+ }
1769
1829
  let storedAttr = null;
1770
1830
  try {
1771
1831
  storedAttr = localStorage.getItem(LAST_ATTR_KEY);
@@ -1813,7 +1873,7 @@ const BulkUpdater = () => {
1813
1873
  setLoading(false);
1814
1874
  }
1815
1875
  };
1816
- const loadCollectionPage = async (colUid, targetPage, customPageSize, customSortField, customSortOrder, overrideFilterOp, overrideFilterQuery, overrideEntrySearch, autoSelectReturned = true) => {
1876
+ const loadCollectionPage = async (colUid, targetPage, customPageSize, customSortField, customSortOrder, overrideFilterOp, overrideFilterQuery, overrideEntrySearch, autoSelectReturned = true, overrideSearchAttr) => {
1817
1877
  const currentPageState = collectionPages[colUid];
1818
1878
  const size = customPageSize || currentPageState?.pageSize || defaultPageSize;
1819
1879
  const currentSort = collectionSorts[colUid] || { field: "createdAt", order: "desc" };
@@ -1822,6 +1882,7 @@ const BulkUpdater = () => {
1822
1882
  const op = overrideFilterOp !== void 0 ? overrideFilterOp : filterOperator;
1823
1883
  const q = overrideFilterQuery !== void 0 ? overrideFilterQuery : filterQuery;
1824
1884
  const s = overrideEntrySearch !== void 0 ? overrideEntrySearch : entrySearch;
1885
+ const searchAttr = overrideSearchAttr !== void 0 ? overrideSearchAttr : searchAttribute;
1825
1886
  setCollectionPages((prev) => ({
1826
1887
  ...prev,
1827
1888
  [colUid]: {
@@ -1835,6 +1896,9 @@ const BulkUpdater = () => {
1835
1896
  pageSize: String(size)
1836
1897
  });
1837
1898
  if (selectedAttribute) params.set("attribute", selectedAttribute);
1899
+ if (searchAttr && searchAttr !== selectedAttribute) {
1900
+ params.set("searchAttribute", searchAttr);
1901
+ }
1838
1902
  if (op && op !== "none") {
1839
1903
  params.set("filterOperator", op);
1840
1904
  if (q) params.set("filterQuery", q);
@@ -1864,7 +1928,7 @@ const BulkUpdater = () => {
1864
1928
  loading: false
1865
1929
  }
1866
1930
  }));
1867
- const isFilterActiveNow = op !== "none" && (["empty", "not_empty"].includes(op) || Boolean(q.trim())) || Boolean(s.trim());
1931
+ const isFilterActiveNow = Boolean(searchAttr && searchAttr !== selectedAttribute) || op !== "none" && (["empty", "not_empty"].includes(op) || Boolean(q.trim())) || Boolean(s.trim());
1868
1932
  if (isFilterActiveNow && autoSelectReturned) {
1869
1933
  setSelectedEntries((prev) => {
1870
1934
  const next = { ...prev };
@@ -1951,13 +2015,14 @@ const BulkUpdater = () => {
1951
2015
  }
1952
2016
  const timer = setTimeout(() => {
1953
2017
  if (collections.length > 0) {
1954
- const isFilterActiveNow = filterOperator !== "none" && (["empty", "not_empty"].includes(filterOperator) || Boolean(filterQuery.trim())) || Boolean(entrySearch.trim());
2018
+ const isFilterActiveNow = Boolean(searchAttribute && searchAttribute !== selectedAttribute) || filterOperator !== "none" && (["empty", "not_empty"].includes(filterOperator) || Boolean(filterQuery.trim())) || Boolean(entrySearch.trim());
1955
2019
  if (isFilterActiveNow) {
1956
2020
  setSelectedEntries({});
1957
2021
  }
2022
+ const targetSearchAttr = searchAttribute || selectedAttribute;
1958
2023
  const targetCols = collections.filter((col) => {
1959
2024
  if (visibleTypeUids[col.uid] === false) return false;
1960
- if (filterOperator !== "none" && !col.attributes.includes(selectedAttribute)) return false;
2025
+ if (filterOperator !== "none" && targetSearchAttr && !col.attributes.includes(targetSearchAttr)) return false;
1961
2026
  return true;
1962
2027
  });
1963
2028
  targetCols.forEach((col) => {
@@ -1970,13 +2035,14 @@ const BulkUpdater = () => {
1970
2035
  filterOperator,
1971
2036
  filterQuery,
1972
2037
  entrySearch,
1973
- true
2038
+ true,
2039
+ searchAttribute
1974
2040
  );
1975
2041
  });
1976
2042
  }
1977
2043
  }, 350);
1978
2044
  return () => clearTimeout(timer);
1979
- }, [filterOperator, filterQuery, entrySearch]);
2045
+ }, [searchAttribute, filterOperator, filterQuery, entrySearch]);
1980
2046
  const KEY_SEP = "@@@";
1981
2047
  const handleAttributeChange = (newAttr) => {
1982
2048
  if (newAttr !== selectedAttribute) {
@@ -2045,39 +2111,119 @@ const BulkUpdater = () => {
2045
2111
  setVisibleTypeUids(newVis);
2046
2112
  };
2047
2113
  const [attributeSearch, setAttributeSearch] = useState("");
2114
+ const [searchAttributeSearch, setSearchAttributeSearch] = useState("");
2115
+ const getAttrDepth = (attr) => attributesMeta[attr]?.depth ?? (attr.includes(".") ? attr.split(".").length - 1 : 0);
2116
+ const depthFilteredAttributes = useMemo(() => {
2117
+ return attributes.filter((attr) => getAttrDepth(attr) <= componentDepth);
2118
+ }, [attributes, componentDepth, attributesMeta]);
2048
2119
  const filteredAttributes = useMemo(() => {
2049
2120
  if (!attributeSearch.trim()) {
2050
- return attributes;
2121
+ return depthFilteredAttributes;
2051
2122
  }
2052
2123
  const q = attributeSearch.toLowerCase().trim();
2053
2124
  const cleanQ = q.replace(/[^a-z0-9]/g, "");
2054
- return attributes.filter((attr) => {
2125
+ return depthFilteredAttributes.filter((attr) => {
2055
2126
  const lower = attr.toLowerCase();
2056
2127
  const cleanLower = lower.replace(/[^a-z0-9]/g, "");
2057
2128
  const rel = relationsMeta[attr];
2058
2129
  const relTarget = (rel?.targetDisplayName || "").toLowerCase();
2059
2130
  return lower.includes(q) || cleanLower.includes(cleanQ) || relTarget.includes(q);
2060
2131
  });
2061
- }, [attributes, attributeSearch, relationsMeta]);
2132
+ }, [depthFilteredAttributes, attributeSearch, relationsMeta]);
2133
+ const filteredSearchAttributes = useMemo(() => {
2134
+ if (!searchAttributeSearch.trim()) {
2135
+ return depthFilteredAttributes;
2136
+ }
2137
+ const q = searchAttributeSearch.toLowerCase().trim();
2138
+ const cleanQ = q.replace(/[^a-z0-9]/g, "");
2139
+ return depthFilteredAttributes.filter((attr) => {
2140
+ const lower = attr.toLowerCase();
2141
+ const cleanLower = lower.replace(/[^a-z0-9]/g, "");
2142
+ const rel = relationsMeta[attr];
2143
+ const relTarget = (rel?.targetDisplayName || "").toLowerCase();
2144
+ return lower.includes(q) || cleanLower.includes(cleanQ) || relTarget.includes(q);
2145
+ });
2146
+ }, [depthFilteredAttributes, searchAttributeSearch, relationsMeta]);
2062
2147
  const memoizedComboboxOptions = useMemo(() => {
2063
2148
  if (filteredAttributes.length === 0) {
2064
2149
  return /* @__PURE__ */ jsx(ComboboxOption, { value: "__none__", disabled: true, textValue: "No matching attributes found", children: /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", children: [
2065
2150
  'No matching attributes found for "',
2066
2151
  attributeSearch,
2067
- '"'
2152
+ '" (Depth L',
2153
+ componentDepth,
2154
+ ")"
2068
2155
  ] }) });
2069
2156
  }
2070
2157
  return filteredAttributes.map((attr) => {
2071
2158
  const rel = relationsMeta[attr];
2159
+ const depth = getAttrDepth(attr);
2072
2160
  return /* @__PURE__ */ jsx(ComboboxOption, { value: attr, textValue: attr, children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", width: "100%", gap: 2, children: [
2073
2161
  /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: attr === selectedAttribute ? "bold" : "regular", children: attr }),
2074
2162
  rel ? /* @__PURE__ */ jsxs(Badge, { backgroundColor: "secondary100", textColor: "secondary700", size: "S", children: [
2075
2163
  "relation ➔ ",
2076
2164
  rel.targetDisplayName
2077
- ] }) : attr.includes(".") ? /* @__PURE__ */ jsx(Badge, { backgroundColor: "neutral150", textColor: "neutral700", size: "S", children: "component" }) : null
2165
+ ] }) : depth > 0 ? /* @__PURE__ */ jsxs(Badge, { backgroundColor: depth === 1 ? "neutral150" : depth === 2 ? "primary100" : "secondary100", textColor: depth === 1 ? "neutral700" : depth === 2 ? "primary700" : "secondary700", size: "S", children: [
2166
+ "L",
2167
+ depth,
2168
+ " component"
2169
+ ] }) : null
2078
2170
  ] }) }, attr);
2079
2171
  });
2080
- }, [filteredAttributes, relationsMeta, selectedAttribute, attributeSearch]);
2172
+ }, [filteredAttributes, relationsMeta, selectedAttribute, attributeSearch, componentDepth, attributesMeta]);
2173
+ const memoizedSearchComboboxOptions = useMemo(() => {
2174
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2175
+ /* @__PURE__ */ jsx(
2176
+ ComboboxOption,
2177
+ {
2178
+ value: "__default__",
2179
+ textValue: `Same as Update Attribute (${selectedAttribute || "None"})`,
2180
+ children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", width: "100%", gap: 2, children: [
2181
+ /* @__PURE__ */ jsxs(Typography, { variant: "omega", fontStyle: "italic", textColor: "neutral600", children: [
2182
+ "-- Same as Update Attribute (",
2183
+ selectedAttribute || "None",
2184
+ ") --"
2185
+ ] }),
2186
+ /* @__PURE__ */ jsx(Badge, { backgroundColor: "neutral150", textColor: "neutral600", size: "S", children: "Default" })
2187
+ ] })
2188
+ }
2189
+ ),
2190
+ filteredSearchAttributes.map((attr) => {
2191
+ const rel = relationsMeta[attr];
2192
+ const depth = getAttrDepth(attr);
2193
+ return /* @__PURE__ */ jsx(ComboboxOption, { value: attr, textValue: attr, children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", width: "100%", gap: 2, children: [
2194
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: attr === searchAttribute ? "bold" : "regular", children: attr }),
2195
+ rel ? /* @__PURE__ */ jsxs(Badge, { backgroundColor: "secondary100", textColor: "secondary700", size: "S", children: [
2196
+ "relation ➔ ",
2197
+ rel.targetDisplayName
2198
+ ] }) : depth > 0 ? /* @__PURE__ */ jsxs(Badge, { backgroundColor: depth === 1 ? "neutral150" : depth === 2 ? "primary100" : "secondary100", textColor: depth === 1 ? "neutral700" : depth === 2 ? "primary700" : "secondary700", size: "S", children: [
2199
+ "L",
2200
+ depth,
2201
+ " component"
2202
+ ] }) : null
2203
+ ] }) }, `search_${attr}`);
2204
+ })
2205
+ ] });
2206
+ }, [filteredSearchAttributes, relationsMeta, searchAttribute, selectedAttribute, componentDepth, attributesMeta]);
2207
+ const handleSearchAttributeChange = (newAttr) => {
2208
+ const finalAttr = newAttr === "__default__" ? "" : newAttr;
2209
+ setSearchAttribute(finalAttr);
2210
+ if (collections.length > 0) {
2211
+ collections.forEach((col) => {
2212
+ loadCollectionPage(
2213
+ col.uid,
2214
+ 1,
2215
+ void 0,
2216
+ void 0,
2217
+ void 0,
2218
+ filterOperator,
2219
+ filterQuery,
2220
+ entrySearch,
2221
+ true,
2222
+ finalAttr
2223
+ );
2224
+ });
2225
+ }
2226
+ };
2081
2227
  const handleGlobalPageSizeChange = (newSize) => {
2082
2228
  const capped = Math.min(500, Math.max(1, newSize));
2083
2229
  setDefaultPageSize(capped);
@@ -2168,12 +2314,14 @@ const BulkUpdater = () => {
2168
2314
  setSelectedEntries(updated);
2169
2315
  };
2170
2316
  const clearFilter = () => {
2317
+ setSearchAttribute("");
2318
+ setSearchAttributeSearch("");
2171
2319
  setFilterOperator("none");
2172
2320
  setFilterQuery("");
2173
2321
  setEntrySearch("");
2174
2322
  setSelectedEntries({});
2175
2323
  collections.forEach((col) => {
2176
- loadCollectionPage(col.uid, 1, void 0, void 0, void 0, "none", "", "", false);
2324
+ loadCollectionPage(col.uid, 1, void 0, void 0, void 0, "none", "", "", false, "");
2177
2325
  });
2178
2326
  };
2179
2327
  const isCollectionFullySelected = (col) => {
@@ -2969,7 +3117,29 @@ const BulkUpdater = () => {
2969
3117
  marginBottom: 6,
2970
3118
  style: { width: "100%", maxWidth: "100%", minWidth: 0, boxSizing: "border-box" },
2971
3119
  children: [
2972
- /* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h2", marginBottom: 4, fontWeight: "semiBold", children: "1. Configure Attribute Update" }),
3120
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, wrap: "wrap", gap: 3, children: [
3121
+ /* @__PURE__ */ jsxs(Box, { children: [
3122
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h2", fontWeight: "semiBold", children: "1. Configure Attribute Update" }),
3123
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Select the target attribute to update across collections." })
3124
+ ] }),
3125
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
3126
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral700", fontWeight: "bold", children: "Component Nesting Depth:" }),
3127
+ [1, 2, 3, 4, 5].map((lvl) => /* @__PURE__ */ jsxs(
3128
+ Button,
3129
+ {
3130
+ variant: componentDepth === lvl ? "default" : "tertiary",
3131
+ size: "S",
3132
+ onClick: () => handleComponentDepthChange(lvl),
3133
+ title: `Discover nested component attributes up to Level ${lvl}`,
3134
+ children: [
3135
+ "Level ",
3136
+ lvl
3137
+ ]
3138
+ },
3139
+ `depth_cfg_${lvl}`
3140
+ ))
3141
+ ] })
3142
+ ] }),
2973
3143
  /* @__PURE__ */ jsxs(Flex, { gap: 4, alignItems: "flex-end", wrap: "wrap", children: [
2974
3144
  /* @__PURE__ */ jsx(Box, { style: { minWidth: 280, flex: 1, maxWidth: 440 }, children: /* @__PURE__ */ jsxs(Field.Root, { children: [
2975
3145
  /* @__PURE__ */ jsx(Field.Label, { children: "Attribute to Update" }),
@@ -3300,26 +3470,83 @@ const BulkUpdater = () => {
3300
3470
  style: { width: "100%", maxWidth: "100%", boxSizing: "border-box" },
3301
3471
  children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, children: [
3302
3472
  /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 2, children: [
3303
- /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
3473
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
3304
3474
  /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", children: "Search & Filter Targets:" }),
3305
3475
  isFilterActive && /* @__PURE__ */ jsxs(Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
3306
3476
  matchingEntriesCount,
3307
3477
  " matching ",
3308
3478
  matchingEntriesCount === 1 ? "entry" : "entries",
3309
3479
  " found in database"
3480
+ ] }),
3481
+ searchAttribute && searchAttribute !== selectedAttribute && /* @__PURE__ */ jsxs(Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: [
3482
+ "Searching by: ",
3483
+ searchAttribute,
3484
+ " → Target to update: ",
3485
+ selectedAttribute
3310
3486
  ] })
3311
3487
  ] }),
3312
- isFilterActive && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "S", onClick: clearFilter, children: "Clear Search & Filter" })
3488
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
3489
+ /* @__PURE__ */ jsxs(Flex, { gap: 1, alignItems: "center", children: [
3490
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Depth:" }),
3491
+ [1, 2, 3, 4, 5].map((lvl) => /* @__PURE__ */ jsxs(
3492
+ Button,
3493
+ {
3494
+ variant: componentDepth === lvl ? "default" : "tertiary",
3495
+ size: "S",
3496
+ onClick: () => handleComponentDepthChange(lvl),
3497
+ title: `Show component attributes up to Level ${lvl}`,
3498
+ style: { minWidth: 26, padding: "2px 6px" },
3499
+ children: [
3500
+ "L",
3501
+ lvl
3502
+ ]
3503
+ },
3504
+ `s2_depth_${lvl}`
3505
+ ))
3506
+ ] }),
3507
+ isFilterActive && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "S", onClick: clearFilter, children: "Clear Search & Filter" })
3508
+ ] })
3313
3509
  ] }),
3314
3510
  /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "flex-end", wrap: "wrap", children: [
3315
- /* @__PURE__ */ jsx(Box, { style: { width: 220, minWidth: 180 }, children: /* @__PURE__ */ jsxs(
3511
+ /* @__PURE__ */ jsx(Box, { style: { width: 280, minWidth: 230 }, children: /* @__PURE__ */ jsxs(Field.Root, { children: [
3512
+ /* @__PURE__ */ jsx(Field.Label, { children: "Search Attribute" }),
3513
+ /* @__PURE__ */ jsx(
3514
+ Combobox,
3515
+ {
3516
+ autocomplete: "none",
3517
+ placeholder: searchAttribute || `Same as Update (${selectedAttribute || "None"})`,
3518
+ value: searchAttribute || "__default__",
3519
+ clearLabel: "Reset search attribute",
3520
+ onClear: () => {
3521
+ handleSearchAttributeChange("");
3522
+ setSearchAttributeSearch("");
3523
+ },
3524
+ onInputChange: (e) => {
3525
+ setSearchAttributeSearch(e.target.value);
3526
+ },
3527
+ onOpenChange: (isOpen) => {
3528
+ if (!isOpen) {
3529
+ setSearchAttributeSearch("");
3530
+ }
3531
+ },
3532
+ onChange: (val) => {
3533
+ if (val && typeof val === "string" && val !== "__none__") {
3534
+ handleSearchAttributeChange(val);
3535
+ setSearchAttributeSearch("");
3536
+ }
3537
+ },
3538
+ children: memoizedSearchComboboxOptions
3539
+ }
3540
+ )
3541
+ ] }) }),
3542
+ /* @__PURE__ */ jsx(Box, { style: { width: 200, minWidth: 170 }, children: /* @__PURE__ */ jsxs(
3316
3543
  SingleSelect,
3317
3544
  {
3318
- label: selectedAttribute ? `Filter condition for "${selectedAttribute}"` : "Filter condition",
3545
+ label: effectiveSearchAttribute ? `Condition for "${effectiveSearchAttribute}"` : "Filter condition",
3319
3546
  value: filterOperator,
3320
3547
  onChange: (val) => setFilterOperator(String(val)),
3321
3548
  children: [
3322
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "none", children: "Show All (No Attribute Filter)" }),
3549
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "none", children: "Show All (No Value Filter)" }),
3323
3550
  /* @__PURE__ */ jsx(SingleSelectOption, { value: "empty", children: "Is Empty or Null" }),
3324
3551
  /* @__PURE__ */ jsx(SingleSelectOption, { value: "not_empty", children: "Is Not Empty" }),
3325
3552
  /* @__PURE__ */ jsx(SingleSelectOption, { value: "equals", children: "Equals Exact Text" }),
@@ -3328,16 +3555,16 @@ const BulkUpdater = () => {
3328
3555
  ]
3329
3556
  }
3330
3557
  ) }),
3331
- (filterOperator === "equals" || filterOperator === "contains" || filterOperator === "starts_with") && /* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 200 }, children: /* @__PURE__ */ jsx(
3558
+ (filterOperator === "equals" || filterOperator === "contains" || filterOperator === "starts_with") && /* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 180 }, children: /* @__PURE__ */ jsx(
3332
3559
  TextInput,
3333
3560
  {
3334
- label: selectedAttribute ? `Value for "${selectedAttribute}"` : "Value to match",
3561
+ label: effectiveSearchAttribute ? `Value for "${effectiveSearchAttribute}"` : "Value to match",
3335
3562
  placeholder: "Type value to match...",
3336
3563
  value: filterQuery,
3337
3564
  onChange: (e) => setFilterQuery(e.target.value)
3338
3565
  }
3339
3566
  ) }),
3340
- /* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 200 }, children: /* @__PURE__ */ jsx(
3567
+ /* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 180 }, children: /* @__PURE__ */ jsx(
3341
3568
  TextInput,
3342
3569
  {
3343
3570
  label: "Full-text Entry Search",
@@ -3535,11 +3762,11 @@ const BulkUpdater = () => {
3535
3762
  const pageSize = pageState ? pageState.pageSize : defaultPageSize;
3536
3763
  const hasAttr = selectedAttribute ? col.attributes.includes(selectedAttribute) : false;
3537
3764
  const visibleEntries = getVisibleEntries(col);
3538
- selectedAttribute ? entries.filter((e) => entryMatchesFilter(e, selectedAttribute)).length : 0;
3765
+ selectedAttribute ? entries.filter((e) => entryMatchesFilter(e, effectiveSearchAttribute)).length : 0;
3539
3766
  const isFullySelected = isCollectionFullySelected(col);
3540
3767
  const isPartiallySelected = isCollectionPartiallySelected(col);
3541
3768
  const currentSort = collectionSorts[col.uid] || { field: "createdAt", order: "desc" };
3542
- const availableColumns = getAvailableColumnsForCollection(col, selectedAttribute);
3769
+ const availableColumns = getAvailableColumnsForCollection(col, selectedAttribute, searchAttribute);
3543
3770
  const savedColKeys = collectionColumnVisibility[col.uid];
3544
3771
  const activeColumns = savedColKeys ? availableColumns.filter((c) => c.isPermanent || savedColKeys.includes(c.key)) : availableColumns.filter((c) => c.defaultVisible);
3545
3772
  return /* @__PURE__ */ jsxs(
@@ -3749,7 +3976,7 @@ const BulkUpdater = () => {
3749
3976
  ),
3750
3977
  /* @__PURE__ */ jsx(Box, { className: "bulk-updater-table-container", style: { width: "100%", maxWidth: "100%", minWidth: 0, overflowX: "auto" }, children: /* @__PURE__ */ jsxs(Table, { colCount: activeColumns.length, rowCount: visibleEntries.length + 1, style: { width: "100%" }, children: [
3751
3978
  /* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsx(Tr, { children: activeColumns.map(
3752
- (colDef) => renderHeaderCell(colDef, col, currentSort, selectedAttribute, handleHeaderSortClick)
3979
+ (colDef) => renderHeaderCell(colDef, col, currentSort, selectedAttribute, searchAttribute, handleHeaderSortClick)
3753
3980
  ) }) }),
3754
3981
  /* @__PURE__ */ jsx(Tbody, { children: visibleEntries.length === 0 ? /* @__PURE__ */ jsx(Tr, { children: /* @__PURE__ */ jsx(Td, { colSpan: activeColumns.length, children: /* @__PURE__ */ jsx(Box, { padding: 4, style: { textAlign: "center" }, children: /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", children: "No entries in this content type match the active filter criteria." }) }) }) }) : visibleEntries.map((entry) => {
3755
3982
  const key = `${col.uid}${KEY_SEP}${entry.documentId}`;
@@ -3767,6 +3994,7 @@ const BulkUpdater = () => {
3767
3994
  entry,
3768
3995
  col,
3769
3996
  selectedAttribute,
3997
+ searchAttribute,
3770
3998
  isMatch,
3771
3999
  hasAttr,
3772
4000
  isSelected,
@@ -4742,4 +4970,4 @@ export {
4742
4970
  getAvailableColumnsForCollection,
4743
4971
  getNestedValue
4744
4972
  };
4745
- //# sourceMappingURL=BulkUpdater-Cfig2f7d.mjs.map
4973
+ //# sourceMappingURL=BulkUpdater-DowSmA56.mjs.map