strapi-plugin-bulk-updater 1.2.12 → 1.3.0
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.
- package/README.md +10 -4
- package/dist/_chunks/{BulkUpdater-CQ9zGdat.js → BulkUpdater-CRvwX326.js} +344 -39
- package/dist/_chunks/BulkUpdater-CRvwX326.js.map +1 -0
- package/dist/_chunks/{BulkUpdater-DvCSr1vU.mjs → BulkUpdater-DowSmA56.mjs} +344 -39
- package/dist/_chunks/BulkUpdater-DowSmA56.mjs.map +1 -0
- package/dist/_chunks/{SettingsPage-BRGTrW9H.js → SettingsPage-BAvuOb7V.js} +2 -2
- package/dist/_chunks/{SettingsPage-BRGTrW9H.js.map → SettingsPage-BAvuOb7V.js.map} +1 -1
- package/dist/_chunks/{SettingsPage-ieu-Out8.mjs → SettingsPage-BWo4l6Gd.mjs} +2 -2
- package/dist/_chunks/{SettingsPage-ieu-Out8.mjs.map → SettingsPage-BWo4l6Gd.mjs.map} +1 -1
- package/dist/_chunks/{ValidationRulesTab-BoqhoTjo.js → ValidationRulesTab-BLZWPGrZ.js} +3 -2
- package/dist/_chunks/{ValidationRulesTab-BoqhoTjo.js.map → ValidationRulesTab-BLZWPGrZ.js.map} +1 -1
- package/dist/_chunks/{ValidationRulesTab-BGcqM9cN.mjs → ValidationRulesTab-Dt53h-Ke.mjs} +3 -2
- package/dist/_chunks/{ValidationRulesTab-BGcqM9cN.mjs.map → ValidationRulesTab-Dt53h-Ke.mjs.map} +1 -1
- package/dist/admin/index.js +2 -2
- package/dist/admin/index.mjs +2 -2
- package/dist/server/index.js +480 -170
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +480 -170
- package/dist/server/index.mjs.map +1 -1
- package/package.json +3 -3
- package/dist/_chunks/BulkUpdater-CQ9zGdat.js.map +0 -1
- package/dist/_chunks/BulkUpdater-DvCSr1vU.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-
|
|
8
|
+
const ValidationRulesTab = require("./ValidationRulesTab-BLZWPGrZ.js");
|
|
9
9
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
10
10
|
const React__default = /* @__PURE__ */ _interopDefault(React);
|
|
11
11
|
function CsvManagerTab() {
|
|
@@ -118,7 +118,8 @@ function CsvManagerTab() {
|
|
|
118
118
|
});
|
|
119
119
|
return;
|
|
120
120
|
}
|
|
121
|
-
const
|
|
121
|
+
const safeContent = content.startsWith("\uFEFF") ? content : "\uFEFF" + content;
|
|
122
|
+
const blob = new Blob([safeContent], { type: "text/csv;charset=utf-8;" });
|
|
122
123
|
const url = URL.createObjectURL(blob);
|
|
123
124
|
const link = document.createElement("a");
|
|
124
125
|
link.href = url;
|
|
@@ -507,7 +508,9 @@ function formatVal(v) {
|
|
|
507
508
|
return String(v);
|
|
508
509
|
}
|
|
509
510
|
function downloadFile(content, filename, mimeType = "text/csv;charset=utf-8;") {
|
|
510
|
-
const
|
|
511
|
+
const isCsv = mimeType.includes("csv") || filename.endsWith(".csv");
|
|
512
|
+
const safeContent = isCsv && !content.startsWith("\uFEFF") ? "\uFEFF" + content : content;
|
|
513
|
+
const blob = new Blob([safeContent], { type: mimeType });
|
|
511
514
|
const url = URL.createObjectURL(blob);
|
|
512
515
|
const a = document.createElement("a");
|
|
513
516
|
a.href = url;
|
|
@@ -629,7 +632,7 @@ function ProcessingDashboardModal({
|
|
|
629
632
|
].join(",")
|
|
630
633
|
)
|
|
631
634
|
];
|
|
632
|
-
downloadFile(csvRows.join("\n"), `bulk_update_report_${targetId}.csv`);
|
|
635
|
+
downloadFile(csvRows.join("\r\n") + "\r\n", `bulk_update_report_${targetId}.csv`);
|
|
633
636
|
toggleNotification({
|
|
634
637
|
type: "success",
|
|
635
638
|
message: "Execution report downloaded successfully"
|
|
@@ -1455,7 +1458,7 @@ function getNestedValue(obj, path) {
|
|
|
1455
1458
|
if (!path.includes(".")) return obj[path];
|
|
1456
1459
|
return path.split(".").reduce((acc, part) => acc && typeof acc === "object" ? acc[part] : void 0, obj);
|
|
1457
1460
|
}
|
|
1458
|
-
function getAvailableColumnsForCollection(col, selectedAttribute) {
|
|
1461
|
+
function getAvailableColumnsForCollection(col, selectedAttribute, searchAttribute) {
|
|
1459
1462
|
const cols = [
|
|
1460
1463
|
{
|
|
1461
1464
|
key: "select",
|
|
@@ -1472,7 +1475,7 @@ function getAvailableColumnsForCollection(col, selectedAttribute) {
|
|
|
1472
1475
|
];
|
|
1473
1476
|
const candidateAttrs = col.attributes || [];
|
|
1474
1477
|
candidateAttrs.forEach((attr) => {
|
|
1475
|
-
if (attr === "name" || attr === "title" || attr === selectedAttribute) {
|
|
1478
|
+
if (attr === "name" || attr === "title" || attr === selectedAttribute || searchAttribute && attr === searchAttribute) {
|
|
1476
1479
|
return;
|
|
1477
1480
|
}
|
|
1478
1481
|
const formattedLabel = attr.includes(".") ? attr.split(".").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" > ") : attr.charAt(0).toUpperCase() + attr.slice(1);
|
|
@@ -1483,9 +1486,19 @@ function getAvailableColumnsForCollection(col, selectedAttribute) {
|
|
|
1483
1486
|
defaultVisible: ["department", "email", "location"].includes(attr)
|
|
1484
1487
|
});
|
|
1485
1488
|
});
|
|
1489
|
+
if (searchAttribute && searchAttribute !== selectedAttribute) {
|
|
1490
|
+
const formattedSearchLabel = searchAttribute.includes(".") ? searchAttribute.split(".").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" > ") : searchAttribute;
|
|
1491
|
+
cols.push({
|
|
1492
|
+
key: "search_attr",
|
|
1493
|
+
label: `Search: "${formattedSearchLabel}"`,
|
|
1494
|
+
sortable: !col.isSingleType && col.attributes.includes(searchAttribute),
|
|
1495
|
+
defaultVisible: true
|
|
1496
|
+
});
|
|
1497
|
+
}
|
|
1498
|
+
const targetLabel = searchAttribute && searchAttribute !== selectedAttribute ? `Update Target: "${selectedAttribute}"` : `Current "${selectedAttribute}"`;
|
|
1486
1499
|
cols.push({
|
|
1487
1500
|
key: "target_attr",
|
|
1488
|
-
label:
|
|
1501
|
+
label: targetLabel,
|
|
1489
1502
|
sortable: !col.isSingleType && col.attributes.includes(selectedAttribute),
|
|
1490
1503
|
defaultVisible: true
|
|
1491
1504
|
});
|
|
@@ -1497,17 +1510,19 @@ function getAvailableColumnsForCollection(col, selectedAttribute) {
|
|
|
1497
1510
|
});
|
|
1498
1511
|
return cols;
|
|
1499
1512
|
}
|
|
1500
|
-
function renderHeaderCell(colDef, col, currentSort, selectedAttribute, onSort) {
|
|
1513
|
+
function renderHeaderCell(colDef, col, currentSort, selectedAttribute, searchAttribute, onSort) {
|
|
1501
1514
|
if (colDef.key === "select") {
|
|
1502
1515
|
return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { width: 48 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Select" }) }, "select");
|
|
1503
1516
|
}
|
|
1504
|
-
const isCurrentSorted = colDef.key === "identifier" && (currentSort.field === "name" || currentSort.field === "title") || colDef.key === "target_attr" && currentSort.field === selectedAttribute || colDef.key.startsWith("attr_") && currentSort.field === colDef.key.replace("attr_", "");
|
|
1517
|
+
const isCurrentSorted = colDef.key === "identifier" && (currentSort.field === "name" || currentSort.field === "title") || colDef.key === "search_attr" && currentSort.field === searchAttribute || colDef.key === "target_attr" && currentSort.field === selectedAttribute || colDef.key.startsWith("attr_") && currentSort.field === colDef.key.replace("attr_", "");
|
|
1505
1518
|
const sortArrow = isCurrentSorted ? currentSort.order === "asc" ? "▲" : "▼" : "⇅";
|
|
1506
1519
|
const handleClick = () => {
|
|
1507
1520
|
if (!colDef.sortable) return;
|
|
1508
1521
|
if (colDef.key === "identifier") {
|
|
1509
1522
|
const field = col.attributes.includes("name") ? "name" : col.attributes.includes("title") ? "title" : "createdAt";
|
|
1510
1523
|
onSort(col.uid, field);
|
|
1524
|
+
} else if (colDef.key === "search_attr") {
|
|
1525
|
+
onSort(col.uid, searchAttribute);
|
|
1511
1526
|
} else if (colDef.key === "target_attr") {
|
|
1512
1527
|
onSort(col.uid, selectedAttribute);
|
|
1513
1528
|
} else if (colDef.key.startsWith("attr_")) {
|
|
@@ -1543,7 +1558,7 @@ function formatDisplayValue(val) {
|
|
|
1543
1558
|
}
|
|
1544
1559
|
return String(val);
|
|
1545
1560
|
}
|
|
1546
|
-
function renderCellContent(colDef, entry, col, selectedAttribute, isMatch, hasAttr, isSelected, onToggleSelect) {
|
|
1561
|
+
function renderCellContent(colDef, entry, col, selectedAttribute, searchAttribute, isMatch, hasAttr, isSelected, onToggleSelect) {
|
|
1547
1562
|
switch (colDef.key) {
|
|
1548
1563
|
case "select":
|
|
1549
1564
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1560,6 +1575,27 @@ function renderCellContent(colDef, entry, col, selectedAttribute, isMatch, hasAt
|
|
|
1560
1575
|
entry.email && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", display: "block", children: entry.email })
|
|
1561
1576
|
] });
|
|
1562
1577
|
}
|
|
1578
|
+
case "search_attr": {
|
|
1579
|
+
const val = getNestedValue(entry, searchAttribute);
|
|
1580
|
+
const displayStr = formatDisplayValue(val);
|
|
1581
|
+
const isUnset = displayStr === "(empty / not set)" || displayStr === "(empty / unlinked)";
|
|
1582
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1583
|
+
designSystem.Typography,
|
|
1584
|
+
{
|
|
1585
|
+
textColor: isUnset ? "neutral500" : "primary700",
|
|
1586
|
+
fontWeight: "semiBold",
|
|
1587
|
+
style: {
|
|
1588
|
+
fontStyle: isUnset ? "italic" : "normal",
|
|
1589
|
+
display: "inline-block",
|
|
1590
|
+
maxWidth: 260,
|
|
1591
|
+
overflow: "hidden",
|
|
1592
|
+
textOverflow: "ellipsis",
|
|
1593
|
+
whiteSpace: "nowrap"
|
|
1594
|
+
},
|
|
1595
|
+
children: displayStr
|
|
1596
|
+
}
|
|
1597
|
+
);
|
|
1598
|
+
}
|
|
1563
1599
|
case "target_attr": {
|
|
1564
1600
|
const val = getNestedValue(entry, selectedAttribute);
|
|
1565
1601
|
const displayStr = formatDisplayValue(val);
|
|
@@ -1650,6 +1686,7 @@ const BulkUpdater = () => {
|
|
|
1650
1686
|
const [showPublishConfirmModal, setShowPublishConfirmModal] = React.useState(false);
|
|
1651
1687
|
const [publishing, setPublishing] = React.useState(false);
|
|
1652
1688
|
const [publishMakerNotesInput, setPublishMakerNotesInput] = React.useState("");
|
|
1689
|
+
const [bulkExporting, setBulkExporting] = React.useState(false);
|
|
1653
1690
|
const MODELS_CACHE_KEY = "strapi_bulk_updater_models_cache";
|
|
1654
1691
|
const LAST_ATTR_KEY = "strapi_bulk_updater_last_attr";
|
|
1655
1692
|
const getInitialAttribute = () => {
|
|
@@ -1683,10 +1720,34 @@ const BulkUpdater = () => {
|
|
|
1683
1720
|
};
|
|
1684
1721
|
const [useRegex, setUseRegex] = React.useState(false);
|
|
1685
1722
|
const [regexPattern, setRegexPattern] = React.useState("");
|
|
1723
|
+
const DEPTH_STORAGE_KEY = "strapi_bulk_updater_component_depth";
|
|
1724
|
+
const [componentDepth, setComponentDepth] = React.useState(() => {
|
|
1725
|
+
try {
|
|
1726
|
+
const stored = localStorage.getItem(DEPTH_STORAGE_KEY);
|
|
1727
|
+
if (stored) {
|
|
1728
|
+
const val = Number(stored);
|
|
1729
|
+
if (val >= 1 && val <= 5) return val;
|
|
1730
|
+
}
|
|
1731
|
+
} catch {
|
|
1732
|
+
}
|
|
1733
|
+
return 3;
|
|
1734
|
+
});
|
|
1735
|
+
const handleComponentDepthChange = (depth) => {
|
|
1736
|
+
const capped = Math.min(5, Math.max(1, depth));
|
|
1737
|
+
setComponentDepth(capped);
|
|
1738
|
+
try {
|
|
1739
|
+
localStorage.setItem(DEPTH_STORAGE_KEY, String(capped));
|
|
1740
|
+
} catch {
|
|
1741
|
+
}
|
|
1742
|
+
};
|
|
1743
|
+
const [searchAttribute, setSearchAttribute] = React.useState("");
|
|
1744
|
+
const [searchAttributeInput, setSearchAttributeInput] = React.useState("");
|
|
1745
|
+
const [attributesMeta, setAttributesMeta] = React.useState({});
|
|
1746
|
+
const effectiveSearchAttribute = searchAttribute || selectedAttribute;
|
|
1686
1747
|
const [filterOperator, setFilterOperator] = React.useState("none");
|
|
1687
1748
|
const [filterQuery, setFilterQuery] = React.useState("");
|
|
1688
1749
|
const [entrySearch, setEntrySearch] = React.useState("");
|
|
1689
|
-
const isFilterActive = filterOperator !== "none" && (["empty", "not_empty"].includes(filterOperator) || Boolean(filterQuery.trim())) || Boolean(entrySearch.trim());
|
|
1750
|
+
const isFilterActive = Boolean(searchAttribute && searchAttribute !== selectedAttribute) || filterOperator !== "none" && (["empty", "not_empty"].includes(filterOperator) || Boolean(filterQuery.trim())) || Boolean(entrySearch.trim());
|
|
1690
1751
|
const [lastResults, setLastResults] = React.useState(null);
|
|
1691
1752
|
const [updateSummary, setUpdateSummary] = React.useState(null);
|
|
1692
1753
|
const [collectionPages, setCollectionPages] = React.useState({});
|
|
@@ -1766,6 +1827,9 @@ const BulkUpdater = () => {
|
|
|
1766
1827
|
if (data.relationsMeta) {
|
|
1767
1828
|
setRelationsMeta(data.relationsMeta);
|
|
1768
1829
|
}
|
|
1830
|
+
if (data.attributesMeta) {
|
|
1831
|
+
setAttributesMeta(data.attributesMeta);
|
|
1832
|
+
}
|
|
1769
1833
|
let storedAttr = null;
|
|
1770
1834
|
try {
|
|
1771
1835
|
storedAttr = localStorage.getItem(LAST_ATTR_KEY);
|
|
@@ -1813,7 +1877,7 @@ const BulkUpdater = () => {
|
|
|
1813
1877
|
setLoading(false);
|
|
1814
1878
|
}
|
|
1815
1879
|
};
|
|
1816
|
-
const loadCollectionPage = async (colUid, targetPage, customPageSize, customSortField, customSortOrder, overrideFilterOp, overrideFilterQuery, overrideEntrySearch, autoSelectReturned = true) => {
|
|
1880
|
+
const loadCollectionPage = async (colUid, targetPage, customPageSize, customSortField, customSortOrder, overrideFilterOp, overrideFilterQuery, overrideEntrySearch, autoSelectReturned = true, overrideSearchAttr) => {
|
|
1817
1881
|
const currentPageState = collectionPages[colUid];
|
|
1818
1882
|
const size = customPageSize || currentPageState?.pageSize || defaultPageSize;
|
|
1819
1883
|
const currentSort = collectionSorts[colUid] || { field: "createdAt", order: "desc" };
|
|
@@ -1822,6 +1886,7 @@ const BulkUpdater = () => {
|
|
|
1822
1886
|
const op = overrideFilterOp !== void 0 ? overrideFilterOp : filterOperator;
|
|
1823
1887
|
const q = overrideFilterQuery !== void 0 ? overrideFilterQuery : filterQuery;
|
|
1824
1888
|
const s = overrideEntrySearch !== void 0 ? overrideEntrySearch : entrySearch;
|
|
1889
|
+
const searchAttr = overrideSearchAttr !== void 0 ? overrideSearchAttr : searchAttribute;
|
|
1825
1890
|
setCollectionPages((prev) => ({
|
|
1826
1891
|
...prev,
|
|
1827
1892
|
[colUid]: {
|
|
@@ -1835,6 +1900,9 @@ const BulkUpdater = () => {
|
|
|
1835
1900
|
pageSize: String(size)
|
|
1836
1901
|
});
|
|
1837
1902
|
if (selectedAttribute) params.set("attribute", selectedAttribute);
|
|
1903
|
+
if (searchAttr && searchAttr !== selectedAttribute) {
|
|
1904
|
+
params.set("searchAttribute", searchAttr);
|
|
1905
|
+
}
|
|
1838
1906
|
if (op && op !== "none") {
|
|
1839
1907
|
params.set("filterOperator", op);
|
|
1840
1908
|
if (q) params.set("filterQuery", q);
|
|
@@ -1864,7 +1932,7 @@ const BulkUpdater = () => {
|
|
|
1864
1932
|
loading: false
|
|
1865
1933
|
}
|
|
1866
1934
|
}));
|
|
1867
|
-
const isFilterActiveNow = op !== "none" && (["empty", "not_empty"].includes(op) || Boolean(q.trim())) || Boolean(s.trim());
|
|
1935
|
+
const isFilterActiveNow = Boolean(searchAttr && searchAttr !== selectedAttribute) || op !== "none" && (["empty", "not_empty"].includes(op) || Boolean(q.trim())) || Boolean(s.trim());
|
|
1868
1936
|
if (isFilterActiveNow && autoSelectReturned) {
|
|
1869
1937
|
setSelectedEntries((prev) => {
|
|
1870
1938
|
const next = { ...prev };
|
|
@@ -1951,13 +2019,14 @@ const BulkUpdater = () => {
|
|
|
1951
2019
|
}
|
|
1952
2020
|
const timer = setTimeout(() => {
|
|
1953
2021
|
if (collections.length > 0) {
|
|
1954
|
-
const isFilterActiveNow = filterOperator !== "none" && (["empty", "not_empty"].includes(filterOperator) || Boolean(filterQuery.trim())) || Boolean(entrySearch.trim());
|
|
2022
|
+
const isFilterActiveNow = Boolean(searchAttribute && searchAttribute !== selectedAttribute) || filterOperator !== "none" && (["empty", "not_empty"].includes(filterOperator) || Boolean(filterQuery.trim())) || Boolean(entrySearch.trim());
|
|
1955
2023
|
if (isFilterActiveNow) {
|
|
1956
2024
|
setSelectedEntries({});
|
|
1957
2025
|
}
|
|
2026
|
+
const targetSearchAttr = searchAttribute || selectedAttribute;
|
|
1958
2027
|
const targetCols = collections.filter((col) => {
|
|
1959
2028
|
if (visibleTypeUids[col.uid] === false) return false;
|
|
1960
|
-
if (filterOperator !== "none" && !col.attributes.includes(
|
|
2029
|
+
if (filterOperator !== "none" && targetSearchAttr && !col.attributes.includes(targetSearchAttr)) return false;
|
|
1961
2030
|
return true;
|
|
1962
2031
|
});
|
|
1963
2032
|
targetCols.forEach((col) => {
|
|
@@ -1970,13 +2039,14 @@ const BulkUpdater = () => {
|
|
|
1970
2039
|
filterOperator,
|
|
1971
2040
|
filterQuery,
|
|
1972
2041
|
entrySearch,
|
|
1973
|
-
true
|
|
2042
|
+
true,
|
|
2043
|
+
searchAttribute
|
|
1974
2044
|
);
|
|
1975
2045
|
});
|
|
1976
2046
|
}
|
|
1977
2047
|
}, 350);
|
|
1978
2048
|
return () => clearTimeout(timer);
|
|
1979
|
-
}, [filterOperator, filterQuery, entrySearch]);
|
|
2049
|
+
}, [searchAttribute, filterOperator, filterQuery, entrySearch]);
|
|
1980
2050
|
const KEY_SEP = "@@@";
|
|
1981
2051
|
const handleAttributeChange = (newAttr) => {
|
|
1982
2052
|
if (newAttr !== selectedAttribute) {
|
|
@@ -2045,39 +2115,119 @@ const BulkUpdater = () => {
|
|
|
2045
2115
|
setVisibleTypeUids(newVis);
|
|
2046
2116
|
};
|
|
2047
2117
|
const [attributeSearch, setAttributeSearch] = React.useState("");
|
|
2118
|
+
const [searchAttributeSearch, setSearchAttributeSearch] = React.useState("");
|
|
2119
|
+
const getAttrDepth = (attr) => attributesMeta[attr]?.depth ?? (attr.includes(".") ? attr.split(".").length - 1 : 0);
|
|
2120
|
+
const depthFilteredAttributes = React.useMemo(() => {
|
|
2121
|
+
return attributes.filter((attr) => getAttrDepth(attr) <= componentDepth);
|
|
2122
|
+
}, [attributes, componentDepth, attributesMeta]);
|
|
2048
2123
|
const filteredAttributes = React.useMemo(() => {
|
|
2049
2124
|
if (!attributeSearch.trim()) {
|
|
2050
|
-
return
|
|
2125
|
+
return depthFilteredAttributes;
|
|
2051
2126
|
}
|
|
2052
2127
|
const q = attributeSearch.toLowerCase().trim();
|
|
2053
2128
|
const cleanQ = q.replace(/[^a-z0-9]/g, "");
|
|
2054
|
-
return
|
|
2129
|
+
return depthFilteredAttributes.filter((attr) => {
|
|
2055
2130
|
const lower = attr.toLowerCase();
|
|
2056
2131
|
const cleanLower = lower.replace(/[^a-z0-9]/g, "");
|
|
2057
2132
|
const rel = relationsMeta[attr];
|
|
2058
2133
|
const relTarget = (rel?.targetDisplayName || "").toLowerCase();
|
|
2059
2134
|
return lower.includes(q) || cleanLower.includes(cleanQ) || relTarget.includes(q);
|
|
2060
2135
|
});
|
|
2061
|
-
}, [
|
|
2136
|
+
}, [depthFilteredAttributes, attributeSearch, relationsMeta]);
|
|
2137
|
+
const filteredSearchAttributes = React.useMemo(() => {
|
|
2138
|
+
if (!searchAttributeSearch.trim()) {
|
|
2139
|
+
return depthFilteredAttributes;
|
|
2140
|
+
}
|
|
2141
|
+
const q = searchAttributeSearch.toLowerCase().trim();
|
|
2142
|
+
const cleanQ = q.replace(/[^a-z0-9]/g, "");
|
|
2143
|
+
return depthFilteredAttributes.filter((attr) => {
|
|
2144
|
+
const lower = attr.toLowerCase();
|
|
2145
|
+
const cleanLower = lower.replace(/[^a-z0-9]/g, "");
|
|
2146
|
+
const rel = relationsMeta[attr];
|
|
2147
|
+
const relTarget = (rel?.targetDisplayName || "").toLowerCase();
|
|
2148
|
+
return lower.includes(q) || cleanLower.includes(cleanQ) || relTarget.includes(q);
|
|
2149
|
+
});
|
|
2150
|
+
}, [depthFilteredAttributes, searchAttributeSearch, relationsMeta]);
|
|
2062
2151
|
const memoizedComboboxOptions = React.useMemo(() => {
|
|
2063
2152
|
if (filteredAttributes.length === 0) {
|
|
2064
2153
|
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: [
|
|
2065
2154
|
'No matching attributes found for "',
|
|
2066
2155
|
attributeSearch,
|
|
2067
|
-
'"'
|
|
2156
|
+
'" (Depth L',
|
|
2157
|
+
componentDepth,
|
|
2158
|
+
")"
|
|
2068
2159
|
] }) });
|
|
2069
2160
|
}
|
|
2070
2161
|
return filteredAttributes.map((attr) => {
|
|
2071
2162
|
const rel = relationsMeta[attr];
|
|
2163
|
+
const depth = getAttrDepth(attr);
|
|
2072
2164
|
return /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: attr, textValue: attr, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", width: "100%", gap: 2, children: [
|
|
2073
2165
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: attr === selectedAttribute ? "bold" : "regular", children: attr }),
|
|
2074
2166
|
rel ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", size: "S", children: [
|
|
2075
2167
|
"relation ➔ ",
|
|
2076
2168
|
rel.targetDisplayName
|
|
2077
|
-
] }) :
|
|
2169
|
+
] }) : depth > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: depth === 1 ? "neutral150" : depth === 2 ? "primary100" : "secondary100", textColor: depth === 1 ? "neutral700" : depth === 2 ? "primary700" : "secondary700", size: "S", children: [
|
|
2170
|
+
"L",
|
|
2171
|
+
depth,
|
|
2172
|
+
" component"
|
|
2173
|
+
] }) : null
|
|
2078
2174
|
] }) }, attr);
|
|
2079
2175
|
});
|
|
2080
|
-
}, [filteredAttributes, relationsMeta, selectedAttribute, attributeSearch]);
|
|
2176
|
+
}, [filteredAttributes, relationsMeta, selectedAttribute, attributeSearch, componentDepth, attributesMeta]);
|
|
2177
|
+
const memoizedSearchComboboxOptions = React.useMemo(() => {
|
|
2178
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2179
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2180
|
+
designSystem.ComboboxOption,
|
|
2181
|
+
{
|
|
2182
|
+
value: "__default__",
|
|
2183
|
+
textValue: `Same as Update Attribute (${selectedAttribute || "None"})`,
|
|
2184
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", width: "100%", gap: 2, children: [
|
|
2185
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "omega", fontStyle: "italic", textColor: "neutral600", children: [
|
|
2186
|
+
"-- Same as Update Attribute (",
|
|
2187
|
+
selectedAttribute || "None",
|
|
2188
|
+
") --"
|
|
2189
|
+
] }),
|
|
2190
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "neutral150", textColor: "neutral600", size: "S", children: "Default" })
|
|
2191
|
+
] })
|
|
2192
|
+
}
|
|
2193
|
+
),
|
|
2194
|
+
filteredSearchAttributes.map((attr) => {
|
|
2195
|
+
const rel = relationsMeta[attr];
|
|
2196
|
+
const depth = getAttrDepth(attr);
|
|
2197
|
+
return /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: attr, textValue: attr, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", width: "100%", gap: 2, children: [
|
|
2198
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: attr === searchAttribute ? "bold" : "regular", children: attr }),
|
|
2199
|
+
rel ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", size: "S", children: [
|
|
2200
|
+
"relation ➔ ",
|
|
2201
|
+
rel.targetDisplayName
|
|
2202
|
+
] }) : depth > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: depth === 1 ? "neutral150" : depth === 2 ? "primary100" : "secondary100", textColor: depth === 1 ? "neutral700" : depth === 2 ? "primary700" : "secondary700", size: "S", children: [
|
|
2203
|
+
"L",
|
|
2204
|
+
depth,
|
|
2205
|
+
" component"
|
|
2206
|
+
] }) : null
|
|
2207
|
+
] }) }, `search_${attr}`);
|
|
2208
|
+
})
|
|
2209
|
+
] });
|
|
2210
|
+
}, [filteredSearchAttributes, relationsMeta, searchAttribute, selectedAttribute, componentDepth, attributesMeta]);
|
|
2211
|
+
const handleSearchAttributeChange = (newAttr) => {
|
|
2212
|
+
const finalAttr = newAttr === "__default__" ? "" : newAttr;
|
|
2213
|
+
setSearchAttribute(finalAttr);
|
|
2214
|
+
if (collections.length > 0) {
|
|
2215
|
+
collections.forEach((col) => {
|
|
2216
|
+
loadCollectionPage(
|
|
2217
|
+
col.uid,
|
|
2218
|
+
1,
|
|
2219
|
+
void 0,
|
|
2220
|
+
void 0,
|
|
2221
|
+
void 0,
|
|
2222
|
+
filterOperator,
|
|
2223
|
+
filterQuery,
|
|
2224
|
+
entrySearch,
|
|
2225
|
+
true,
|
|
2226
|
+
finalAttr
|
|
2227
|
+
);
|
|
2228
|
+
});
|
|
2229
|
+
}
|
|
2230
|
+
};
|
|
2081
2231
|
const handleGlobalPageSizeChange = (newSize) => {
|
|
2082
2232
|
const capped = Math.min(500, Math.max(1, newSize));
|
|
2083
2233
|
setDefaultPageSize(capped);
|
|
@@ -2168,12 +2318,14 @@ const BulkUpdater = () => {
|
|
|
2168
2318
|
setSelectedEntries(updated);
|
|
2169
2319
|
};
|
|
2170
2320
|
const clearFilter = () => {
|
|
2321
|
+
setSearchAttribute("");
|
|
2322
|
+
setSearchAttributeSearch("");
|
|
2171
2323
|
setFilterOperator("none");
|
|
2172
2324
|
setFilterQuery("");
|
|
2173
2325
|
setEntrySearch("");
|
|
2174
2326
|
setSelectedEntries({});
|
|
2175
2327
|
collections.forEach((col) => {
|
|
2176
|
-
loadCollectionPage(col.uid, 1, void 0, void 0, void 0, "none", "", "", false);
|
|
2328
|
+
loadCollectionPage(col.uid, 1, void 0, void 0, void 0, "none", "", "", false, "");
|
|
2177
2329
|
});
|
|
2178
2330
|
};
|
|
2179
2331
|
const isCollectionFullySelected = (col) => {
|
|
@@ -2435,6 +2587,57 @@ const BulkUpdater = () => {
|
|
|
2435
2587
|
setMakerNotesInput("");
|
|
2436
2588
|
}
|
|
2437
2589
|
};
|
|
2590
|
+
const handleBulkExportSelected = async () => {
|
|
2591
|
+
const targets = [];
|
|
2592
|
+
Object.keys(selectedEntries).forEach((key) => {
|
|
2593
|
+
if (selectedEntries[key]) {
|
|
2594
|
+
const parts = key.split(KEY_SEP);
|
|
2595
|
+
if (parts.length === 2) {
|
|
2596
|
+
targets.push({ uid: parts[0], documentId: parts[1] });
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
});
|
|
2600
|
+
if (targets.length === 0) {
|
|
2601
|
+
toggleNotification({
|
|
2602
|
+
type: "warning",
|
|
2603
|
+
message: "Please select at least one entry to export"
|
|
2604
|
+
});
|
|
2605
|
+
return;
|
|
2606
|
+
}
|
|
2607
|
+
try {
|
|
2608
|
+
setBulkExporting(true);
|
|
2609
|
+
const res = await post("/bulk-updater/csv/export-bulk", { selectedEntries: targets });
|
|
2610
|
+
const csvStr = res.data?.csvContent || (typeof res.data === "string" ? res.data : "");
|
|
2611
|
+
if (!csvStr) {
|
|
2612
|
+
toggleNotification({
|
|
2613
|
+
type: "warning",
|
|
2614
|
+
message: "No CSV content returned for selected entries"
|
|
2615
|
+
});
|
|
2616
|
+
return;
|
|
2617
|
+
}
|
|
2618
|
+
const safeContent = csvStr.startsWith("\uFEFF") ? csvStr : "\uFEFF" + csvStr;
|
|
2619
|
+
const blob = new Blob([safeContent], { type: "text/csv;charset=utf-8;" });
|
|
2620
|
+
const url = URL.createObjectURL(blob);
|
|
2621
|
+
const link = document.createElement("a");
|
|
2622
|
+
link.href = url;
|
|
2623
|
+
link.setAttribute("download", res.data?.filename || `bulk_export_selected_${targets.length}.csv`);
|
|
2624
|
+
document.body.appendChild(link);
|
|
2625
|
+
link.click();
|
|
2626
|
+
document.body.removeChild(link);
|
|
2627
|
+
URL.revokeObjectURL(url);
|
|
2628
|
+
toggleNotification({
|
|
2629
|
+
type: "success",
|
|
2630
|
+
message: `Successfully exported ${res.data?.count || targets.length} selected records to Excel CSV`
|
|
2631
|
+
});
|
|
2632
|
+
} catch (e) {
|
|
2633
|
+
toggleNotification({
|
|
2634
|
+
type: "danger",
|
|
2635
|
+
message: e.message || "Failed exporting selected records to CSV"
|
|
2636
|
+
});
|
|
2637
|
+
} finally {
|
|
2638
|
+
setBulkExporting(false);
|
|
2639
|
+
}
|
|
2640
|
+
};
|
|
2438
2641
|
const handleOpenPublishModal = () => {
|
|
2439
2642
|
const totalSelected = Object.values(selectedEntries).filter(Boolean).length;
|
|
2440
2643
|
if (totalSelected === 0) {
|
|
@@ -2918,7 +3121,29 @@ const BulkUpdater = () => {
|
|
|
2918
3121
|
marginBottom: 6,
|
|
2919
3122
|
style: { width: "100%", maxWidth: "100%", minWidth: 0, boxSizing: "border-box" },
|
|
2920
3123
|
children: [
|
|
2921
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3124
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, wrap: "wrap", gap: 3, children: [
|
|
3125
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
3126
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", fontWeight: "semiBold", children: "1. Configure Attribute Update" }),
|
|
3127
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Select the target attribute to update across collections." })
|
|
3128
|
+
] }),
|
|
3129
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
|
|
3130
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral700", fontWeight: "bold", children: "Component Nesting Depth:" }),
|
|
3131
|
+
[1, 2, 3, 4, 5].map((lvl) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3132
|
+
designSystem.Button,
|
|
3133
|
+
{
|
|
3134
|
+
variant: componentDepth === lvl ? "default" : "tertiary",
|
|
3135
|
+
size: "S",
|
|
3136
|
+
onClick: () => handleComponentDepthChange(lvl),
|
|
3137
|
+
title: `Discover nested component attributes up to Level ${lvl}`,
|
|
3138
|
+
children: [
|
|
3139
|
+
"Level ",
|
|
3140
|
+
lvl
|
|
3141
|
+
]
|
|
3142
|
+
},
|
|
3143
|
+
`depth_cfg_${lvl}`
|
|
3144
|
+
))
|
|
3145
|
+
] })
|
|
3146
|
+
] }),
|
|
2922
3147
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, alignItems: "flex-end", wrap: "wrap", children: [
|
|
2923
3148
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { minWidth: 280, flex: 1, maxWidth: 440 }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
|
|
2924
3149
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Attribute to Update" }),
|
|
@@ -3078,6 +3303,23 @@ const BulkUpdater = () => {
|
|
|
3078
3303
|
")"
|
|
3079
3304
|
]
|
|
3080
3305
|
}
|
|
3306
|
+
),
|
|
3307
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3308
|
+
designSystem.Button,
|
|
3309
|
+
{
|
|
3310
|
+
startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Download, {}),
|
|
3311
|
+
variant: "tertiary",
|
|
3312
|
+
size: "L",
|
|
3313
|
+
onClick: handleBulkExportSelected,
|
|
3314
|
+
loading: bulkExporting,
|
|
3315
|
+
disabled: loading || totalSelectedCount === 0,
|
|
3316
|
+
title: "Export selected entries to Excel-compatible CSV with full nested component support",
|
|
3317
|
+
children: [
|
|
3318
|
+
"Export Selected (",
|
|
3319
|
+
totalSelectedCount,
|
|
3320
|
+
")"
|
|
3321
|
+
]
|
|
3322
|
+
}
|
|
3081
3323
|
)
|
|
3082
3324
|
] })
|
|
3083
3325
|
] }),
|
|
@@ -3232,26 +3474,83 @@ const BulkUpdater = () => {
|
|
|
3232
3474
|
style: { width: "100%", maxWidth: "100%", boxSizing: "border-box" },
|
|
3233
3475
|
children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 3, children: [
|
|
3234
3476
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 2, children: [
|
|
3235
|
-
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
|
|
3477
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
|
|
3236
3478
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", children: "Search & Filter Targets:" }),
|
|
3237
3479
|
isFilterActive && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
|
|
3238
3480
|
matchingEntriesCount,
|
|
3239
3481
|
" matching ",
|
|
3240
3482
|
matchingEntriesCount === 1 ? "entry" : "entries",
|
|
3241
3483
|
" found in database"
|
|
3484
|
+
] }),
|
|
3485
|
+
searchAttribute && searchAttribute !== selectedAttribute && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: [
|
|
3486
|
+
"Searching by: ",
|
|
3487
|
+
searchAttribute,
|
|
3488
|
+
" → Target to update: ",
|
|
3489
|
+
selectedAttribute
|
|
3242
3490
|
] })
|
|
3243
3491
|
] }),
|
|
3244
|
-
|
|
3492
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
|
|
3493
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, alignItems: "center", children: [
|
|
3494
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Depth:" }),
|
|
3495
|
+
[1, 2, 3, 4, 5].map((lvl) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3496
|
+
designSystem.Button,
|
|
3497
|
+
{
|
|
3498
|
+
variant: componentDepth === lvl ? "default" : "tertiary",
|
|
3499
|
+
size: "S",
|
|
3500
|
+
onClick: () => handleComponentDepthChange(lvl),
|
|
3501
|
+
title: `Show component attributes up to Level ${lvl}`,
|
|
3502
|
+
style: { minWidth: 26, padding: "2px 6px" },
|
|
3503
|
+
children: [
|
|
3504
|
+
"L",
|
|
3505
|
+
lvl
|
|
3506
|
+
]
|
|
3507
|
+
},
|
|
3508
|
+
`s2_depth_${lvl}`
|
|
3509
|
+
))
|
|
3510
|
+
] }),
|
|
3511
|
+
isFilterActive && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "ghost", size: "S", onClick: clearFilter, children: "Clear Search & Filter" })
|
|
3512
|
+
] })
|
|
3245
3513
|
] }),
|
|
3246
3514
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "flex-end", wrap: "wrap", children: [
|
|
3247
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width:
|
|
3515
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 280, minWidth: 230 }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
|
|
3516
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Search Attribute" }),
|
|
3517
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3518
|
+
designSystem.Combobox,
|
|
3519
|
+
{
|
|
3520
|
+
autocomplete: "none",
|
|
3521
|
+
placeholder: searchAttribute || `Same as Update (${selectedAttribute || "None"})`,
|
|
3522
|
+
value: searchAttribute || "__default__",
|
|
3523
|
+
clearLabel: "Reset search attribute",
|
|
3524
|
+
onClear: () => {
|
|
3525
|
+
handleSearchAttributeChange("");
|
|
3526
|
+
setSearchAttributeSearch("");
|
|
3527
|
+
},
|
|
3528
|
+
onInputChange: (e) => {
|
|
3529
|
+
setSearchAttributeSearch(e.target.value);
|
|
3530
|
+
},
|
|
3531
|
+
onOpenChange: (isOpen) => {
|
|
3532
|
+
if (!isOpen) {
|
|
3533
|
+
setSearchAttributeSearch("");
|
|
3534
|
+
}
|
|
3535
|
+
},
|
|
3536
|
+
onChange: (val) => {
|
|
3537
|
+
if (val && typeof val === "string" && val !== "__none__") {
|
|
3538
|
+
handleSearchAttributeChange(val);
|
|
3539
|
+
setSearchAttributeSearch("");
|
|
3540
|
+
}
|
|
3541
|
+
},
|
|
3542
|
+
children: memoizedSearchComboboxOptions
|
|
3543
|
+
}
|
|
3544
|
+
)
|
|
3545
|
+
] }) }),
|
|
3546
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 200, minWidth: 170 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3248
3547
|
designSystem.SingleSelect,
|
|
3249
3548
|
{
|
|
3250
|
-
label:
|
|
3549
|
+
label: effectiveSearchAttribute ? `Condition for "${effectiveSearchAttribute}"` : "Filter condition",
|
|
3251
3550
|
value: filterOperator,
|
|
3252
3551
|
onChange: (val) => setFilterOperator(String(val)),
|
|
3253
3552
|
children: [
|
|
3254
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "none", children: "Show All (No
|
|
3553
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "none", children: "Show All (No Value Filter)" }),
|
|
3255
3554
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "empty", children: "Is Empty or Null" }),
|
|
3256
3555
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "not_empty", children: "Is Not Empty" }),
|
|
3257
3556
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "equals", children: "Equals Exact Text" }),
|
|
@@ -3260,16 +3559,16 @@ const BulkUpdater = () => {
|
|
|
3260
3559
|
]
|
|
3261
3560
|
}
|
|
3262
3561
|
) }),
|
|
3263
|
-
(filterOperator === "equals" || filterOperator === "contains" || filterOperator === "starts_with") && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1, minWidth:
|
|
3562
|
+
(filterOperator === "equals" || filterOperator === "contains" || filterOperator === "starts_with") && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1, minWidth: 180 }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3264
3563
|
designSystem.TextInput,
|
|
3265
3564
|
{
|
|
3266
|
-
label:
|
|
3565
|
+
label: effectiveSearchAttribute ? `Value for "${effectiveSearchAttribute}"` : "Value to match",
|
|
3267
3566
|
placeholder: "Type value to match...",
|
|
3268
3567
|
value: filterQuery,
|
|
3269
3568
|
onChange: (e) => setFilterQuery(e.target.value)
|
|
3270
3569
|
}
|
|
3271
3570
|
) }),
|
|
3272
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1, minWidth:
|
|
3571
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1, minWidth: 180 }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3273
3572
|
designSystem.TextInput,
|
|
3274
3573
|
{
|
|
3275
3574
|
label: "Full-text Entry Search",
|
|
@@ -3467,11 +3766,11 @@ const BulkUpdater = () => {
|
|
|
3467
3766
|
const pageSize = pageState ? pageState.pageSize : defaultPageSize;
|
|
3468
3767
|
const hasAttr = selectedAttribute ? col.attributes.includes(selectedAttribute) : false;
|
|
3469
3768
|
const visibleEntries = getVisibleEntries(col);
|
|
3470
|
-
selectedAttribute ? entries.filter((e) => entryMatchesFilter(e,
|
|
3769
|
+
selectedAttribute ? entries.filter((e) => entryMatchesFilter(e, effectiveSearchAttribute)).length : 0;
|
|
3471
3770
|
const isFullySelected = isCollectionFullySelected(col);
|
|
3472
3771
|
const isPartiallySelected = isCollectionPartiallySelected(col);
|
|
3473
3772
|
const currentSort = collectionSorts[col.uid] || { field: "createdAt", order: "desc" };
|
|
3474
|
-
const availableColumns = getAvailableColumnsForCollection(col, selectedAttribute);
|
|
3773
|
+
const availableColumns = getAvailableColumnsForCollection(col, selectedAttribute, searchAttribute);
|
|
3475
3774
|
const savedColKeys = collectionColumnVisibility[col.uid];
|
|
3476
3775
|
const activeColumns = savedColKeys ? availableColumns.filter((c) => c.isPermanent || savedColKeys.includes(c.key)) : availableColumns.filter((c) => c.defaultVisible);
|
|
3477
3776
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -3579,6 +3878,10 @@ const BulkUpdater = () => {
|
|
|
3579
3878
|
if (entrySearch && entrySearch.trim()) {
|
|
3580
3879
|
params.set("search", entrySearch.trim());
|
|
3581
3880
|
}
|
|
3881
|
+
const colSelectedDocIds = col.entries.filter((e) => selectedEntries[`${col.uid}${KEY_SEP}${e.documentId}`]).map((e) => e.documentId);
|
|
3882
|
+
if (colSelectedDocIds.length > 0) {
|
|
3883
|
+
params.set("documentIds", colSelectedDocIds.join(","));
|
|
3884
|
+
}
|
|
3582
3885
|
const res = await get(`/bulk-updater/csv/export/${col.uid}?${params.toString()}`);
|
|
3583
3886
|
const csvStr = res.data?.csvContent || (typeof res.data === "string" ? res.data : "");
|
|
3584
3887
|
if (!csvStr) {
|
|
@@ -3588,7 +3891,8 @@ const BulkUpdater = () => {
|
|
|
3588
3891
|
});
|
|
3589
3892
|
return;
|
|
3590
3893
|
}
|
|
3591
|
-
const
|
|
3894
|
+
const safeContent = csvStr.startsWith("\uFEFF") ? csvStr : "\uFEFF" + csvStr;
|
|
3895
|
+
const blob = new Blob([safeContent], { type: "text/csv;charset=utf-8;" });
|
|
3592
3896
|
const url = URL.createObjectURL(blob);
|
|
3593
3897
|
const link = document.createElement("a");
|
|
3594
3898
|
link.href = url;
|
|
@@ -3599,7 +3903,7 @@ const BulkUpdater = () => {
|
|
|
3599
3903
|
URL.revokeObjectURL(url);
|
|
3600
3904
|
toggleNotification({
|
|
3601
3905
|
type: "success",
|
|
3602
|
-
message: `Exported ${col.displayName} entries to CSV`
|
|
3906
|
+
message: `Exported ${res.data?.count ?? (colSelectedDocIds.length || col.entries.length)} ${col.displayName} entries to Excel CSV`
|
|
3603
3907
|
});
|
|
3604
3908
|
} catch (e) {
|
|
3605
3909
|
toggleNotification({
|
|
@@ -3676,7 +3980,7 @@ const BulkUpdater = () => {
|
|
|
3676
3980
|
),
|
|
3677
3981
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { className: "bulk-updater-table-container", style: { width: "100%", maxWidth: "100%", minWidth: 0, overflowX: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: activeColumns.length, rowCount: visibleEntries.length + 1, style: { width: "100%" }, children: [
|
|
3678
3982
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: activeColumns.map(
|
|
3679
|
-
(colDef) => renderHeaderCell(colDef, col, currentSort, selectedAttribute, handleHeaderSortClick)
|
|
3983
|
+
(colDef) => renderHeaderCell(colDef, col, currentSort, selectedAttribute, searchAttribute, handleHeaderSortClick)
|
|
3680
3984
|
) }) }),
|
|
3681
3985
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: visibleEntries.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { colSpan: activeColumns.length, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 4, style: { textAlign: "center" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: "No entries in this content type match the active filter criteria." }) }) }) }) : visibleEntries.map((entry) => {
|
|
3682
3986
|
const key = `${col.uid}${KEY_SEP}${entry.documentId}`;
|
|
@@ -3694,6 +3998,7 @@ const BulkUpdater = () => {
|
|
|
3694
3998
|
entry,
|
|
3695
3999
|
col,
|
|
3696
4000
|
selectedAttribute,
|
|
4001
|
+
searchAttribute,
|
|
3697
4002
|
isMatch,
|
|
3698
4003
|
hasAttr,
|
|
3699
4004
|
isSelected,
|
|
@@ -4667,4 +4972,4 @@ exports.default = BulkUpdater;
|
|
|
4667
4972
|
exports.formatDisplayValue = formatDisplayValue;
|
|
4668
4973
|
exports.getAvailableColumnsForCollection = getAvailableColumnsForCollection;
|
|
4669
4974
|
exports.getNestedValue = getNestedValue;
|
|
4670
|
-
//# sourceMappingURL=BulkUpdater-
|
|
4975
|
+
//# sourceMappingURL=BulkUpdater-CRvwX326.js.map
|