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
@@ -5,7 +5,7 @@ const React = require("react");
5
5
  const admin = require("@strapi/strapi/admin");
6
6
  const designSystem = require("@strapi/design-system");
7
7
  const icons = require("@strapi/icons");
8
- const ValidationRulesTab = require("./ValidationRulesTab-DuvL-K4F.js");
8
+ const ValidationRulesTab = require("./ValidationRulesTab-BTm3QXxM.js");
9
9
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
10
10
  const React__default = /* @__PURE__ */ _interopDefault(React);
11
11
  function parseCsvRows(csvText) {
@@ -95,6 +95,51 @@ function parseCsv(csvText) {
95
95
  }
96
96
  return records;
97
97
  }
98
+ const LIFECYCLE_STATUS_WORDS = /* @__PURE__ */ new Set([
99
+ "pending review",
100
+ "pending_review",
101
+ "pending approval",
102
+ "pending_approval",
103
+ "approved",
104
+ "rejected",
105
+ "draft",
106
+ "published",
107
+ "modified",
108
+ "supported",
109
+ "ignored"
110
+ ]);
111
+ function isValidDocId(val) {
112
+ if (!val || typeof val !== "string") return false;
113
+ const trimmed = val.trim();
114
+ if (!trimmed) return false;
115
+ if (LIFECYCLE_STATUS_WORDS.has(trimmed.toLowerCase())) return false;
116
+ if (/\s/.test(trimmed)) return false;
117
+ return /^[a-zA-Z0-9_-]+$/.test(trimmed);
118
+ }
119
+ function extractDocId(row) {
120
+ if (!row || typeof row !== "object") return "";
121
+ const candidates = [
122
+ row.documentId,
123
+ row.document_id,
124
+ row["Document ID"],
125
+ row["Document Id"],
126
+ row["document_id"],
127
+ row.docId,
128
+ row["docId"],
129
+ row.id,
130
+ row.ID,
131
+ row.Id
132
+ ];
133
+ for (const c of candidates) {
134
+ if (c !== void 0 && c !== null) {
135
+ const str = String(c).trim();
136
+ if (isValidDocId(str)) {
137
+ return str;
138
+ }
139
+ }
140
+ }
141
+ return "";
142
+ }
98
143
  function CsvManagerTab() {
99
144
  const { get, post } = admin.useFetchClient();
100
145
  const { toggleNotification } = admin.useNotification();
@@ -135,6 +180,7 @@ function CsvManagerTab() {
135
180
  }
136
181
  }
137
182
  } catch (err) {
183
+ if (ValidationRulesTab.isAbortError(err)) return;
138
184
  toggleNotification({
139
185
  type: "danger",
140
186
  message: err.message || "Failed loading collections for CSV Manager"
@@ -155,6 +201,7 @@ function CsvManagerTab() {
155
201
  message: "CSV Template downloaded successfully"
156
202
  });
157
203
  } catch (err) {
204
+ if (ValidationRulesTab.isAbortError(err)) return;
158
205
  toggleNotification({
159
206
  type: "danger",
160
207
  message: err.message || "Failed downloading CSV template"
@@ -173,6 +220,7 @@ function CsvManagerTab() {
173
220
  message: "Collection exported to CSV successfully"
174
221
  });
175
222
  } catch (err) {
223
+ if (ValidationRulesTab.isAbortError(err)) return;
176
224
  toggleNotification({
177
225
  type: "danger",
178
226
  message: err.message || "Failed exporting collection to CSV"
@@ -191,6 +239,7 @@ function CsvManagerTab() {
191
239
  message: "Rule violations exported to CSV successfully"
192
240
  });
193
241
  } catch (err) {
242
+ if (ValidationRulesTab.isAbortError(err)) return;
194
243
  toggleNotification({
195
244
  type: "danger",
196
245
  message: err.message || "Failed exporting violations to CSV"
@@ -260,6 +309,7 @@ function CsvManagerTab() {
260
309
  throw new Error(res.data?.error || "Import failed");
261
310
  }
262
311
  } catch (err) {
312
+ if (ValidationRulesTab.isAbortError(err)) return;
263
313
  toggleNotification({
264
314
  type: "danger",
265
315
  message: err.message || "Error processing CSV import"
@@ -413,7 +463,7 @@ function CsvManagerTab() {
413
463
  ),
414
464
  parsedPreview.length > 0 && (() => {
415
465
  const missingDocIdCount = parsedPreview.filter(
416
- (row) => !(row.documentId || row.id || "").trim()
466
+ (row) => !extractDocId(row)
417
467
  ).length;
418
468
  const headers = Object.keys(parsedPreview[0]);
419
469
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginTop: 4, background: "neutral0", padding: 4, hasRadius: true, children: [
@@ -458,7 +508,7 @@ function CsvManagerTab() {
458
508
  headers.map((h) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { minWidth: 160 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: h }) }, h))
459
509
  ] }) }),
460
510
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: parsedPreview.map((row, rIdx) => {
461
- const hasDocId = !!(row.documentId || row.id || "").trim();
511
+ const hasDocId = !!extractDocId(row);
462
512
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
463
513
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: hasDocId ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: "🔄 Match / Update" }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: "➕ New Entry" }) }),
464
514
  headers.map((h) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { verticalAlign: "top", maxWidth: 300 }, children: (h === "documentId" || h === "id") && !row[h]?.trim() ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "warning700", fontWeight: "bold", children: "(Auto-generate ID)" }) : /* @__PURE__ */ jsxRuntime.jsx(
@@ -544,12 +594,53 @@ function CsvManagerTab() {
544
594
  importResult.stats.errors.length,
545
595
  "):"
546
596
  ] }),
547
- /* @__PURE__ */ jsxRuntime.jsx("ul", { style: { margin: "4px 0 0 16px", padding: 0 }, children: importResult.stats.errors.slice(0, 5).map((err, idx) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "danger600", children: [
597
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { style: { margin: "4px 0 0 16px", padding: 0 }, children: importResult.stats.errors.slice(0, 10).map((err, idx) => /* @__PURE__ */ jsxRuntime.jsx("li", { style: { marginBottom: "2px" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "danger600", children: [
548
598
  "Row ",
549
599
  err.row,
550
600
  ": ",
551
601
  err.error
552
- ] }) }, idx)) })
602
+ ] }) }, idx)) }),
603
+ importResult.stats.errors.length > 10 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { display: "block", marginTop: 4, fontStyle: "italic" }, children: [
604
+ "...and ",
605
+ importResult.stats.errors.length - 10,
606
+ " more error",
607
+ importResult.stats.errors.length - 10 > 1 ? "s" : ""
608
+ ] })
609
+ ] }),
610
+ importResult.stats.diffs && importResult.stats.diffs.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginTop: 3, padding: 3, background: "neutral0", hasRadius: true, style: { border: "1px solid #dcdce4" }, children: [
611
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "sigma", textColor: "neutral800", fontWeight: "bold", display: "block", marginBottom: 2, children: [
612
+ "CHANGES APPLIED (",
613
+ importResult.stats.diffs.length,
614
+ "):"
615
+ ] }),
616
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { overflowX: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: 4, rowCount: importResult.stats.diffs.length + 1, children: [
617
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
618
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Entry / Document" }) }),
619
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Attribute" }) }),
620
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Previous Value" }) }),
621
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Applied Value" }) })
622
+ ] }) }),
623
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: importResult.stats.diffs.slice(0, 10).map((d, idx) => {
624
+ const oldDisplay = d.oldValue !== null && d.oldValue !== void 0 ? typeof d.oldValue === "object" ? JSON.stringify(d.oldValue) : String(d.oldValue) : "(empty)";
625
+ const newDisplay = d.newValue !== null && d.newValue !== void 0 ? typeof d.newValue === "object" ? JSON.stringify(d.newValue) : String(d.newValue) : "(empty)";
626
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
627
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Td, { children: [
628
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: d.label || d.documentId }),
629
+ d.documentId && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", display: "block", children: d.documentId })
630
+ ] }),
631
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: d.attribute || "field" }) }),
632
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "danger600", children: oldDisplay }) }),
633
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", fontWeight: "semiBold", children: newDisplay }) })
634
+ ] }, idx);
635
+ }) })
636
+ ] }) }),
637
+ importResult.stats.diffs.length > 10 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { display: "block", marginTop: 6, fontStyle: "italic" }, children: [
638
+ "...and ",
639
+ importResult.stats.diffs.length - 10,
640
+ " more change",
641
+ importResult.stats.diffs.length - 10 > 1 ? "s" : "",
642
+ " recorded in Audit Trail & Version History."
643
+ ] })
553
644
  ] })
