strapi-plugin-bulk-updater 1.3.1 → 1.3.6

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.
@@ -1,9 +1,96 @@
1
1
  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
- import { Box, Flex, Typography, Button, SingleSelect, SingleSelectOption, Checkbox, Badge, Alert, Table, Thead, Tr, Th, Tbody, Td, Loader, TextInput, ComboboxOption, Field, Combobox } from "@strapi/design-system";
5
- import { ArrowClockwise, Download, Upload, Check, Eye, Play } from "@strapi/icons";
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
6
  import { V as ValidationRulesTab } from "./ValidationRulesTab-Dt53h-Ke.mjs";
7
+ function parseCsvRows(csvText) {
8
+ if (!csvText) return [];
9
+ let text = csvText;
10
+ if (text.charCodeAt(0) === 65279) {
11
+ text = text.slice(1);
12
+ }
13
+ const rows = [];
14
+ let currentRow = [];
15
+ let currentCell = "";
16
+ let inQuotes = false;
17
+ let cellIsQuoted = false;
18
+ for (let i = 0; i < text.length; i++) {
19
+ const char = text[i];
20
+ const nextChar = text[i + 1];
21
+ if (inQuotes) {
22
+ if (char === '"') {
23
+ if (nextChar === '"') {
24
+ currentCell += '"';
25
+ i++;
26
+ } else {
27
+ inQuotes = false;
28
+ }
29
+ } else {
30
+ currentCell += char;
31
+ }
32
+ } else {
33
+ if (char === '"' && !cellIsQuoted && currentCell.trim() === "") {
34
+ inQuotes = true;
35
+ cellIsQuoted = true;
36
+ currentCell = "";
37
+ } else if (char === ",") {
38
+ currentRow.push(cellIsQuoted ? currentCell : currentCell.trim());
39
+ currentCell = "";
40
+ cellIsQuoted = false;
41
+ } else if (char === "\r") {
42
+ if (nextChar === "\n") {
43
+ i++;
44
+ }
45
+ currentRow.push(cellIsQuoted ? currentCell : currentCell.trim());
46
+ if (currentRow.length > 1 || currentRow.length === 1 && currentRow[0] !== "") {
47
+ rows.push(currentRow);
48
+ }
49
+ currentRow = [];
50
+ currentCell = "";
51
+ cellIsQuoted = false;
52
+ } else if (char === "\n") {
53
+ currentRow.push(cellIsQuoted ? currentCell : currentCell.trim());
54
+ if (currentRow.length > 1 || currentRow.length === 1 && currentRow[0] !== "") {
55
+ rows.push(currentRow);
56
+ }
57
+ currentRow = [];
58
+ currentCell = "";
59
+ cellIsQuoted = false;
60
+ } else {
61
+ if (!(cellIsQuoted && !inQuotes && (char === " " || char === " "))) {
62
+ currentCell += char;
63
+ }
64
+ }
65
+ }
66
+ }
67
+ if (currentCell !== "" || cellIsQuoted || currentRow.length > 0) {
68
+ currentRow.push(cellIsQuoted ? currentCell : currentCell.trim());
69
+ if (currentRow.length > 1 || currentRow.length === 1 && currentRow[0] !== "") {
70
+ rows.push(currentRow);
71
+ }
72
+ }
73
+ return rows;
74
+ }
75
+ function parseCsv(csvText) {
76
+ const rows = parseCsvRows(csvText);
77
+ if (rows.length < 2) return [];
78
+ const headers = rows[0].map((h) => h.trim());
79
+ const records = [];
80
+ for (let i = 1; i < rows.length; i++) {
81
+ const row = rows[i];
82
+ const record = {};
83
+ headers.forEach((h, idx) => {
84
+ let val = row[idx] !== void 0 ? row[idx] : "";
85
+ if (typeof val === "string" && /^'[=+@-]/.test(val)) {
86
+ val = val.substring(1);
87
+ }
88
+ record[h] = val;
89
+ });
90
+ records.push(record);
91
+ }
92
+ return records;
93
+ }
7
94
  function CsvManagerTab() {
8
95
  const { get, post } = useFetchClient();
9
96
  const { toggleNotification } = useNotification();
@@ -135,20 +222,8 @@ function CsvManagerTab() {
135
222
  const text = event.target?.result;
136
223
  setCsvRawText(text);
137
224
  try {
138
- const lines = text.split(/\r?\n/).filter((l) => l.trim().length > 0);
139
- if (lines.length > 0) {
140
- const headers = lines[0].split(",").map((h) => h.replace(/^"|"$/g, "").trim());
141
- const previewRows = [];
142
- for (let i = 1; i < Math.min(lines.length, 6); i++) {
143
- const vals = lines[i].split(",").map((v) => v.replace(/^"|"$/g, "").trim());
144
- const row = {};
145
- headers.forEach((h, idx) => {
146
- row[h] = vals[idx] || "";
147
- });
148
- previewRows.push(row);
149
- }
150
- setParsedPreview(previewRows);
151
- }
225
+ const records = parseCsv(text);
226
+ setParsedPreview(records.slice(0, 10));
152
227
  } catch (err) {
153
228
  setParsedPreview([]);
154
229
  }
@@ -278,53 +353,20 @@ function CsvManagerTab() {
278
353
  /* @__PURE__ */ jsxs(Box, { padding: 5, background: "neutral100", hasRadius: true, children: [
279
354
  /* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h3", fontWeight: "bold", marginBottom: 2, children: "2. Import & Batch Override CSV" }),
280
355
  /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", marginBottom: 4, children: "Upload an updated CSV file to bulk update existing records or insert new records." }),
281
- /* @__PURE__ */ jsxs(Flex, { gap: 6, marginBottom: 4, wrap: "wrap", children: [
356
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, wrap: "wrap", gap: 4, children: [
282
357
  /* @__PURE__ */ jsxs(Box, { children: [
283
- /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", marginBottom: 2, children: "Operation Mode" }),
284
- /* @__PURE__ */ jsxs(Flex, { gap: 4, wrap: "wrap", children: [
285
- /* @__PURE__ */ jsxs("label", { style: { display: "flex", alignItems: "center", gap: 6, cursor: "pointer" }, children: [
286
- /* @__PURE__ */ jsx(
287
- "input",
288
- {
289
- type: "radio",
290
- name: "importMode",
291
- value: "auto",
292
- checked: importMode === "auto",
293
- onChange: () => setImportMode("auto")
294
- }
295
- ),
296
- /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: importMode === "auto" ? "bold" : "normal", children: "Smart Auto-Detect (Auto-updates matching documentId, creates new if missing)" })
297
- ] }),
298
- /* @__PURE__ */ jsxs("label", { style: { display: "flex", alignItems: "center", gap: 6, cursor: "pointer" }, children: [
299
- /* @__PURE__ */ jsx(
300
- "input",
301
- {
302
- type: "radio",
303
- name: "importMode",
304
- value: "update",
305
- checked: importMode === "update",
306
- onChange: () => setImportMode("update")
307
- }
308
- ),
309
- /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: importMode === "update" ? "bold" : "normal", children: "Update Existing Only (Requires documentId)" })
310
- ] }),
311
- /* @__PURE__ */ jsxs("label", { style: { display: "flex", alignItems: "center", gap: 6, cursor: "pointer" }, children: [
312
- /* @__PURE__ */ jsx(
313
- "input",
314
- {
315
- type: "radio",
316
- name: "importMode",
317
- value: "create_or_override",
318
- checked: importMode === "create_or_override",
319
- onChange: () => setImportMode("create_or_override")
320
- }
321
- ),
322
- /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: importMode === "create_or_override" ? "bold" : "normal", children: "Add / Override (Inserts new or replaces existing)" })
358
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", marginBottom: 1, display: "block", children: "Operation Mode" }),
359
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
360
+ /* @__PURE__ */ jsx(Badge, { backgroundColor: "primary100", textColor: "primary700", children: "⚡ Smart Auto-Detect" }),
361
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", children: [
362
+ "Automatically updates matching records by ",
363
+ /* @__PURE__ */ jsx("code", { children: "documentId" }),
364
+ " and creates new entries when ID is omitted."
323
365
  ] })
324
366
  ] })
325
367
  ] }),
326
- /* @__PURE__ */ jsxs(Box, { style: { marginLeft: "auto" }, children: [
327
- /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", marginBottom: 2, children: "Publish Options" }),
368
+ /* @__PURE__ */ jsxs(Box, { children: [
369
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", marginBottom: 1, display: "block", children: "Publish Options" }),
328
370
  /* @__PURE__ */ jsx(
329
371
  Checkbox,
330
372
  {
@@ -392,8 +434,8 @@ function CsvManagerTab() {
392
434
  missingDocIdCount > 0 && /* @__PURE__ */ jsx(Box, { marginBottom: 3, children: /* @__PURE__ */ jsx(
393
435
  Alert,
394
436
  {
395
- variant: importMode === "update" ? "warning" : "default",
396
- title: importMode === "update" ? "Warning: Missing Document IDs" : "Smart Auto-Detect Active",
437
+ variant: "default",
438
+ title: "Smart Auto-Detect Active",
397
439
  children: /* @__PURE__ */ jsxs(Typography, { variant: "omega", children: [
398
440
  missingDocIdCount,
399
441
  " row",
@@ -402,23 +444,40 @@ function CsvManagerTab() {
402
444
  /* @__PURE__ */ jsx("code", { children: "documentId" }),
403
445
  ".",
404
446
  " ",
405
- importMode === "auto" ? "Because no documentId is provided, the engine will automatically treat them as new entries and generate new database IDs for them." : importMode === "update" ? 'In "Update Existing Only" mode, rows without a documentId will be skipped. Switch to "Smart Auto-Detect" to automatically create them as new entries.' : "These will be created as new entries."
447
+ "Because no documentId is provided, the engine will automatically treat them as new entries and generate new database IDs for them."
406
448
  ] })
407
449
  }
408
450
  ) }),
409
- /* @__PURE__ */ jsxs(Table, { colCount: headers.length + 1, rowCount: parsedPreview.length + 1, children: [
451
+ /* @__PURE__ */ jsx(Box, { style: { overflowX: "auto", maxWidth: "100%", border: "1px solid #e0e0e0", borderRadius: 4 }, children: /* @__PURE__ */ jsxs(Table, { colCount: headers.length + 1, rowCount: parsedPreview.length + 1, children: [
410
452
  /* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [
411
- /* @__PURE__ */ jsx(Th, { style: { width: 140 }, children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Detected Action" }) }),
412
- headers.map((h) => /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: h }) }, h))
453
+ /* @__PURE__ */ jsx(Th, { style: { width: 150, minWidth: 150 }, children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Detected Action" }) }),
454
+ headers.map((h) => /* @__PURE__ */ jsx(Th, { style: { minWidth: 160 }, children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: h }) }, h))
413
455
  ] }) }),
414
456
  /* @__PURE__ */ jsx(Tbody, { children: parsedPreview.map((row, rIdx) => {
415
457
  const hasDocId = !!(row.documentId || row.id || "").trim();
416
458
  return /* @__PURE__ */ jsxs(Tr, { children: [
417
459
  /* @__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" }) }),
418
- headers.map((h) => /* @__PURE__ */ jsx(Td, { children: (h === "documentId" || h === "id") && !row[h]?.trim() ? /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "warning700", fontWeight: "bold", children: "(Auto-generate ID)" }) : /* @__PURE__ */ jsx(Typography, { variant: "omega", children: row[h] || "-" }) }, h))
460
+ 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(
461
+ Typography,
462
+ {
463
+ variant: "omega",
464
+ style: {
465
+ display: "-webkit-box",
466
+ WebkitLineClamp: 3,
467
+ WebkitBoxOrient: "vertical",
468
+ overflow: "hidden",
469
+ textOverflow: "ellipsis",
470
+ whiteSpace: "pre-wrap",
471
+ wordBreak: "break-word",
472
+ maxHeight: 65
473
+ },
474
+ title: row[h] || "",
475
+ children: row[h] || "-"
476
+ }
477
+ ) }, h))
419
478
  ] }, rIdx);
420
479
  }) })
421
- ] }),
480
+ ] }) }),
422
481
  /* @__PURE__ */ jsx(Flex, { justifyContent: "flex-end", marginTop: 4, children: /* @__PURE__ */ jsx(
423
482
  Button,
424
483
  {
@@ -427,7 +486,7 @@ function CsvManagerTab() {
427
486
  startIcon: /* @__PURE__ */ jsx(Check, {}),
428
487
  onClick: handleExecuteImport,
429
488
  loading: importing,
430
- children: importMode === "auto" ? "Execute Smart Import (Auto-Detect)" : importMode === "update" ? "Execute Bulk Update" : "Execute Add / Override"
489
+ children: "Execute Smart Import (Auto-Detect)"
431
490
  }
432
491
  ) })
433
492
  ] });
