strapi-plugin-bulk-updater 1.1.0 → 1.2.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 CHANGED
@@ -54,7 +54,13 @@ Strapi allows editing entries individually or applying bulk actions within a sin
54
54
  - **Tier 2 (Strapi Enterprise Bridge)**: Bridges events directly to native Strapi Enterprise Audit Logs (`admin::audit-logs`).
55
55
  - **Tier 3 (Custom Declarative Schema)**: Custom collection types and declarative column mappings.
56
56
  - **Deterministic Rollback**: Restores exact prior values and relationship links using Strapi v5 Document Service.
57
- - 🎯 **UI-Configurable Cross-Component Validation & Quotas**: Real-time cross-component quota enforcement (e.g. max one H1 heading across dynamic zones) configurable straight from the admin UI.
57
+ - 🧩 **Deep Nested Component Support**:
58
+ - Full dot-notation introspection (e.g. `metaFields.metaDescription`, `defaultSeo.metaTitle`) in attribute dropdown and table views.
59
+ - Non-destructive deep merging preserves sibling component properties during bulk updates and rollbacks.
60
+ - ⚡ **Server-Side Filter & Full-Text Search Pushdown**:
61
+ - Pushes text searches and attribute filters (`equals`, `contains`, `empty`, `starts_with`) directly to the database.
62
+ - Automatically hides non-matching records and pre-selects matching rows with real-time match counters.
63
+ - Scalable server-side pagination up to 500 records per page with a one-click "Load all entries" button.
58
64
  - 🌓 **Adaptive Light & Dark Theme Design**: Built with native `@strapi/design-system` tokens with high-contrast text and sticky viewport-constrained modals.
59
65
  - ⚡ **Zero-Infra Strapi Cloud Ready**: In-memory caching, pod-level high-watermark ETags, zero external Redis/SQL requirements.
60
66
 
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
- import React__default, { useState, useEffect } from "react";
2
+ import React__default, { useState, useEffect, useRef } from "react";
3
3
  import { useFetchClient, useNotification } from "@strapi/strapi/admin";
4
4
  import { Box, Flex, Typography, Button, SingleSelect, SingleSelectOption, Checkbox, Badge, Table, Thead, Tr, Th, Tbody, Td, Alert, TextInput } from "@strapi/design-system";
5
5
  import { ArrowClockwise, Download, Upload, Check, Play } from "@strapi/icons";
@@ -418,6 +418,11 @@ function CsvManagerTab() {
418
418
  ] })
419
419
  ] }) });
420
420
  }