554
645
  ]
555
646
  }
@@ -1570,13 +1661,15 @@ function getAvailableColumnsForCollection(col, selectedAttribute, searchAttribut
1570
1661
  defaultVisible: true
1571
1662
  });
1572
1663
  }
1573
- const targetLabel = searchAttribute && searchAttribute !== selectedAttribute ? `Update Target: "${selectedAttribute}"` : `Current "${selectedAttribute}"`;
1574
- cols.push({
1575
- key: "target_attr",
1576
- label: targetLabel,
1577
- sortable: !col.isSingleType && col.attributes.includes(selectedAttribute),
1578
- defaultVisible: true
1579
- });
1664
+ if (selectedAttribute && selectedAttribute.trim()) {
1665
+ const targetLabel = searchAttribute && searchAttribute !== selectedAttribute ? `Update Target: "${selectedAttribute}"` : `Current "${selectedAttribute}"`;
1666
+ cols.push({
1667
+ key: "target_attr",
1668
+ label: targetLabel,
1669
+ sortable: !col.isSingleType && col.attributes.includes(selectedAttribute),
1670
+ defaultVisible: true
1671
+ });
1672
+ }
1580
1673
  cols.push({
1581
1674
  key: "status",
1582
1675
  label: "Status",
@@ -1633,7 +1726,68 @@ function formatDisplayValue(val) {
1633
1726
  }
1634
1727
  return String(val);
1635
1728
  }
1636
- function renderCellContent(colDef, entry, col, selectedAttribute, searchAttribute, isMatch, hasAttr, isSelected, onToggleSelect) {
1729
+ function getEntryLifecycleStatus(entry, col, pendingBatches = []) {
1730
+ const isPendingApproval = pendingBatches.some((b) => {
1731
+ if (b.status !== "pending_approval") return false;
1732
+ const targets = b.payload?.selectedEntries || [];
1733
+ return targets.some((t) => t.documentId === entry.documentId || String(t.documentId) === String(entry.id));
1734
+ });
1735
+ if (isPendingApproval) {
1736
+ return {
1737
+ status: "pending_review",
1738
+ label: "Pending Review",
1739
+ badgeColor: "warning100",
1740
+ textColor: "warning700"
1741
+ };
1742
+ }
1743
+ if (entry.status === "modified") {
1744
+ return {
1745
+ status: "modified",
1746
+ label: "Modified",
1747
+ badgeColor: "primary100",
1748
+ textColor: "primary700"
1749
+ };
1750
+ }
1751
+ if (entry.status === "draft") {
1752
+ return {
1753
+ status: "draft",
1754
+ label: "Draft",
1755
+ badgeColor: "neutral150",
1756
+ textColor: "neutral700"
1757
+ };
1758
+ }
1759
+ if (entry.status === "published") {
1760
+ return {
1761
+ status: "published",
1762
+ label: "Published",
1763
+ badgeColor: "success100",
1764
+ textColor: "success700"
1765
+ };
1766
+ }
1767
+ if (col && col.draftAndPublish === false) {
1768
+ return {
1769
+ status: "published",
1770
+ label: "Published",
1771
+ badgeColor: "success100",
1772
+ textColor: "success700"
1773
+ };
1774
+ }
1775
+ if (entry.publishedAt) {
1776
+ return {
1777
+ status: "published",
1778
+ label: "Published",
1779
+ badgeColor: "success100",
1780
+ textColor: "success700"
1781
+ };
1782
+ }
1783
+ return {
1784
+ status: "draft",
1785
+ label: "Draft",
1786
+ badgeColor: "neutral150",
1787
+ textColor: "neutral700"
1788
+ };
1789
+ }
1790
+ function renderCellContent(colDef, entry, col, selectedAttribute, searchAttribute, isMatch, hasAttr, isSelected, onToggleSelect, pendingBatches = []) {
1637
1791
  switch (colDef.key) {
1638
1792
  case "select":
1639
1793
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -1692,11 +1846,12 @@ function renderCellContent(colDef, entry, col, selectedAttribute, searchAttribut
1692
1846
  );
1693
1847
  }
1694
1848
  case "status": {
1695
- if (!hasAttr) {
1849
+ if (selectedAttribute && !hasAttr) {
1696
1850
  return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "neutral200", textColor: "neutral600", children: "Ignored" });
1697
1851
  }
1852
+ const lifecycle = getEntryLifecycleStatus(entry, col, pendingBatches);
1698
1853
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, alignItems: "center", children: [
1699
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: "Supported" }),
1854
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: lifecycle.badgeColor, textColor: lifecycle.textColor, children: lifecycle.label }),
1700
1855
  isMatch && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: "Matches Filter" })
1701
1856
  ] });
1702
1857
  }
@@ -1780,7 +1935,7 @@ const BulkUpdater = () => {
1780
1935
  if (stored) return stored;
1781
1936
  } catch {
1782
1937
  }
1783
- return "metaDescription";
1938
+ return "";
1784
1939
  };
1785
1940
  const [selectedAttribute, setSelectedAttribute] = React.useState(getInitialAttribute);
1786
1941
  const [attributeValue, setAttributeValue] = React.useState("");
@@ -1832,7 +1987,8 @@ const BulkUpdater = () => {
1832
1987
  const [filterOperator, setFilterOperator] = React.useState("none");
1833
1988
  const [filterQuery, setFilterQuery] = React.useState("");
1834
1989
  const [entrySearch, setEntrySearch] = React.useState("");
1835
- const isFilterActive = Boolean(searchAttribute && searchAttribute !== selectedAttribute) || filterOperator !== "none" && (["empty", "not_empty"].includes(filterOperator) || Boolean(filterQuery.trim())) || Boolean(entrySearch.trim()) || Boolean(useRegex && regexPattern.trim());
1990
+ const [statusFilter, setStatusFilter] = React.useState("all");
1991
+ 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());
1836
1992
  const [lastResults, setLastResults] = React.useState(null);
1837
1993
  const [updateSummary, setUpdateSummary] = React.useState(null);
1838
1994
  const [collectionPages, setCollectionPages] = React.useState({});
@@ -1938,7 +2094,7 @@ const BulkUpdater = () => {
1938
2094
  storedAttr = localStorage.getItem(LAST_ATTR_KEY);
1939
2095
  } catch {
1940
2096
  }
1941
- const resolvedAttr = storedAttr && activeAttrs.includes(storedAttr) ? storedAttr : selectedAttribute && activeAttrs.includes(selectedAttribute) ? selectedAttribute : activeAttrs[0] || "";
2097
+ const resolvedAttr = storedAttr && activeAttrs.includes(storedAttr) ? storedAttr : selectedAttribute && activeAttrs.includes(selectedAttribute) ? selectedAttribute : "";
1942
2098
  setSelectedAttribute(resolvedAttr);