@@ -1685,6 +1744,16 @@ const BulkUpdater = () => {
1685
1744
  const [bulkExporting, setBulkExporting] = useState(false);
1686
1745
  const MODELS_CACHE_KEY = "strapi_bulk_updater_models_cache";
1687
1746
  const LAST_ATTR_KEY = "strapi_bulk_updater_last_attr";
1747
+ const LAST_COLLECTION_KEY = "strapi_bulk_updater_last_collection";
1748
+ const getInitialCollectionUid = () => {
1749
+ try {
1750
+ const stored = localStorage.getItem(LAST_COLLECTION_KEY);
1751
+ if (stored) return stored;
1752
+ } catch {
1753
+ }
1754
+ return "";
1755
+ };
1756
+ const [selectedCollectionUid, setSelectedCollectionUid] = useState(getInitialCollectionUid);
1688
1757
  const getInitialAttribute = () => {
1689
1758
  try {
1690
1759
  const stored = localStorage.getItem(LAST_ATTR_KEY);
@@ -1743,7 +1812,7 @@ const BulkUpdater = () => {
1743
1812
  const [filterOperator, setFilterOperator] = useState("none");
1744
1813
  const [filterQuery, setFilterQuery] = useState("");
1745
1814
  const [entrySearch, setEntrySearch] = useState("");
1746
- const isFilterActive = Boolean(searchAttribute && searchAttribute !== selectedAttribute) || filterOperator !== "none" && (["empty", "not_empty"].includes(filterOperator) || Boolean(filterQuery.trim())) || Boolean(entrySearch.trim());
1815
+ const isFilterActive = Boolean(searchAttribute && searchAttribute !== selectedAttribute) || filterOperator !== "none" && (["empty", "not_empty"].includes(filterOperator) || Boolean(filterQuery.trim())) || Boolean(entrySearch.trim()) || Boolean(useRegex && regexPattern.trim());
1747
1816
  const [lastResults, setLastResults] = useState(null);
1748
1817
  const [updateSummary, setUpdateSummary] = useState(null);
1749
1818
  const [collectionPages, setCollectionPages] = useState({});
@@ -1756,6 +1825,7 @@ const BulkUpdater = () => {
1756
1825
  const [collectionSorts, setCollectionSorts] = useState({});
1757
1826
  const [collectionColumnVisibility, setCollectionColumnVisibility] = useState({});
1758
1827
  const [openColumnPickerUid, setOpenColumnPickerUid] = useState(null);
1828
+ const [columnSearchTerm, setColumnSearchTerm] = useState("");
1759
1829
  const toggleColumn = (colUid, colKey, availableCols) => {
1760
1830
  setCollectionColumnVisibility((prev) => {
1761
1831
  const current = prev[colUid] || availableCols.filter((c) => c.defaultVisible).map((c) => c.key);
@@ -1826,12 +1896,29 @@ const BulkUpdater = () => {
1826
1896
  if (data.attributesMeta) {
1827
1897
  setAttributesMeta(data.attributesMeta);
1828
1898
  }
1899
+ let resolvedColUid = selectedCollectionUid;
1900
+ let storedCol = null;
1901
+ try {
1902
+ storedCol = localStorage.getItem(LAST_COLLECTION_KEY);
1903
+ } catch {
1904
+ }
1905
+ if (!resolvedColUid || !cols.some((c) => c.uid === resolvedColUid)) {
1906
+ resolvedColUid = storedCol && cols.some((c) => c.uid === storedCol) ? storedCol : cols[0]?.uid || "";
1907
+ }
1908
+ setSelectedCollectionUid(resolvedColUid);
1909
+ try {
1910
+ if (resolvedColUid) localStorage.setItem(LAST_COLLECTION_KEY, resolvedColUid);
1911
+ } catch {
1912
+ }
1913
+ const activeCol = cols.find((c) => c.uid === resolvedColUid) || cols[0];
1914
+ const activeAttrs = activeCol?.attributes || [];
1915
+ setAttributes(activeAttrs);
1829
1916
  let storedAttr = null;
1830
1917
  try {
1831
1918
  storedAttr = localStorage.getItem(LAST_ATTR_KEY);
1832
1919
  } catch {
1833
1920
  }
1834
- const resolvedAttr = storedAttr && attrs.includes(storedAttr) ? storedAttr : selectedAttribute && attrs.includes(selectedAttribute) ? selectedAttribute : "";
1921
+ const resolvedAttr = storedAttr && activeAttrs.includes(storedAttr) ? storedAttr : selectedAttribute && activeAttrs.includes(selectedAttribute) ? selectedAttribute : activeAttrs[0] || "";
1835
1922
  setSelectedAttribute(resolvedAttr);
1836
1923
  if (resolvedAttr && data.relationsMeta && data.relationsMeta[resolvedAttr]) {
1837
1924
  fetchRelationOptions(data.relationsMeta[resolvedAttr].targetUid);
@@ -2014,36 +2101,60 @@ const BulkUpdater = () => {
2014
2101
  return;
2015
2102
  }
2016
2103
  const timer = setTimeout(() => {
2017
- if (collections.length > 0) {
2018
- const isFilterActiveNow = Boolean(searchAttribute && searchAttribute !== selectedAttribute) || filterOperator !== "none" && (["empty", "not_empty"].includes(filterOperator) || Boolean(filterQuery.trim())) || Boolean(entrySearch.trim());
2104
+ if (selectedCollectionUid) {
2105
+ const isFilterActiveNow = Boolean(searchAttribute && searchAttribute !== selectedAttribute) || filterOperator !== "none" && (["empty", "not_empty"].includes(filterOperator) || Boolean(filterQuery.trim())) || Boolean(entrySearch.trim()) || Boolean(useRegex && regexPattern.trim());
2019
2106
  if (isFilterActiveNow) {
2020
2107
  setSelectedEntries({});
2021
2108
  }
2022
- const targetSearchAttr = searchAttribute || selectedAttribute;
2023
- const targetCols = collections.filter((col) => {
2024
- if (visibleTypeUids[col.uid] === false) return false;
2025
- if (filterOperator !== "none" && targetSearchAttr && !col.attributes.includes(targetSearchAttr)) return false;
2026
- return true;
2027
- });
2028
- targetCols.forEach((col) => {
2029
- loadCollectionPage(
2030
- col.uid,
2031
- 1,
2032
- void 0,
2033
- void 0,
2034
- void 0,
2035
- filterOperator,
2036
- filterQuery,
2037
- entrySearch,
2038
- true,
2039
- searchAttribute
2040
- );
2041
- });
2109
+ loadCollectionPage(
2110
+ selectedCollectionUid,
2111
+ 1,
2112
+ void 0,
2113
+ void 0,
2114
+ void 0,
2115
+ filterOperator,
2116
+ filterQuery,
2117
+ entrySearch,
2118
+ true,
2119
+ searchAttribute
2120
+ );
2042
2121
  }
2043
2122
  }, 350);
2044
2123
  return () => clearTimeout(timer);
2045
- }, [searchAttribute, filterOperator, filterQuery, entrySearch]);
2124
+ }, [selectedCollectionUid, searchAttribute, filterOperator, filterQuery, entrySearch, useRegex, regexPattern]);
2046
2125
  const KEY_SEP = "@@@";
2126
+ const selectedCol = useMemo(() => {
2127
+ if (!collections.length) return null;
2128
+ return collections.find((c) => c.uid === selectedCollectionUid) || collections[0] || null;
2129
+ }, [collections, selectedCollectionUid]);
2130
+ const handleCollectionChange = (newUid) => {
2131
+ if (newUid === selectedCollectionUid) return;
2132
+ setSelectedCollectionUid(newUid);
2133
+ try {
2134
+ localStorage.setItem(LAST_COLLECTION_KEY, newUid);
2135
+ } catch {
2136
+ }
2137
+ setSelectedEntries({});
2138
+ setSearchAttribute("");
2139
+ setSearchAttributeSearch("");
2140
+ const targetCol = collections.find((c) => c.uid === newUid);
2141
+ const colAttrs = targetCol?.attributes || [];
2142
+ setAttributes(colAttrs);
2143
+ if (!colAttrs.includes(selectedAttribute)) {
2144
+ const newAttr = colAttrs[0] || "";
2145
+ setSelectedAttribute(newAttr);
2146
+ try {
2147
+ localStorage.setItem(LAST_ATTR_KEY, newAttr);
2148
+ } catch {
2149
+ }
2150
+ if (newAttr && relationsMeta[newAttr]) {
2151
+ fetchRelationOptions(relationsMeta[newAttr].targetUid);
2152
+ } else {
2153
+ setSelectedRelationDocId("");
2154
+ }
2155
+ }
2156
+ loadCollectionPage(newUid, 1, void 0, void 0, void 0, filterOperator, filterQuery, entrySearch, true, "");
2157
+ };
2047
2158
  const handleAttributeChange = (newAttr) => {
2048
2159
  if (newAttr !== selectedAttribute) {
2049
2160
  setSelectedAttribute(newAttr);
@@ -2066,56 +2177,23 @@ const BulkUpdater = () => {
2066
2177
  } else {
2067
2178
  setRelationOptions([]);
2068
2179
  }
2069
- if (newAttr) {
2070
- const collectionsToLoad = collections.filter(
2071
- (col) => visibleTypeUids[col.uid] !== false && col.attributes.includes(newAttr) && (!collectionPages[col.uid]?.entries || collectionPages[col.uid].entries.length === 0)
2072
- );
2073
- collectionsToLoad.forEach((col) => {
2074
- loadCollectionPage(col.uid, 1);
2075
- });
2180
+ if (newAttr && selectedCollectionUid) {
2181
+ loadCollectionPage(selectedCollectionUid, 1);
2076
2182
  }
2077
2183
  }
2078
2184
  };
2079
- const filteredCollections = collections.filter(
2080
- (c) => visibleTypeUids[c.uid] !== false && (!collectionSearch.trim() || c.displayName.toLowerCase().includes(collectionSearch.toLowerCase().trim()) || c.uid.toLowerCase().includes(collectionSearch.toLowerCase().trim()))
2081
- );
2082
- const supportedCollections = useMemo(() => {
2083
- return collections.filter((c) => c.attributes.includes(selectedAttribute));
2084
- }, [collections, selectedAttribute]);
2085
- const unsupportedCollections = useMemo(() => {
2086
- return collections.filter((c) => !c.attributes.includes(selectedAttribute));
2087
- }, [collections, selectedAttribute]);
2088
- const handleFocusOnlySupported = () => {
2089
- const newVis = {};
2090
- collections.forEach((c) => {
2091
- newVis[c.uid] = c.attributes.includes(selectedAttribute);
2092
- });
2093
- setVisibleTypeUids(newVis);
2094
- toggleNotification({
2095
- type: "info",
2096
- message: `Workspace focused: Showing ${supportedCollections.length} of ${collections.length} collections supporting "${selectedAttribute}".`
2097
- });
2098
- };
2099
- const handleShowAllTypes = () => {
2100
- const newVis = {};
2101
- collections.forEach((c) => {
2102
- newVis[c.uid] = true;
2103
- });
2104
- setVisibleTypeUids(newVis);
2105
- };
2106
- const handleHideAllTypes = () => {
2107
- const newVis = {};
2108
- collections.forEach((c) => {
2109
- newVis[c.uid] = false;
2110
- });
2111
- setVisibleTypeUids(newVis);
2112
- };
2185
+ const filteredCollections = useMemo(() => {
2186
+ return selectedCol ? [selectedCol] : [];
2187
+ }, [selectedCol]);
2113
2188
  const [attributeSearch, setAttributeSearch] = useState("");
2114
2189
  const [searchAttributeSearch, setSearchAttributeSearch] = useState("");
2115
2190
  const getAttrDepth = (attr) => attributesMeta[attr]?.depth ?? (attr.includes(".") ? attr.split(".").length - 1 : 0);
2191
+ const availableColAttributes = useMemo(() => {
2192
+ return selectedCol?.attributes || attributes || [];
2193
+ }, [selectedCol, attributes]);
2116
2194
  const depthFilteredAttributes = useMemo(() => {
2117
- return attributes.filter((attr) => getAttrDepth(attr) <= componentDepth);
2118
- }, [attributes, componentDepth, attributesMeta]);
2195
+ return availableColAttributes.filter((attr) => getAttrDepth(attr) <= componentDepth);
2196
+ }, [availableColAttributes, componentDepth, attributesMeta]);
2119
2197
  const filteredAttributes = useMemo(() => {
2120
2198
  if (!attributeSearch.trim()) {
2121
2199
  return depthFilteredAttributes;
@@ -2224,15 +2302,6 @@ const BulkUpdater = () => {
2224
2302
  });
2225
2303
  }
2226
2304
  };
2227
- const handleGlobalPageSizeChange = (newSize) => {
2228
- const capped = Math.min(500, Math.max(1, newSize));
2229
- setDefaultPageSize(capped);
2230
- collections.forEach((col) => {
2231
- if (!col.isSingleType) {
2232
- loadCollectionPage(col.uid, 1, capped);
2233
- }
2234
- });
2235
- };
2236
2305
  const handleCollectionPageSizeChange = (colUid, newSize) => {
2237
2306
  const capped = Math.min(500, Math.max(1, newSize));
2238
2307
  loadCollectionPage(colUid, 1, capped);
@@ -2257,6 +2326,16 @@ const BulkUpdater = () => {
2257
2326
  }));
2258
2327
  };
2259
2328
  const entryMatchesFilter = (entry, attr) => {
2329
+ if (useRegex && regexPattern.trim()) {
2330
+ try {
2331
+ const regex = new RegExp(regexPattern, "i");
2332
+ const targetSearchVal = formatDisplayValue(getNestedValue(entry, attr));
2333
+ if (!regex.test(targetSearchVal)) {
2334
+ return false;
2335
+ }
2336
+ } catch {
2337
+ }
2338
+ }
2260
2339
  if (filterOperator === "none") return true;
2261
2340
  const val = getNestedValue(entry, attr);
2262
2341
  const displayStr = formatDisplayValue(val);
@@ -2319,6 +2398,7 @@ const BulkUpdater = () => {
2319
2398
  setFilterOperator("none");
2320
2399
  setFilterQuery("");
2321
2400
  setEntrySearch("");
2401
+ setRegexPattern("");
2322
2402
  setSelectedEntries({});
2323
2403
  collections.forEach((col) => {
2324
2404
  loadCollectionPage(col.uid, 1, void 0, void 0, void 0, "none", "", "", false, "");
@@ -3107,41 +3187,79 @@ const BulkUpdater = () => {
3107
3187
  ] })
3108
3188
  }
3109
3189
  ) : activeTab === "editor" ? /* @__PURE__ */ jsxs(Fragment, { children: [
3110
- /* @__PURE__ */ jsxs(
3190
+ /* @__PURE__ */ jsx(
3111
3191
  Box,
3112
3192
  {
3113
- padding: 6,
3193
+ padding: 4,
3114
3194
  background: "neutral0",
3115
3195
  shadow: "filterShadow",
3116
3196
  hasRadius: true,
3117
- marginBottom: 6,
3197
+ marginBottom: 5,
3118
3198
  style: { width: "100%", maxWidth: "100%", minWidth: 0, boxSizing: "border-box" },
3119
- children: [
3120
- /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, wrap: "wrap", gap: 3, children: [
3121
- /* @__PURE__ */ jsxs(Box, { children: [
3122
- /* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h2", fontWeight: "semiBold", children: "1. Configure Attribute Update" }),
3123
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Select the target attribute to update across collections." })
3124
- ] }),
3125
- /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
3126
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral700", fontWeight: "bold", children: "Component Nesting Depth:" }),
3127
- [1, 2, 3, 4, 5].map((lvl) => /* @__PURE__ */ jsxs(
3128
- Button,
3199
+ children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: [
3200
+ /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "center", wrap: "wrap", style: { flex: 1, minWidth: 280 }, children: [
3201
+ /* @__PURE__ */ jsx(Box, { style: { width: 340, minWidth: 260 }, children: /* @__PURE__ */ jsxs(Field.Root, { children: [
3202
+ /* @__PURE__ */ jsx(Field.Label, { children: "Target Collection Type" }),
3203
+ /* @__PURE__ */ jsx(
3204
+ Combobox,
3129
3205
  {
3130
- variant: componentDepth === lvl ? "default" : "tertiary",
3131
- size: "S",
3132
- onClick: () => handleComponentDepthChange(lvl),
3133
- title: `Discover nested component attributes up to Level ${lvl}`,
3134
- children: [
3135
- "Level ",
3136
- lvl
3137
- ]
3206
+ autocomplete: "none",
3207
+ placeholder: "Select Collection Type...",
3208
+ value: selectedCollectionUid || void 0,
3209
+ onChange: (val) => {
3210
+ if (val && typeof val === "string") {
3211
+ handleCollectionChange(val);
3212
+ }
3213
+ },
3214
+ children: collections.map((col) => /* @__PURE__ */ jsx(ComboboxOption, { value: col.uid, textValue: `${col.displayName} (${col.uid})`, children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", style: { width: "100%" }, children: [
3215
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", children: col.displayName }),
3216
+ /* @__PURE__ */ jsx(Flex, { gap: 1, alignItems: "center", children: col.isSingleType ? /* @__PURE__ */ jsx(Badge, { backgroundColor: "secondary100", textColor: "secondary700", size: "S", children: "Single" }) : /* @__PURE__ */ jsx(Badge, { backgroundColor: "neutral150", textColor: "neutral700", size: "S", children: col.totalEntries !== void 0 ? `${col.totalEntries} entries` : "" }) })
3217
+ ] }) }, col.uid))
3138
3218
  },
3139
- `depth_cfg_${lvl}`
3140
- ))
3219
+ `top-col-${selectedCollectionUid}`
3220
+ )
3221
+ ] }) }),
3222
+ selectedCol && /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", style: { paddingTop: 20 }, children: [
3223
+ /* @__PURE__ */ jsx(Badge, { backgroundColor: "primary100", textColor: "primary700", children: selectedCol.isSingleType ? "SINGLE TYPE" : "COLLECTION" }),
3224
+ /* @__PURE__ */ jsx(Badge, { backgroundColor: "neutral150", textColor: "neutral700", children: selectedCol.isSingleType ? "1 single entry" : `${collectionPages[selectedCol.uid]?.total ?? selectedCol.totalEntries ?? selectedCol.entries?.length ?? 0} records` })
3141
3225
  ] })
3142
3226
  ] }),
3143
- /* @__PURE__ */ jsxs(Flex, { gap: 4, alignItems: "flex-end", wrap: "wrap", children: [
3144
- /* @__PURE__ */ jsx(Box, { style: { minWidth: 280, flex: 1, maxWidth: 440 }, children: /* @__PURE__ */ jsxs(Field.Root, { children: [
3227
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
3228
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral700", fontWeight: "bold", children: "Component Nesting Depth:" }),
3229
+ [1, 2, 3, 4, 5].map((lvl) => /* @__PURE__ */ jsxs(
3230
+ Button,
3231
+ {
3232
+ variant: componentDepth === lvl ? "default" : "tertiary",
3233
+ size: "S",
3234
+ onClick: () => handleComponentDepthChange(lvl),
3235
+ title: `Discover nested component attributes up to Level ${lvl}`,
3236
+ children: [
3237
+ "Level ",
3238
+ lvl
3239
+ ]
3240
+ },
3241
+ `depth_cfg_${lvl}`
3242
+ ))
3243
+ ] })
3244
+ ] })
3245
+ }
3246
+ ),
3247
+ /* @__PURE__ */ jsxs(
3248
+ Box,
3249
+ {
3250
+ padding: 6,
3251
+ background: "neutral0",
3252
+ shadow: "filterShadow",
3253
+ hasRadius: true,
3254
+ marginBottom: 6,
3255
+ style: { width: "100%", maxWidth: "100%", minWidth: 0, boxSizing: "border-box" },
3256
+ children: [
3257
+ /* @__PURE__ */ jsx(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, wrap: "wrap", gap: 3, children: /* @__PURE__ */ jsxs(Box, { children: [
3258
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h2", fontWeight: "semiBold", children: "1. Configure Attribute Update" }),
3259
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Select the target attribute to update and configure the replacement value." })
3260
+ ] }) }),
3261
+ /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "flex-end", wrap: "wrap", children: [
3262
+ /* @__PURE__ */ jsx(Box, { style: { width: 280, minWidth: 230 }, children: /* @__PURE__ */ jsxs(Field.Root, { children: [
3145
3263
  /* @__PURE__ */ jsx(Field.Label, { children: "Attribute to Update" }),
3146
3264
  /* @__PURE__ */ jsx(
3147
3265
  Combobox,
@@ -3171,7 +3289,8 @@ const BulkUpdater = () => {
3171
3289
  }
3172
3290
  },
3173
3291
  children: memoizedComboboxOptions
3174
- }
3292
+ },
3293
+ `update-attr-${selectedCollectionUid}-${selectedAttribute}`
3175
3294
  )
3176
3295
  ] }) }),
3177
3296
  /* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 340 }, children: relationsMeta[selectedAttribute] ? /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 1, style: { width: "100%" }, children: [
@@ -3340,103 +3459,24 @@ const BulkUpdater = () => {
3340
3459
  ] }),
3341
3460
  /* @__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." })
3342
3461
  ] }) }),
3343
- /* @__PURE__ */ jsx(Box, { marginTop: 4, padding: 4, background: "neutral100", hasRadius: true, children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: [
3344
- /* @__PURE__ */ jsx(Flex, { gap: 2, alignItems: "center", children: /* @__PURE__ */ jsx(
3345
- Checkbox,
3346
- {
3347
- checked: useRegex,
3348
- onCheckedChange: (checked) => setUseRegex(Boolean(checked)),
3349
- children: /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: "Enable Regex Find & Replace" })
3350
- }
3351
- ) }),
3352
- useRegex && /* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 240 }, children: /* @__PURE__ */ jsx(
3353
- TextInput,
3354
- {
3355
- placeholder: "Regex pattern (e.g. \\b(OldName)\\b)...",
3356
- value: regexPattern,
3357
- onChange: (e) => setRegexPattern(e.target.value)
3358
- }
3359
- ) })
3360
- ] }) }),
3361
- selectedAttribute && /* @__PURE__ */ jsxs(Box, { marginTop: 4, padding: 4, background: "neutral100", hasRadius: true, style: { border: "1px solid #eaeaef" }, children: [
3362
- /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: [
3363
- /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
3364
- /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", children: "Attribute Info:" }),
3365
- /* @__PURE__ */ jsxs(Typography, { variant: "pi", children: [
3366
- "Targeting attribute ",
3367
- /* @__PURE__ */ jsx("strong", { children: /* @__PURE__ */ jsx("code", { children: selectedAttribute }) })
3368
- ] }),
3369
- /* @__PURE__ */ jsxs(Badge, { backgroundColor: "success100", textColor: "success700", children: [
3370
- "✓ Supported in ",
3371
- supportedCollections.length,
3372
- " of ",
3373
- collections.length,
3374
- " Collections"
3375
- ] }),
3376
- unsupportedCollections.length > 0 && /* @__PURE__ */ jsxs(Badge, { backgroundColor: "neutral200", textColor: "neutral700", children: [
3377
- "— Not in schema: ",
3378
- unsupportedCollections.length
3379
- ] })
3380
- ] }),
3381
- /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
3382
- /* @__PURE__ */ jsxs(
3383
- Button,
3384
- {
3385
- variant: "secondary",
3386
- size: "S",
3387
- onClick: handleFocusOnlySupported,
3388
- children: [
3389
- "Focus Workspace: Show Supported Only (",
3390
- supportedCollections.length,
3391
- ")"
3392
- ]
3393
- }
3394
- ),
3395
- /* @__PURE__ */ jsx(
3396
- Button,
3397
- {
3398
- variant: "tertiary",
3399
- size: "S",
3400
- onClick: () => setAttributeInfoExpanded((prev) => !prev),
3401
- children: attributeInfoExpanded ? "Hide Schema Breakdown ▲" : "Inspect Breakdown ▼"
3402
- }
3403
- )
3404
- ] })
3462
+ 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: [
3463
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", children: "Target Selection:" }),
3464
+ /* @__PURE__ */ jsxs(Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
3465
+ "Collection: ",
3466
+ selectedCol.displayName
3405
3467
  ] }),
