strapi-plugin-bulk-updater 1.5.0 → 1.5.3

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.
@@ -2857,6 +2857,91 @@ const CheckerReviewModal = ({
2857
2857
  }
2858
2858
  );
2859
2859
  };
2860
+ function getEntryLifecycleStatus(entry, col, pendingBatches = []) {
2861
+ const isPendingApproval = pendingBatches.some((b) => {
2862
+ if (b.status !== "pending_approval") return false;
2863
+ const targets = b.payload?.selectedEntries || [];
2864
+ return targets.some((t) => entry.documentId && t.documentId === entry.documentId || String(t.documentId) === String(entry.id));
2865
+ });
2866
+ if (isPendingApproval) {
2867
+ return {
2868
+ status: "pending_review",
2869
+ label: "Pending Review",
2870
+ badgeColor: "warning100",
2871
+ textColor: "warning700"
2872
+ };
2873
+ }
2874
+ if (col && col.draftAndPublish === false) {
2875
+ return {
2876
+ status: "published",
2877
+ label: "Published",
2878
+ badgeColor: "success100",
2879
+ textColor: "success700"
2880
+ };
2881
+ }
2882
+ if (entry.status === "modified") {
2883
+ return {
2884
+ status: "modified",
2885
+ label: "Modified",
2886
+ badgeColor: "primary100",
2887
+ textColor: "primary700"
2888
+ };
2889
+ }
2890
+ if (entry.status === "draft") {
2891
+ return {
2892
+ status: "draft",
2893
+ label: "Draft",
2894
+ badgeColor: "neutral150",
2895
+ textColor: "neutral700"
2896
+ };
2897
+ }
2898
+ if (entry.status === "published") {
2899
+ return {
2900
+ status: "published",
2901
+ label: "Published",
2902
+ badgeColor: "success100",
2903
+ textColor: "success700"
2904
+ };
2905
+ }
2906
+ if (entry.status === "unknown") {
2907
+ return {
2908
+ status: "unknown",
2909
+ label: "Unknown",
2910
+ badgeColor: "neutral200",
2911
+ textColor: "neutral600"
2912
+ };
2913
+ }
2914
+ if (!entry.documentId && !entry.id && !entry.slug) {
2915
+ return {
2916
+ status: "unknown",
2917
+ label: "Unknown",
2918
+ badgeColor: "neutral200",
2919
+ textColor: "neutral600"
2920
+ };
2921
+ }
2922
+ if (entry.publishedAt) {
2923
+ if (entry.updatedAt && new Date(entry.updatedAt).getTime() > new Date(entry.publishedAt).getTime()) {
2924
+ return {
2925
+ status: "modified",
2926
+ label: "Modified",
2927
+ badgeColor: "primary100",
2928
+ textColor: "primary700"
2929
+ };
2930
+ }
2931
+ return {
2932
+ status: "published",
2933
+ label: "Published",
2934
+ badgeColor: "success100",
2935
+ textColor: "success700"
2936
+ };
2937
+ }
2938
+ return {
2939
+ status: "draft",
2940
+ label: "Draft",
2941
+ badgeColor: "neutral150",
2942
+ textColor: "neutral700"
2943
+ };
2944
+ }
2860
2945
  function parseDynamicZonePath(path) {
2861
2946
  if (!path) return { cleanPath: "", rootKey: "", subPath: "" };
2862
2947
  const bracketMatch = path.match(/^([^.[]+)\[([^\]]+)\]\.(.+)$/);
@@ -3269,67 +3354,6 @@ function formatDisplayValue(val) {
3269
3354
  }
3270
3355
  return String(val);
3271
3356
  }