1943
2099
  if (resolvedAttr && data.relationsMeta && data.relationsMeta[resolvedAttr]) {
1944
2100
  fetchRelationOptions(data.relationsMeta[resolvedAttr].targetUid);
@@ -1969,8 +2125,7 @@ const BulkUpdater = () => {
1969
2125
  }
1970
2126
  }
1971
2127
  } catch (err) {
1972
- const isAborted = err?.name === "AbortError" || err?.name === "CanceledError" || err?.code === "ERR_CANCELED" || err?.message?.includes("aborted") || err?.message?.includes("canceled");
1973
- if (!isAborted) {
2128
+ if (!ValidationRulesTab.isAbortError(err)) {
1974
2129
  toggleNotification({
1975
2130
  type: "danger",
1976
2131
  message: err?.message || "Failed to load collections data"
@@ -2047,8 +2202,7 @@ const BulkUpdater = () => {
2047
2202
  }
2048
2203
  }
2049
2204
  } catch (err) {
2050
- const isAborted = err?.name === "AbortError" || err?.name === "CanceledError" || err?.code === "ERR_CANCELED" || err?.message?.includes("aborted") || err?.message?.includes("canceled");
2051
- if (!isAborted) {
2205
+ if (!ValidationRulesTab.isAbortError(err)) {
2052
2206
  toggleNotification({
2053
2207
  type: "danger",
2054
2208
  message: `Failed to load entries for ${colUid}: ${err?.message || "Network error"}`
@@ -2077,8 +2231,7 @@ const BulkUpdater = () => {
2077
2231
  }
2078
2232
  }
2079
2233
  } catch (err) {
2080
- const isAborted = err?.name === "AbortError" || err?.name === "CanceledError" || err?.code === "ERR_CANCELED" || err?.message?.includes("aborted") || err?.message?.includes("canceled");
2081
- if (!isAborted) {
2234
+ if (!ValidationRulesTab.isAbortError(err)) {
2082
2235
  toggleNotification({
2083
2236
  type: "danger",
2084
2237
  message: err?.message || "Failed to fetch audit history."
@@ -2160,18 +2313,14 @@ const BulkUpdater = () => {
2160
2313
  const targetCol = collections.find((c) => c.uid === newUid);
2161
2314
  const colAttrs = targetCol?.attributes || [];
2162
2315
  setAttributes(colAttrs);
2163
- if (!colAttrs.includes(selectedAttribute)) {
2164
- const newAttr = colAttrs[0] || "";
2165
- setSelectedAttribute(newAttr);
2316
+ if (selectedAttribute && !colAttrs.includes(selectedAttribute)) {
2317
+ setSelectedAttribute("");
2166
2318
  try {
2167
- localStorage.setItem(LAST_ATTR_KEY, newAttr);
2319
+ localStorage.removeItem(LAST_ATTR_KEY);
2168
2320
  } catch {
2169
2321
  }
2170
- if (newAttr && relationsMeta[newAttr]) {
2171
- fetchRelationOptions(relationsMeta[newAttr].targetUid);
2172
- } else {
2173
- setSelectedRelationDocId("");
2174
- }
2322
+ setSelectedRelationDocId("");
2323
+ setAttributeValue("");
2175
2324
  }
2176
2325
  loadCollectionPage(newUid, 1, void 0, void 0, void 0, filterOperator, filterQuery, entrySearch, true, "");
2177
2326
  };
@@ -2186,10 +2335,6 @@ const BulkUpdater = () => {
2186
2335
  }
2187
2336
  } catch {
2188
2337
  }
2189
- setSelectedEntries({});
2190
- setFilterOperator("none");
2191
- setFilterQuery("");
2192
- setEntrySearch("");
2193
2338
  setSelectedRelationDocId("");
2194
2339
  setAttributeValue("");
2195
2340
  if (newAttr && relationsMeta[newAttr]) {
@@ -2197,9 +2342,6 @@ const BulkUpdater = () => {
2197
2342
  } else {
2198
2343
  setRelationOptions([]);
2199
2344
  }
2200
- if (newAttr && selectedCollectionUid) {
2201
- loadCollectionPage(selectedCollectionUid, 1);
2202
- }
2203
2345
  }
2204
2346
  };
2205
2347
  const filteredCollections = React.useMemo(() => {
@@ -2215,10 +2357,10 @@ const BulkUpdater = () => {
2215
2357
  return availableColAttributes.filter((attr) => getAttrDepth(attr) <= componentDepth);
2216
2358
  }, [availableColAttributes, componentDepth, attributesMeta]);
2217
2359
  const filteredAttributes = React.useMemo(() => {
2218
- if (!attributeSearch.trim()) {
2360
+ const q = attributeSearch.toLowerCase().trim();
2361
+ if (!q || q === (selectedAttribute || "").toLowerCase().trim()) {
2219
2362
  return depthFilteredAttributes;
2220
2363
  }
2221
- const q = attributeSearch.toLowerCase().trim();
2222
2364
  const cleanQ = q.replace(/[^a-z0-9]/g, "");
2223
2365
  return depthFilteredAttributes.filter((attr) => {
2224
2366
  const lower = attr.toLowerCase();
@@ -2227,12 +2369,12 @@ const BulkUpdater = () => {
2227
2369
  const relTarget = (rel?.targetDisplayName || "").toLowerCase();
2228
2370
  return lower.includes(q) || cleanLower.includes(cleanQ) || relTarget.includes(q);
2229
2371
  });
2230
- }, [depthFilteredAttributes, attributeSearch, relationsMeta]);
2372
+ }, [depthFilteredAttributes, attributeSearch, selectedAttribute, relationsMeta]);
2231
2373
  const filteredSearchAttributes = React.useMemo(() => {
2232
- if (!searchAttributeSearch.trim()) {
2374
+ const q = searchAttributeSearch.toLowerCase().trim();
2375
+ if (!q || q === (searchAttribute || "").toLowerCase().trim()) {
2233
2376
  return depthFilteredAttributes;
2234
2377
  }
2235
- const q = searchAttributeSearch.toLowerCase().trim();
2236
2378
  const cleanQ = q.replace(/[^a-z0-9]/g, "");
2237
2379
  return depthFilteredAttributes.filter((attr) => {
2238
2380
  const lower = attr.toLowerCase();
@@ -2241,7 +2383,7 @@ const BulkUpdater = () => {
2241
2383
  const relTarget = (rel?.targetDisplayName || "").toLowerCase();
2242
2384
  return lower.includes(q) || cleanLower.includes(cleanQ) || relTarget.includes(q);
2243
2385
  });
2244
- }, [depthFilteredAttributes, searchAttributeSearch, relationsMeta]);
2386
+ }, [depthFilteredAttributes, searchAttributeSearch, searchAttribute, relationsMeta]);
2245
2387
  const memoizedComboboxOptions = React.useMemo(() => {
2246
2388
  if (filteredAttributes.length === 0) {
2247
2389
  return /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: "__none__", disabled: true, textValue: "No matching attributes found", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
@@ -2377,24 +2519,25 @@ const BulkUpdater = () => {
2377
2519
  };
2378
2520
  const getVisibleEntries = (col) => {
2379
2521
  const pageState = collectionPages[col.uid];
2380
- return pageState ? pageState.entries : col.entries || [];
2522
+ const raw = pageState ? pageState.entries : col.entries || [];
2523
+ if (statusFilter === "all") return raw;
2524
+ return raw.filter((entry) => {
2525
+ const lifecycle = getEntryLifecycleStatus(entry, col, pendingBatches);
2526
+ return lifecycle.status === statusFilter;
2527
+ });
2381
2528
  };
2382
2529
  const matchingEntriesCount = React__default.default.useMemo(() => {
2383
2530
  if (!isFilterActive) return 0;
2384
2531
  let count = 0;
2385
2532
  filteredCollections.forEach((col) => {
2386
- const pageState = collectionPages[col.uid];
2387
- if (pageState) {
2388
- count += pageState.total;
2389
- }
2533
+ count += getVisibleEntries(col).length;
2390
2534
  });
2391
2535
  return count;
2392
- }, [filteredCollections, collectionPages, isFilterActive]);
2536
+ }, [filteredCollections, collectionPages, isFilterActive, statusFilter, pendingBatches]);
2393
2537
  const selectAllMatching = () => {
2394
2538
  const updated = { ...selectedEntries };
2395
2539
  filteredCollections.forEach((col) => {
2396
- const pageState = collectionPages[col.uid];
2397
- const entries = pageState ? pageState.entries : col.entries || [];
2540
+ const entries = getVisibleEntries(col);
2398
2541
  entries.forEach((e) => {
2399
2542
  updated[`${col.uid}${KEY_SEP}${e.documentId}`] = true;
2400
2543
  });
@@ -2404,8 +2547,7 @@ const BulkUpdater = () => {
2404
2547
  const deselectAllMatching = () => {
2405
2548
  const updated = { ...selectedEntries };
2406
2549
  filteredCollections.forEach((col) => {
2407
- const pageState = collectionPages[col.uid];
2408
- const entries = pageState ? pageState.entries : col.entries || [];
2550
+ const entries = getVisibleEntries(col);
2409
2551
  entries.forEach((e) => {
2410
2552
  delete updated[`${col.uid}${KEY_SEP}${e.documentId}`];
2411
2553
  });
@@ -2418,6 +2560,7 @@ const BulkUpdater = () => {
2418
2560
  setFilterOperator("none");
2419
2561
  setFilterQuery("");
2420
2562
  setEntrySearch("");
2563
+ setStatusFilter("all");
2421
2564
  setRegexPattern("");
2422
2565
  setSelectedEntries({});
2423
2566
  collections.forEach((col) => {
@@ -2538,10 +2681,12 @@ const BulkUpdater = () => {
2538
2681
  throw new Error(res.data?.error || "Failed to generate preview");
2539
2682
  }
2540
2683
  } catch (err) {
2541
- toggleNotification({
2542
- type: "danger",
2543
- message: err.message || "Failed to fetch preview diffs"
2544
- });
2684
+ if (!ValidationRulesTab.isAbortError(err)) {
2685
+ toggleNotification({
2686
+ type: "danger",
2687
+ message: err.message || "Failed to fetch preview diffs"
2688
+ });
2689
+ }
2545
2690
  setShowPreviewModal(false);
2546
2691
  } finally {
2547
2692
  setPreviewLoading(false);
@@ -3274,349 +3419,128 @@ const BulkUpdater = () => {
3274
3419
  marginBottom: 6,
3275
3420
  style: { width: "100%", maxWidth: "100%", minWidth: 0, boxSizing: "border-box" },
3276
3421
  children: [
3277
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, wrap: "wrap", gap: 3, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
3278
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", fontWeight: "semiBold", children: "1. Configure Attribute Update" }),
3279
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Select the target attribute to update and configure the replacement value." })
3280
- ] }) }),
3281
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "flex-end", wrap: "wrap", children: [
3282
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 280, minWidth: 230 }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
3283
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Attribute to Update" }),
3284
- /* @__PURE__ */ jsxRuntime.jsx(
3285
- designSystem.Combobox,
3286
- {
3287
- autocomplete: "none",
3288
- placeholder: "Search attribute by name or relation...",
3289
- value: selectedAttribute || void 0,
3290
- clearLabel: "Clear attribute selection",
3291
- onClear: () => {
3292
- handleAttributeChange("");
3293
- setAttributeSearch("");
3294
- },
3295
- onInputChange: (e) => {
3296
- setAttributeSearch(e.target.value);
3297
- },
3298
- onOpenChange: (isOpen) => {
3299
- if (!isOpen) {
3300
- setAttributeSearch("");
3422
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, wrap: "wrap", gap: 3, children: [
3423
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
3424
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", fontWeight: "semiBold", children: "1. Search & Filter Targets" }),
3425
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
3426
+ "Search, filter, and select entries within ",
3427
+ selectedCol ? selectedCol.displayName : "the selected collection",
3428
+ " before updating."
3429
+ ] })
3430
+ ] }),
3431
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
3432
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "secondary", size: "S", onClick: selectAll, children: "Select All" }),
3433
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", size: "S", onClick: deselectAll, children: "Deselect All" })
3434
+ ] })
3435
+ ] }),
3436
+ /* @__PURE__ */ jsxRuntime.jsx(
3437
+ designSystem.Box,
3438
+ {
3439
+ padding: 4,
3440
+ background: "neutral100",
3441
+ hasRadius: true,
3442
+ marginBottom: 5,
3443
+ style: { width: "100%", maxWidth: "100%", boxSizing: "border-box" },
3444
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 3, children: [
3445
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 2, children: [
3446
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
3447
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", children: "Search & Filter Targets:" }),
3448
+ isFilterActive && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
3449
+ matchingEntriesCount,
3450
+ " matching ",
3451
+ matchingEntriesCount === 1 ? "entry" : "entries",
3452
+ " found in database"
3453
+ ] }),
3454
+ searchAttribute && searchAttribute !== selectedAttribute && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: [
3455
+ "Searching by: ",
3456
+ searchAttribute,
3457
+ " → Target to update: ",
3458
+ selectedAttribute
3459
+ ] })
3460
+ ] }),
3461
+ isFilterActive && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "ghost", size: "S", onClick: clearFilter, children: "Clear Search & Filter" })
3462
+ ] }),
3463
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "flex-end", wrap: "wrap", children: [
3464
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 280, minWidth: 230 }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
3465
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Search Attribute" }),
3466
+ /* @__PURE__ */ jsxRuntime.jsx(
3467
+ designSystem.Combobox,
3468
+ {
3469
+ autocomplete: "none",
3470
+ placeholder: searchAttribute || `Same as Update (${selectedAttribute || "None"})`,
3471
+ value: searchAttribute || "__default__",
3472
+ clearLabel: "Reset search attribute",
3473
+ onClear: () => {
3474
+ handleSearchAttributeChange("");
3475
+ setSearchAttributeSearch("");
3476
+ },
3477
+ onInputChange: (e) => {
3478
+ setSearchAttributeSearch(e.target.value);
3479
+ },
3480
+ onOpenChange: (isOpen) => {
3481
+ if (!isOpen) {
3482
+ setSearchAttributeSearch("");
3483
+ }
3484
+ },
3485
+ onChange: (val) => {
3486
+ if (val && typeof val === "string" && val !== "__none__") {
3487
+ handleSearchAttributeChange(val);
3488
+ setSearchAttributeSearch("");
3489
+ }
3490
+ },
3491
+ children: memoizedSearchComboboxOptions
3492
+ },
3493
+ `search-attr-${selectedCollectionUid}`
3494
+ )
3495
+ ] }) }),
3496
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 200, minWidth: 170 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
3497
+ designSystem.SingleSelect,
3498
+ {
3499
+ label: effectiveSearchAttribute ? `Condition for "${effectiveSearchAttribute}"` : "Filter condition",
3500
+ value: filterOperator,
3501
+ onChange: (val) => setFilterOperator(String(val)),
3502
+ children: [
3503
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "none", children: "Show All (No Value Filter)" }),
3504
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "empty", children: "Is Empty or Null" }),
3505
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "not_empty", children: "Is Not Empty" }),
3506
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "equals", children: "Equals Exact Text" }),
3507
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "contains", children: "Contains Substring" }),
3508
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "starts_with", children: "Starts With" })
3509
+ ]
3301
3510
  }
