strapi-plugin-bulk-updater 1.3.10 → 1.4.2

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.
Files changed (25) hide show
  1. package/dist/_chunks/{BulkUpdater-J21ZgJWx.mjs → BulkUpdater-D4v5fP-1.mjs} +603 -420
  2. package/dist/_chunks/BulkUpdater-D4v5fP-1.mjs.map +1 -0
  3. package/dist/_chunks/{BulkUpdater-DRHQBZZV.js → BulkUpdater-L2FdlJfh.js} +602 -419
  4. package/dist/_chunks/BulkUpdater-L2FdlJfh.js.map +1 -0
  5. package/dist/_chunks/{SettingsPage-BDObdCIY.mjs → SettingsPage-D2qYhqpF.mjs} +2 -2
  6. package/dist/_chunks/{SettingsPage-BDObdCIY.mjs.map → SettingsPage-D2qYhqpF.mjs.map} +1 -1
  7. package/dist/_chunks/{SettingsPage-M-HQ8Jtr.js → SettingsPage-Dv2DRplw.js} +2 -2
  8. package/dist/_chunks/{SettingsPage-M-HQ8Jtr.js.map → SettingsPage-Dv2DRplw.js.map} +1 -1
  9. package/dist/_chunks/{ValidationRulesTab-DuvL-K4F.js → ValidationRulesTab-BTm3QXxM.js} +11 -1
  10. package/dist/_chunks/ValidationRulesTab-BTm3QXxM.js.map +1 -0
  11. package/dist/_chunks/{ValidationRulesTab-VeRr-vxa.mjs → ValidationRulesTab-BtOCWK8E.mjs} +12 -2
  12. package/dist/_chunks/ValidationRulesTab-BtOCWK8E.mjs.map +1 -0
  13. package/dist/admin/index.js +98 -4
  14. package/dist/admin/index.js.map +1 -1
  15. package/dist/admin/index.mjs +98 -4
  16. package/dist/admin/index.mjs.map +1 -1
  17. package/dist/server/index.js +803 -29893
  18. package/dist/server/index.js.map +1 -1
  19. package/dist/server/index.mjs +803 -29883
  20. package/dist/server/index.mjs.map +1 -1
  21. package/package.json +2 -1
  22. package/dist/_chunks/BulkUpdater-DRHQBZZV.js.map +0 -1
  23. package/dist/_chunks/BulkUpdater-J21ZgJWx.mjs.map +0 -1
  24. package/dist/_chunks/ValidationRulesTab-DuvL-K4F.js.map +0 -1
  25. package/dist/_chunks/ValidationRulesTab-VeRr-vxa.mjs.map +0 -1
@@ -2,8 +2,8 @@ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
  import React__default, { useState, useEffect, useMemo, useRef } from "react";
3
3
  import { useFetchClient, useNotification } from "@strapi/strapi/admin";
4
4
  import { Box, Flex, Typography, Button, SingleSelect, SingleSelectOption, Badge, Checkbox, Alert, Table, Thead, Tr, Th, Tbody, Td, Loader, TextInput, ComboboxOption, Field, Combobox } from "@strapi/design-system";
5
- import { ArrowClockwise, Download, Upload, Check, Eye, Play, Cog } from "@strapi/icons";
6
- import { V as ValidationRulesTab } from "./ValidationRulesTab-VeRr-vxa.mjs";
5
+ import { ArrowClockwise, Download, Upload, Check, Cog, Eye, Play } from "@strapi/icons";
6
+ import { i as isAbortError, V as ValidationRulesTab } from "./ValidationRulesTab-BtOCWK8E.mjs";
7
7
  function parseCsvRows(csvText) {
8
8
  if (!csvText) return [];
9
9
  let text = csvText;
@@ -91,6 +91,51 @@ function parseCsv(csvText) {
91
91
  }
92
92
  return records;
93
93
  }
94
+ const LIFECYCLE_STATUS_WORDS = /* @__PURE__ */ new Set([
95
+ "pending review",
96
+ "pending_review",
97
+ "pending approval",
98
+ "pending_approval",
99
+ "approved",
100
+ "rejected",
101
+ "draft",
102
+ "published",
103
+ "modified",
104
+ "supported",
105
+ "ignored"
106
+ ]);
107
+ function isValidDocId(val) {
108
+ if (!val || typeof val !== "string") return false;
109
+ const trimmed = val.trim();
110
+ if (!trimmed) return false;
111
+ if (LIFECYCLE_STATUS_WORDS.has(trimmed.toLowerCase())) return false;
112
+ if (/\s/.test(trimmed)) return false;
113
+ return /^[a-zA-Z0-9_-]+$/.test(trimmed);
114
+ }
115
+ function extractDocId(row) {
116
+ if (!row || typeof row !== "object") return "";
117
+ const candidates = [
118
+ row.documentId,
119
+ row.document_id,
120
+ row["Document ID"],
121
+ row["Document Id"],
122
+ row["document_id"],
123
+ row.docId,
124
+ row["docId"],
125
+ row.id,
126
+ row.ID,
127
+ row.Id
128
+ ];
129
+ for (const c of candidates) {
130
+ if (c !== void 0 && c !== null) {
131
+ const str = String(c).trim();
132
+ if (isValidDocId(str)) {
133
+ return str;
134
+ }
135
+ }
136
+ }
137
+ return "";
138
+ }
94
139
  function CsvManagerTab() {
95
140
  const { get, post } = useFetchClient();
96
141
  const { toggleNotification } = useNotification();
@@ -131,6 +176,7 @@ function CsvManagerTab() {
131
176
  }
132
177
  }