3272
- function getEntryLifecycleStatus(entry, col, pendingBatches = []) {
3273
- const isPendingApproval = pendingBatches.some((b) => {
3274
- if (b.status !== "pending_approval") return false;
3275
- const targets = b.payload?.selectedEntries || [];
3276
- return targets.some((t) => t.documentId === entry.documentId || String(t.documentId) === String(entry.id));
3277
- });
3278
- if (isPendingApproval) {
3279
- return {
3280
- status: "pending_review",
3281
- label: "Pending Review",
3282
- badgeColor: "warning100",
3283
- textColor: "warning700"
3284
- };
3285
- }
3286
- if (entry.status === "modified") {
3287
- return {
3288
- status: "modified",
3289
- label: "Modified",
3290
- badgeColor: "primary100",
3291
- textColor: "primary700"
3292
- };
3293
- }
3294
- if (entry.status === "draft") {
3295
- return {
3296
- status: "draft",
3297
- label: "Draft",
3298
- badgeColor: "neutral150",
3299
- textColor: "neutral700"
3300
- };
3301
- }
3302
- if (entry.status === "published") {
3303
- return {
3304
- status: "published",
3305
- label: "Published",
3306
- badgeColor: "success100",
3307
- textColor: "success700"
3308
- };
3309
- }
3310
- if (col && col.draftAndPublish === false) {
3311
- return {
3312
- status: "published",
3313
- label: "Published",
3314
- badgeColor: "success100",
3315
- textColor: "success700"
3316
- };
3317
- }
3318
- if (entry.publishedAt) {
3319
- return {
3320
- status: "published",
3321
- label: "Published",
3322
- badgeColor: "success100",
3323
- textColor: "success700"
3324
- };
3325
- }
3326
- return {
3327
- status: "draft",
3328
- label: "Draft",
3329
- badgeColor: "neutral150",
3330
- textColor: "neutral700"
3331
- };
3332
- }
3333
3357
  function renderCellContent(colDef, entry, col, selectedAttribute, searchAttribute, isMatch, hasAttr, isSelected, onToggleSelect, pendingBatches = [], isDzExpanded = false, onToggleExpandDz, filterQuery = "") {
3334
3358
  switch (colDef.key) {
3335
3359
  case "select":
@@ -3736,12 +3760,23 @@ const BulkUpdater = () => {
3736
3760
  cols.forEach((col) => {
3737
3761
  const total = col.totalEntries !== void 0 ? col.totalEntries : col.entries ? col.entries.length : 0;
3738
3762
  const initialPageSize = col.isSingleType ? 1 : 10;
3763
+ const previewSlice = (col.entries || []).slice(0, initialPageSize).map((entry) => {
3764
+ if (!entry.status) {
3765
+ const lifecycle = getEntryLifecycleStatus(entry, col, pendingBatches);
3766
+ return {
3767
+ ...entry,
3768
+ status: lifecycle.status,
3769
+ publishedAt: entry.publishedAt ?? (lifecycle.status === "published" ? entry.updatedAt || null : null)
3770
+ };
3771
+ }
3772
+ return entry;
3773
+ });
3739
3774
  pages[col.uid] = {
3740
3775
  page: 1,
3741
3776
  pageSize: initialPageSize,
3742
3777
  total,
3743
3778
  pageCount: col.isSingleType ? 1 : Math.ceil(total / initialPageSize) || 1,
3744
- entries: (col.entries || []).slice(0, initialPageSize),
3779
+ entries: previewSlice,
3745
3780
  loading: false
3746
3781
  };
3747
3782
  initialVisibility[col.uid] = true;
@@ -3863,6 +3898,18 @@ const BulkUpdater = () => {
3863
3898
  const pageSizeNum = res.data.pagination?.pageSize ?? size ?? defaultPageSize;
3864
3899
  const totalNum = res.data.pagination?.total !== void 0 ? res.data.pagination.total : fetchedEntries.length;
3865
3900
  const pageCountNum = res.data.pagination?.pageCount ?? Math.max(1, Math.ceil(totalNum / pageSizeNum));
3901
+ const col = collections.find((c) => c.uid === colUid) || null;
3902
+ const enrichedEntries = fetchedEntries.map((entry) => {
3903
+ if (!entry.status) {
3904
+ const lifecycle = getEntryLifecycleStatus(entry, col, pendingBatches);
3905
+ return {
3906
+ ...entry,
3907
+ status: lifecycle.status,
3908
+ publishedAt: entry.publishedAt ?? (lifecycle.status === "published" ? entry.updatedAt || null : null)
3909
+ };
3910
+ }
3911
+ return entry;
3912
+ });
3866
3913
  setCollectionPages((prev) => ({
3867
3914
  ...prev,
3868
3915
  [colUid]: {
@@ -3870,7 +3917,7 @@ const BulkUpdater = () => {
3870
3917
  pageSize: pageSizeNum,
3871
3918
  total: totalNum,
3872
3919
  pageCount: pageCountNum,
3873
- entries: fetchedEntries,
3920
+ entries: enrichedEntries,
3874
3921
  loading: false
3875
3922
  }
3876
3923
  }));
@@ -4031,8 +4078,6 @@ const BulkUpdater = () => {
4031
4078
  const filteredCollections = React.useMemo(() => {
4032
4079
  return selectedCol ? [selectedCol] : [];
4033
4080
  }, [selectedCol]);
4034
- const [attributeSearch, setAttributeSearch] = React.useState("");
4035
- const [searchAttributeSearch, setSearchAttributeSearch] = React.useState("");
4036
4081
  const getAttrDepth = (attr) => attributesMeta[attr]?.depth ?? (attr.includes(".") ? attr.split(".").length - 1 : 0);
4037
4082
  const availableColAttributes = React.useMemo(() => {
4038
4083
  return selectedCol?.attributes || attributes || [];
@@ -4040,48 +4085,20 @@ const BulkUpdater = () => {
4040
4085
  const depthFilteredAttributes = React.useMemo(() => {
4041
4086
  return availableColAttributes.filter((attr) => getAttrDepth(attr) <= componentDepth);
4042
4087
  }, [availableColAttributes, componentDepth, attributesMeta]);
4043
- const filteredAttributes = React.useMemo(() => {
4044
- const q = attributeSearch.toLowerCase().trim();
4045
- if (!q || q === (selectedAttribute || "").toLowerCase().trim()) {
4046
- return depthFilteredAttributes;
4047
- }
4048
- const cleanQ = q.replace(/[^a-z0-9]/g, "");
4049
- return depthFilteredAttributes.filter((attr) => {
4050
- const lower = attr.toLowerCase();
4051
- const cleanLower = lower.replace(/[^a-z0-9]/g, "");
4052
- const rel = relationsMeta[attr];
4053
- const relTarget = (rel?.targetDisplayName || "").toLowerCase();
4054
- return lower.includes(q) || cleanLower.includes(cleanQ) || relTarget.includes(q);
4055
- });
4056
- }, [depthFilteredAttributes, attributeSearch, selectedAttribute, relationsMeta]);
4057
- const filteredSearchAttributes = React.useMemo(() => {
4058
- const q = searchAttributeSearch.toLowerCase().trim();
4059
- if (!q || q === (searchAttribute || "").toLowerCase().trim()) {
4060
- return depthFilteredAttributes;
4061
- }
4062
- const cleanQ = q.replace(/[^a-z0-9]/g, "");
4063
- return depthFilteredAttributes.filter((attr) => {
4064
- const lower = attr.toLowerCase();
4065
- const cleanLower = lower.replace(/[^a-z0-9]/g, "");
4066
- const rel = relationsMeta[attr];
4067
- const relTarget = (rel?.targetDisplayName || "").toLowerCase();
4068
- return lower.includes(q) || cleanLower.includes(cleanQ) || relTarget.includes(q);
4069
- });
4070
- }, [depthFilteredAttributes, searchAttributeSearch, searchAttribute, relationsMeta]);
4071
4088
  const memoizedComboboxOptions = React.useMemo(() => {
4072
- if (filteredAttributes.length === 0) {
4073
- 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: [
4074
- 'No matching attributes found for "',
4075
- attributeSearch,
4076
- '" (Depth L',
4089
+ if (depthFilteredAttributes.length === 0) {
4090
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: "__none__", disabled: true, textValue: "No attributes available", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
4091
+ "No attributes available for this collection type (Depth L",
4077
4092
  componentDepth,
4078
4093
  ")"
4079
4094
  ] }) });
4080
4095
  }
4081
- return filteredAttributes.map((attr) => {
4096
+ return depthFilteredAttributes.map((attr) => {
4082
4097
  const rel = relationsMeta[attr];
4083
4098
  const depth = getAttrDepth(attr);
4084
- 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: [
4099
+ const badgeText = rel ? `relation ${rel.targetDisplayName}` : depth > 0 ? `L${depth} component` : "";
4100
+ const textValue = badgeText ? `${attr} ${badgeText}` : attr;
4101
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: attr, textValue, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", width: "100%", gap: 2, children: [
4085
4102
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: attr === selectedAttribute ? "bold" : "regular", children: attr }),
4086
4103
  rel ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", size: "S", children: [
4087
4104
  "relation ➔ ",
@@ -4093,14 +4110,14 @@ const BulkUpdater = () => {
4093
4110
  ] }) : null
4094
4111
  ] }) }, attr);
4095
4112
  });
4096
- }, [filteredAttributes, relationsMeta, selectedAttribute, attributeSearch, componentDepth, attributesMeta]);
4113
+ }, [depthFilteredAttributes, relationsMeta, selectedAttribute, componentDepth, attributesMeta]);
4097
4114
  const memoizedSearchComboboxOptions = React.useMemo(() => {
4098
4115
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4099
4116
  /* @__PURE__ */ jsxRuntime.jsx(
4100
4117
  designSystem.ComboboxOption,
4101
4118
  {
4102
4119
  value: "__default__",
4103
- textValue: `Same as Update Attribute (${selectedAttribute || "None"})`,
4120
+ textValue: `Same as Update Attribute (${selectedAttribute || "None"}) default`,
4104
4121
  children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", width: "100%", gap: 2, children: [
4105
4122
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "omega", fontStyle: "italic", textColor: "neutral600", children: [
4106
4123
  "-- Same as Update Attribute (",
@@ -4111,10 +4128,12 @@ const BulkUpdater = () => {
4111
4128
  ] })
4112
4129
  }
4113
4130
  ),
4114
- filteredSearchAttributes.map((attr) => {
4131
+ depthFilteredAttributes.map((attr) => {
4115
4132
  const rel = relationsMeta[attr];
4116
4133
  const depth = getAttrDepth(attr);
4117
- 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: [
4134
+ const badgeText = rel ? `relation ${rel.targetDisplayName}` : depth > 0 ? `L${depth} component` : "";
4135
+ const textValue = badgeText ? `${attr} ${badgeText}` : attr;
4136
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: attr, textValue, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", width: "100%", gap: 2, children: [
4118
4137
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: attr === searchAttribute ? "bold" : "regular", children: attr }),
4119
4138
  rel ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", size: "S", children: [
4120
4139
  "relation ➔ ",
@@ -4127,7 +4146,7 @@ const BulkUpdater = () => {
4127
4146
  ] }) }, `search_${attr}`);