3302
- },
3303
- loading: loading && attributes.length === 0,
3304
- loadingMessage: "Discovering schema attributes...",
3305
- onChange: (val) => {
3306
- if (val && typeof val === "string" && val !== "__none__") {
3307
- handleAttributeChange(val);
3308
- setAttributeSearch("");
3511
+ ) }),
3512
+ (filterOperator === "equals" || filterOperator === "contains" || filterOperator === "starts_with") && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1, minWidth: 180 }, children: /* @__PURE__ */ jsxRuntime.jsx(
3513
+ designSystem.TextInput,
3514
+ {
3515
+ label: effectiveSearchAttribute ? `Value for "${effectiveSearchAttribute}"` : "Value to match",
3516
+ placeholder: "Type value to match...",
3517
+ value: filterQuery,
3518
+ onChange: (e) => setFilterQuery(e.target.value)
3309
3519
  }
3310
- },
3311
- children: memoizedComboboxOptions
3312
- },
3313
- `update-attr-${selectedCollectionUid}-${selectedAttribute}`
3314
- )
3315
- ] }) }),
3316
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1, minWidth: 340 }, children: relationsMeta[selectedAttribute] ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 1, style: { width: "100%" }, children: [
3317
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
3318
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral700", children: [
3319
- "Target ",
3320
- relationsMeta[selectedAttribute].targetDisplayName,
3321
- " Relation:"
3322
- ] }),
3323
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: [
3324
- relationsMeta[selectedAttribute].relationType,
3325
- " → ",
3326
- relationsMeta[selectedAttribute].targetDisplayName
3327
- ] }),
3328
- relationsMeta[selectedAttribute].isMulti && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: "Multi-Relation" })
3329
- ] }),
3330
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", style: { width: "100%" }, children: [
3331
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1, minWidth: 200 }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
3332
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: `Choose ${relationsMeta[selectedAttribute].targetDisplayName}` }),
3333
- /* @__PURE__ */ jsxRuntime.jsxs(
3334
- designSystem.Combobox,
3520
+ ) }),
3521
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1, minWidth: 180 }, children: /* @__PURE__ */ jsxRuntime.jsx(
3522
+ designSystem.TextInput,
3335
3523
  {
3336
- autocomplete: { type: "list", filter: "contains" },
3337
- placeholder: relationOptionsLoading ? "Loading entities..." : `Search & pick ${relationsMeta[selectedAttribute].targetDisplayName}...`,
3338
- value: selectedRelationDocId,
3339
- onChange: (val) => {
3340
- const docId = val ? String(val) : "";
3341
- setSelectedRelationDocId(docId);
3342
- if (docId === "__clear__") {
3343
- setAttributeValue(JSON.stringify({ mode: "clear", label: "(clear relation)" }));
3344
- } else {
3345
- const opt = relationOptions.find((o) => o.documentId === docId);
3346
- setAttributeValue(JSON.stringify({ mode: relationMode, documentIds: [docId], label: opt?.label || docId }));
3347
- }
3348
- },
3524
+ label: "Full-text Entry Search",
3525
+ placeholder: "Search title, name, or text across records...",
3526
+ value: entrySearch,
3527
+ onChange: (e) => setEntrySearch(e.target.value)
3528
+ }
3529
+ ) }),
3530
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 190, minWidth: 160 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
3531
+ designSystem.SingleSelect,
3532
+ {
3533
+ label: "Lifecycle Status",
3534
+ value: statusFilter,
3535
+ onChange: (val) => setStatusFilter(String(val)),
3349
3536
  children: [
3350
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: "__clear__", textValue: "Clear / Unlink Relation", children: "-- Clear / Unlink Relation --" }),
3351
- relationOptions.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: opt.documentId, textValue: opt.label, children: opt.label }, opt.documentId))
3537
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "all", children: "All Statuses" }),
3538
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "pending_review", children: "Pending Review (Due)" }),
3539
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "draft", children: "Draft" }),
3540
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "published", children: "Published" }),
3541
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "modified", children: "Modified" })
3352
3542
  ]