133
178
  } catch (err) {
179
+ if (isAbortError(err)) return;
134
180
  toggleNotification({
135
181
  type: "danger",
136
182
  message: err.message || "Failed loading collections for CSV Manager"
@@ -151,6 +197,7 @@ function CsvManagerTab() {
151
197
  message: "CSV Template downloaded successfully"
152
198
  });
153
199
  } catch (err) {
200
+ if (isAbortError(err)) return;
154
201
  toggleNotification({
155
202
  type: "danger",
156
203
  message: err.message || "Failed downloading CSV template"
@@ -169,6 +216,7 @@ function CsvManagerTab() {
169
216
  message: "Collection exported to CSV successfully"
170
217
  });
171
218
  } catch (err) {
219
+ if (isAbortError(err)) return;
172
220
  toggleNotification({
173
221
  type: "danger",
174
222
  message: err.message || "Failed exporting collection to CSV"
@@ -187,6 +235,7 @@ function CsvManagerTab() {
187
235
  message: "Rule violations exported to CSV successfully"
188
236
  });
189
237
  } catch (err) {
238
+ if (isAbortError(err)) return;
190
239
  toggleNotification({
191
240
  type: "danger",
192
241
  message: err.message || "Failed exporting violations to CSV"
@@ -256,6 +305,7 @@ function CsvManagerTab() {
256
305
  throw new Error(res.data?.error || "Import failed");
257
306
  }
258
307
  } catch (err) {
308
+ if (isAbortError(err)) return;
259
309
  toggleNotification({
260
310
  type: "danger",
261
311
  message: err.message || "Error processing CSV import"
@@ -409,7 +459,7 @@ function CsvManagerTab() {
409
459
  ),
410
460
  parsedPreview.length > 0 && (() => {
411
461
  const missingDocIdCount = parsedPreview.filter(
412
- (row) => !(row.documentId || row.id || "").trim()
462
+ (row) => !extractDocId(row)
413
463
  ).length;
414
464
  const headers = Object.keys(parsedPreview[0]);
415
465
  return /* @__PURE__ */ jsxs(Box, { marginTop: 4, background: "neutral0", padding: 4, hasRadius: true, children: [
@@ -454,7 +504,7 @@ function CsvManagerTab() {
454
504
  headers.map((h) => /* @__PURE__ */ jsx(Th, { style: { minWidth: 160 }, children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: h }) }, h))
455
505
  ] }) }),
456
506
  /* @__PURE__ */ jsx(Tbody, { children: parsedPreview.map((row, rIdx) => {
457
- const hasDocId = !!(row.documentId || row.id || "").trim();
507
+ const hasDocId = !!extractDocId(row);
458
508
  return /* @__PURE__ */ jsxs(Tr, { children: [
459
509
  /* @__PURE__ */ jsx(Td, { children: hasDocId ? /* @__PURE__ */ jsx(Badge, { backgroundColor: "primary100", textColor: "primary700", children: "🔄 Match / Update" }) : /* @__PURE__ */ jsx(Badge, { backgroundColor: "warning100", textColor: "warning700", children: "➕ New Entry" }) }),
460
510
  headers.map((h) => /* @__PURE__ */ jsx(Td, { style: { verticalAlign: "top", maxWidth: 300 }, children: (h === "documentId" || h === "id") && !row[h]?.trim() ? /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "warning700", fontWeight: "bold", children: "(Auto-generate ID)" }) : /* @__PURE__ */ jsx(
@@ -540,12 +590,53 @@ function CsvManagerTab() {
540
590
  importResult.stats.errors.length,
541
591
  "):"
542
592
  ] }),
543
- /* @__PURE__ */ jsx("ul", { style: { margin: "4px 0 0 16px", padding: 0 }, children: importResult.stats.errors.slice(0, 5).map((err, idx) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "danger600", children: [
593
+ /* @__PURE__ */ jsx("ul", { style: { margin: "4px 0 0 16px", padding: 0 }, children: importResult.stats.errors.slice(0, 10).map((err, idx) => /* @__PURE__ */ jsx("li", { style: { marginBottom: "2px" }, children: /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "danger600", children: [
544
594
  "Row ",
545
595
  err.row,
546
596
  ": ",
547
597
  err.error
548
- ] }) }, idx)) })
598
+ ] }) }, idx)) }),
599
+ importResult.stats.errors.length > 10 && /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", style: { display: "block", marginTop: 4, fontStyle: "italic" }, children: [
600
+ "...and ",
601
+ importResult.stats.errors.length - 10,
602
+ " more error",
603
+ importResult.stats.errors.length - 10 > 1 ? "s" : ""
604
+ ] })
605
+ ] }),
606
+ importResult.stats.diffs && importResult.stats.diffs.length > 0 && /* @__PURE__ */ jsxs(Box, { marginTop: 3, padding: 3, background: "neutral0", hasRadius: true, style: { border: "1px solid #dcdce4" }, children: [
607
+ /* @__PURE__ */ jsxs(Typography, { variant: "sigma", textColor: "neutral800", fontWeight: "bold", display: "block", marginBottom: 2, children: [
608
+ "CHANGES APPLIED (",
609
+ importResult.stats.diffs.length,
610
+ "):"
611
+ ] }),
612
+ /* @__PURE__ */ jsx(Box, { style: { overflowX: "auto" }, children: /* @__PURE__ */ jsxs(Table, { colCount: 4, rowCount: importResult.stats.diffs.length + 1, children: [
613
+ /* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [
614
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Entry / Document" }) }),
615
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Attribute" }) }),
616
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Previous Value" }) }),
617
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Applied Value" }) })
618
+ ] }) }),
619
+ /* @__PURE__ */ jsx(Tbody, { children: importResult.stats.diffs.slice(0, 10).map((d, idx) => {
620
+ const oldDisplay = d.oldValue !== null && d.oldValue !== void 0 ? typeof d.oldValue === "object" ? JSON.stringify(d.oldValue) : String(d.oldValue) : "(empty)";
621
+ const newDisplay = d.newValue !== null && d.newValue !== void 0 ? typeof d.newValue === "object" ? JSON.stringify(d.newValue) : String(d.newValue) : "(empty)";
622
+ return /* @__PURE__ */ jsxs(Tr, { children: [
623
+ /* @__PURE__ */ jsxs(Td, { children: [
624
+ /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", children: d.label || d.documentId }),
625
+ d.documentId && /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", display: "block", children: d.documentId })
626
+ ] }),
627
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: d.attribute || "field" }) }),
628
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "danger600", children: oldDisplay }) }),
629
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "success600", fontWeight: "semiBold", children: newDisplay }) })
630
+ ] }, idx);
631
+ }) })
632
+ ] }) }),
633
+ importResult.stats.diffs.length > 10 && /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", style: { display: "block", marginTop: 6, fontStyle: "italic" }, children: [
634
+ "...and ",
635
+ importResult.stats.diffs.length - 10,
636
+ " more change",
637
+ importResult.stats.diffs.length - 10 > 1 ? "s" : "",
638
+ " recorded in Audit Trail & Version History."
639
+ ] })
549
640
  ] })
550
641
  ]
551
642
  }
@@ -1566,13 +1657,15 @@ function getAvailableColumnsForCollection(col, selectedAttribute, searchAttribut
1566
1657
  defaultVisible: true
1567
1658
  });
1568
1659
  }
