strapi-plugin-bulk-updater 1.2.12 → 1.2.13

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.
@@ -3,7 +3,7 @@ import React__default, { useState, useEffect, useMemo, useRef } from "react";
3
3
  import { useFetchClient, useNotification } from "@strapi/strapi/admin";
4
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
5
  import { ArrowClockwise, Download, Upload, Check, Eye, Play } from "@strapi/icons";
6
- import { V as ValidationRulesTab } from "./ValidationRulesTab-BGcqM9cN.mjs";
6
+ import { V as ValidationRulesTab } from "./ValidationRulesTab-Dt53h-Ke.mjs";
7
7
  function CsvManagerTab() {
8
8
  const { get, post } = useFetchClient();
9
9
  const { toggleNotification } = useNotification();
@@ -114,7 +114,8 @@ function CsvManagerTab() {
114
114
  });
115
115
  return;
116
116
  }
117
- const blob = new Blob([content], { type: "text/csv;charset=utf-8;" });
117
+ const safeContent = content.startsWith("\uFEFF") ? content : "\uFEFF" + content;
118
+ const blob = new Blob([safeContent], { type: "text/csv;charset=utf-8;" });
118
119
  const url = URL.createObjectURL(blob);
119
120
  const link = document.createElement("a");
120
121
  link.href = url;
@@ -503,7 +504,9 @@ function formatVal(v) {
503
504
  return String(v);
504
505
  }
505
506
  function downloadFile(content, filename, mimeType = "text/csv;charset=utf-8;") {
506
- const blob = new Blob([content], { type: mimeType });
507
+ const isCsv = mimeType.includes("csv") || filename.endsWith(".csv");
508
+ const safeContent = isCsv && !content.startsWith("\uFEFF") ? "\uFEFF" + content : content;
509
+ const blob = new Blob([safeContent], { type: mimeType });
507
510
  const url = URL.createObjectURL(blob);
508
511
  const a = document.createElement("a");
509
512
  a.href = url;
@@ -625,7 +628,7 @@ function ProcessingDashboardModal({
625
628
  ].join(",")
626
629
  )
627
630
  ];
628
- downloadFile(csvRows.join("\n"), `bulk_update_report_${targetId}.csv`);
631
+ downloadFile(csvRows.join("\r\n") + "\r\n", `bulk_update_report_${targetId}.csv`);
629
632
  toggleNotification({
630
633
  type: "success",
631
634
  message: "Execution report downloaded successfully"
@@ -1646,6 +1649,7 @@ const BulkUpdater = () => {
1646
1649
  const [showPublishConfirmModal, setShowPublishConfirmModal] = useState(false);
1647
1650
  const [publishing, setPublishing] = useState(false);
1648
1651
  const [publishMakerNotesInput, setPublishMakerNotesInput] = useState("");
1652
+ const [bulkExporting, setBulkExporting] = useState(false);
1649
1653
  const MODELS_CACHE_KEY = "strapi_bulk_updater_models_cache";
1650
1654
  const LAST_ATTR_KEY = "strapi_bulk_updater_last_attr";
1651
1655
  const getInitialAttribute = () => {
@@ -2431,6 +2435,57 @@ const BulkUpdater = () => {
2431
2435
  setMakerNotesInput("");
2432
2436
  }
2433
2437
  };
2438
+ const handleBulkExportSelected = async () => {
2439
+ const targets = [];
2440
+ Object.keys(selectedEntries).forEach((key) => {
2441
+ if (selectedEntries[key]) {
2442
+ const parts = key.split(KEY_SEP);
2443
+ if (parts.length === 2) {
2444
+ targets.push({ uid: parts[0], documentId: parts[1] });
2445
+ }
2446
+ }
2447
+ });
2448
+ if (targets.length === 0) {
2449
+ toggleNotification({
2450
+ type: "warning",
2451
+ message: "Please select at least one entry to export"
2452
+ });
2453
+ return;
2454
+ }
2455
+ try {
2456
+ setBulkExporting(true);
2457
+ const res = await post("/bulk-updater/csv/export-bulk", { selectedEntries: targets });
2458
+ const csvStr = res.data?.csvContent || (typeof res.data === "string" ? res.data : "");
2459
+ if (!csvStr) {
2460
+ toggleNotification({
2461
+ type: "warning",
2462
+ message: "No CSV content returned for selected entries"
2463
+ });
2464
+ return;
2465
+ }
2466
+ const safeContent = csvStr.startsWith("\uFEFF") ? csvStr : "\uFEFF" + csvStr;
2467
+ const blob = new Blob([safeContent], { type: "text/csv;charset=utf-8;" });
2468
+ const url = URL.createObjectURL(blob);
2469
+ const link = document.createElement("a");
2470
+ link.href = url;
2471
+ link.setAttribute("download", res.data?.filename || `bulk_export_selected_${targets.length}.csv`);
2472
+ document.body.appendChild(link);
2473
+ link.click();
2474
+ document.body.removeChild(link);
2475
+ URL.revokeObjectURL(url);
2476
+ toggleNotification({
2477
+ type: "success",
2478
+ message: `Successfully exported ${res.data?.count || targets.length} selected records to Excel CSV`
2479
+ });
2480
+ } catch (e) {
2481
+ toggleNotification({
2482
+ type: "danger",
2483
+ message: e.message || "Failed exporting selected records to CSV"
2484
+ });
2485
+ } finally {
2486
+ setBulkExporting(false);
2487
+ }
2488
+ };
2434
2489
  const handleOpenPublishModal = () => {
2435
2490
  const totalSelected = Object.values(selectedEntries).filter(Boolean).length;
2436
2491
  if (totalSelected === 0) {
@@ -3074,6 +3129,23 @@ const BulkUpdater = () => {
3074
3129
  ")"
3075
3130
  ]
3076
3131
  }
3132
+ ),
3133
+ /* @__PURE__ */ jsxs(
3134
+ Button,
3135
+ {
3136
+ startIcon: /* @__PURE__ */ jsx(Download, {}),
3137
+ variant: "tertiary",
3138
+ size: "L",
3139
+ onClick: handleBulkExportSelected,
3140
+ loading: bulkExporting,
3141
+ disabled: loading || totalSelectedCount === 0,
3142
+ title: "Export selected entries to Excel-compatible CSV with full nested component support",
3143
+ children: [
3144
+ "Export Selected (",
3145
+ totalSelectedCount,
3146
+ ")"
3147
+ ]
3148
+ }
3077
3149
  )
3078
3150
  ] })