4128
4147
  })
4129
4148
  ] });
4130
- }, [filteredSearchAttributes, relationsMeta, searchAttribute, selectedAttribute, componentDepth, attributesMeta]);
4149
+ }, [depthFilteredAttributes, relationsMeta, searchAttribute, selectedAttribute, componentDepth, attributesMeta]);
4131
4150
  const handleSearchAttributeChange = (newAttr) => {
4132
4151
  const finalAttr = newAttr === "__default__" ? "" : newAttr;
4133
4152
  setSearchAttribute(finalAttr);
@@ -4302,7 +4321,6 @@ const BulkUpdater = () => {
4302
4321
  };
4303
4322
  const clearFilter = () => {
4304
4323
  setSearchAttribute("");
4305
- setSearchAttributeSearch("");
4306
4324
  setFilterOperator("none");
4307
4325
  setFilterQuery("");
4308
4326
  setEntrySearch("");
@@ -4373,6 +4391,7 @@ const BulkUpdater = () => {
4373
4391
  let modified = 0;
4374
4392
  let draft = 0;
4375
4393
  let pendingReview = 0;
4394
+ let unknown = 0;
4376
4395
  Object.keys(selectedEntries).forEach((key) => {
4377
4396
  if (!selectedEntries[key]) return;
4378
4397
  const parts = key.split(KEY_SEP);
@@ -4388,18 +4407,20 @@ const BulkUpdater = () => {
4388
4407
  else if (status === "modified") modified++;
4389
4408
  else if (status === "draft") draft++;
4390
4409
  else if (status === "pending_review") pendingReview++;
4410
+ else unknown++;
4391
4411
  } else {
4392
4412
  draft++;
4393
4413
  }
4394
4414
  });
4395
- const unreviewedCount = draft + modified + pendingReview;
4415
+ const unreviewedCount = draft + modified + pendingReview + unknown;
4396
4416
  return {
4397
4417
  published,
4398
4418
  modified,
4399
4419
  draft,
4400
4420
  pendingReview,
4421
+ unknown,
4401
4422
  unreviewedCount,
4402
- total: published + modified + draft + pendingReview
4423
+ total: published + modified + draft + pendingReview + unknown
4403
4424
  };
4404
4425
  }, [selectedEntries, collections, collectionPages, pendingBatches]);
4405
4426
  const handleOpenConfirmModal = () => {
@@ -5327,25 +5348,13 @@ const BulkUpdater = () => {
5327
5348
  placeholder: searchAttribute || `Same as Update (${selectedAttribute || "None"})`,
5328
5349
  value: searchAttribute || "__default__",
5329
5350
  clearLabel: "Reset search attribute",
5330
- onClear: () => {
5331
- handleSearchAttributeChange("");
5332
- setSearchAttributeSearch("");
5333
- },
5351
+ onClear: () => handleSearchAttributeChange(""),
5334
5352
  onFocus: (e) => {
5335
5353
  e.target.select();
5336
5354
  },
5337
- onInputChange: (e) => {
5338
- setSearchAttributeSearch(e.target.value);
5339
- },
5340
- onOpenChange: (isOpen) => {
5341
- if (!isOpen) {
5342
- setSearchAttributeSearch("");
5343
- }
5344
- },
5345
5355
  onChange: (val) => {
5346
5356
  if (val && typeof val === "string" && val !== "__none__") {
5347
5357
  handleSearchAttributeChange(val);
5348
- setSearchAttributeSearch("");
5349
5358
  }
5350
5359
  },
5351
5360
  children: memoizedSearchComboboxOptions
@@ -5418,7 +5427,8 @@ const BulkUpdater = () => {
5418
5427
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "pending_review", children: "Pending Review (Due)" }),
5419
5428
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "draft", children: "Draft" }),
5420
5429
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "published", children: "Published" }),
5421
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "modified", children: "Modified" })
5430
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "modified", children: "Modified" }),
5431
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "unknown", children: "Unknown" })
5422
5432
  ]