3406
- attributeInfoExpanded && /* @__PURE__ */ jsxs(Box, { marginTop: 3, paddingTop: 3, style: { borderTop: "1px solid #dcdce4" }, children: [
3407
- /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 3, wrap: "wrap", gap: 2, children: [
3408
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Showing collection types schema compatibility. Type to filter:" }),
3409
- /* @__PURE__ */ jsx(Box, { style: { width: 220 }, children: /* @__PURE__ */ jsx(
3410
- TextInput,
3411
- {
3412
- placeholder: "Filter collection name...",
3413
- size: "S",
3414
- value: attributeInfoSearch,
3415
- onChange: (e) => setAttributeInfoSearch(e.target.value)
3416
- }
3417
- ) })
3418
- ] }),
3419
- /* @__PURE__ */ jsx(Flex, { wrap: "wrap", gap: 2, alignItems: "center", style: { maxHeight: 180, overflowY: "auto" }, children: collections.filter(
3420
- (c) => !attributeInfoSearch.trim() || c.displayName.toLowerCase().includes(attributeInfoSearch.toLowerCase().trim()) || c.uid.toLowerCase().includes(attributeInfoSearch.toLowerCase().trim())
3421
- ).map((c) => {
3422
- const hasAttr = c.attributes.includes(selectedAttribute);
3423
- return /* @__PURE__ */ jsxs(
3424
- Badge,
3425
- {
3426
- backgroundColor: hasAttr ? "success100" : "neutral200",
3427
- textColor: hasAttr ? "success700" : "neutral600",
3428
- size: "S",
3429
- children: [
3430
- c.displayName,
3431
- ": ",
3432
- hasAttr ? "✓ Supported" : "— Not in schema"
3433
- ]
3434
- },
3435
- c.uid
3436
- );
3437
- }) })
3438
- ] })
3439
- ] })
3468
+ /* @__PURE__ */ jsxs(Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: [
3469
+ "Attribute: ",
3470
+ selectedAttribute,
3471
+ " ",
3472
+ attributesMeta[selectedAttribute]?.type ? `(${attributesMeta[selectedAttribute].type})` : ""
3473
+ ] }),
3474
+ attributesMeta[selectedAttribute]?.depth ? /* @__PURE__ */ jsxs(Badge, { backgroundColor: "neutral200", textColor: "neutral700", children: [
3475
+ "Nesting Level: L",
3476
+ attributesMeta[selectedAttribute].depth
3477
+ ] }) : null,
3478
+ selectedCol.draftAndPublish ? /* @__PURE__ */ jsx(Badge, { backgroundColor: "success100", textColor: "success700", children: "Draft & Publish Enabled" }) : /* @__PURE__ */ jsx(Badge, { backgroundColor: "warning100", textColor: "warning700", children: "Direct Publish Only" })
3479
+ ] }) }) })
3440
3480
  ]
