strapi-plugin-bulk-updater 1.2.9 → 1.2.11

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.
@@ -1117,6 +1117,330 @@ function ProcessingDashboardModal({
1117
1117
  }
1118
1118
  );
1119
1119
  }
1120
+ const BulkUpdatePreviewModal = ({
1121
+ isOpen,
1122
+ onClose,
1123
+ onProceed,
1124
+ loading,
1125
+ previewData,
1126
+ attribute,
1127
+ targetValueDisplay
1128
+ }) => {
1129
+ const [filterTab, setFilterTab] = React.useState("all");
1130
+ const [searchQuery, setSearchQuery] = React.useState("");
1131
+ const filteredDiffs = React.useMemo(() => {
1132
+ if (!previewData || !previewData.diffs) return [];
1133
+ let items = previewData.diffs;
1134
+ if (filterTab !== "all") {
1135
+ items = items.filter((d) => d.status === filterTab);
1136
+ }
1137
+ if (searchQuery.trim()) {
1138
+ const q = searchQuery.toLowerCase().trim();
1139
+ items = items.filter(
1140
+ (d) => d.label.toLowerCase().includes(q) || d.documentId.toLowerCase().includes(q) || d.collectionName.toLowerCase().includes(q) || String(d.oldDisplay || "").toLowerCase().includes(q) || String(d.newDisplay || "").toLowerCase().includes(q)
1141
+ );
1142
+ }
1143
+ return items;
1144
+ }, [previewData, filterTab, searchQuery]);
1145
+ if (!isOpen) return null;
1146
+ return /* @__PURE__ */ jsxRuntime.jsx(
1147
+ "div",
1148
+ {
1149
+ style: {
1150
+ position: "fixed",
1151
+ inset: 0,
1152
+ backgroundColor: "rgba(20, 20, 30, 0.7)",
1153
+ backdropFilter: "blur(3px)",
1154
+ display: "flex",
1155
+ alignItems: "center",
1156
+ justifyContent: "center",
1157
+ zIndex: 9999,
1158
+ padding: 16,
1159
+ boxSizing: "border-box"
1160
+ },
1161
+ onClick: onClose,
1162
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
1163
+ designSystem.Box,
1164
+ {
1165
+ background: "neutral0",
1166
+ hasRadius: true,
1167
+ shadow: "popupShadow",
1168
+ style: {
1169
+ maxWidth: 1040,
1170
+ width: "95%",
1171
+ maxHeight: "calc(100vh - 48px)",
1172
+ display: "flex",
1173
+ flexDirection: "column",
1174
+ overflow: "hidden",
1175
+ position: "relative",
1176
+ boxShadow: "0 24px 48px rgba(0, 0, 0, 0.35), 0 0 1px rgba(0, 0, 0, 0.2)"
1177
+ },
1178
+ onClick: (e) => e.stopPropagation(),
1179
+ children: [
1180
+ /* @__PURE__ */ jsxRuntime.jsx(
1181
+ designSystem.Box,
1182
+ {
1183
+ padding: 5,
1184
+ background: "neutral100",
1185
+ style: {
1186
+ borderBottom: "1px solid rgba(120, 120, 150, 0.2)",
1187
+ flexShrink: 0
1188
+ },
1189
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: [
1190
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
1191
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", marginBottom: 1, children: [
1192
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", fontWeight: "bold", children: "Pre-Update Change Preview" }),
1193
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
1194
+ "Target: ",
1195
+ attribute
1196
+ ] })
1197
+ ] }),
1198
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Inspect exact before & after values across target records before applying changes to the database." })
1199
+ ] }),
1200
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
1201
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "New Value:" }),
1202
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: targetValueDisplay || "(empty)" })
1203
+ ] })
1204
+ ] })
1205
+ }
1206
+ ),
1207
+ /* @__PURE__ */ jsxRuntime.jsx(
1208
+ designSystem.Box,
1209
+ {
1210
+ padding: 5,
1211
+ style: {
1212
+ flex: "1 1 auto",
1213
+ minHeight: 0,
1214
+ overflowY: "auto",
1215
+ WebkitOverflowScrolling: "touch"
1216
+ },
1217
+ children: loading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 8, style: { textAlign: "center" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "center", gap: 3, children: [
1218
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: "Inspecting target records from database..." }),
1219
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", fontWeight: "semiBold", children: "Inspecting Target Records..." }),
1220
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Computing proposed diffs and verifying against database schema." })
1221
+ ] }) }) : !previewData ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 6, style: { textAlign: "center" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: "No preview data available." }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1222
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, marginBottom: 4, wrap: "wrap", children: [
1223
+ /* @__PURE__ */ jsxRuntime.jsxs(
1224
+ designSystem.Box,
1225
+ {
1226
+ padding: 3,
1227
+ background: "neutral100",
1228
+ hasRadius: true,
1229
+ style: { flex: 1, minWidth: 140, border: "1px solid rgba(120, 120, 150, 0.15)" },
1230
+ children: [
1231
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", display: "block", children: "TOTAL TARGETS" }),
1232
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", fontWeight: "bold", textColor: "neutral800", children: previewData.summary.total })
1233
+ ]
1234
+ }
1235
+ ),
1236
+ /* @__PURE__ */ jsxRuntime.jsxs(
1237
+ designSystem.Box,
1238
+ {
1239
+ padding: 3,
1240
+ background: "success100",
1241
+ hasRadius: true,
1242
+ style: { flex: 1, minWidth: 140, border: "1px solid #b7eb8f" },
1243
+ children: [
1244
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "success700", display: "block", children: "WILL UPDATE" }),
1245
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", fontWeight: "bold", textColor: "success700", children: previewData.summary.willUpdate })
1246
+ ]
1247
+ }
1248
+ ),
1249
+ /* @__PURE__ */ jsxRuntime.jsxs(
1250
+ designSystem.Box,
1251
+ {
1252
+ padding: 3,
1253
+ background: "neutral100",
1254
+ hasRadius: true,
1255
+ style: { flex: 1, minWidth: 140, border: "1px solid rgba(120, 120, 150, 0.15)" },
1256
+ children: [
1257
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", display: "block", children: "ALREADY UP-TO-DATE" }),
1258
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", fontWeight: "bold", textColor: "neutral600", children: previewData.summary.unchanged })
1259
+ ]
1260
+ }
1261
+ ),
1262
+ /* @__PURE__ */ jsxRuntime.jsxs(
1263
+ designSystem.Box,
1264
+ {
1265
+ padding: 3,
1266
+ background: previewData.summary.skipped > 0 ? "warning100" : "neutral100",
1267
+ hasRadius: true,
1268
+ style: { flex: 1, minWidth: 140, border: "1px solid rgba(120, 120, 150, 0.15)" },
1269
+ children: [
1270
+ /* @__PURE__ */ jsxRuntime.jsx(
1271
+ designSystem.Typography,
1272
+ {
1273
+ variant: "sigma",
1274
+ textColor: previewData.summary.skipped > 0 ? "warning700" : "neutral600",
1275
+ display: "block",
1276
+ children: "WILL SKIP (NO FIELD)"
1277
+ }
1278
+ ),
1279
+ /* @__PURE__ */ jsxRuntime.jsx(
1280
+ designSystem.Typography,
1281
+ {
1282
+ variant: "beta",
1283
+ fontWeight: "bold",
1284
+ textColor: previewData.summary.skipped > 0 ? "warning700" : "neutral600",
1285
+ children: previewData.summary.skipped
1286
+ }
1287
+ )
1288
+ ]
1289
+ }
1290
+ )
1291
+ ] }),
1292
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 3, wrap: "wrap", gap: 3, children: [
1293
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, wrap: "wrap", children: [
1294
+ /* @__PURE__ */ jsxRuntime.jsxs(
1295
+ designSystem.Button,
1296
+ {
1297
+ variant: filterTab === "all" ? "default" : "tertiary",
1298
+ size: "S",
1299
+ onClick: () => setFilterTab("all"),
1300
+ children: [
1301
+ "All (",
1302
+ previewData.summary.total,
1303
+ ")"
1304
+ ]
1305
+ }
1306
+ ),
1307
+ /* @__PURE__ */ jsxRuntime.jsxs(
1308
+ designSystem.Button,
1309
+ {
1310
+ variant: filterTab === "will_update" ? "default" : "tertiary",
1311
+ size: "S",
1312
+ onClick: () => setFilterTab("will_update"),
1313
+ children: [
1314
+ "Will Update (",
1315
+ previewData.summary.willUpdate,
1316
+ ")"
1317
+ ]
1318
+ }
1319
+ ),
1320
+ /* @__PURE__ */ jsxRuntime.jsxs(
1321
+ designSystem.Button,
1322
+ {
1323
+ variant: filterTab === "unchanged" ? "default" : "tertiary",
1324
+ size: "S",
1325
+ onClick: () => setFilterTab("unchanged"),
1326
+ children: [
1327
+ "Unchanged (",
1328
+ previewData.summary.unchanged,
1329
+ ")"
1330
+ ]
1331
+ }
1332
+ ),
1333
+ previewData.summary.skipped > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
1334
+ designSystem.Button,
1335
+ {
1336
+ variant: filterTab === "will_skip" ? "default" : "tertiary",
1337
+ size: "S",
1338
+ onClick: () => setFilterTab("will_skip"),
1339
+ children: [
1340
+ "Skipped (",
1341
+ previewData.summary.skipped,
1342
+ ")"
1343
+ ]
1344
+ }
1345
+ )
1346
+ ] }),
1347
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 260 }, children: /* @__PURE__ */ jsxRuntime.jsx(
1348
+ designSystem.TextInput,
1349
+ {
1350
+ placeholder: "Search records or values...",
1351
+ size: "S",
1352
+ value: searchQuery,
1353
+ onChange: (e) => setSearchQuery(e.target.value)
1354
+ }
1355
+ ) })
1356
+ ] }),
1357
+ /* @__PURE__ */ jsxRuntime.jsx(
1358
+ designSystem.Box,
1359
+ {
1360
+ hasRadius: true,
1361
+ style: {
1362
+ border: "1px solid #eaeaef",
1363
+ maxHeight: 380,
1364
+ overflowY: "auto",
1365
+ overflowX: "auto"
1366
+ },
1367
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: 5, rowCount: filteredDiffs.length + 1, style: { width: "100%", minWidth: 780 }, children: [
1368
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
1369
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { width: 150 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: "COLLECTION" }) }),
1370
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { minWidth: 200 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: "RECORD IDENTIFIER" }) }),
1371
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { minWidth: 180 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: "CURRENT VALUE (BEFORE)" }) }),
1372
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { minWidth: 180 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: "NEW VALUE (AFTER)" }) }),
1373
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { width: 140, textAlign: "right" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: "IMPACT" }) })
1374
+ ] }) }),
1375
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: filteredDiffs.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { colSpan: 5, style: { textAlign: "center", padding: "24px" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: "No records match the current filter or search criteria." }) }) }) : filteredDiffs.map((diff) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
1376
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "neutral150", textColor: "neutral700", children: diff.collectionName }) }),
1377
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
1378
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", display: "block", children: diff.label }),
1379
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: diff.documentId }) })
1380
+ ] }) }),
1381
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(
1382
+ designSystem.Typography,
1383
+ {
1384
+ variant: "omega",
1385
+ style: {
1386
+ color: diff.hasChange ? "#666687" : "#272635",
1387
+ textDecoration: diff.hasChange ? "line-through" : "none",
1388
+ wordBreak: "break-all"
1389
+ },
1390
+ children: diff.oldDisplay || "(empty)"
1391
+ }
1392
+ ) }),
1393
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: diff.status === "will_skip" ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "warning700", children: "— Skipped (field absent) —" }) : diff.hasChange ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", textColor: "success700", style: { wordBreak: "break-all" }, children: diff.newDisplay || "(empty)" }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", children: diff.newDisplay || "(already matching)" }) }),
1394
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Td, { style: { textAlign: "right" }, children: [
1395
+ diff.status === "will_update" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: "✓ Will Update" }),
1396
+ diff.status === "unchanged" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "neutral150", textColor: "neutral600", children: "= Unchanged" }),
1397
+ diff.status === "will_skip" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: "⚠ Will Skip" })
1398
+ ] })
1399
+ ] }, `${diff.uid}-${diff.documentId}`)) })
1400
+ ] })
1401
+ }
1402
+ )
1403
+ ] })
1404
+ }
1405
+ ),
1406
+ /* @__PURE__ */ jsxRuntime.jsx(
1407
+ designSystem.Box,
1408
+ {
1409
+ padding: 4,
1410
+ background: "neutral100",
1411
+ style: {
1412
+ borderTop: "1px solid rgba(120, 120, 150, 0.2)",
1413
+ flexShrink: 0
1414
+ },
1415
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
1416
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", size: "M", onClick: onClose, children: "Close Preview" }),
1417
+ /* @__PURE__ */ jsxRuntime.jsxs(
1418
+ designSystem.Button,
1419
+ {
1420
+ variant: "default",
1421
+ size: "M",
1422
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Check, {}),
1423
+ onClick: () => {
1424
+ onClose();
1425
+ onProceed();
1426
+ },
1427
+ disabled: loading || !previewData || previewData.summary.total === 0,
1428
+ children: [
1429
+ "Proceed to Execute (",
1430
+ previewData?.summary.willUpdate || 0,
1431
+ " updates)"
1432
+ ]
1433
+ }
1434
+ )
1435
+ ] })
1436
+ }
1437
+ )
1438
+ ]
1439
+ }
1440
+ )
1441
+ }
1442
+ );
1443
+ };
1120
1444
  function getNestedValue(obj, path) {
1121
1445
  if (!obj || !path) return void 0;
1122
1446
  if (!path.includes(".")) return obj[path];
@@ -1311,6 +1635,9 @@ const BulkUpdater = () => {
1311
1635
  const [dashboardBatchId, setDashboardBatchId] = React.useState("");
1312
1636
  const [dashboardJob, setDashboardJob] = React.useState(null);
1313
1637
  const [isSubmittingBatch, setIsSubmittingBatch] = React.useState(false);
1638
+ const [showPreviewModal, setShowPreviewModal] = React.useState(false);
1639
+ const [previewLoading, setPreviewLoading] = React.useState(false);
1640
+ const [previewData, setPreviewData] = React.useState(null);
1314
1641
  const MODELS_CACHE_KEY = "strapi_bulk_updater_models_cache";
1315
1642
  const LAST_ATTR_KEY = "strapi_bulk_updater_last_attr";
1316
1643
  const getInitialAttribute = () => {
@@ -1463,10 +1790,13 @@ const BulkUpdater = () => {
1463
1790
  }
1464
1791
  }
1465
1792
  } catch (err) {
1466
- toggleNotification({
1467
- type: "danger",
1468
- message: err.message || "Failed to load collections data"
1469
- });
1793
+ const isAborted = err?.name === "AbortError" || err?.name === "CanceledError" || err?.code === "ERR_CANCELED" || err?.message?.includes("aborted") || err?.message?.includes("canceled");
1794
+ if (!isAborted) {
1795
+ toggleNotification({
1796
+ type: "danger",
1797
+ message: err?.message || "Failed to load collections data"
1798
+ });
1799
+ }
1470
1800
  } finally {
1471
1801
  setLoading(false);
1472
1802
  }
@@ -1507,13 +1837,17 @@ const BulkUpdater = () => {
1507
1837
  const res = await get(`/bulk-updater/collections/${colUid}/entries?${params.toString()}`);
1508
1838
  if (res.data && (res.data.success || res.data.entries !== void 0)) {
1509
1839
  const fetchedEntries = res.data.entries || [];
1840
+ const pageNum = res.data.pagination?.page ?? targetPage ?? 1;
1841
+ const pageSizeNum = res.data.pagination?.pageSize ?? size ?? defaultPageSize;
1842
+ const totalNum = res.data.pagination?.total !== void 0 ? res.data.pagination.total : fetchedEntries.length;
1843
+ const pageCountNum = res.data.pagination?.pageCount ?? Math.max(1, Math.ceil(totalNum / pageSizeNum));
1510
1844
  setCollectionPages((prev) => ({
1511
1845
  ...prev,
1512
1846
  [colUid]: {
1513
- page: res.data.pagination.page,
1514
- pageSize: res.data.pagination.pageSize,
1515
- total: res.data.pagination.total,
1516
- pageCount: res.data.pagination.pageCount,
1847
+ page: pageNum,
1848
+ pageSize: pageSizeNum,
1849
+ total: totalNum,
1850
+ pageCount: pageCountNum,
1517
1851
  entries: fetchedEntries,
1518
1852
  loading: false
1519
1853
  }
@@ -1530,10 +1864,13 @@ const BulkUpdater = () => {
1530
1864
  }
1531
1865
  }
1532
1866
  } catch (err) {
1533
- toggleNotification({
1534
- type: "danger",
1535
- message: `Failed to load entries for ${colUid}: ${err.message}`
1536
- });
1867
+ const isAborted = err?.name === "AbortError" || err?.name === "CanceledError" || err?.code === "ERR_CANCELED" || err?.message?.includes("aborted") || err?.message?.includes("canceled");
1868
+ if (!isAborted) {
1869
+ toggleNotification({
1870
+ type: "danger",
1871
+ message: `Failed to load entries for ${colUid}: ${err?.message || "Network error"}`
1872
+ });
1873
+ }
1537
1874
  setCollectionPages((prev) => ({
1538
1875
  ...prev,
1539
1876
  [colUid]: {
@@ -1557,10 +1894,13 @@ const BulkUpdater = () => {
1557
1894
  }
1558
1895
  }
1559
1896
  } catch (err) {
1560
- toggleNotification({
1561
- type: "danger",
1562
- message: err.message || "Failed to fetch audit history."
1563
- });
1897
+ const isAborted = err?.name === "AbortError" || err?.name === "CanceledError" || err?.code === "ERR_CANCELED" || err?.message?.includes("aborted") || err?.message?.includes("canceled");
1898
+ if (!isAborted) {
1899
+ toggleNotification({
1900
+ type: "danger",
1901
+ message: err?.message || "Failed to fetch audit history."
1902
+ });
1903
+ }
1564
1904
  } finally {
1565
1905
  setAuditLoading(false);
1566
1906
  }
@@ -1897,6 +2237,56 @@ const BulkUpdater = () => {
1897
2237
  }
1898
2238
  setShowConfirmModal(true);
1899
2239
  };
2240
+ const handleOpenPreviewModal = async () => {
2241
+ if (!selectedAttribute) {
2242
+ toggleNotification({
2243
+ type: "warning",
2244
+ message: "Please select an attribute from the dropdown"
2245
+ });
2246
+ return;
2247
+ }
2248
+ const targets = [];
2249
+ Object.keys(selectedEntries).forEach((key) => {
2250
+ if (selectedEntries[key]) {
2251
+ const parts = key.split(KEY_SEP);
2252
+ if (parts.length === 2) {
2253
+ targets.push({ uid: parts[0], documentId: parts[1] });
2254
+ }
2255
+ }
2256
+ });
2257
+ if (targets.length === 0) {
2258
+ toggleNotification({
2259
+ type: "warning",
2260
+ message: "Please select at least one collection type or entry to preview"
2261
+ });
2262
+ return;
2263
+ }
2264
+ setShowPreviewModal(true);
2265
+ setPreviewLoading(true);
2266
+ try {
2267
+ const res = await post("/bulk-updater/preview", {
2268
+ attribute: selectedAttribute,
2269
+ value: attributeValue,
2270
+ selectedEntries: targets,
2271
+ isRegex: useRegex,
2272
+ regexPattern,
2273
+ publishOnApproval
2274
+ });
2275
+ if (res.data && res.data.success) {
2276
+ setPreviewData(res.data);
2277
+ } else {
2278
+ throw new Error(res.data?.error || "Failed to generate preview");
2279
+ }
2280
+ } catch (err) {
2281
+ toggleNotification({
2282
+ type: "danger",
2283
+ message: err.message || "Failed to fetch preview diffs"
2284
+ });
2285
+ setShowPreviewModal(false);
2286
+ } finally {
2287
+ setPreviewLoading(false);
2288
+ }
2289
+ };
1900
2290
  const executeBatchWithMode = async (executionMode, forceImmediate = false) => {
1901
2291
  const targets = [];
1902
2292
  Object.keys(selectedEntries).forEach((key) => {
@@ -2485,22 +2875,40 @@ const BulkUpdater = () => {
2485
2875
  onChange: (e) => setAttributeValue(e.target.value)
2486
2876
  }
2487
2877
  ) }),
2488
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { children: /* @__PURE__ */ jsxRuntime.jsxs(
2489
- designSystem.Button,
2490
- {
2491
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Play, {}),
2492
- variant: "default",
2493
- size: "L",
2494
- onClick: handleOpenConfirmModal,
2495
- loading: updating,
2496
- disabled: loading || totalSelectedCount === 0,
2497
- children: [
2498
- "Update Selected (",
2499
- totalSelectedCount,
2500
- ")"
2501
- ]
2502
- }
2503
- ) })
2878
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
2879
+ /* @__PURE__ */ jsxRuntime.jsxs(
2880
+ designSystem.Button,
2881
+ {
2882
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Eye, {}),
2883
+ variant: "secondary",
2884
+ size: "L",
2885
+ onClick: handleOpenPreviewModal,
2886
+ loading: previewLoading,
2887
+ disabled: loading || totalSelectedCount === 0 || !selectedAttribute,
2888
+ children: [
2889
+ "Preview Changes (",
2890
+ totalSelectedCount,
2891
+ ")"
2892
+ ]
2893
+ }
2894
+ ),
2895
+ /* @__PURE__ */ jsxRuntime.jsxs(
2896
+ designSystem.Button,
2897
+ {
2898
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Play, {}),
2899
+ variant: "default",
2900
+ size: "L",
2901
+ onClick: handleOpenConfirmModal,
2902
+ loading: updating,
2903
+ disabled: loading || totalSelectedCount === 0 || !selectedAttribute,
2904
+ children: [
2905
+ "Update Selected (",
2906
+ totalSelectedCount,
2907
+ ")"
2908
+ ]
2909
+ }
2910
+ )
2911
+ ] })
2504
2912
  ] }),