421
+ function getNestedValue(obj, path) {
422
+ if (!obj || !path) return void 0;
423
+ if (!path.includes(".")) return obj[path];
424
+ return path.split(".").reduce((acc, part) => acc && typeof acc === "object" ? acc[part] : void 0, obj);
425
+ }
421
426
  function getAvailableColumnsForCollection(col, selectedAttribute) {
422
427
  const cols = [
423
428
  {
@@ -438,7 +443,7 @@ function getAvailableColumnsForCollection(col, selectedAttribute) {
438
443
  if (attr === "name" || attr === "title" || attr === selectedAttribute) {
439
444
  return;
440
445
  }
441
- const formattedLabel = attr.charAt(0).toUpperCase() + attr.slice(1);
446
+ const formattedLabel = attr.includes(".") ? attr.split(".").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" > ") : attr.charAt(0).toUpperCase() + attr.slice(1);
442
447
  cols.push({
443
448
  key: `attr_${attr}`,
444
449
  label: formattedLabel,
@@ -524,7 +529,7 @@ function renderCellContent(colDef, entry, col, selectedAttribute, isMatch, hasAt
524
529
  ] });
525
530
  }
526
531
  case "target_attr": {
527
- const val = entry[selectedAttribute];
532
+ const val = getNestedValue(entry, selectedAttribute);
528
533
  const displayStr = formatDisplayValue(val);
529
534
  const isUnset = displayStr === "(empty / not set)" || displayStr === "(empty / unlinked)";
530
535
  return /* @__PURE__ */ jsx(
@@ -554,7 +559,7 @@ function renderCellContent(colDef, entry, col, selectedAttribute, isMatch, hasAt
554
559
  }
555
560
  default: {
556
561
  const attrName = colDef.key.replace("attr_", "");
557
- const rawVal = entry[attrName];
562
+ const rawVal = getNestedValue(entry, attrName);
558
563
  const displayStr = formatDisplayValue(rawVal);
559
564
  return /* @__PURE__ */ jsx(
560
565
  Typography,
@@ -628,7 +633,8 @@ const BulkUpdater = () => {
628
633
  const [regexPattern, setRegexPattern] = useState("");
629
634
  const [filterOperator, setFilterOperator] = useState("none");
630
635
  const [filterQuery, setFilterQuery] = useState("");
631
- const [hideNonMatching, setHideNonMatching] = useState(false);
636
+ const [entrySearch, setEntrySearch] = useState("");
637
+ const isFilterActive = filterOperator !== "none" && (["empty", "not_empty"].includes(filterOperator) || Boolean(filterQuery.trim())) || Boolean(entrySearch.trim());
632
638
  const [lastResults, setLastResults] = useState(null);
633
639
  const [updateSummary, setUpdateSummary] = useState(null);
634
640
  const [collectionPages, setCollectionPages] = useState({});
@@ -724,12 +730,15 @@ const BulkUpdater = () => {
724
730
  setLoading(false);
725
731
  }
726
732
  };
727
- const loadCollectionPage = async (colUid, targetPage, customPageSize, customSortField, customSortOrder) => {
733
+ const loadCollectionPage = async (colUid, targetPage, customPageSize, customSortField, customSortOrder, overrideFilterOp, overrideFilterQuery, overrideEntrySearch, autoSelectReturned = true) => {
728
734
  const currentPageState = collectionPages[colUid];
729
735
  const size = customPageSize || currentPageState?.pageSize || defaultPageSize;
730
736
  const currentSort = collectionSorts[colUid] || { field: "createdAt", order: "desc" };
731
737
  const sortField = customSortField !== void 0 ? customSortField : currentSort.field;
732
738
  const sortOrder = customSortOrder !== void 0 ? customSortOrder : currentSort.order;
739
+ const op = overrideFilterOp !== void 0 ? overrideFilterOp : filterOperator;
740
+ const q = overrideFilterQuery !== void 0 ? overrideFilterQuery : filterQuery;
741
+ const s = overrideEntrySearch !== void 0 ? overrideEntrySearch : entrySearch;
733
742
  setCollectionPages((prev) => ({
734
743
  ...prev,
735
744
  [colUid]: {
@@ -743,16 +752,20 @@ const BulkUpdater = () => {
743
752
  pageSize: String(size)
744
753
  });
745
754
  if (selectedAttribute) params.set("attribute", selectedAttribute);
746
- if (filterOperator && filterOperator !== "none") {
747
- params.set("filterOperator", filterOperator);
748
- if (filterQuery) params.set("filterQuery", filterQuery);
755
+ if (op && op !== "none") {
756
+ params.set("filterOperator", op);
757
+ if (q) params.set("filterQuery", q);
758
+ }
759
+ if (s && s.trim()) {
760
+ params.set("search", s.trim());
749
761
  }
750
762
  if (sortField) {
751
763
  params.set("sortField", sortField);
752
764
  params.set("sortOrder", sortOrder);
753
765
  }
754
766
  const res = await get(`/bulk-updater/collections/${colUid}/entries?${params.toString()}`);
755
- if (res.data && res.data.success) {
767
+ if (res.data && (res.data.success || res.data.entries !== void 0)) {
768
+ const fetchedEntries = res.data.entries || [];
756
769
  setCollectionPages((prev) => ({
757
770
  ...prev,
758
771
  [colUid]: {
@@ -760,10 +773,20 @@ const BulkUpdater = () => {
760
773
  pageSize: res.data.pagination.pageSize,
761
774
  total: res.data.pagination.total,
762
775
  pageCount: res.data.pagination.pageCount,
763
- entries: res.data.entries || [],
776
+ entries: fetchedEntries,
764
777
  loading: false
765
778
  }
766
779
  }));
780
+ const isFilterActiveNow = op !== "none" && (["empty", "not_empty"].includes(op) || Boolean(q.trim())) || Boolean(s.trim());
781
+ if (isFilterActiveNow && autoSelectReturned) {
782
+ setSelectedEntries((prev) => {
783
+ const next = { ...prev };
784
+ fetchedEntries.forEach((e) => {
785
+ next[`${colUid}${KEY_SEP}${e.documentId}`] = true;
786
+ });
787
+ return next;
788
+ });
789
+ }
767
790
  }
768
791
  } catch (err) {
769
792
  toggleNotification({
@@ -827,6 +850,35 @@ const BulkUpdater = () => {
827
850
  fetchPendingBatches();
828
851
  }
829
852
  }, [activeTab]);
853
+ const isInitialFilterMount = useRef(true);
854
+ useEffect(() => {
855
+ if (isInitialFilterMount.current) {
856
+ isInitialFilterMount.current = false;
857
+ return;
858
+ }
859
+ const timer = setTimeout(() => {
860
+ if (collections.length > 0) {
861
+ const isFilterActiveNow = filterOperator !== "none" && (["empty", "not_empty"].includes(filterOperator) || Boolean(filterQuery.trim())) || Boolean(entrySearch.trim());
862
+ if (isFilterActiveNow) {
863
+ setSelectedEntries({});
864
+ }
865
+ collections.forEach((col) => {
866
+ loadCollectionPage(
867
+ col.uid,
868
+ 1,
869
+ void 0,
870
+ void 0,
871
+ void 0,
872
+ filterOperator,
873
+ filterQuery,
874
+ entrySearch,
875
+ true
876
+ );
877
+ });
878
+ }
879
+ }, 350);
880
+ return () => clearTimeout(timer);
881
+ }, [filterOperator, filterQuery, entrySearch]);
830
882
  const KEY_SEP = "@@@";
831
883
  const handleAttributeChange = (newAttr) => {
832
884
  if (newAttr !== selectedAttribute) {
@@ -834,7 +886,7 @@ const BulkUpdater = () => {
834
886
  setSelectedEntries({});
835
887
  setFilterOperator("none");
836
888
  setFilterQuery("");
837
- setHideNonMatching(false);
889
+ setEntrySearch("");
838
890
  setSelectedRelationDocId("");
839
891
  setAttributeValue("");
840
892
  if (relationsMeta[newAttr]) {
@@ -851,7 +903,7 @@ const BulkUpdater = () => {
851
903
  (c) => visibleTypeUids[c.uid] !== false && (!collectionSearch.trim() || c.displayName.toLowerCase().includes(collectionSearch.toLowerCase().trim()) || c.uid.toLowerCase().includes(collectionSearch.toLowerCase().trim()))
852
904
  );
853
905
  const handleGlobalPageSizeChange = (newSize) => {
854
- const capped = Math.min(100, Math.max(1, newSize));
906
+ const capped = Math.min(500, Math.max(1, newSize));
855
907
  setDefaultPageSize(capped);
856
908
  collections.forEach((col) => {
857
909
  if (!col.isSingleType) {
@@ -860,7 +912,7 @@ const BulkUpdater = () => {
860
912
  });
861
913
  };
862
914
  const handleCollectionPageSizeChange = (colUid, newSize) => {
863
- const capped = Math.min(100, Math.max(1, newSize));
915
+ const capped = Math.min(500, Math.max(1, newSize));
864
916
  loadCollectionPage(colUid, 1, capped);
865
917
  };
866
918
  const handleCollectionSortChange = (colUid, field, order) => {
@@ -884,7 +936,7 @@ const BulkUpdater = () => {
884
936
  };
885
937
  const entryMatchesFilter = (entry, attr) => {
886
938
  if (filterOperator === "none") return true;
887
- const val = entry[attr];
939
+ const val = getNestedValue(entry, attr);
888
940
  const displayStr = formatDisplayValue(val);
889
941
  const isValEmpty = val === void 0 || val === null || displayStr.trim() === "" || displayStr.trim().toLowerCase() === "(not set)" || displayStr.trim().toLowerCase() === "(empty / not set)" || displayStr.trim().toLowerCase() === "(empty / unlinked)";
890
942
  switch (filterOperator) {
@@ -904,64 +956,48 @@ const BulkUpdater = () => {
904
956
  };
905
957
  const getVisibleEntries = (col) => {
906
958
  const pageState = collectionPages[col.uid];
907
- const entries = pageState ? pageState.entries : col.entries || [];
908
- if (!hideNonMatching || filterOperator === "none") {
909
- return entries;
910
- }
911
- return entries.filter((e) => entryMatchesFilter(e, selectedAttribute));
959
+ return pageState ? pageState.entries : col.entries || [];
912
960
  };
913
961
  const matchingEntriesCount = React__default.useMemo(() => {
914
- if (filterOperator === "none") return 0;
962
+ if (!isFilterActive) return 0;
915
963
  let count = 0;
916
964
  filteredCollections.forEach((col) => {
917
- if (col.attributes.includes(selectedAttribute)) {
918
- const pageState = collectionPages[col.uid];
919
- const entries = pageState ? pageState.entries : col.entries || [];
920
- entries.forEach((entry) => {
921
- if (entryMatchesFilter(entry, selectedAttribute)) {
922
- count++;
923
- }
924
- });
965
+ const pageState = collectionPages[col.uid];
966
+ if (pageState) {
967
+ count += pageState.total;
925
968
  }
926
969
  });
927
970
  return count;
928
- }, [filteredCollections, collectionPages, selectedAttribute, filterOperator, filterQuery]);
971
+ }, [filteredCollections, collectionPages, isFilterActive]);
929
972
  const selectAllMatching = () => {
930
973
  const updated = { ...selectedEntries };
931
974
  filteredCollections.forEach((col) => {
932
- if (col.attributes.includes(selectedAttribute)) {
933
- const pageState = collectionPages[col.uid];
934
- const entries = pageState ? pageState.entries : col.entries || [];
935
- entries.forEach((e) => {
936
- if (entryMatchesFilter(e, selectedAttribute)) {
937
- updated[`${col.uid}${KEY_SEP}${e.documentId}`] = true;
938
- }
939
- });
940
- }
975
+ const pageState = collectionPages[col.uid];
976
+ const entries = pageState ? pageState.entries : col.entries || [];
977
+ entries.forEach((e) => {
978
+ updated[`${col.uid}${KEY_SEP}${e.documentId}`] = true;
979
+ });
941
980
  });
942
981
  setSelectedEntries(updated);
943
982
  };
944
983
  const deselectAllMatching = () => {
945
984
  const updated = { ...selectedEntries };
946
985
  filteredCollections.forEach((col) => {
947
- if (col.attributes.includes(selectedAttribute)) {
948
- const pageState = collectionPages[col.uid];
949
- const entries = pageState ? pageState.entries : col.entries || [];
950
- entries.forEach((e) => {
951
- if (entryMatchesFilter(e, selectedAttribute)) {
952
- delete updated[`${col.uid}${KEY_SEP}${e.documentId}`];
953
- }
954
- });
955
- }
986
+ const pageState = collectionPages[col.uid];
987
+ const entries = pageState ? pageState.entries : col.entries || [];
988
+ entries.forEach((e) => {
989
+ delete updated[`${col.uid}${KEY_SEP}${e.documentId}`];
990
+ });
956
991
  });
957
992
  setSelectedEntries(updated);
958
993
  };
959
994
  const clearFilter = () => {
960
995
  setFilterOperator("none");
961
996
  setFilterQuery("");
962
- setHideNonMatching(false);
997
+ setEntrySearch("");
998
+ setSelectedEntries({});
963
999
  collections.forEach((col) => {
964
- loadCollectionPage(col.uid, 1);
1000
+ loadCollectionPage(col.uid, 1, void 0, void 0, void 0, "none", "", "", false);
965
1001
  });
966
1002
  };
967
1003
  const isCollectionFullySelected = (col) => {
@@ -1083,6 +1119,7 @@ const BulkUpdater = () => {
1083
1119
  message: `Bulk update complete! Batch ID: ${res.data.batchId}. Updated: ${res.data.updatedCount}, Skipped: ${res.data.skippedCount}`
1084
1120
  });
1085
1121
  await fetchModels();
1122
+ await fetchAuditHistory();
1086
1123
  }
1087
1124
  } else {
1088
1125
  toggleNotification({
@@ -1369,7 +1406,7 @@ const BulkUpdater = () => {
1369
1406
  /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral700", children: [
1370
1407
  /* @__PURE__ */ jsx("code", { children: latestActiveBatch.batchId }),
1371
1408
  " (",
1372
- latestActiveBatch.recordCount,
1409
+ latestActiveBatch.updatedCount,
1373
1410
  " entries updated by ",
1374
1411
  latestActiveBatch.userEmail,
1375
1412
  ")"
@@ -1596,24 +1633,25 @@ const BulkUpdater = () => {
1596
1633
  children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, children: [
1597
1634
  /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 2, children: [
1598
1635
  /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
1599
- /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", children: "Find Targets by Current Value:" }),
1600
- filterOperator !== "none" && /* @__PURE__ */ jsxs(Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
1636
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", children: "Search & Filter Targets:" }),
1637
+ isFilterActive && /* @__PURE__ */ jsxs(Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
1601
1638
  matchingEntriesCount,
1602
1639
  " matching ",
1603
- matchingEntriesCount === 1 ? "entry" : "entries"
1640
+ matchingEntriesCount === 1 ? "entry" : "entries",
1641
+ " found in database"
1604
1642
  ] })
1605
1643
  ] }),
1606
- filterOperator !== "none" && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "S", onClick: clearFilter, children: "Clear Filter" })
1644
+ isFilterActive && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "S", onClick: clearFilter, children: "Clear Search & Filter" })
1607
1645
  ] }),
1608
1646
  /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "flex-end", wrap: "wrap", children: [
1609
- /* @__PURE__ */ jsx(Box, { style: { width: 240, minWidth: 200 }, children: /* @__PURE__ */ jsxs(
1647
+ /* @__PURE__ */ jsx(Box, { style: { width: 220, minWidth: 180 }, children: /* @__PURE__ */ jsxs(
1610
1648
  SingleSelect,
1611
1649
  {
1612
1650
  label: `Filter condition for "${selectedAttribute}"`,
1613
1651
  value: filterOperator,
1614
1652
  onChange: (val) => setFilterOperator(String(val)),
1615
1653
  children: [
1616
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "none", children: "Show All (No Filter)" }),
1654
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "none", children: "Show All (No Attribute Filter)" }),
1617
1655
  /* @__PURE__ */ jsx(SingleSelectOption, { value: "empty", children: "Is Empty or Null" }),
1618
1656
  /* @__PURE__ */ jsx(SingleSelectOption, { value: "not_empty", children: "Is Not Empty" }),
1619
1657
  /* @__PURE__ */ jsx(SingleSelectOption, { value: "equals", children: "Equals Exact Text" }),
@@ -1622,28 +1660,33 @@ const BulkUpdater = () => {
1622
1660
  ]
1623
1661
  }
1624
1662
  ) }),
1625
- (filterOperator === "equals" || filterOperator === "contains" || filterOperator === "starts_with") && /* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 220 }, children: /* @__PURE__ */ jsx(
1663
+ (filterOperator === "equals" || filterOperator === "contains" || filterOperator === "starts_with") && /* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 200 }, children: /* @__PURE__ */ jsx(
1626
1664
  TextInput,
1627
1665
  {
1628
- label: "Value to Search",
1666
+ label: `Value for "${selectedAttribute}"`,
1629
1667
  placeholder: "Type value to match...",
1630
1668
  value: filterQuery,
1631
1669
  onChange: (e) => setFilterQuery(e.target.value)
1632
1670
  }
1633
1671
  ) }),
1634
- filterOperator !== "none" && /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
1635
- /* @__PURE__ */ jsxs(
1672
+ /* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: 200 }, children: /* @__PURE__ */ jsx(
1673
+ TextInput,
1674
+ {
1675
+ label: "Full-text Entry Search",
1676
+ placeholder: "Search title, name, or text across records...",
1677
+ value: entrySearch,
1678
+ onChange: (e) => setEntrySearch(e.target.value)
1679
+ }
1680
+ ) }),
1681
+ isFilterActive && /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
1682
+ /* @__PURE__ */ jsx(
1636
1683
  Button,
1637
1684
  {
1638
1685
  variant: "default",
1639
1686
  size: "S",
1640
1687
  onClick: selectAllMatching,
1641
1688
  disabled: matchingEntriesCount === 0,
1642
- children: [
1643
- "Select Matching (",
1644
- matchingEntriesCount,
1645
- ")"
1646
- ]
1689
+ children: "Select All Loaded"
1647
1690
  }
1648
1691
  ),
1649
1692
  /* @__PURE__ */ jsx(
@@ -1653,19 +1696,12 @@ const BulkUpdater = () => {
1653
1696
  size: "S",
1654
1697
  onClick: deselectAllMatching,
1655
1698
  disabled: matchingEntriesCount === 0,
1656
- children: "Deselect Matching"
1699
+ children: "Deselect All Loaded"
1657
1700
  }
1658
1701
  )
1659
1702
  ] })
1660
1703
  ] }),
1661
- filterOperator !== "none" && /* @__PURE__ */ jsx(Flex, { gap: 2, alignItems: "center", marginTop: 1, children: /* @__PURE__ */ jsx(
1662
- Checkbox,
1663
- {
1664
- checked: hideNonMatching,
1665
- onCheckedChange: (checked) => setHideNonMatching(Boolean(checked)),
1666
- children: /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral700", children: "Only display matching entries in tables (hide non-matching)" })
1667
- }
1668
- ) })
1704
+ 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." }) })
1669
1705
  ] })
1670
1706
  }
1671
1707
  ),
@@ -1759,8 +1795,8 @@ const BulkUpdater = () => {
1759
1795
  }
1760
1796
  ) }),
1761
1797
  /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", style: { marginLeft: "auto" }, children: [
1762
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Default page size (max 100):" }),
1763
- /* @__PURE__ */ jsx(Box, { style: { width: 120 }, children: /* @__PURE__ */ jsxs(
1798
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Default page size (max 500):" }),
1799
+ /* @__PURE__ */ jsx(Box, { style: { width: 130 }, children: /* @__PURE__ */ jsxs(
1764
1800
  SingleSelect,
1765
1801
  {
1766
1802
  size: "S",
@@ -1771,7 +1807,9 @@ const BulkUpdater = () => {
1771
1807
  /* @__PURE__ */ jsx(SingleSelectOption, { value: "10", children: "10 / page" }),
1772
1808
  /* @__PURE__ */ jsx(SingleSelectOption, { value: "25", children: "25 / page" }),
1773
1809
  /* @__PURE__ */ jsx(SingleSelectOption, { value: "50", children: "50 / page" }),
1774
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "100", children: "100 / page" })
1810
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "100", children: "100 / page" }),
1811
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "250", children: "250 / page" }),
1812
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "500", children: "500 / page" })
1775
1813
  ]