3441
3481
  }
3442
3482
  ),
@@ -3453,7 +3493,11 @@ const BulkUpdater = () => {
3453
3493
  /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, wrap: "wrap", gap: 3, children: [
3454
3494
  /* @__PURE__ */ jsxs(Box, { children: [
3455
3495
  /* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h2", fontWeight: "semiBold", children: "2. Select Targets" }),
3456
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Pick entire collection types or individual entries to apply the update to." })
3496
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", children: [
3497
+ "Search and select entries to apply the update to within ",
3498
+ selectedCol ? selectedCol.displayName : "the selected collection",
3499
+ "."
3500
+ ] })
3457
3501
  ] }),
3458
3502
  /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3459
3503
  /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "S", onClick: selectAll, children: "Select All" }),
@@ -3485,27 +3529,7 @@ const BulkUpdater = () => {
3485
3529
  selectedAttribute
3486
3530
  ] })
3487
3531
  ] }),
3488
- /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
3489
- /* @__PURE__ */ jsxs(Flex, { gap: 1, alignItems: "center", children: [
3490
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Depth:" }),
3491
- [1, 2, 3, 4, 5].map((lvl) => /* @__PURE__ */ jsxs(
3492
- Button,
3493
- {
3494
- variant: componentDepth === lvl ? "default" : "tertiary",
3495
- size: "S",
3496
- onClick: () => handleComponentDepthChange(lvl),
3497
- title: `Show component attributes up to Level ${lvl}`,
3498
- style: { minWidth: 26, padding: "2px 6px" },
3499
- children: [
3500
- "L",
3501
- lvl
3502
- ]
3503
- },
3504
- `s2_depth_${lvl}`
3505
- ))
3506
- ] }),
3507
- isFilterActive && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "S", onClick: clearFilter, children: "Clear Search & Filter" })
3508
- ] })
3532
+ isFilterActive && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "S", onClick: clearFilter, children: "Clear Search & Filter" })
3509
3533
  ] }),