2505
2913
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 4, padding: 4, background: "neutral100", hasRadius: true, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: [
2506
2914
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, alignItems: "center", children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -3677,6 +4085,22 @@ const BulkUpdater = () => {
3677
4085
  children: publishOnApproval ? "Publish Immediately" : "Keep as Draft"
3678
4086
  }
3679
4087
  )
4088
+ ] }),
4089
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginTop: 2, paddingTop: 2, style: { borderTop: "1px solid #eaeaef" }, children: [
4090
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Need to inspect before & after values?" }),
4091
+ /* @__PURE__ */ jsxRuntime.jsx(
4092
+ designSystem.Button,
4093
+ {
4094
+ variant: "secondary",
4095
+ size: "S",
4096
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Eye, {}),
4097
+ onClick: () => {
4098
+ setShowConfirmModal(false);
4099
+ handleOpenPreviewModal();
4100
+ },
4101
+ children: "Open Change Preview"
4102
+ }
4103
+ )
3680
4104
  ] })
3681
4105
  ] })
3682
4106
  ] }),
@@ -3813,6 +4237,21 @@ const BulkUpdater = () => {
3813
4237
  )
3814
4238
  }
3815
4239
  ),
4240
+ /* @__PURE__ */ jsxRuntime.jsx(
4241
+ BulkUpdatePreviewModal,
4242
+ {
4243
+ isOpen: showPreviewModal,
4244
+ onClose: () => setShowPreviewModal(false),
4245
+ onProceed: () => {
4246
+ setShowPreviewModal(false);
4247
+ handleOpenConfirmModal();
4248
+ },
4249
+ loading: previewLoading,
4250
+ previewData,
4251
+ attribute: selectedAttribute,
4252
+ targetValueDisplay: relationsMeta[selectedAttribute] ? selectedRelationDocId === "__clear__" ? "Clear / Unlink Relation" : relationOptions.find((o) => o.documentId === selectedRelationDocId)?.label || selectedRelationDocId || "(none)" : useRegex ? `Replace /${regexPattern}/g with "${attributeValue}"` : attributeValue
4253
+ }
4254
+ ),
3816
4255
  /* @__PURE__ */ jsxRuntime.jsx(
3817
4256
  ProcessingDashboardModal,
3818
4257
  {
@@ -3831,4 +4270,4 @@ exports.default = BulkUpdater;
3831
4270
  exports.formatDisplayValue = formatDisplayValue;
3832
4271
  exports.getAvailableColumnsForCollection = getAvailableColumnsForCollection;
3833
4272
  exports.getNestedValue = getNestedValue;
3834
- //# sourceMappingURL=BulkUpdater-Q4rX3L5w.js.map
4273
+ //# sourceMappingURL=BulkUpdater-DS6gm_qd.js.map