3079
3151
  ] }),
@@ -3575,6 +3647,10 @@ const BulkUpdater = () => {
3575
3647
  if (entrySearch && entrySearch.trim()) {
3576
3648
  params.set("search", entrySearch.trim());
3577
3649
  }
3650
+ const colSelectedDocIds = col.entries.filter((e) => selectedEntries[`${col.uid}${KEY_SEP}${e.documentId}`]).map((e) => e.documentId);
3651
+ if (colSelectedDocIds.length > 0) {
3652
+ params.set("documentIds", colSelectedDocIds.join(","));
3653
+ }
3578
3654
  const res = await get(`/bulk-updater/csv/export/${col.uid}?${params.toString()}`);
3579
3655
  const csvStr = res.data?.csvContent || (typeof res.data === "string" ? res.data : "");
3580
3656
  if (!csvStr) {
@@ -3584,7 +3660,8 @@ const BulkUpdater = () => {
3584
3660
  });
3585
3661
  return;
3586
3662
  }
3587
- const blob = new Blob([csvStr], { type: "text/csv;charset=utf-8;" });
3663
+ const safeContent = csvStr.startsWith("\uFEFF") ? csvStr : "\uFEFF" + csvStr;
3664
+ const blob = new Blob([safeContent], { type: "text/csv;charset=utf-8;" });
3588
3665
  const url = URL.createObjectURL(blob);
3589
3666
  const link = document.createElement("a");
3590
3667
  link.href = url;
@@ -3595,7 +3672,7 @@ const BulkUpdater = () => {
3595
3672
  URL.revokeObjectURL(url);
3596
3673
  toggleNotification({
3597
3674
  type: "success",
3598
- message: `Exported ${col.displayName} entries to CSV`
3675
+ message: `Exported ${res.data?.count ?? (colSelectedDocIds.length || col.entries.length)} ${col.displayName} entries to Excel CSV`
3599
3676
  });
3600
3677
  } catch (e) {
3601
3678
  toggleNotification({
@@ -4665,4 +4742,4 @@ export {
4665
4742
  getAvailableColumnsForCollection,
4666
4743
  getNestedValue
4667
4744
  };
4668
- //# sourceMappingURL=BulkUpdater-DvCSr1vU.mjs.map
4745
+ //# sourceMappingURL=BulkUpdater-Cfig2f7d.mjs.map