1776
1814
  }
1777
1815
  ) })
@@ -1786,7 +1824,7 @@ const BulkUpdater = () => {
1786
1824
  const pageSize = pageState ? pageState.pageSize : defaultPageSize;
1787
1825
  const hasAttr = col.attributes.includes(selectedAttribute);
1788
1826
  const visibleEntries = getVisibleEntries(col);
1789
- const matchingColCount = entries.filter((e) => entryMatchesFilter(e, selectedAttribute)).length;
1827
+ entries.filter((e) => entryMatchesFilter(e, selectedAttribute)).length;
1790
1828
  const isFullySelected = isCollectionFullySelected(col);
1791
1829
  const isPartiallySelected = isCollectionPartiallySelected(col);
1792
1830
  const currentSort = collectionSorts[col.uid] || { field: "createdAt", order: "desc" };
@@ -1895,6 +1933,9 @@ const BulkUpdater = () => {
1895
1933
  params.set("filterOperator", filterOperator);
1896
1934
  if (filterQuery) params.set("filterQuery", filterQuery);
1897
1935
  }
1936
+ if (entrySearch && entrySearch.trim()) {
1937
+ params.set("search", entrySearch.trim());
1938
+ }
1898
1939
  const res = await get(`/bulk-updater/csv/export/${col.uid}?${params.toString()}`);
1899
1940
  const csvStr = res.data?.csvContent || (typeof res.data === "string" ? res.data : "");
1900
1941
  if (!csvStr) {
@@ -1927,7 +1968,7 @@ const BulkUpdater = () => {
1927
1968
  children: "Export CSV"
1928
1969
  }
1929
1970
  ),
1930
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", children: col.isSingleType ? "1 single entry" : filterOperator !== "none" ? `${matchingColCount} matching of ${totalEntries} entries` : `${totalEntries} entries` })
1971
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", children: col.isSingleType ? "1 single entry" : isFilterActive ? `${totalEntries} matching entries in DB` : `${totalEntries} entries` })
1931
1972
  ] })