3353
3543
  }
3354
- )
3355
- ] }) }),
3356
- relationsMeta[selectedAttribute].isMulti && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 170 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
3357
- designSystem.SingleSelect,
3358
- {
3359
- label: "Action Mode",
3360
- value: relationMode,
3361
- onChange: (val) => {
3362
- const m = String(val);
3363
- setRelationMode(m);
3364
- if (selectedRelationDocId) {
3365
- if (selectedRelationDocId === "__clear__" || m === "clear") {
3366
- setAttributeValue(JSON.stringify({ mode: "clear", label: "(clear relation)" }));
3367
- } else {
3368
- const opt = relationOptions.find((o) => o.documentId === selectedRelationDocId);
3369
- setAttributeValue(JSON.stringify({ mode: m, documentIds: [selectedRelationDocId], label: opt?.label || selectedRelationDocId }));
3370
- }
3371
- }
3372
- },
3373
- children: [
3374
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "set", children: "Set / Overwrite" }),
3375
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "connect", children: "Add / Connect" }),
3376
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "disconnect", children: "Remove / Disconnect" }),
3377
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "clear", children: "Clear All" })
3378
- ]
3379
- }
3380
- ) })
3381
- ] })
3382
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(
3383
- designSystem.TextInput,
3384
- {
3385
- label: useRegex ? "Replacement Value" : "New Attribute Value",
3386
- name: "attributeValue",
3387
- placeholder: useRegex ? "Replace matched pattern with..." : "Enter the value to apply...",
3388
- value: attributeValue,
3389
- onChange: (e) => setAttributeValue(e.target.value)
3390
- }
3391
- ) }),
3392
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
3393
- /* @__PURE__ */ jsxRuntime.jsxs(
3394
- designSystem.Button,
3395
- {
3396
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Eye, {}),
3397
- variant: "secondary",
3398
- size: "L",
3399
- onClick: handleOpenPreviewModal,
3400
- loading: previewLoading,
3401
- disabled: loading || totalSelectedCount === 0 || !selectedAttribute,
3402
- children: [
3403
- "Preview Changes (",
3404
- totalSelectedCount,
3405
- ")"
3406
- ]
3407
- }
3408
- ),
3409
- /* @__PURE__ */ jsxRuntime.jsxs(
3410
- designSystem.Button,
3411
- {
3412
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Play, {}),
3413
- variant: "default",
3414
- size: "L",
3415
- onClick: handleOpenConfirmModal,
3416
- loading: updating,
3417
- disabled: loading || totalSelectedCount === 0 || !selectedAttribute,
3418
- children: [
3419
- "Update Selected (",
3420
- totalSelectedCount,
3421
- ")"
3422
- ]
3423
- }
3424
- ),
3425
- /* @__PURE__ */ jsxRuntime.jsxs(
3426
- designSystem.Button,
3427
- {
3428
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Check, {}),
3429
- variant: "success-light",
3430
- size: "L",
3431
- onClick: handleOpenPublishModal,
3432
- loading: publishing,
3433
- disabled: loading || totalSelectedCount === 0,
3434
- title: "Directly publish selected draft records across collections",
3435
- children: [
3436
- "Publish Selected (",
3437
- totalSelectedCount,
3438
- ")"
3439
- ]
3440
- }
3441
- ),
3442
- /* @__PURE__ */ jsxRuntime.jsxs(
3443
- designSystem.Button,
3444
- {
3445
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Download, {}),
3446
- variant: "tertiary",
3447
- size: "L",
3448
- onClick: handleBulkExportSelected,
3449
- loading: bulkExporting,
3450
- disabled: loading || totalSelectedCount === 0,
3451
- title: "Export selected entries to Excel-compatible CSV with full nested component support",
3452
- children: [
3453
- "Export Selected (",
3454
- totalSelectedCount,
3455
- ")"
3456
- ]
3457
- }
3458
- )
3459
- ] })
3460
- ] }),
3461
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 4, padding: 4, background: "neutral100", hasRadius: true, style: { border: "1px solid #eaeaef" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: [
3462
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
3463
- /* @__PURE__ */ jsxRuntime.jsx(
3464
- designSystem.Checkbox,
3465
- {
3466
- checked: publishOnApproval,
3467
- onCheckedChange: (checked) => setPublishOnApproval(Boolean(checked)),
3468
- children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children: "Auto-Publish after Update" })
3469
- }
3470
- ),
3471
- /* @__PURE__ */ jsxRuntime.jsx(
3472
- designSystem.Badge,
3473
- {
3474
- backgroundColor: publishOnApproval ? "success100" : "warning100",
3475
- textColor: publishOnApproval ? "success700" : "warning700",
3476
- children: publishOnApproval ? "✓ LIVE PUBLISH" : "DRAFT ONLY"
3477
- }
3478
- )
3479
- ] }),
3480
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.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." })
3481
- ] }) }),
3482
- selectedCol && selectedAttribute && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 4, padding: 3, background: "neutral100", hasRadius: true, style: { border: "1px solid #eaeaef" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
3483
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: "Target Selection:" }),
3484
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
3485
- "Collection: ",
3486
- selectedCol.displayName
3487
- ] }),
3488
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: [
3489
- "Attribute: ",
3490
- selectedAttribute,
3491
- " ",
3492
- attributesMeta[selectedAttribute]?.type ? `(${attributesMeta[selectedAttribute].type})` : ""
3493
- ] }),
3494
- attributesMeta[selectedAttribute]?.depth ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "neutral200", textColor: "neutral700", children: [
3495
- "Nesting Level: L",
3496
- attributesMeta[selectedAttribute].depth
3497
- ] }) : null,
3498
- selectedCol.draftAndPublish ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: "Draft & Publish Enabled" }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: "Direct Publish Only" })
3499
- ] }) }) })
3500
- ]
3501
- }
3502
- ),
3503
- /* @__PURE__ */ jsxRuntime.jsxs(
3504
- designSystem.Box,
3505
- {
3506
- padding: 6,
3507
- background: "neutral0",
3508
- shadow: "filterShadow",
3509
- hasRadius: true,
3510
- marginBottom: 6,
3511
- style: { width: "100%", maxWidth: "100%", minWidth: 0, boxSizing: "border-box" },
3512
- children: [
3513
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, wrap: "wrap", gap: 3, children: [
3514
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
3515
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", fontWeight: "semiBold", children: "2. Select Targets" }),
3516
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
3517
- "Search and select entries to apply the update to within ",
3518
- selectedCol ? selectedCol.displayName : "the selected collection",
3519
- "."
3520
- ] })
3521
- ] }),
3522
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
3523
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "secondary", size: "S", onClick: selectAll, children: "Select All" }),
3524
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", size: "S", onClick: deselectAll, children: "Deselect All" })
3525
- ] })
3526
- ] }),
3527
- /* @__PURE__ */ jsxRuntime.jsx(
3528
- designSystem.Box,
3529
- {
3530
- padding: 4,
3531
- background: "neutral100",
3532
- hasRadius: true,
3533
- marginBottom: 5,
3534
- style: { width: "100%", maxWidth: "100%", boxSizing: "border-box" },
3535
- children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 3, children: [
3536
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 2, children: [
3537
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
3538
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", children: "Search & Filter Targets:" }),
3539
- isFilterActive && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
3540
- matchingEntriesCount,
3541
- " matching ",
3542
- matchingEntriesCount === 1 ? "entry" : "entries",
3543
- " found in database"
3544
- ] }),
3545
- searchAttribute && searchAttribute !== selectedAttribute && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: [
3546
- "Searching by: ",
3547
- searchAttribute,
3548
- " → Target to update: ",
3549
- selectedAttribute
3550
- ] })
3551
- ] }),
3552
- isFilterActive && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "ghost", size: "S", onClick: clearFilter, children: "Clear Search & Filter" })
3553
- ] }),
3554
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "flex-end", wrap: "wrap", children: [
3555
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 280, minWidth: 230 }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
3556
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Search Attribute" }),
3557
- /* @__PURE__ */ jsxRuntime.jsx(
3558
- designSystem.Combobox,
3559
- {
3560
- autocomplete: "none",
3561
- placeholder: searchAttribute || `Same as Update (${selectedAttribute || "None"})`,
3562
- value: searchAttribute || "__default__",
3563
- clearLabel: "Reset search attribute",
3564
- onClear: () => {
3565
- handleSearchAttributeChange("");
3566
- setSearchAttributeSearch("");
3567
- },
3568
- onInputChange: (e) => {
3569
- setSearchAttributeSearch(e.target.value);
3570
- },
3571
- onOpenChange: (isOpen) => {
3572
- if (!isOpen) {
3573
- setSearchAttributeSearch("");
3574
- }
3575
- },
3576
- onChange: (val) => {
3577
- if (val && typeof val === "string" && val !== "__none__") {
3578
- handleSearchAttributeChange(val);
3579
- setSearchAttributeSearch("");
3580
- }
3581
- },
3582
- children: memoizedSearchComboboxOptions
3583
- },
3584
- `search-attr-${selectedCollectionUid}-${searchAttribute}`
3585
- )
3586
- ] }) }),
3587
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 200, minWidth: 170 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
3588
- designSystem.SingleSelect,
3589
- {
3590
- label: effectiveSearchAttribute ? `Condition for "${effectiveSearchAttribute}"` : "Filter condition",
3591
- value: filterOperator,
3592
- onChange: (val) => setFilterOperator(String(val)),
3593
- children: [
3594
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "none", children: "Show All (No Value Filter)" }),
3595
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "empty", children: "Is Empty or Null" }),
3596
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "not_empty", children: "Is Not Empty" }),
3597
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "equals", children: "Equals Exact Text" }),
3598
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "contains", children: "Contains Substring" }),
3599
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "starts_with", children: "Starts With" })
3600
- ]
3601
- }
3602
- ) }),
3603
- (filterOperator === "equals" || filterOperator === "contains" || filterOperator === "starts_with") && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1, minWidth: 180 }, children: /* @__PURE__ */ jsxRuntime.jsx(
3604
- designSystem.TextInput,
3605
- {
3606
- label: effectiveSearchAttribute ? `Value for "${effectiveSearchAttribute}"` : "Value to match",
3607
- placeholder: "Type value to match...",
3608
- value: filterQuery,
3609
- onChange: (e) => setFilterQuery(e.target.value)
3610
- }
3611
- ) }),
3612
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1, minWidth: 180 }, children: /* @__PURE__ */ jsxRuntime.jsx(
3613
- designSystem.TextInput,
3614
- {
3615
- label: "Full-text Entry Search",
3616
- placeholder: "Search title, name, or text across records...",
3617
- value: entrySearch,
3618
- onChange: (e) => setEntrySearch(e.target.value)
3619
- }
3620
3544
  ) }),