3510
3534
  /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "flex-end", wrap: "wrap", children: [
3511
3535
  /* @__PURE__ */ jsx(Box, { style: { width: 280, minWidth: 230 }, children: /* @__PURE__ */ jsxs(Field.Root, { children: [
@@ -3536,7 +3560,8 @@ const BulkUpdater = () => {
3536
3560
  }
3537
3561
  },
3538
3562
  children: memoizedSearchComboboxOptions
3539
- }
3563
+ },
3564
+ `search-attr-${selectedCollectionUid}-${searchAttribute}`
3540
3565
  )
3541
3566
  ] }) }),
3542
3567
  /* @__PURE__ */ jsx(Box, { style: { width: 200, minWidth: 170 }, children: /* @__PURE__ */ jsxs(
@@ -3596,164 +3621,48 @@ const BulkUpdater = () => {
3596
3621
  )
3597
3622
  ] })
3598
3623
  ] }),
3599
- isFilterActive && /* @__PURE__ */ jsx(Flex, { gap: 2, alignItems: "center", marginTop: 1, children: /* @__PURE__ */ jsx(Badge, { backgroundColor: "success100", textColor: "success700", size: "S", children: "✓ Filter active: Showing only matching entries. All returned matching records are pre-selected." }) })
3600
- ] })
3601
- }
3602
- ),
3603
- collections.length > 0 && /* @__PURE__ */ jsxs(
3604
- Box,
3605
- {
3606
- padding: 4,
3607
- background: "neutral100",
3608
- hasRadius: true,
3609
- marginBottom: 4,
3610
- style: { width: "100%", maxWidth: "100%", boxSizing: "border-box", border: "1px solid #eaeaef" },
3611
- children: [
3612
- /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, marginBottom: 3, children: [
3613
- /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
3614
- /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", children: "Filter Visible Content Types:" }),
3615
- /* @__PURE__ */ jsxs(Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
3616
- filteredCollections.length,
3617
- " of ",
3618
- collections.length,
3619
- " visible"
3624
+ /* @__PURE__ */ jsxs(Box, { marginTop: 3, paddingTop: 3, style: { borderTop: "1px solid #dcdce4" }, children: [
3625
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: [
3626
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
3627
+ /* @__PURE__ */ jsx(
3628
+ Checkbox,
3629
+ {
3630
+ checked: useRegex,
3631
+ onCheckedChange: (checked) => setUseRegex(Boolean(checked)),
3632
+ children: /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: "Enable Regex Search & Replace" })
3633
+ }
3634
+ ),
3635
+ /* @__PURE__ */ jsx(
3636
+ Badge,
3637
+ {
3638
+ backgroundColor: useRegex ? "secondary100" : "neutral200",
3639
+ textColor: useRegex ? "secondary700" : "neutral600",
3640
+ size: "S",
3641
+ children: useRegex ? "REGEX SEARCH ACTIVE" : "REGEX OFF"
3642
+ }
3643
+ )
3620
3644
  ] }),
3621
- /* @__PURE__ */ jsxs(Badge, { backgroundColor: "success100", textColor: "success700", children: [
3622
- supportedCollections.filter((c) => visibleTypeUids[c.uid] !== false).length,
3623
- " of ",
3624
- supportedCollections.length,
3625
- " supported visible"
3626
- ] })
3627
- ] }),
3628
- /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
3629
- /* @__PURE__ */ jsx(Box, { style: { width: 220 }, children: /* @__PURE__ */ jsx(
3645
+ useRegex && /* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 260 }, children: /* @__PURE__ */ jsx(
3630
3646
  TextInput,
3631
3647
  {
3632
- placeholder: "Search types...",
3633
- size: "S",
3634
- value: typeVisibilitySearch,
3635
- onChange: (e) => setTypeVisibilitySearch(e.target.value)
3636
- }
3637
- ) }),
3638
- /* @__PURE__ */ jsxs(
3639
- Button,
3640
- {
3641
- variant: "secondary",
3642
- size: "S",
3643
- onClick: handleFocusOnlySupported,
3644
- children: [
3645
- "Show Supported Only (",
3646
- supportedCollections.length,
3647
- ")"
3648
- ]
3649
- }
3650
- ),
3651
- /* @__PURE__ */ jsx(
3652
- Button,
3653
- {
3654
- variant: "tertiary",
3655
- size: "S",
3656
- onClick: handleShowAllTypes,
3657
- children: "Show All"
3648
+ placeholder: `Regex pattern to search in "${effectiveSearchAttribute || "target"}" (e.g. \\b(OldName)\\b)...`,
3649
+ value: regexPattern,
3650
+ onChange: (e) => setRegexPattern(e.target.value)
3658
3651
  }
3659
- ),
3660
- /* @__PURE__ */ jsx(
3661
- Button,
3662
- {
3663
- variant: "tertiary",
3664
- size: "S",
3665
- onClick: handleHideAllTypes,
3666
- children: "Hide All"
3667
- }
3668
- )
3652
+ ) })
3653
+ ] }),
3654
+ useRegex && /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", marginTop: 1, children: [
3655
+ "Searches ",
3656
+ effectiveSearchAttribute ? /* @__PURE__ */ jsx("strong", { children: effectiveSearchAttribute }) : "target entries",
3657
+ " for text matching this regular expression pattern. Matches will be substituted with the Replacement Value configured in Section 1."
3669
3658
  ] })