1569
- const targetLabel = searchAttribute && searchAttribute !== selectedAttribute ? `Update Target: "${selectedAttribute}"` : `Current "${selectedAttribute}"`;
1570
- cols.push({
1571
- key: "target_attr",
1572
- label: targetLabel,
1573
- sortable: !col.isSingleType && col.attributes.includes(selectedAttribute),
1574
- defaultVisible: true
1575
- });
1660
+ if (selectedAttribute && selectedAttribute.trim()) {
1661
+ const targetLabel = searchAttribute && searchAttribute !== selectedAttribute ? `Update Target: "${selectedAttribute}"` : `Current "${selectedAttribute}"`;
1662
+ cols.push({
1663
+ key: "target_attr",
1664
+ label: targetLabel,
1665
+ sortable: !col.isSingleType && col.attributes.includes(selectedAttribute),
1666
+ defaultVisible: true
1667
+ });
1668
+ }
1576
1669
  cols.push({
1577
1670
  key: "status",
1578
1671
  label: "Status",
@@ -1629,7 +1722,68 @@ function formatDisplayValue(val) {
1629
1722
  }
1630
1723
  return String(val);
1631
1724
  }
1632
- function renderCellContent(colDef, entry, col, selectedAttribute, searchAttribute, isMatch, hasAttr, isSelected, onToggleSelect) {
1725
+ function getEntryLifecycleStatus(entry, col, pendingBatches = []) {
1726
+ const isPendingApproval = pendingBatches.some((b) => {
1727
+ if (b.status !== "pending_approval") return false;
1728
+ const targets = b.payload?.selectedEntries || [];
1729
+ return targets.some((t) => t.documentId === entry.documentId || String(t.documentId) === String(entry.id));
1730
+ });
1731
+ if (isPendingApproval) {
1732
+ return {
1733
+ status: "pending_review",
1734
+ label: "Pending Review",
1735
+ badgeColor: "warning100",
1736
+ textColor: "warning700"
1737
+ };
1738
+ }
1739
+ if (entry.status === "modified") {
1740
+ return {
1741
+ status: "modified",
1742
+ label: "Modified",
1743
+ badgeColor: "primary100",
1744
+ textColor: "primary700"
1745
+ };
1746
+ }
1747
+ if (entry.status === "draft") {
1748
+ return {
1749
+ status: "draft",
1750
+ label: "Draft",
1751
+ badgeColor: "neutral150",
1752
+ textColor: "neutral700"
1753
+ };
1754
+ }
1755
+ if (entry.status === "published") {
1756
+ return {
1757
+ status: "published",
1758
+ label: "Published",
1759
+ badgeColor: "success100",
1760
+ textColor: "success700"
1761
+ };
1762
+ }
1763
+ if (col && col.draftAndPublish === false) {
1764
+ return {
1765
+ status: "published",
1766
+ label: "Published",
1767
+ badgeColor: "success100",
1768
+ textColor: "success700"
1769
+ };
1770
+ }
1771
+ if (entry.publishedAt) {
1772
+ return {
1773
+ status: "published",
1774
+ label: "Published",
1775
+ badgeColor: "success100",
1776
+ textColor: "success700"
1777
+ };
1778
+ }
1779
+ return {
1780
+ status: "draft",
1781
+ label: "Draft",
1782
+ badgeColor: "neutral150",
1783
+ textColor: "neutral700"
1784
+ };
1785
+ }
1786
+ function renderCellContent(colDef, entry, col, selectedAttribute, searchAttribute, isMatch, hasAttr, isSelected, onToggleSelect, pendingBatches = []) {
1633
1787
  switch (colDef.key) {
1634
1788
  case "select":
1635
1789
  return /* @__PURE__ */ jsx(
@@ -1688,11 +1842,12 @@ function renderCellContent(colDef, entry, col, selectedAttribute, searchAttribut
1688
1842
  );
1689
1843
  }
1690
1844
  case "status": {
1691
- if (!hasAttr) {
1845
+ if (selectedAttribute && !hasAttr) {
1692
1846
  return /* @__PURE__ */ jsx(Badge, { backgroundColor: "neutral200", textColor: "neutral600", children: "Ignored" });
1693
1847
  }
1848
+ const lifecycle = getEntryLifecycleStatus(entry, col, pendingBatches);
1694
1849
  return /* @__PURE__ */ jsxs(Flex, { gap: 1, alignItems: "center", children: [
1695
- /* @__PURE__ */ jsx(Badge, { backgroundColor: "success100", textColor: "success700", children: "Supported" }),
1850
+ /* @__PURE__ */ jsx(Badge, { backgroundColor: lifecycle.badgeColor, textColor: lifecycle.textColor, children: lifecycle.label }),
1696
1851
  isMatch && /* @__PURE__ */ jsx(Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: "Matches Filter" })
1697
1852
  ] });
1698
1853
  }
@@ -1776,7 +1931,7 @@ const BulkUpdater = () => {
1776
1931
  if (stored) return stored;
1777
1932
  } catch {
1778
1933
  }
1779
- return "metaDescription";
1934
+ return "";
1780
1935
  };
1781
1936
  const [selectedAttribute, setSelectedAttribute] = useState(getInitialAttribute);
1782
1937
  const [attributeValue, setAttributeValue] = useState("");
@@ -1828,7 +1983,8 @@ const BulkUpdater = () => {
1828
1983
  const [filterOperator, setFilterOperator] = useState("none");
1829
1984
  const [filterQuery, setFilterQuery] = useState("");
1830
1985
  const [entrySearch, setEntrySearch] = useState("");
1831
- const isFilterActive = Boolean(searchAttribute && searchAttribute !== selectedAttribute) || filterOperator !== "none" && (["empty", "not_empty"].includes(filterOperator) || Boolean(filterQuery.trim())) || Boolean(entrySearch.trim()) || Boolean(useRegex && regexPattern.trim());
1986
+ const [statusFilter, setStatusFilter] = useState("all");
1987
+ const isFilterActive = Boolean(searchAttribute && searchAttribute !== selectedAttribute) || filterOperator !== "none" && (["empty", "not_empty"].includes(filterOperator) || Boolean(filterQuery.trim())) || Boolean(entrySearch.trim()) || Boolean(statusFilter !== "all") || Boolean(useRegex && regexPattern.trim());
1832
1988
  const [lastResults, setLastResults] = useState(null);
1833
1989
  const [updateSummary, setUpdateSummary] = useState(null);
1834
1990
  const [collectionPages, setCollectionPages] = useState({});
@@ -1934,7 +2090,7 @@ const BulkUpdater = () => {
1934
2090
  storedAttr = localStorage.getItem(LAST_ATTR_KEY);
1935
2091
  } catch {
1936
2092
  }
1937
- const resolvedAttr = storedAttr && activeAttrs.includes(storedAttr) ? storedAttr : selectedAttribute && activeAttrs.includes(selectedAttribute) ? selectedAttribute : activeAttrs[0] || "";
2093
+ const resolvedAttr = storedAttr && activeAttrs.includes(storedAttr) ? storedAttr : selectedAttribute && activeAttrs.includes(selectedAttribute) ? selectedAttribute : "";
1938
2094
  setSelectedAttribute(resolvedAttr);
1939
2095
  if (resolvedAttr && data.relationsMeta && data.relationsMeta[resolvedAttr]) {
1940
2096
  fetchRelationOptions(data.relationsMeta[resolvedAttr].targetUid);
@@ -1965,8 +2121,7 @@ const BulkUpdater = () => {
1965
2121
  }
1966
2122
  }
1967
2123
  } catch (err) {
1968
- const isAborted = err?.name === "AbortError" || err?.name === "CanceledError" || err?.code === "ERR_CANCELED" || err?.message?.includes("aborted") || err?.message?.includes("canceled");
1969
- if (!isAborted) {
2124
+ if (!isAbortError(err)) {
1970
2125
  toggleNotification({
1971
2126
  type: "danger",
1972
2127
  message: err?.message || "Failed to load collections data"
@@ -2043,8 +2198,7 @@ const BulkUpdater = () => {
2043
2198
  }
2044
2199
  }
2045
2200
  } catch (err) {
2046
- const isAborted = err?.name === "AbortError" || err?.name === "CanceledError" || err?.code === "ERR_CANCELED" || err?.message?.includes("aborted") || err?.message?.includes("canceled");
2047
- if (!isAborted) {
2201
+ if (!isAbortError(err)) {
2048
2202
  toggleNotification({
2049
2203
  type: "danger",
2050
2204
  message: `Failed to load entries for ${colUid}: ${err?.message || "Network error"}`
@@ -2073,8 +2227,7 @@ const BulkUpdater = () => {
2073
2227
  }
2074
2228
  }
2075
2229
  } catch (err) {
2076
- const isAborted = err?.name === "AbortError" || err?.name === "CanceledError" || err?.code === "ERR_CANCELED" || err?.message?.includes("aborted") || err?.message?.includes("canceled");
2077
- if (!isAborted) {
2230
+ if (!isAbortError(err)) {
2078
2231
  toggleNotification({
2079
2232
  type: "danger",
2080
2233
  message: err?.message || "Failed to fetch audit history."
@@ -2156,18 +2309,14 @@ const BulkUpdater = () => {
2156
2309
  const targetCol = collections.find((c) => c.uid === newUid);
2157
2310
  const colAttrs = targetCol?.attributes || [];
2158
2311
  setAttributes(colAttrs);
2159
- if (!colAttrs.includes(selectedAttribute)) {
2160
- const newAttr = colAttrs[0] || "";
2161
- setSelectedAttribute(newAttr);
2312
+ if (selectedAttribute && !colAttrs.includes(selectedAttribute)) {
2313
+ setSelectedAttribute("");
2162
2314
  try {
2163
- localStorage.setItem(LAST_ATTR_KEY, newAttr);
2315
+ localStorage.removeItem(LAST_ATTR_KEY);
2164
2316
  } catch {
2165
2317
  }
2166
- if (newAttr && relationsMeta[newAttr]) {
2167
- fetchRelationOptions(relationsMeta[newAttr].targetUid);
2168
- } else {
2169
- setSelectedRelationDocId("");
2170
- }
2318
+ setSelectedRelationDocId("");
2319
+ setAttributeValue("");
2171
2320
  }
2172
2321
  loadCollectionPage(newUid, 1, void 0, void 0, void 0, filterOperator, filterQuery, entrySearch, true, "");
2173
2322
  };
@@ -2182,10 +2331,6 @@ const BulkUpdater = () => {
2182
2331
  }
2183
2332
  } catch {
2184
2333
  }
2185
- setSelectedEntries({});
2186
- setFilterOperator("none");
2187
- setFilterQuery("");
2188
- setEntrySearch("");
2189
2334
  setSelectedRelationDocId("");
2190
2335
  setAttributeValue("");
2191
2336
  if (newAttr && relationsMeta[newAttr]) {
@@ -2193,9 +2338,6 @@ const BulkUpdater = () => {
2193
2338
  } else {
2194
2339
  setRelationOptions([]);
2195
2340
  }
2196
- if (newAttr && selectedCollectionUid) {
2197
- loadCollectionPage(selectedCollectionUid, 1);
2198
- }
2199
2341
  }
2200
2342
  };
2201
2343
  const filteredCollections = useMemo(() => {
@@ -2211,10 +2353,10 @@ const BulkUpdater = () => {
2211
2353
  return availableColAttributes.filter((attr) => getAttrDepth(attr) <= componentDepth);
2212
2354
  }, [availableColAttributes, componentDepth, attributesMeta]);
2213
2355
  const filteredAttributes = useMemo(() => {
2214
- if (!attributeSearch.trim()) {
2356
+ const q = attributeSearch.toLowerCase().trim();
2357
+ if (!q || q === (selectedAttribute || "").toLowerCase().trim()) {
2215
2358
  return depthFilteredAttributes;
2216
2359
  }
2217
- const q = attributeSearch.toLowerCase().trim();
2218
2360
  const cleanQ = q.replace(/[^a-z0-9]/g, "");
2219
2361
  return depthFilteredAttributes.filter((attr) => {
2220
2362
  const lower = attr.toLowerCase();
@@ -2223,12 +2365,12 @@ const BulkUpdater = () => {
2223
2365
  const relTarget = (rel?.targetDisplayName || "").toLowerCase();
2224
2366
  return lower.includes(q) || cleanLower.includes(cleanQ) || relTarget.includes(q);
2225
2367
  });
2226
- }, [depthFilteredAttributes, attributeSearch, relationsMeta]);
2368
+ }, [depthFilteredAttributes, attributeSearch, selectedAttribute, relationsMeta]);
2227
2369
  const filteredSearchAttributes = useMemo(() => {
2228
- if (!searchAttributeSearch.trim()) {
2370
+ const q = searchAttributeSearch.toLowerCase().trim();
2371
+ if (!q || q === (searchAttribute || "").toLowerCase().trim()) {
2229
2372
  return depthFilteredAttributes;
2230
2373
  }
2231
- const q = searchAttributeSearch.toLowerCase().trim();
2232
2374
  const cleanQ = q.replace(/[^a-z0-9]/g, "");
2233
2375
  return depthFilteredAttributes.filter((attr) => {
2234
2376
  const lower = attr.toLowerCase();
@@ -2237,7 +2379,7 @@ const BulkUpdater = () => {
2237
2379
  const relTarget = (rel?.targetDisplayName || "").toLowerCase();
2238
2380
  return lower.includes(q) || cleanLower.includes(cleanQ) || relTarget.includes(q);
2239
2381
  });
2240
- }, [depthFilteredAttributes, searchAttributeSearch, relationsMeta]);
2382
+ }, [depthFilteredAttributes, searchAttributeSearch, searchAttribute, relationsMeta]);
2241
2383
  const memoizedComboboxOptions = useMemo(() => {
2242
2384
  if (filteredAttributes.length === 0) {
2243
2385
  return /* @__PURE__ */ jsx(ComboboxOption, { value: "__none__", disabled: true, textValue: "No matching attributes found", children: /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", children: [
@@ -2373,24 +2515,25 @@ const BulkUpdater = () => {
2373
2515
  };
2374
2516
  const getVisibleEntries = (col) => {
2375
2517
  const pageState = collectionPages[col.uid];
2376
- return pageState ? pageState.entries : col.entries || [];
2518
+ const raw = pageState ? pageState.entries : col.entries || [];
2519
+ if (statusFilter === "all") return raw;
2520
+ return raw.filter((entry) => {
2521
+ const lifecycle = getEntryLifecycleStatus(entry, col, pendingBatches);
2522
+ return lifecycle.status === statusFilter;
2523
+ });
2377
2524
  };
2378
2525
  const matchingEntriesCount = React__default.useMemo(() => {
2379
2526
  if (!isFilterActive) return 0;
2380
2527
  let count = 0;
2381
2528
  filteredCollections.forEach((col) => {
2382
- const pageState = collectionPages[col.uid];
2383
- if (pageState) {
2384
- count += pageState.total;
2385
- }
2529
+ count += getVisibleEntries(col).length;
2386
2530
  });
2387
2531
  return count;
2388
- }, [filteredCollections, collectionPages, isFilterActive]);
2532
+ }, [filteredCollections, collectionPages, isFilterActive, statusFilter, pendingBatches]);
2389
2533
  const selectAllMatching = () => {
2390
2534
  const updated = { ...selectedEntries };
2391
2535
  filteredCollections.forEach((col) => {
2392
- const pageState = collectionPages[col.uid];
2393
- const entries = pageState ? pageState.entries : col.entries || [];
2536
+ const entries = getVisibleEntries(col);
2394
2537
  entries.forEach((e) => {
2395
2538
  updated[`${col.uid}${KEY_SEP}${e.documentId}`] = true;
2396
2539
  });
@@ -2400,8 +2543,7 @@ const BulkUpdater = () => {
2400
2543
  const deselectAllMatching = () => {
2401
2544
  const updated = { ...selectedEntries };
2402
2545
  filteredCollections.forEach((col) => {
2403
- const pageState = collectionPages[col.uid];
2404
- const entries = pageState ? pageState.entries : col.entries || [];
2546
+ const entries = getVisibleEntries(col);
2405
2547
  entries.forEach((e) => {
2406
2548
  delete updated[`${col.uid}${KEY_SEP}${e.documentId}`];
2407
2549
  });
@@ -2414,6 +2556,7 @@ const BulkUpdater = () => {
2414
2556
  setFilterOperator("none");
2415
2557
  setFilterQuery("");
2416
2558
  setEntrySearch("");
2559
+ setStatusFilter("all");
2417
2560
  setRegexPattern("");
2418
2561
  setSelectedEntries({});
2419
2562
  collections.forEach((col) => {
@@ -2534,10 +2677,12 @@ const BulkUpdater = () => {
2534
2677
  throw new Error(res.data?.error || "Failed to generate preview");
2535
2678
  }
2536
2679
  } catch (err) {
2537
- toggleNotification({
2538
- type: "danger",
2539
- message: err.message || "Failed to fetch preview diffs"
2540
- });
2680
+ if (!isAbortError(err)) {
2681
+ toggleNotification({
2682
+ type: "danger",
2683
+ message: err.message || "Failed to fetch preview diffs"
2684
+ });
2685
+ }
2541
2686
  setShowPreviewModal(false);
2542
2687
  } finally {
2543
2688
  setPreviewLoading(false);
@@ -3270,349 +3415,128 @@ const BulkUpdater = () => {
3270
3415
  marginBottom: 6,
3271
3416
  style: { width: "100%", maxWidth: "100%", minWidth: 0, boxSizing: "border-box" },
3272
3417
  children: [
3273
- /* @__PURE__ */ jsx(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, wrap: "wrap", gap: 3, children: /* @__PURE__ */ jsxs(Box, { children: [
3274
- /* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h2", fontWeight: "semiBold", children: "1. Configure Attribute Update" }),
3275
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Select the target attribute to update and configure the replacement value." })
3276
- ] }) }),
3277
- /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "flex-end", wrap: "wrap", children: [
3278
- /* @__PURE__ */ jsx(Box, { style: { width: 280, minWidth: 230 }, children: /* @__PURE__ */ jsxs(Field.Root, { children: [
3279
- /* @__PURE__ */ jsx(Field.Label, { children: "Attribute to Update" }),
3280
- /* @__PURE__ */ jsx(
3281
- Combobox,
3282
- {
3283
- autocomplete: "none",
3284
- placeholder: "Search attribute by name or relation...",
3285
- value: selectedAttribute || void 0,
3286
- clearLabel: "Clear attribute selection",
3287
- onClear: () => {
3288
- handleAttributeChange("");
3289
- setAttributeSearch("");
3290
- },
3291
- onInputChange: (e) => {
3292
- setAttributeSearch(e.target.value);
3293
- },
3294
- onOpenChange: (isOpen) => {
3295
- if (!isOpen) {
3296
- setAttributeSearch("");
3418
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, wrap: "wrap", gap: 3, children: [
3419
+ /* @__PURE__ */ jsxs(Box, { children: [
3420
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h2", fontWeight: "semiBold", children: "1. Search & Filter Targets" }),
3421
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", children: [
3422
+ "Search, filter, and select entries within ",
3423
+ selectedCol ? selectedCol.displayName : "the selected collection",
3424
+ " before updating."
3425
+ ] })
3426
+ ] }),
3427
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3428
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "S", onClick: selectAll, children: "Select All" }),
3429
+ /* @__PURE__ */ jsx(Button, { variant: "tertiary", size: "S", onClick: deselectAll, children: "Deselect All" })
3430
+ ] })
3431
+ ] }),
3432
+ /* @__PURE__ */ jsx(
3433
+ Box,
3434
+ {
3435
+ padding: 4,
3436
+ background: "neutral100",
3437
+ hasRadius: true,
3438
+ marginBottom: 5,
3439
+ style: { width: "100%", maxWidth: "100%", boxSizing: "border-box" },
3440
+ children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, children: [
3441
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 2, children: [
3442
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
3443
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", children: "Search & Filter Targets:" }),
3444
+ isFilterActive && /* @__PURE__ */ jsxs(Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
3445
+ matchingEntriesCount,
3446
+ " matching ",
3447
+ matchingEntriesCount === 1 ? "entry" : "entries",
3448
+ " found in database"
3449
+ ] }),
3450
+ searchAttribute && searchAttribute !== selectedAttribute && /* @__PURE__ */ jsxs(Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: [
3451
+ "Searching by: ",
3452
+ searchAttribute,
3453
+ " → Target to update: ",
3454
+ selectedAttribute
3455
+ ] })
3456
+ ] }),
3457
+ isFilterActive && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "S", onClick: clearFilter, children: "Clear Search & Filter" })
3458
+ ] }),
3459
+ /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "flex-end", wrap: "wrap", children: [
3460
+ /* @__PURE__ */ jsx(Box, { style: { width: 280, minWidth: 230 }, children: /* @__PURE__ */ jsxs(Field.Root, { children: [
3461
+ /* @__PURE__ */ jsx(Field.Label, { children: "Search Attribute" }),
3462
+ /* @__PURE__ */ jsx(
3463
+ Combobox,
3464
+ {
3465
+ autocomplete: "none",
3466
+ placeholder: searchAttribute || `Same as Update (${selectedAttribute || "None"})`,
3467
+ value: searchAttribute || "__default__",
3468
+ clearLabel: "Reset search attribute",
3469
+ onClear: () => {
3470
+ handleSearchAttributeChange("");
3471
+ setSearchAttributeSearch("");
3472
+ },
3473
+ onInputChange: (e) => {
3474
+ setSearchAttributeSearch(e.target.value);
3475
+ },
3476
+ onOpenChange: (isOpen) => {
3477
+ if (!isOpen) {
3478
+ setSearchAttributeSearch("");
3479
+ }
3480
+ },
3481
+ onChange: (val) => {
3482
+ if (val && typeof val === "string" && val !== "__none__") {
3483
+ handleSearchAttributeChange(val);
3484
+ setSearchAttributeSearch("");
3485
+ }
3486
+ },
3487
+ children: memoizedSearchComboboxOptions
3488
+ },
3489
+ `search-attr-${selectedCollectionUid}`
3490
+ )
3491
+ ] }) }),
3492
+ /* @__PURE__ */ jsx(Box, { style: { width: 200, minWidth: 170 }, children: /* @__PURE__ */ jsxs(
3493
+ SingleSelect,
3494
+ {
3495
+ label: effectiveSearchAttribute ? `Condition for "${effectiveSearchAttribute}"` : "Filter condition",
3496
+ value: filterOperator,
3497
+ onChange: (val) => setFilterOperator(String(val)),
3498
+ children: [
3499
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "none", children: "Show All (No Value Filter)" }),
3500
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "empty", children: "Is Empty or Null" }),
3501
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "not_empty", children: "Is Not Empty" }),
3502
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "equals", children: "Equals Exact Text" }),
3503
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "contains", children: "Contains Substring" }),
3504
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "starts_with", children: "Starts With" })
3505
+ ]
3297
3506
  }
3298
- },
3299
- loading: loading && attributes.length === 0,
3300
- loadingMessage: "Discovering schema attributes...",
3301
- onChange: (val) => {
3302
- if (val && typeof val === "string" && val !== "__none__") {
3303
- handleAttributeChange(val);
3304
- setAttributeSearch("");
3507
+ ) }),
3508
+ (filterOperator === "equals" || filterOperator === "contains" || filterOperator === "starts_with") && /* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 180 }, children: /* @__PURE__ */ jsx(
3509
+ TextInput,
3510
+ {
3511
+ label: effectiveSearchAttribute ? `Value for "${effectiveSearchAttribute}"` : "Value to match",
3512
+ placeholder: "Type value to match...",
3513
+ value: filterQuery,
3514
+ onChange: (e) => setFilterQuery(e.target.value)
3305
3515
  }
3306
- },
3307
- children: memoizedComboboxOptions
3308
- },
3309
- `update-attr-${selectedCollectionUid}-${selectedAttribute}`
3310
- )
3311
- ] }) }),
3312
- /* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 340 }, children: relationsMeta[selectedAttribute] ? /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 1, style: { width: "100%" }, children: [
3313
- /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
3314
- /* @__PURE__ */ jsxs(Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral700", children: [
3315
- "Target ",
3316
- relationsMeta[selectedAttribute].targetDisplayName,
3317
- " Relation:"
3318
- ] }),
3319
- /* @__PURE__ */ jsxs(Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: [
3320
- relationsMeta[selectedAttribute].relationType,
3321
- " → ",
3322
- relationsMeta[selectedAttribute].targetDisplayName
3323
- ] }),
3324
- relationsMeta[selectedAttribute].isMulti && /* @__PURE__ */ jsx(Badge, { backgroundColor: "primary100", textColor: "primary700", children: "Multi-Relation" })
3325
- ] }),
3326
- /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", style: { width: "100%" }, children: [
3327
- /* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 200 }, children: /* @__PURE__ */ jsxs(Field.Root, { children: [
3328
- /* @__PURE__ */ jsx(Field.Label, { children: `Choose ${relationsMeta[selectedAttribute].targetDisplayName}` }),
3329
- /* @__PURE__ */ jsxs(
3330
- Combobox,
3516
+ ) }),
3517
+ /* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 180 }, children: /* @__PURE__ */ jsx(
3518
+ TextInput,
3331
3519
  {
3332
- autocomplete: { type: "list", filter: "contains" },
3333
- placeholder: relationOptionsLoading ? "Loading entities..." : `Search & pick ${relationsMeta[selectedAttribute].targetDisplayName}...`,
3334
- value: selectedRelationDocId,
3335
- onChange: (val) => {
3336
- const docId = val ? String(val) : "";
3337
- setSelectedRelationDocId(docId);
3338
- if (docId === "__clear__") {
3339
- setAttributeValue(JSON.stringify({ mode: "clear", label: "(clear relation)" }));
3340
- } else {
3341
- const opt = relationOptions.find((o) => o.documentId === docId);
3342
- setAttributeValue(JSON.stringify({ mode: relationMode, documentIds: [docId], label: opt?.label || docId }));
3343
- }
3344
- },
3520
+ label: "Full-text Entry Search",
3521
+ placeholder: "Search title, name, or text across records...",
3522
+ value: entrySearch,
3523
+ onChange: (e) => setEntrySearch(e.target.value)
3524
+ }
3525
+ ) }),
3526
+ /* @__PURE__ */ jsx(Box, { style: { width: 190, minWidth: 160 }, children: /* @__PURE__ */ jsxs(
3527
+ SingleSelect,
3528
+ {
3529
+ label: "Lifecycle Status",
3530
+ value: statusFilter,
3531
+ onChange: (val) => setStatusFilter(String(val)),
3345
3532
  children: [
3346
- /* @__PURE__ */ jsx(ComboboxOption, { value: "__clear__", textValue: "Clear / Unlink Relation", children: "-- Clear / Unlink Relation --" }),
3347
- relationOptions.map((opt) => /* @__PURE__ */ jsx(ComboboxOption, { value: opt.documentId, textValue: opt.label, children: opt.label }, opt.documentId))
3533
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "all", children: "All Statuses" }),
3534
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "pending_review", children: "Pending Review (Due)" }),
3535
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "draft", children: "Draft" }),
3536
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "published", children: "Published" }),
3537
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "modified", children: "Modified" })
3348
3538
  ]