1932
1973
  ] }),
1933
1974
  openColumnPickerUid === col.uid && /* @__PURE__ */ jsx(
@@ -1997,7 +2038,7 @@ const BulkUpdater = () => {
1997
2038
  /* @__PURE__ */ jsx(Tbody, { children: visibleEntries.length === 0 ? /* @__PURE__ */ jsx(Tr, { children: /* @__PURE__ */ jsx(Td, { colSpan: activeColumns.length, children: /* @__PURE__ */ jsx(Box, { padding: 4, style: { textAlign: "center" }, children: /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", children: "No entries in this content type match the active filter criteria." }) }) }) }) : visibleEntries.map((entry) => {
1998
2039
  const key = `${col.uid}${KEY_SEP}${entry.documentId}`;
1999
2040
  const isSelected = Boolean(selectedEntries[key]);
2000
- const isMatch = filterOperator !== "none" && entryMatchesFilter(entry, selectedAttribute);
2041
+ const isMatch = isFilterActive;
2001
2042
  return /* @__PURE__ */ jsx(
2002
2043
  Tr,
2003
2044
  {
@@ -2029,11 +2070,12 @@ const BulkUpdater = () => {
2029
2070
  Math.min(page * pageSize, totalEntries),
2030
2071
  " of ",
2031
2072
  totalEntries,
2032
- " entries"
2073
+ " ",
2074
+ isFilterActive ? "matching entries" : "entries"
2033
2075
  ] }),
2034
2076
  /* @__PURE__ */ jsxs(Flex, { gap: 1, alignItems: "center", children: [
2035
2077
  /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Show:" }),
2036
- /* @__PURE__ */ jsx(Box, { style: { width: 80 }, children: /* @__PURE__ */ jsxs(
2078
+ /* @__PURE__ */ jsx(Box, { style: { width: 85 }, children: /* @__PURE__ */ jsxs(
2037
2079
  SingleSelect,
2038
2080
  {
2039
2081
  size: "S",
@@ -2044,12 +2086,27 @@ const BulkUpdater = () => {
2044
2086
  /* @__PURE__ */ jsx(SingleSelectOption, { value: "10", children: "10" }),
2045
2087
  /* @__PURE__ */ jsx(SingleSelectOption, { value: "25", children: "25" }),
2046
2088
  /* @__PURE__ */ jsx(SingleSelectOption, { value: "50", children: "50" }),
2047
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "100", children: "100" })
2089
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "100", children: "100" }),
2090
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "250", children: "250" }),
2091
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "500", children: "500" })
2048
2092
  ]
2049
2093
  }
2050
2094
  ) }),
2051
2095
  /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", children: "per page" })
2052
- ] })
2096
+ ] }),
2097
+ totalEntries > visibleEntries.length && /* @__PURE__ */ jsxs(
2098
+ Button,
2099
+ {
2100
+ variant: "tertiary",
2101
+ size: "S",
2102
+ onClick: () => handleCollectionPageSizeChange(col.uid, Math.min(totalEntries, 500)),
2103
+ children: [
2104
+ "Load all ",
2105
+ totalEntries,
2106
+ " entries"
2107
+ ]
2108
+ }
2109
+ )
2053
2110
  ] }),
2054
2111
  pageCount > 1 && /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
2055
2112
  /* @__PURE__ */ jsx(
@@ -2707,6 +2764,7 @@ const BulkUpdater = () => {
2707
2764
  export {
2708
2765
  BulkUpdater as default,
2709
2766
  formatDisplayValue,
2710
- getAvailableColumnsForCollection
2767
+ getAvailableColumnsForCollection,
2768
+ getNestedValue
2711
2769
  };
2712
- //# sourceMappingURL=BulkUpdater-BSZP0U18.mjs.map
2770
+ //# sourceMappingURL=BulkUpdater-BDszahRM.mjs.map