3670
3659
  ] }),
3671
- /* @__PURE__ */ jsx(
3672
- Box,
3673
- {
3674
- style: {
3675
- maxHeight: 220,
3676
- overflowY: "auto",
3677
- padding: 8,
3678
- background: "#ffffff",
3679
- borderRadius: 4,
3680
- border: "1px solid #dcdce4",
3681
- display: "grid",
3682
- gridTemplateColumns: "repeat(auto-fill, minmax(260px, 1fr))",
3683
- gap: 8
3684
- },
3685
- children: collections.filter(
3686
- (col) => !typeVisibilitySearch.trim() || col.displayName.toLowerCase().includes(typeVisibilitySearch.toLowerCase().trim()) || col.uid.toLowerCase().includes(typeVisibilitySearch.toLowerCase().trim())
3687
- ).map((col) => {
3688
- const isVisible = visibleTypeUids[col.uid] !== false;
3689
- const hasAttr = selectedAttribute ? col.attributes.includes(selectedAttribute) : false;
3690
- return /* @__PURE__ */ jsx(
3691
- Box,
3692
- {
3693
- padding: 2,
3694
- paddingLeft: 3,
3695
- paddingRight: 3,
3696
- background: isVisible ? "#f0f0ff" : "#f6f6f9",
3697
- hasRadius: true,
3698
- style: {
3699
- border: isVisible ? "1px solid #4945ff" : "1px solid #eaeaef",
3700
- transition: "all 0.15s ease"
3701
- },
3702
- children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", gap: 2, children: [
3703
- /* @__PURE__ */ jsx(
3704
- Checkbox,
3705
- {
3706
- checked: isVisible,
3707
- onCheckedChange: (checked) => setVisibleTypeUids((prev) => ({ ...prev, [col.uid]: Boolean(checked) })),
3708
- children: /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: isVisible ? "bold" : "regular", children: col.displayName })
3709
- }
3710
- ),
3711
- /* @__PURE__ */ jsxs(Flex, { gap: 1, alignItems: "center", children: [
3712
- col.isSingleType ? /* @__PURE__ */ jsx(Badge, { backgroundColor: "secondary100", textColor: "secondary700", size: "S", children: "Single" }) : null,
3713
- hasAttr ? /* @__PURE__ */ jsx(Badge, { backgroundColor: "success100", textColor: "success700", size: "S", children: "✓ Supports" }) : /* @__PURE__ */ jsx(Badge, { backgroundColor: "neutral150", textColor: "neutral600", size: "S", children: "—" })
3714
- ] })
3715
- ] })
3716
- },
3717
- col.uid
3718
- );
3719
- })
3720
- }
3721
- )
3722
- ]
3660
+ isFilterActive && /* @__PURE__ */ jsx(Flex, { gap: 2, alignItems: "center", marginTop: 1, children: /* @__PURE__ */ jsx(Badge, { backgroundColor: "success100", textColor: "success700", size: "S", children: "✓ Filter active: Showing only matching entries. All returned matching records are pre-selected." }) })
3661
+ ] })
3723
3662
  }