3349
3539
  }
3350
- )
3351
- ] }) }),
3352
- relationsMeta[selectedAttribute].isMulti && /* @__PURE__ */ jsx(Box, { style: { width: 170 }, children: /* @__PURE__ */ jsxs(
3353
- SingleSelect,
3354
- {
3355
- label: "Action Mode",
3356
- value: relationMode,
3357
- onChange: (val) => {
3358
- const m = String(val);
3359
- setRelationMode(m);
3360
- if (selectedRelationDocId) {
3361
- if (selectedRelationDocId === "__clear__" || m === "clear") {
3362
- setAttributeValue(JSON.stringify({ mode: "clear", label: "(clear relation)" }));
3363
- } else {
3364
- const opt = relationOptions.find((o) => o.documentId === selectedRelationDocId);
3365
- setAttributeValue(JSON.stringify({ mode: m, documentIds: [selectedRelationDocId], label: opt?.label || selectedRelationDocId }));
3366
- }
3367
- }
3368
- },
3369
- children: [
3370
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "set", children: "Set / Overwrite" }),
3371
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "connect", children: "Add / Connect" }),
3372
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "disconnect", children: "Remove / Disconnect" }),
3373
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "clear", children: "Clear All" })
3374
- ]
3375
- }
3376
- ) })
3377
- ] })
3378
- ] }) : /* @__PURE__ */ jsx(
3379
- TextInput,
3380
- {
3381
- label: useRegex ? "Replacement Value" : "New Attribute Value",
3382
- name: "attributeValue",
3383
- placeholder: useRegex ? "Replace matched pattern with..." : "Enter the value to apply...",
3384
- value: attributeValue,
3385
- onChange: (e) => setAttributeValue(e.target.value)
3386
- }
3387
- ) }),
3388
- /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
3389
- /* @__PURE__ */ jsxs(
3390
- Button,
3391
- {
3392
- startIcon: /* @__PURE__ */ jsx(Eye, {}),
3393
- variant: "secondary",
3394
- size: "L",
3395
- onClick: handleOpenPreviewModal,
3396
- loading: previewLoading,
3397
- disabled: loading || totalSelectedCount === 0 || !selectedAttribute,
3398
- children: [
3399
- "Preview Changes (",
3400
- totalSelectedCount,
3401
- ")"
3402
- ]
3403
- }
3404
- ),
3405
- /* @__PURE__ */ jsxs(
3406
- Button,
3407
- {
3408
- startIcon: /* @__PURE__ */ jsx(Play, {}),
3409
- variant: "default",
3410
- size: "L",
3411
- onClick: handleOpenConfirmModal,
3412
- loading: updating,
3413
- disabled: loading || totalSelectedCount === 0 || !selectedAttribute,
3414
- children: [
3415
- "Update Selected (",
3416
- totalSelectedCount,
3417
- ")"
3418
- ]
3419
- }
3420
- ),
3421
- /* @__PURE__ */ jsxs(
3422
- Button,
3423
- {
3424
- startIcon: /* @__PURE__ */ jsx(Check, {}),
3425
- variant: "success-light",
3426
- size: "L",
3427
- onClick: handleOpenPublishModal,
3428
- loading: publishing,
3429
- disabled: loading || totalSelectedCount === 0,
3430
- title: "Directly publish selected draft records across collections",
3431
- children: [
3432
- "Publish Selected (",
3433
- totalSelectedCount,
3434
- ")"
3435
- ]
3436
- }
3437
- ),
3438
- /* @__PURE__ */ jsxs(
3439
- Button,
3440
- {
3441
- startIcon: /* @__PURE__ */ jsx(Download, {}),
3442
- variant: "tertiary",
3443
- size: "L",
3444
- onClick: handleBulkExportSelected,
3445
- loading: bulkExporting,
3446
- disabled: loading || totalSelectedCount === 0,
3447
- title: "Export selected entries to Excel-compatible CSV with full nested component support",
3448
- children: [
3449
- "Export Selected (",
3450
- totalSelectedCount,
3451
- ")"
3452
- ]
3453
- }
3454
- )
3455
- ] })
3456
- ] }),
3457
- /* @__PURE__ */ jsx(Box, { marginTop: 4, padding: 4, background: "neutral100", hasRadius: true, style: { border: "1px solid #eaeaef" }, children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: [
3458
- /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
3459
- /* @__PURE__ */ jsx(
3460
- Checkbox,
3461
- {
3462
- checked: publishOnApproval,
3463
- onCheckedChange: (checked) => setPublishOnApproval(Boolean(checked)),
3464
- children: /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: "Auto-Publish after Update" })
3465
- }
3466
- ),
3467
- /* @__PURE__ */ jsx(
3468
- Badge,
3469
- {
3470
- backgroundColor: publishOnApproval ? "success100" : "warning100",
3471
- textColor: publishOnApproval ? "success700" : "warning700",
3472
- children: publishOnApproval ? "✓ LIVE PUBLISH" : "DRAFT ONLY"
3473
- }
3474
- )
3475
- ] }),
3476
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: publishOnApproval ? "When enabled, entries will be published live immediately upon batch execution for draft-enabled content types." : "When disabled, entries will be saved as draft versions without publishing to live visitors." })
3477
- ] }) }),
3478
- selectedCol && selectedAttribute && /* @__PURE__ */ jsx(Box, { marginTop: 4, padding: 3, background: "neutral100", hasRadius: true, style: { border: "1px solid #eaeaef" }, children: /* @__PURE__ */ jsx(Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
3479
- /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", children: "Target Selection:" }),
3480
- /* @__PURE__ */ jsxs(Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
3481
- "Collection: ",
3482
- selectedCol.displayName
3483
- ] }),
3484
- /* @__PURE__ */ jsxs(Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: [
3485
- "Attribute: ",
3486
- selectedAttribute,
3487
- " ",
3488
- attributesMeta[selectedAttribute]?.type ? `(${attributesMeta[selectedAttribute].type})` : ""
3489
- ] }),
3490
- attributesMeta[selectedAttribute]?.depth ? /* @__PURE__ */ jsxs(Badge, { backgroundColor: "neutral200", textColor: "neutral700", children: [
3491
- "Nesting Level: L",
3492
- attributesMeta[selectedAttribute].depth
3493
- ] }) : null,
3494
- selectedCol.draftAndPublish ? /* @__PURE__ */ jsx(Badge, { backgroundColor: "success100", textColor: "success700", children: "Draft & Publish Enabled" }) : /* @__PURE__ */ jsx(Badge, { backgroundColor: "warning100", textColor: "warning700", children: "Direct Publish Only" })
3495
- ] }) }) })
3496
- ]
3497
- }
3498
- ),
3499
- /* @__PURE__ */ jsxs(
3500
- Box,
3501
- {
3502
- padding: 6,
3503
- background: "neutral0",
3504
- shadow: "filterShadow",
3505
- hasRadius: true,
3506
- marginBottom: 6,
3507
- style: { width: "100%", maxWidth: "100%", minWidth: 0, boxSizing: "border-box" },
3508
- children: [
3509
- /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, wrap: "wrap", gap: 3, children: [
3510
- /* @__PURE__ */ jsxs(Box, { children: [
3511
- /* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h2", fontWeight: "semiBold", children: "2. Select Targets" }),
3512
- /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", children: [
3513
- "Search and select entries to apply the update to within ",
3514
- selectedCol ? selectedCol.displayName : "the selected collection",
3515
- "."
3516
- ] })
3517
- ] }),
3518
- /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3519
- /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "S", onClick: selectAll, children: "Select All" }),
3520
- /* @__PURE__ */ jsx(Button, { variant: "tertiary", size: "S", onClick: deselectAll, children: "Deselect All" })
3521
- ] })
3522
- ] }),
3523
- /* @__PURE__ */ jsx(
3524
- Box,
3525
- {
3526
- padding: 4,
3527
- background: "neutral100",
3528
- hasRadius: true,
3529
- marginBottom: 5,
3530
- style: { width: "100%", maxWidth: "100%", boxSizing: "border-box" },
3531
- children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, children: [
3532
- /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 2, children: [
3533
- /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
3534
- /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", children: "Search & Filter Targets:" }),
3535
- isFilterActive && /* @__PURE__ */ jsxs(Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
3536
- matchingEntriesCount,
3537
- " matching ",
3538
- matchingEntriesCount === 1 ? "entry" : "entries",
3539
- " found in database"
3540
- ] }),
3541
- searchAttribute && searchAttribute !== selectedAttribute && /* @__PURE__ */ jsxs(Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: [
3542
- "Searching by: ",
3543
- searchAttribute,
3544
- " → Target to update: ",
3545
- selectedAttribute
3546
- ] })
3547
- ] }),
3548
- isFilterActive && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "S", onClick: clearFilter, children: "Clear Search & Filter" })
3549
- ] }),
3550
- /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "flex-end", wrap: "wrap", children: [
3551
- /* @__PURE__ */ jsx(Box, { style: { width: 280, minWidth: 230 }, children: /* @__PURE__ */ jsxs(Field.Root, { children: [
3552
- /* @__PURE__ */ jsx(Field.Label, { children: "Search Attribute" }),
3553
- /* @__PURE__ */ jsx(
3554
- Combobox,
3555
- {
3556
- autocomplete: "none",
3557
- placeholder: searchAttribute || `Same as Update (${selectedAttribute || "None"})`,
3558
- value: searchAttribute || "__default__",
3559
- clearLabel: "Reset search attribute",
3560
- onClear: () => {
3561
- handleSearchAttributeChange("");
3562
- setSearchAttributeSearch("");
3563
- },
3564
- onInputChange: (e) => {
3565
- setSearchAttributeSearch(e.target.value);
3566
- },
3567
- onOpenChange: (isOpen) => {
3568
- if (!isOpen) {
3569
- setSearchAttributeSearch("");
3570
- }
3571
- },
3572
- onChange: (val) => {
3573
- if (val && typeof val === "string" && val !== "__none__") {
3574
- handleSearchAttributeChange(val);
3575
- setSearchAttributeSearch("");
3576
- }
3577
- },
3578
- children: memoizedSearchComboboxOptions
3579
- },
3580
- `search-attr-${selectedCollectionUid}-${searchAttribute}`
3581
- )
3582
- ] }) }),
3583
- /* @__PURE__ */ jsx(Box, { style: { width: 200, minWidth: 170 }, children: /* @__PURE__ */ jsxs(
3584
- SingleSelect,
3585
- {
3586
- label: effectiveSearchAttribute ? `Condition for "${effectiveSearchAttribute}"` : "Filter condition",
3587
- value: filterOperator,
3588
- onChange: (val) => setFilterOperator(String(val)),
3589
- children: [
3590
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "none", children: "Show All (No Value Filter)" }),
3591
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "empty", children: "Is Empty or Null" }),
3592
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "not_empty", children: "Is Not Empty" }),
3593
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "equals", children: "Equals Exact Text" }),
3594
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "contains", children: "Contains Substring" }),
3595
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "starts_with", children: "Starts With" })
3596
- ]
3597
- }
3598
- ) }),
3599
- (filterOperator === "equals" || filterOperator === "contains" || filterOperator === "starts_with") && /* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 180 }, children: /* @__PURE__ */ jsx(
3600
- TextInput,
3601
- {
3602
- label: effectiveSearchAttribute ? `Value for "${effectiveSearchAttribute}"` : "Value to match",
3603
- placeholder: "Type value to match...",
3604
- value: filterQuery,
3605
- onChange: (e) => setFilterQuery(e.target.value)
3606
- }
3607
- ) }),
3608
- /* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 180 }, children: /* @__PURE__ */ jsx(
3609
- TextInput,
3610
- {
3611
- label: "Full-text Entry Search",
3612
- placeholder: "Search title, name, or text across records...",
3613
- value: entrySearch,
3614
- onChange: (e) => setEntrySearch(e.target.value)
3615
- }
3616
3540
  ) }),