3621
3545
  isFilterActive && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
3622
3546
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -4020,7 +3944,8 @@ const BulkUpdater = () => {
4020
3944
  isMatch,
4021
3945
  hasAttr,
4022
3946
  isSelected,
4023
- () => toggleEntry(col.uid, entry.documentId)
3947
+ () => toggleEntry(col.uid, entry.documentId),
3948
+ pendingBatches
4024
3949
  ) }, colDef.key))
4025
3950
  },
4026
3951
  key
@@ -4111,6 +4036,242 @@ const BulkUpdater = () => {
4111
4036
  ]
4112
4037
  }
4113
4038
  ),
4039
+ /* @__PURE__ */ jsxRuntime.jsxs(
4040
+ designSystem.Box,
4041
+ {
4042
+ padding: 6,
4043
+ background: "neutral0",
4044
+ shadow: "filterShadow",
4045
+ hasRadius: true,
4046
+ marginBottom: 6,
4047
+ style: { width: "100%", maxWidth: "100%", minWidth: 0, boxSizing: "border-box" },
4048
+ children: [
4049
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, wrap: "wrap", gap: 3, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
4050
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", fontWeight: "semiBold", children: "2. Configure Attribute Update" }),
4051
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Select the target attribute to update and configure the replacement value for your selected entries." })
4052
+ ] }) }),
4053
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "flex-end", wrap: "wrap", children: [
4054
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 280, minWidth: 230 }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
4055
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Attribute to Update" }),
4056
+ /* @__PURE__ */ jsxRuntime.jsx(
4057
+ designSystem.Combobox,
4058
+ {
4059
+ autocomplete: "none",
4060
+ placeholder: "Search attribute by name or relation...",
4061
+ value: selectedAttribute || void 0,
4062
+ clearLabel: "Clear attribute selection",
4063
+ onClear: () => {
4064
+ handleAttributeChange("");
4065
+ setAttributeSearch("");
4066
+ },
4067
+ onInputChange: (e) => {
4068
+ setAttributeSearch(e.target.value);
4069
+ },
4070
+ onOpenChange: (isOpen) => {
4071
+ if (!isOpen) {
4072
+ setAttributeSearch("");
4073
+ }
4074
+ },
4075
+ loading: loading && attributes.length === 0,
4076
+ loadingMessage: "Discovering schema attributes...",
4077
+ onChange: (val) => {
4078
+ if (val && typeof val === "string" && val !== "__none__") {
4079
+ handleAttributeChange(val);
4080
+ setAttributeSearch("");
4081
+ }
4082
+ },
4083
+ children: memoizedComboboxOptions
4084
+ },
4085
+ `update-attr-${selectedCollectionUid}`
4086
+ )
4087
+ ] }) }),
4088
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1, minWidth: 340 }, children: relationsMeta[selectedAttribute] ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 1, style: { width: "100%" }, children: [
4089
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
4090
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral700", children: [
4091
+ "Target ",
4092
+ relationsMeta[selectedAttribute].targetDisplayName,
4093
+ " Relation:"
4094
+ ] }),
4095
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: [
4096
+ relationsMeta[selectedAttribute].relationType,
4097
+ " → ",
4098
+ relationsMeta[selectedAttribute].targetDisplayName
4099
+ ] }),
4100
+ relationsMeta[selectedAttribute].isMulti && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: "Multi-Relation" })
4101
+ ] }),
4102
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", style: { width: "100%" }, children: [
4103
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1, minWidth: 200 }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
4104
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: `Choose ${relationsMeta[selectedAttribute].targetDisplayName}` }),
4105
+ /* @__PURE__ */ jsxRuntime.jsxs(
4106
+ designSystem.Combobox,
4107
+ {
4108
+ autocomplete: { type: "list", filter: "contains" },
4109
+ placeholder: relationOptionsLoading ? "Loading entities..." : `Search & pick ${relationsMeta[selectedAttribute].targetDisplayName}...`,
4110
+ value: selectedRelationDocId,
4111
+ onChange: (val) => {
4112
+ const docId = val ? String(val) : "";
4113
+ setSelectedRelationDocId(docId);
4114
+ if (docId === "__clear__") {
4115
+ setAttributeValue(JSON.stringify({ mode: "clear", label: "(clear relation)" }));
4116
+ } else {
4117
+ const opt = relationOptions.find((o) => o.documentId === docId);
4118
+ setAttributeValue(JSON.stringify({ mode: relationMode, documentIds: [docId], label: opt?.label || docId }));
4119
+ }
4120
+ },
4121
+ children: [
4122
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: "__clear__", textValue: "Clear / Unlink Relation", children: "-- Clear / Unlink Relation --" }),
4123
+ relationOptions.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: opt.documentId, textValue: opt.label, children: opt.label }, opt.documentId))
4124
+ ]
4125
+ }
4126
+ )
4127
+ ] }) }),
4128
+ relationsMeta[selectedAttribute].isMulti && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 170 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
4129
+ designSystem.SingleSelect,
4130
+ {
4131
+ label: "Action Mode",
4132
+ value: relationMode,
4133
+ onChange: (val) => {
4134
+ const m = String(val);
4135
+ setRelationMode(m);
4136
+ if (selectedRelationDocId) {
4137
+ if (selectedRelationDocId === "__clear__" || m === "clear") {
4138
+ setAttributeValue(JSON.stringify({ mode: "clear", label: "(clear relation)" }));
4139
+ } else {
4140
+ const opt = relationOptions.find((o) => o.documentId === selectedRelationDocId);
4141
+ setAttributeValue(JSON.stringify({ mode: m, documentIds: [selectedRelationDocId], label: opt?.label || selectedRelationDocId }));
4142
+ }
4143
+ }
4144
+ },
4145
+ children: [
4146
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "set", children: "Set / Overwrite" }),
4147
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "connect", children: "Add / Connect" }),
4148
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "disconnect", children: "Remove / Disconnect" }),
4149
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "clear", children: "Clear All" })
4150
+ ]
4151
+ }
4152
+ ) })
4153
+ ] })
4154
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
4155
+ designSystem.TextInput,
4156
+ {
4157
+ label: useRegex ? "Replacement Value" : "New Attribute Value",
4158
+ name: "attributeValue",
4159
+ placeholder: useRegex ? "Replace matched pattern with..." : "Enter the value to apply...",
4160
+ value: attributeValue,
4161
+ onChange: (e) => setAttributeValue(e.target.value)
4162
+ }
4163
+ ) }),
4164
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
4165
+ /* @__PURE__ */ jsxRuntime.jsxs(
4166
+ designSystem.Button,
4167
+ {
4168
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Eye, {}),
4169
+ variant: "secondary",
4170
+ size: "L",
4171
+ onClick: handleOpenPreviewModal,
4172
+ loading: previewLoading,
4173
+ disabled: loading || totalSelectedCount === 0 || !selectedAttribute || !selectedAttribute.trim(),
4174
+ children: [
4175
+ "Preview Changes (",
4176
+ totalSelectedCount,
4177
+ ")"
4178
+ ]
4179
+ }
4180
+ ),
4181
+ /* @__PURE__ */ jsxRuntime.jsxs(
4182
+ designSystem.Button,
4183
+ {
4184
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Play, {}),
4185
+ variant: "default",
4186
+ size: "L",
4187
+ onClick: handleOpenConfirmModal,
4188
+ loading: updating,
4189
+ disabled: loading || totalSelectedCount === 0 || !selectedAttribute || !selectedAttribute.trim(),
4190
+ children: [
4191
+ "Update Selected (",
4192
+ totalSelectedCount,
4193
+ ")"
4194
+ ]
4195
+ }
4196
+ ),
4197
+ /* @__PURE__ */ jsxRuntime.jsxs(
4198
+ designSystem.Button,
4199
+ {
4200
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Check, {}),
4201
+ variant: "success-light",
4202
+ size: "L",
4203
+ onClick: handleOpenPublishModal,
4204
+ loading: publishing,
4205
+ disabled: loading || totalSelectedCount === 0,
4206
+ title: "Directly publish selected draft records across collections",
4207
+ children: [
4208
+ "Publish Selected (",
4209
+ totalSelectedCount,
4210
+ ")"
4211
+ ]
4212
+ }
4213
+ ),
4214
+ /* @__PURE__ */ jsxRuntime.jsxs(
4215
+ designSystem.Button,
4216
+ {
4217
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Download, {}),
4218
+ variant: "tertiary",
4219
+ size: "L",
4220
+ onClick: handleBulkExportSelected,
4221
+ loading: bulkExporting,
4222
+ disabled: loading || totalSelectedCount === 0,
4223
+ title: "Export selected entries to Excel-compatible CSV with full nested component support",
4224
+ children: [
4225
+ "Export Selected (",
4226
+ totalSelectedCount,
4227
+ ")"
4228
+ ]
4229
+ }
4230
+ )
4231
+ ] })
4232
+ ] }),
4233
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 4, padding: 4, background: "neutral100", hasRadius: true, style: { border: "1px solid #eaeaef" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: [
4234
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
4235
+ /* @__PURE__ */ jsxRuntime.jsx(
4236
+ designSystem.Checkbox,
4237
+ {
4238
+ checked: publishOnApproval,
4239
+ onCheckedChange: (checked) => setPublishOnApproval(Boolean(checked)),
4240
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children: "Auto-Publish after Update" })
4241
+ }
4242
+ ),
4243
+ /* @__PURE__ */ jsxRuntime.jsx(
4244
+ designSystem.Badge,
4245
+ {
4246
+ backgroundColor: publishOnApproval ? "success100" : "warning100",
4247
+ textColor: publishOnApproval ? "success700" : "warning700",
4248
+ children: publishOnApproval ? "✓ LIVE PUBLISH" : "DRAFT ONLY"
4249
+ }
4250
+ )
4251
+ ] }),
4252
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.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." })
4253
+ ] }) }),
4254
+ selectedCol && selectedAttribute && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 4, padding: 3, background: "neutral100", hasRadius: true, style: { border: "1px solid #eaeaef" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
4255
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: "Target Selection:" }),
4256
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
4257
+ "Collection: ",
4258
+ selectedCol.displayName
4259
+ ] }),
4260
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: [
4261
+ "Attribute: ",
4262
+ selectedAttribute,
4263
+ " ",
4264
+ attributesMeta[selectedAttribute]?.type ? `(${attributesMeta[selectedAttribute].type})` : ""
4265
+ ] }),
4266
+ attributesMeta[selectedAttribute]?.depth ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "neutral200", textColor: "neutral700", children: [
4267
+ "Nesting Level: L",
4268
+ attributesMeta[selectedAttribute].depth
4269
+ ] }) : null,
4270
+ selectedCol.draftAndPublish ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: "Draft & Publish Enabled" }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: "Direct Publish Only" })
4271
+ ] }) }) })
4272
+ ]
4273
+ }
4274
+ ),
4114
4275
  updateSummary && /* @__PURE__ */ jsxRuntime.jsxs(
4115
4276
  designSystem.Box,
4116
4277
  {
@@ -4375,25 +4536,44 @@ const BulkUpdater = () => {
4375
4536
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: rec.userName }),
4376
4537
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", display: "block", style: { wordBreak: "break-all" }, children: rec.userEmail })
4377
4538
  ] }),