3724
3663
  ),
3725
- /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, wrap: "wrap", gap: 3, children: [
3726
- collections.length > 1 && /* @__PURE__ */ jsx(Box, { style: { flex: 1, maxWidth: 360, minWidth: 220 }, children: /* @__PURE__ */ jsx(
3727
- TextInput,
3728
- {
3729
- label: "Search Content Types",
3730
- placeholder: "Search by name or UID...",
3731
- value: collectionSearch,
3732
- onChange: (e) => setCollectionSearch(e.target.value)
3733
- }
3734
- ) }),
3735
- /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", style: { marginLeft: "auto" }, children: [
3736
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Default page size (max 500):" }),
3737
- /* @__PURE__ */ jsx(Box, { style: { width: 130 }, children: /* @__PURE__ */ jsxs(
3738
- SingleSelect,
3739
- {
3740
- size: "S",
3741
- value: String(defaultPageSize),
3742
- onChange: (val) => handleGlobalPageSizeChange(Number(val)),
3743
- children: [
3744
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "5", children: "5 / page" }),
3745
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "10", children: "10 / page" }),
3746
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "25", children: "25 / page" }),
3747
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "50", children: "50 / page" }),
3748
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "100", children: "100 / page" }),
3749
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "250", children: "250 / page" }),
3750
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "500", children: "500 / page" })
3751
- ]
3752
- }
3753
- ) })
3754
- ] })
3755
- ] }),
3756
- filteredCollections.length === 0 ? /* @__PURE__ */ jsx(Box, { padding: 6, background: "neutral100", hasRadius: true, style: { textAlign: "center" }, children: /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "No content types match the visibility or search filters." }) }) : filteredCollections.map((col) => {
3664
+ !selectedCol ? /* @__PURE__ */ jsx(Box, { padding: 6, background: "neutral100", hasRadius: true, style: { textAlign: "center" }, children: /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Please select a collection type above to display entries." }) }) : (() => {
3665
+ const col = selectedCol;
3757
3666
  const pageState = collectionPages[col.uid];
3758
3667
  const entries = pageState ? pageState.entries : col.entries || [];
3759
3668
  const totalEntries = pageState ? pageState.total : col.totalEntries !== void 0 ? col.totalEntries : entries.length;
@@ -3842,21 +3751,174 @@ const BulkUpdater = () => {
3842
3751
  }
3843
3752
  ) })
3844
3753
  ] }),