5423
5433
  }
5424
5434
  ) }),
@@ -6127,27 +6137,15 @@ const BulkUpdater = () => {
6127
6137
  placeholder: "Search attribute by name or relation...",
6128
6138
  value: selectedAttribute || void 0,
6129
6139
  clearLabel: "Clear attribute selection",
6130
- onClear: () => {
6131
- handleAttributeChange("");
6132
- setAttributeSearch("");
6133
- },
6140
+ onClear: () => handleAttributeChange(""),
6134
6141
  onFocus: (e) => {
6135
6142
  e.target.select();
6136
6143
  },
6137
- onInputChange: (e) => {
6138
- setAttributeSearch(e.target.value);
6139
- },
6140
- onOpenChange: (isOpen) => {
6141
- if (!isOpen) {
6142
- setAttributeSearch("");
6143
- }
6144
- },
6145
6144
  loading: loading && attributes.length === 0,
6146
6145
  loadingMessage: "Discovering schema attributes...",
6147
6146
  onChange: (val) => {
6148
6147
  if (val && typeof val === "string" && val !== "__none__") {
6149
6148
  handleAttributeChange(val);
6150
- setAttributeSearch("");
6151
6149
  }
6152
6150
  },
6153
6151
  children: memoizedComboboxOptions
@@ -7520,4 +7518,4 @@ exports.getNestedValue = getNestedValue;
7520
7518
  exports.getPropertyValue = getPropertyValue;
7521
7519
  exports.getSortOptionsForCollection = getSortOptionsForCollection;
7522
7520
  exports.parseDynamicZonePath = parseDynamicZonePath;
7523
- //# sourceMappingURL=BulkUpdater-CSmtXjbM.js.map
7521
+ //# sourceMappingURL=BulkUpdater-DMkx6dLf.js.map