3617
3541
  isFilterActive && /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
3618
3542
  /* @__PURE__ */ jsx(
@@ -4016,7 +3940,8 @@ const BulkUpdater = () => {
4016
3940
  isMatch,
4017
3941
  hasAttr,
4018
3942
  isSelected,
4019
- () => toggleEntry(col.uid, entry.documentId)
3943
+ () => toggleEntry(col.uid, entry.documentId),
3944
+ pendingBatches
4020
3945
  ) }, colDef.key))
4021
3946
  },
4022
3947
  key
@@ -4107,6 +4032,242 @@ const BulkUpdater = () => {
4107
4032
  ]
4108
4033
  }
4109
4034
  ),
4035
+ /* @__PURE__ */ jsxs(
4036
+ Box,
4037
+ {
4038
+ padding: 6,
4039
+ background: "neutral0",
4040
+ shadow: "filterShadow",
4041
+ hasRadius: true,
4042
+ marginBottom: 6,
4043
+ style: { width: "100%", maxWidth: "100%", minWidth: 0, boxSizing: "border-box" },
4044
+ children: [
4045
+ /* @__PURE__ */ jsx(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, wrap: "wrap", gap: 3, children: /* @__PURE__ */ jsxs(Box, { children: [
4046
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h2", fontWeight: "semiBold", children: "2. Configure Attribute Update" }),
4047
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Select the target attribute to update and configure the replacement value for your selected entries." })
4048
+ ] }) }),
4049
+ /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "flex-end", wrap: "wrap", children: [
4050
+ /* @__PURE__ */ jsx(Box, { style: { width: 280, minWidth: 230 }, children: /* @__PURE__ */ jsxs(Field.Root, { children: [
4051
+ /* @__PURE__ */ jsx(Field.Label, { children: "Attribute to Update" }),
4052
+ /* @__PURE__ */ jsx(
4053
+ Combobox,
4054
+ {
4055
+ autocomplete: "none",
4056
+ placeholder: "Search attribute by name or relation...",
4057
+ value: selectedAttribute || void 0,
4058
+ clearLabel: "Clear attribute selection",
4059
+ onClear: () => {
4060
+ handleAttributeChange("");
4061
+ setAttributeSearch("");
4062
+ },
4063
+ onInputChange: (e) => {
4064
+ setAttributeSearch(e.target.value);
4065
+ },
4066
+ onOpenChange: (isOpen) => {
4067
+ if (!isOpen) {
4068
+ setAttributeSearch("");
4069
+ }
4070
+ },
4071
+ loading: loading && attributes.length === 0,
4072
+ loadingMessage: "Discovering schema attributes...",
4073
+ onChange: (val) => {
4074
+ if (val && typeof val === "string" && val !== "__none__") {
4075
+ handleAttributeChange(val);
4076
+ setAttributeSearch("");
4077
+ }
4078
+ },
4079
+ children: memoizedComboboxOptions
4080
+ },
4081
+ `update-attr-${selectedCollectionUid}`
4082
+ )
4083
+ ] }) }),
4084
+ /* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 340 }, children: relationsMeta[selectedAttribute] ? /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 1, style: { width: "100%" }, children: [
4085
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
4086
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral700", children: [
4087
+ "Target ",
4088
+ relationsMeta[selectedAttribute].targetDisplayName,
4089
+ " Relation:"
4090
+ ] }),
4091
+ /* @__PURE__ */ jsxs(Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: [
4092
+ relationsMeta[selectedAttribute].relationType,
4093
+ " → ",
4094
+ relationsMeta[selectedAttribute].targetDisplayName
4095
+ ] }),
4096
+ relationsMeta[selectedAttribute].isMulti && /* @__PURE__ */ jsx(Badge, { backgroundColor: "primary100", textColor: "primary700", children: "Multi-Relation" })
4097
+ ] }),
4098
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", style: { width: "100%" }, children: [
4099
+ /* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 200 }, children: /* @__PURE__ */ jsxs(Field.Root, { children: [
4100
+ /* @__PURE__ */ jsx(Field.Label, { children: `Choose ${relationsMeta[selectedAttribute].targetDisplayName}` }),
4101
+ /* @__PURE__ */ jsxs(
4102
+ Combobox,
4103
+ {
4104
+ autocomplete: { type: "list", filter: "contains" },
4105
+ placeholder: relationOptionsLoading ? "Loading entities..." : `Search & pick ${relationsMeta[selectedAttribute].targetDisplayName}...`,
4106
+ value: selectedRelationDocId,
4107
+ onChange: (val) => {
4108
+ const docId = val ? String(val) : "";
4109
+ setSelectedRelationDocId(docId);
4110
+ if (docId === "__clear__") {
4111
+ setAttributeValue(JSON.stringify({ mode: "clear", label: "(clear relation)" }));
4112
+ } else {
4113
+ const opt = relationOptions.find((o) => o.documentId === docId);
4114
+ setAttributeValue(JSON.stringify({ mode: relationMode, documentIds: [docId], label: opt?.label || docId }));
4115
+ }
4116
+ },
4117
+ children: [
4118
+ /* @__PURE__ */ jsx(ComboboxOption, { value: "__clear__", textValue: "Clear / Unlink Relation", children: "-- Clear / Unlink Relation --" }),
4119
+ relationOptions.map((opt) => /* @__PURE__ */ jsx(ComboboxOption, { value: opt.documentId, textValue: opt.label, children: opt.label }, opt.documentId))
4120
+ ]
4121
+ }
4122
+ )
4123
+ ] }) }),
4124
+ relationsMeta[selectedAttribute].isMulti && /* @__PURE__ */ jsx(Box, { style: { width: 170 }, children: /* @__PURE__ */ jsxs(
4125
+ SingleSelect,
4126
+ {
4127
+ label: "Action Mode",
4128
+ value: relationMode,
4129
+ onChange: (val) => {
4130
+ const m = String(val);
4131
+ setRelationMode(m);
4132
+ if (selectedRelationDocId) {
4133
+ if (selectedRelationDocId === "__clear__" || m === "clear") {
4134
+ setAttributeValue(JSON.stringify({ mode: "clear", label: "(clear relation)" }));
4135
+ } else {
4136
+ const opt = relationOptions.find((o) => o.documentId === selectedRelationDocId);
4137
+ setAttributeValue(JSON.stringify({ mode: m, documentIds: [selectedRelationDocId], label: opt?.label || selectedRelationDocId }));
4138
+ }
4139
+ }
4140
+ },
4141
+ children: [
4142
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "set", children: "Set / Overwrite" }),
4143
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "connect", children: "Add / Connect" }),
4144
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "disconnect", children: "Remove / Disconnect" }),
4145
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "clear", children: "Clear All" })
4146
+ ]
4147
+ }
4148
+ ) })
4149
+ ] })
4150
+ ] }) : /* @__PURE__ */ jsx(
4151
+ TextInput,
4152
+ {
4153
+ label: useRegex ? "Replacement Value" : "New Attribute Value",
4154
+ name: "attributeValue",
4155
+ placeholder: useRegex ? "Replace matched pattern with..." : "Enter the value to apply...",
4156
+ value: attributeValue,
4157
+ onChange: (e) => setAttributeValue(e.target.value)
4158
+ }
4159
+ ) }),
4160
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
4161
+ /* @__PURE__ */ jsxs(
4162
+ Button,
4163
+ {
4164
+ startIcon: /* @__PURE__ */ jsx(Eye, {}),
4165
+ variant: "secondary",
4166
+ size: "L",
4167
+ onClick: handleOpenPreviewModal,
4168
+ loading: previewLoading,
4169
+ disabled: loading || totalSelectedCount === 0 || !selectedAttribute || !selectedAttribute.trim(),
4170
+ children: [
4171
+ "Preview Changes (",
4172
+ totalSelectedCount,
4173
+ ")"
4174
+ ]
4175
+ }
4176
+ ),
4177
+ /* @__PURE__ */ jsxs(
4178
+ Button,
4179
+ {
4180
+ startIcon: /* @__PURE__ */ jsx(Play, {}),
4181
+ variant: "default",
4182
+ size: "L",
4183
+ onClick: handleOpenConfirmModal,
4184
+ loading: updating,
4185
+ disabled: loading || totalSelectedCount === 0 || !selectedAttribute || !selectedAttribute.trim(),
4186
+ children: [
4187
+ "Update Selected (",
4188
+ totalSelectedCount,
4189
+ ")"
4190
+ ]
4191
+ }
4192
+ ),
4193
+ /* @__PURE__ */ jsxs(
4194
+ Button,
4195
+ {
4196
+ startIcon: /* @__PURE__ */ jsx(Check, {}),
4197
+ variant: "success-light",
4198
+ size: "L",
4199
+ onClick: handleOpenPublishModal,
4200
+ loading: publishing,
4201
+ disabled: loading || totalSelectedCount === 0,
4202
+ title: "Directly publish selected draft records across collections",
4203
+ children: [
4204
+ "Publish Selected (",
4205
+ totalSelectedCount,
4206
+ ")"
4207
+ ]
4208
+ }
4209
+ ),
4210
+ /* @__PURE__ */ jsxs(
4211
+ Button,
4212
+ {
4213
+ startIcon: /* @__PURE__ */ jsx(Download, {}),
4214
+ variant: "tertiary",
4215
+ size: "L",
4216
+ onClick: handleBulkExportSelected,
4217
+ loading: bulkExporting,
4218
+ disabled: loading || totalSelectedCount === 0,
4219
+ title: "Export selected entries to Excel-compatible CSV with full nested component support",
4220
+ children: [
4221
+ "Export Selected (",
4222
+ totalSelectedCount,
4223
+ ")"
4224
+ ]
4225
+ }
4226
+ )
4227
+ ] })
4228
+ ] }),
4229
+ /* @__PURE__ */ jsx(Box, { marginTop: 4, padding: 4, background: "neutral100", hasRadius: true, style: { border: "1px solid #eaeaef" }, children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: [
4230
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
4231
+ /* @__PURE__ */ jsx(
4232
+ Checkbox,
4233
+ {
4234
+ checked: publishOnApproval,
4235
+ onCheckedChange: (checked) => setPublishOnApproval(Boolean(checked)),
4236
+ children: /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: "Auto-Publish after Update" })
4237
+ }
4238
+ ),
4239
+ /* @__PURE__ */ jsx(
4240
+ Badge,
4241
+ {
4242
+ backgroundColor: publishOnApproval ? "success100" : "warning100",
4243
+ textColor: publishOnApproval ? "success700" : "warning700",
4244
+ children: publishOnApproval ? "✓ LIVE PUBLISH" : "DRAFT ONLY"
4245
+ }
4246
+ )
4247
+ ] }),
4248
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: publishOnApproval ? "When enabled, entries will be published live immediately upon batch execution for draft-enabled content types." : "When disabled, entries will be saved as draft versions without publishing to live visitors." })
4249
+ ] }) }),
4250
+ selectedCol && selectedAttribute && /* @__PURE__ */ jsx(Box, { marginTop: 4, padding: 3, background: "neutral100", hasRadius: true, style: { border: "1px solid #eaeaef" }, children: /* @__PURE__ */ jsx(Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
4251
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", children: "Target Selection:" }),
4252
+ /* @__PURE__ */ jsxs(Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
4253
+ "Collection: ",
4254
+ selectedCol.displayName
4255
+ ] }),
4256
+ /* @__PURE__ */ jsxs(Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: [
4257
+ "Attribute: ",
4258
+ selectedAttribute,
4259
+ " ",
4260
+ attributesMeta[selectedAttribute]?.type ? `(${attributesMeta[selectedAttribute].type})` : ""
4261
+ ] }),
4262
+ attributesMeta[selectedAttribute]?.depth ? /* @__PURE__ */ jsxs(Badge, { backgroundColor: "neutral200", textColor: "neutral700", children: [
4263
+ "Nesting Level: L",
4264
+ attributesMeta[selectedAttribute].depth
4265
+ ] }) : null,
4266
+ selectedCol.draftAndPublish ? /* @__PURE__ */ jsx(Badge, { backgroundColor: "success100", textColor: "success700", children: "Draft & Publish Enabled" }) : /* @__PURE__ */ jsx(Badge, { backgroundColor: "warning100", textColor: "warning700", children: "Direct Publish Only" })
4267
+ ] }) }) })
4268
+ ]
4269
+ }
4270
+ ),
4110
4271
  updateSummary && /* @__PURE__ */ jsxs(
4111
4272
  Box,
4112
4273
  {
@@ -4371,25 +4532,44 @@ const BulkUpdater = () => {
4371
4532
  /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", children: rec.userName }),
4372
4533
  /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", display: "block", style: { wordBreak: "break-all" }, children: rec.userEmail })
4373
4534
  ] }),