3845
- /* @__PURE__ */ jsxs(
3846
- Button,
3847
- {
3848
- variant: openColumnPickerUid === col.uid ? "secondary" : "tertiary",
3849
- size: "S",
3850
- onClick: () => setOpenColumnPickerUid(openColumnPickerUid === col.uid ? null : col.uid),
3851
- children: [
3852
- "Columns (",
3853
- activeColumns.filter((c) => !c.isPermanent).length,
3854
- "/",
3855
- availableColumns.filter((c) => !c.isPermanent).length,
3856
- ")"
3857
- ]
3858
- }
3859
- ),
3754
+ /* @__PURE__ */ jsxs(Box, { style: { position: "relative", display: "inline-block" }, children: [
3755
+ /* @__PURE__ */ jsxs(
3756
+ Button,
3757
+ {
3758
+ variant: openColumnPickerUid === col.uid ? "secondary" : "tertiary",
3759
+ size: "S",
3760
+ startIcon: /* @__PURE__ */ jsx(Cog, {}),
3761
+ onClick: () => {
3762
+ setOpenColumnPickerUid(openColumnPickerUid === col.uid ? null : col.uid);
3763
+ setColumnSearchTerm("");
3764
+ },
3765
+ children: [
3766
+ "Columns (",
3767
+ activeColumns.filter((c) => !c.isPermanent).length,
3768
+ "/",
3769
+ availableColumns.filter((c) => !c.isPermanent).length,
3770
+ ")"
3771
+ ]
3772
+ }
3773
+ ),
3774
+ openColumnPickerUid === col.uid && /* @__PURE__ */ jsxs(
3775
+ Box,
3776
+ {
3777
+ style: {
3778
+ position: "absolute",
3779
+ top: "calc(100% + 6px)",
3780
+ right: 0,
3781
+ zIndex: 200,
3782
+ width: 320,
3783
+ maxHeight: 420,
3784
+ background: "#ffffff",
3785
+ borderRadius: 8,
3786
+ boxShadow: "0 10px 25px rgba(33, 33, 52, 0.18)",
3787
+ border: "1px solid #dcdce4",
3788
+ padding: 12,
3789
+ display: "flex",
3790
+ flexDirection: "column",
3791
+ gap: 8
3792
+ },
3793
+ children: [
3794
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
3795
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", children: "Configure Columns" }),
3796
+ /* @__PURE__ */ jsx(
3797
+ Button,
3798
+ {
3799
+ variant: "ghost",
3800
+ size: "S",
3801
+ onClick: () => setOpenColumnPickerUid(null),
3802
+ style: { padding: "2px 6px", height: "auto", minWidth: "unset" },
3803
+ children: "✕"
3804
+ }
3805
+ )
3806
+ ] }),
3807
+ /* @__PURE__ */ jsx(
3808
+ TextInput,
3809
+ {
3810
+ size: "S",
3811
+ placeholder: "Search columns...",
3812
+ value: columnSearchTerm,
3813
+ onChange: (e) => setColumnSearchTerm(e.target.value)
3814
+ }
3815
+ ),
3816
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", style: { borderBottom: "1px solid #f0f0f5", paddingBottom: 6 }, children: [
3817
+ /* @__PURE__ */ jsx(
3818
+ Button,
3819
+ {
3820
+ variant: "ghost",
3821
+ size: "S",
3822
+ onClick: () => showAllColumns(col.uid, availableColumns),
3823
+ style: { fontSize: 11, padding: 0 },
3824
+ children: "Show All"
3825
+ }
3826
+ ),
3827
+ /* @__PURE__ */ jsx(
3828
+ Button,
3829
+ {
3830
+ variant: "ghost",
3831
+ size: "S",
3832
+ onClick: () => {
3833
+ setCollectionColumnVisibility((prev) => ({
3834
+ ...prev,
3835
+ [col.uid]: []
3836
+ }));
3837
+ },
3838
+ style: { fontSize: 11, padding: 0 },
3839
+ children: "Hide All"
3840
+ }
3841
+ ),
3842
+ /* @__PURE__ */ jsx(
3843
+ Button,
3844
+ {
3845
+ variant: "ghost",
3846
+ size: "S",
3847
+ onClick: () => resetDefaultColumns(col.uid, availableColumns),
3848
+ style: { fontSize: 11, padding: 0 },
3849
+ children: "Reset"
3850
+ }
3851
+ )
3852
+ ] }),
3853
+ /* @__PURE__ */ jsx(
3854
+ Box,
3855
+ {
3856
+ style: {
3857
+ maxHeight: 250,
3858
+ overflowY: "auto",
3859
+ display: "flex",
3860
+ flexDirection: "column",
3861
+ gap: 4
3862
+ },
3863
+ children: availableColumns.filter((c) => {
3864
+ if (!columnSearchTerm.trim()) return true;
3865
+ const term = columnSearchTerm.toLowerCase().trim();
3866
+ return c.label.toLowerCase().includes(term) || c.key.toLowerCase().includes(term);
3867
+ }).map((c) => {
3868
+ const isChecked = activeColumns.some((ac) => ac.key === c.key);
3869
+ const isNested = c.key.includes(".");
3870
+ const depth = isNested ? c.key.split(".").length - 1 : 0;
3871
+ return /* @__PURE__ */ jsxs(
3872
+ Flex,
3873
+ {
3874
+ justifyContent: "space-between",
3875
+ alignItems: "center",
3876
+ style: {
3877
+ padding: "4px 6px",
3878
+ borderRadius: 4,
3879
+ background: isChecked ? "#f0f0ff" : "transparent"
3880
+ },
3881
+ children: [
3882
+ /* @__PURE__ */ jsx(Box, { style: { minWidth: 0, flex: 1, overflow: "hidden" }, children: /* @__PURE__ */ jsx(
3883
+ Checkbox,
3884
+ {
3885
+ checked: isChecked,
3886
+ disabled: c.isPermanent,
3887
+ onCheckedChange: () => {
3888
+ if (!c.isPermanent) {
3889
+ toggleColumn(col.uid, c.key, availableColumns);
3890
+ }
3891
+ },
3892
+ children: /* @__PURE__ */ jsx(
3893
+ Typography,
3894
+ {
3895
+ variant: "pi",
3896
+ ellipsis: true,
3897
+ style: {
3898
+ color: c.isPermanent ? "#8e8ea9" : "#272833",
3899
+ fontWeight: isChecked ? 600 : 400
3900
+ },
3901
+ title: c.key,
3902
+ children: c.label
3903
+ }
3904
+ )
3905
+ }
3906
+ ) }),
3907
+ c.isPermanent ? /* @__PURE__ */ jsx(Badge, { size: "S", backgroundColor: "neutral150", textColor: "neutral700", children: "Fixed" }) : depth > 0 ? /* @__PURE__ */ jsxs(Badge, { size: "S", backgroundColor: "secondary100", textColor: "secondary700", children: [
3908
+ "L",
3909
+ depth
3910
+ ] }) : null
3911
+ ]
3912
+ },
3913
+ c.key
3914
+ );
3915
+ })
3916
+ }
3917
+ )
3918
+ ]
3919
+ }
3920
+ )
3921
+ ] }),
3860
3922
  /* @__PURE__ */ jsx(
3861
3923
  Button,
3862
3924
  {
@@ -3914,66 +3976,6 @@ const BulkUpdater = () => {
3914
3976
  /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", children: col.isSingleType ? "1 single entry" : isFilterActive ? `${totalEntries} matching entries in DB` : `${totalEntries} entries` })
3915
3977
  ] })
3916
3978
  ] }),
3917
- openColumnPickerUid === col.uid && /* @__PURE__ */ jsx(
3918
- Box,
3919
- {
3920
- padding: 3,
3921
- marginBottom: 3,
3922
- background: "neutral100",
3923
- hasRadius: true,
3924
- borderColor: "neutral200",
3925
- style: { border: "1px solid #dcdce4" },
3926
- children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: [
3927
- /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "center", wrap: "wrap", children: [
3928
- /* @__PURE__ */ jsxs(Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral700", children: [
3929
- "Columns for ",
3930
- col.displayName,
3931
- ":"
3932
- ] }),
3933
- availableColumns.filter((c) => !c.isPermanent).map((c) => {
3934
- const isChecked = activeColumns.some((ac) => ac.key === c.key);
3935
- return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(
3936
- Checkbox,
3937
- {
3938
- checked: isChecked,
3939
- onCheckedChange: () => toggleColumn(col.uid, c.key, availableColumns),
3940
- children: /* @__PURE__ */ jsx(Typography, { variant: "pi", children: c.label })
3941
- }
3942
- ) }, c.key);
3943
- })
3944
- ] }),
3945
- /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3946
- /* @__PURE__ */ jsx(
3947
- Button,
3948
- {
3949
- variant: "tertiary",
3950
- size: "S",
3951
- onClick: () => showAllColumns(col.uid, availableColumns),
3952
- children: "Show All"
3953
- }
3954
- ),
3955
- /* @__PURE__ */ jsx(
3956
- Button,
3957
- {
3958
- variant: "tertiary",
3959
- size: "S",
3960
- onClick: () => resetDefaultColumns(col.uid, availableColumns),
3961
- children: "Reset"
3962
- }
3963
- ),
3964
- /* @__PURE__ */ jsx(
3965
- Button,
3966
- {
3967
- variant: "ghost",
3968
- size: "S",
3969
- onClick: () => setOpenColumnPickerUid(null),
3970
- children: "Close"
3971
- }
3972
- )
3973
- ] })
3974
- ] })
3975
- }
3976
- ),
3977
3979
  /* @__PURE__ */ jsx(Box, { className: "bulk-updater-table-container", style: { width: "100%", maxWidth: "100%", minWidth: 0, overflowX: "auto" }, children: /* @__PURE__ */ jsxs(Table, { colCount: activeColumns.length, rowCount: visibleEntries.length + 1, style: { width: "100%" }, children: [
3978
3980
  /* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsx(Tr, { children: activeColumns.map(
3979
3981
  (colDef) => renderHeaderCell(colDef, col, currentSort, selectedAttribute, searchAttribute, handleHeaderSortClick)
@@ -4085,7 +4087,7 @@ const BulkUpdater = () => {
4085
4087
  },
4086
4088
  col.uid
4087
4089
  );
4088
- })
4090
+ })()
4089
4091
  ]
4090
4092
  }
4091
4093
  ),
@@ -4970,4 +4972,4 @@ export {
4970
4972
  getAvailableColumnsForCollection,
4971
4973
  getNestedValue
4972
4974
  };
4973
- //# sourceMappingURL=BulkUpdater-DowSmA56.mjs.map
4975
+ //# sourceMappingURL=BulkUpdater-D812YfOE.mjs.map