4378
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { minWidth: 160 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
4379
- designSystem.Typography,
4380
- {
4381
- style: {
4382
- display: "inline-block",
4383
- maxWidth: "100%",
4384
- overflow: "hidden",
4385
- textOverflow: "ellipsis",
4386
- whiteSpace: "nowrap"
4387
- },
4388
- title: `${rec.attribute} → "${formatDisplayValue(rec.value)}"`,
4389
- children: [
4390
- /* @__PURE__ */ jsxRuntime.jsx("strong", { children: rec.attribute }),
4391
- " → “",
4392
- formatDisplayValue(rec.value),
4393
- "”"
4394
- ]
4539
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { minWidth: 200 }, children: (() => {
4540
+ const isCsv = rec.attribute === "CSV_IMPORT" || rec.attribute?.startsWith("CSV");
4541
+ let attrText = rec.attribute;
4542
+ let valText = formatDisplayValue(rec.value);
4543
+ if (rec.attribute === "CSV_IMPORT" && rec.diffs && rec.diffs.length > 0) {
4544
+ const colName = rec.diffs[0]?.collectionName || "Collection";
4545
+ const attrs = Array.from(new Set(rec.diffs.map((d) => d.attribute).filter(Boolean))).join(", ");
4546
+ attrText = `CSV [${colName}]: ${attrs || "fields"}`;
4547
+ if (rec.value.startsWith("Mode:") && rec.diffs.length === 1) {
4548
+ const d = rec.diffs[0];
4549
+ valText = `"${d.label || d.documentId}": ${d.attribute || "field"} → "${formatDisplayValue(d.newValue)}"`;
4550
+ } else if (rec.value.startsWith("Mode:")) {
4551
+ valText = `${rec.diffs.length} attribute${rec.diffs.length > 1 ? "s" : ""} updated in ${colName}`;
4552
+ }
4395
4553
  }
4396
- ) }),
4554
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
4555
+ isCsv && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: "CSV IMPORT" }),
4556
+ /* @__PURE__ */ jsxRuntime.jsxs(
4557
+ designSystem.Typography,
4558
+ {
4559
+ style: {
4560
+ display: "inline-block",
4561
+ maxWidth: "100%",
4562
+ overflow: "hidden",
4563
+ textOverflow: "ellipsis",
4564
+ whiteSpace: "nowrap"
4565
+ },
4566
+ title: `${attrText} → "${valText}"`,
4567
+ children: [
4568
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: attrText.replace(/^CSV\s*\[?([^\]]+)\]?:\s*/, "$1: ") }),
4569
+ " → “",
4570
+ valText,
4571
+ "”"
4572
+ ]
4573
+ }
4574
+ )
4575
+ ] });
4576
+ })() }),
4397
4577
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Td, { style: { minWidth: 90, whiteSpace: "nowrap" }, children: [
4398
4578
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: [
4399
4579
  rec.updatedCount,
@@ -4466,18 +4646,20 @@ const BulkUpdater = () => {
4466
4646
  rec.batchId,
4467
4647
  ":"
4468
4648
  ] }),
4469
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: 4, rowCount: rec.diffs.length + 1, children: [
4649
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: 5, rowCount: rec.diffs.length + 1, children: [
4470
4650
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
4471
4651
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Collection" }) }),
4472
4652
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Document / Entry" }) }),
4653
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Attribute" }) }),
4473
4654
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Previous Value" }) }),
4474
4655
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Applied Value" }) })
4475
4656
  ] }) }),
4476
4657
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: rec.diffs.map((d, dIdx) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
4477
4658
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: d.collectionName }) }),
4478
4659
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: d.label }) }),
4660
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: d.attribute || "field" }) }),
4479
4661
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "danger600", children: formatDisplayValue(d.oldValue) }) }),
4480
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", children: formatDisplayValue(d.newValue) }) })
4662
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", fontWeight: "semiBold", children: formatDisplayValue(d.newValue) }) })
4481
4663
  ] }, dIdx)) })
4482
4664
  ] })
4483
4665
  ]
@@ -4989,5 +5171,6 @@ const BulkUpdater = () => {
4989
5171
  exports.default = BulkUpdater;
4990
5172
  exports.formatDisplayValue = formatDisplayValue;
4991
5173
  exports.getAvailableColumnsForCollection = getAvailableColumnsForCollection;
5174
+ exports.getEntryLifecycleStatus = getEntryLifecycleStatus;
4992
5175
  exports.getNestedValue = getNestedValue;
4993
- //# sourceMappingURL=BulkUpdater-DRHQBZZV.js.map
5176
+ //# sourceMappingURL=BulkUpdater-L2FdlJfh.js.map