4374
- /* @__PURE__ */ jsx(Td, { style: { minWidth: 160 }, children: /* @__PURE__ */ jsxs(
4375
- Typography,
4376
- {
4377
- style: {
4378
- display: "inline-block",
4379
- maxWidth: "100%",
4380
- overflow: "hidden",
4381
- textOverflow: "ellipsis",
4382
- whiteSpace: "nowrap"
4383
- },
4384
- title: `${rec.attribute} → "${formatDisplayValue(rec.value)}"`,
4385
- children: [
4386
- /* @__PURE__ */ jsx("strong", { children: rec.attribute }),
4387
- " → “",
4388
- formatDisplayValue(rec.value),
4389
- "”"
4390
- ]
4535
+ /* @__PURE__ */ jsx(Td, { style: { minWidth: 200 }, children: (() => {
4536
+ const isCsv = rec.attribute === "CSV_IMPORT" || rec.attribute?.startsWith("CSV");
4537
+ let attrText = rec.attribute;
4538
+ let valText = formatDisplayValue(rec.value);
4539
+ if (rec.attribute === "CSV_IMPORT" && rec.diffs && rec.diffs.length > 0) {
4540
+ const colName = rec.diffs[0]?.collectionName || "Collection";
4541
+ const attrs = Array.from(new Set(rec.diffs.map((d) => d.attribute).filter(Boolean))).join(", ");
4542
+ attrText = `CSV [${colName}]: ${attrs || "fields"}`;
4543
+ if (rec.value.startsWith("Mode:") && rec.diffs.length === 1) {
4544
+ const d = rec.diffs[0];
4545
+ valText = `"${d.label || d.documentId}": ${d.attribute || "field"} → "${formatDisplayValue(d.newValue)}"`;
4546
+ } else if (rec.value.startsWith("Mode:")) {
4547
+ valText = `${rec.diffs.length} attribute${rec.diffs.length > 1 ? "s" : ""} updated in ${colName}`;
4548
+ }
4391
4549
  }
4392
- ) }),
4550
+ return /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
4551
+ isCsv && /* @__PURE__ */ jsx(Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: "CSV IMPORT" }),
4552
+ /* @__PURE__ */ jsxs(
4553
+ Typography,
4554
+ {
4555
+ style: {
4556
+ display: "inline-block",
4557
+ maxWidth: "100%",
4558
+ overflow: "hidden",
4559
+ textOverflow: "ellipsis",
4560
+ whiteSpace: "nowrap"
4561
+ },
4562
+ title: `${attrText} → "${valText}"`,
4563
+ children: [
4564
+ /* @__PURE__ */ jsx("strong", { children: attrText.replace(/^CSV\s*\[?([^\]]+)\]?:\s*/, "$1: ") }),
4565
+ " → “",
4566
+ valText,
4567
+ "”"
4568
+ ]
4569
+ }
4570
+ )
4571
+ ] });
4572
+ })() }),
4393
4573
  /* @__PURE__ */ jsxs(Td, { style: { minWidth: 90, whiteSpace: "nowrap" }, children: [
4394
4574
  /* @__PURE__ */ jsxs(Badge, { backgroundColor: "success100", textColor: "success700", children: [
4395
4575
  rec.updatedCount,
@@ -4462,18 +4642,20 @@ const BulkUpdater = () => {
4462
4642
  rec.batchId,
4463
4643
  ":"
4464
4644
  ] }),
4465
- /* @__PURE__ */ jsxs(Table, { colCount: 4, rowCount: rec.diffs.length + 1, children: [
4645
+ /* @__PURE__ */ jsxs(Table, { colCount: 5, rowCount: rec.diffs.length + 1, children: [
4466
4646
  /* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [
4467
4647
  /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Collection" }) }),
4468
4648
  /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Document / Entry" }) }),
4649
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Attribute" }) }),
4469
4650
  /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Previous Value" }) }),
4470
4651
  /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Applied Value" }) })
4471
4652
  ] }) }),
4472
4653
  /* @__PURE__ */ jsx(Tbody, { children: rec.diffs.map((d, dIdx) => /* @__PURE__ */ jsxs(Tr, { children: [
4473
4654
  /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", children: d.collectionName }) }),
4474
4655
  /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { children: d.label }) }),
4656
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: d.attribute || "field" }) }),
4475
4657
  /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "danger600", children: formatDisplayValue(d.oldValue) }) }),
4476
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "success600", children: formatDisplayValue(d.newValue) }) })
4658
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "success600", fontWeight: "semiBold", children: formatDisplayValue(d.newValue) }) })
4477
4659
  ] }, dIdx)) })
4478
4660
  ] })
4479
4661
  ]
@@ -4986,6 +5168,7 @@ export {
4986
5168
  BulkUpdater as default,
4987
5169
  formatDisplayValue,
4988
5170
  getAvailableColumnsForCollection,
5171
+ getEntryLifecycleStatus,
4989
5172
  getNestedValue
4990
5173
  };
4991
- //# sourceMappingURL=BulkUpdater-J21ZgJWx.mjs.map
5174
+ //# sourceMappingURL=BulkUpdater-D4v5fP-1.mjs.map