strapi-plugin-bulk-updater 1.4.9 → 1.5.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 +3 -0
- package/dist/_chunks/{BulkUpdater-CHdA0546.mjs → BulkUpdater-By9Dd_YF.mjs} +659 -80
- package/dist/_chunks/BulkUpdater-By9Dd_YF.mjs.map +1 -0
- package/dist/_chunks/{BulkUpdater-C0uPoso2.js → BulkUpdater-CSmtXjbM.js} +658 -79
- package/dist/_chunks/BulkUpdater-CSmtXjbM.js.map +1 -0
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +646 -152
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +646 -152
- package/dist/server/index.mjs.map +1 -1
- package/package.json +3 -3
- package/dist/_chunks/BulkUpdater-C0uPoso2.js.map +0 -1
- package/dist/_chunks/BulkUpdater-CHdA0546.mjs.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import React__default, { useState, useEffect, useMemo, useRef } from "react";
|
|
3
3
|
import { useFetchClient, useNotification } from "@strapi/strapi/admin";
|
|
4
|
-
import { Box, Flex, Typography, Button, SingleSelect, SingleSelectOption, Badge, Checkbox, Alert, Table, Thead, Tr, Th, Tbody, Td, TextInput, Loader,
|
|
4
|
+
import { Box, Flex, Typography, Button, Combobox, ComboboxOption, SingleSelect, SingleSelectOption, Badge, Checkbox, Alert, Table, Thead, Tr, Th, Tbody, Td, TextInput, Loader, Field } from "@strapi/design-system";
|
|
5
5
|
import { ArrowClockwise, Download, Upload, ChevronLeft, ChevronRight, Eye, Check, Cog, Play } from "@strapi/icons";
|
|
6
6
|
import { i as isAbortError, V as ValidationRulesTab } from "./ValidationRulesTab-BtOCWK8E.mjs";
|
|
7
7
|
function parseCsvRows(csvText) {
|
|
@@ -162,8 +162,8 @@ function CsvManagerTab() {
|
|
|
162
162
|
const [showConfirmModal, setShowConfirmModal] = useState(false);
|
|
163
163
|
const [makerNotesInput, setMakerNotesInput] = useState("");
|
|
164
164
|
const [submittingBatch, setSubmittingBatch] = useState(false);
|
|
165
|
-
const [publishStrategy, setPublishStrategy] = useState("
|
|
166
|
-
const [unreviewedConsent, setUnreviewedConsent] = useState(
|
|
165
|
+
const [publishStrategy, setPublishStrategy] = useState("all");
|
|
166
|
+
const [unreviewedConsent, setUnreviewedConsent] = useState(true);
|
|
167
167
|
useEffect(() => {
|
|
168
168
|
fetchInitialData();
|
|
169
169
|
}, []);
|
|
@@ -480,23 +480,36 @@ function CsvManagerTab() {
|
|
|
480
480
|
/* @__PURE__ */ jsxs(Box, { style: { flex: 1 }, children: [
|
|
481
481
|
/* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", marginBottom: 2, children: "Target Collection Type" }),
|
|
482
482
|
/* @__PURE__ */ jsx(
|
|
483
|
-
|
|
483
|
+
Combobox,
|
|
484
484
|
{
|
|
485
|
-
value: selectedUid,
|
|
486
|
-
|
|
487
|
-
|
|
485
|
+
value: selectedUid || void 0,
|
|
486
|
+
autocomplete: { type: "list", filter: "contains" },
|
|
487
|
+
placeholder: "Search & select a collection...",
|
|
488
|
+
disabled: loadingCollections,
|
|
489
|
+
clearLabel: "Clear collection selection",
|
|
490
|
+
onClear: () => {
|
|
491
|
+
setSelectedUid("");
|
|
488
492
|
setImportResult(null);
|
|
489
493
|
setImportProgress(null);
|
|
490
494
|
},
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
495
|
+
onFocus: (e) => {
|
|
496
|
+
e.target.select();
|
|
497
|
+
},
|
|
498
|
+
onChange: (val) => {
|
|
499
|
+
if (val && typeof val === "string") {
|
|
500
|
+
setSelectedUid(val);
|
|
501
|
+
setImportResult(null);
|
|
502
|
+
setImportProgress(null);
|
|
503
|
+
}
|
|
504
|
+
},
|
|
505
|
+
children: collections.map((col) => /* @__PURE__ */ jsxs(ComboboxOption, { value: col.uid, textValue: `${col.displayName} (${col.uid})`, children: [
|
|
494
506
|
col.displayName,
|
|
495
507
|
" (",
|
|
496
508
|
col.totalEntries ?? 0,
|
|
497
509
|
" records)"
|
|
498
510
|
] }, col.uid))
|
|
499
|
-
}
|
|
511
|
+
},
|
|
512
|
+
`csv-col-${selectedUid}`
|
|
500
513
|
)
|
|
501
514
|
] }),
|
|
502
515
|
/* @__PURE__ */ jsx(
|
|
@@ -591,7 +604,13 @@ function CsvManagerTab() {
|
|
|
591
604
|
Checkbox,
|
|
592
605
|
{
|
|
593
606
|
checked: publishOnImport,
|
|
594
|
-
onValueChange: (val) =>
|
|
607
|
+
onValueChange: (val) => {
|
|
608
|
+
setPublishOnImport(val);
|
|
609
|
+
if (val) {
|
|
610
|
+
setPublishStrategy("all");
|
|
611
|
+
setUnreviewedConsent(true);
|
|
612
|
+
}
|
|
613
|
+
},
|
|
595
614
|
children: "Auto-publish on import"
|
|
596
615
|
}
|
|
597
616
|
)
|
|
@@ -854,8 +873,13 @@ function CsvManagerTab() {
|
|
|
854
873
|
size: "L",
|
|
855
874
|
startIcon: /* @__PURE__ */ jsx(Check, {}),
|
|
856
875
|
onClick: () => {
|
|
857
|
-
|
|
858
|
-
|
|
876
|
+
if (publishOnImport) {
|
|
877
|
+
setPublishStrategy("all");
|
|
878
|
+
setUnreviewedConsent(true);
|
|
879
|
+
} else {
|
|
880
|
+
setPublishStrategy("safe");
|
|
881
|
+
setUnreviewedConsent(false);
|
|
882
|
+
}
|
|
859
883
|
setShowConfirmModal(true);
|
|
860
884
|
},
|
|
861
885
|
loading: importing || submittingBatch,
|
|
@@ -1178,7 +1202,7 @@ function CsvManagerTab() {
|
|
|
1178
1202
|
] })
|
|
1179
1203
|
}
|
|
1180
1204
|
),
|
|
1181
|
-
publishOnImport &&
|
|
1205
|
+
publishOnImport && /* @__PURE__ */ jsxs(
|
|
1182
1206
|
Box,
|
|
1183
1207
|
{
|
|
1184
1208
|
padding: 4,
|
|
@@ -1187,7 +1211,7 @@ function CsvManagerTab() {
|
|
|
1187
1211
|
marginBottom: 4,
|
|
1188
1212
|
style: { border: "1px solid #d9d8ff" },
|
|
1189
1213
|
children: [
|
|
1190
|
-
/* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", marginBottom: 3, display: "block", children: "PUBLISHING STRATEGY & GOVERNANCE
|
|
1214
|
+
/* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", marginBottom: 3, display: "block", children: "PUBLISHING STRATEGY & GOVERNANCE" }),
|
|
1191
1215
|
/* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, children: [
|
|
1192
1216
|
/* @__PURE__ */ jsx(
|
|
1193
1217
|
Box,
|
|
@@ -1195,40 +1219,32 @@ function CsvManagerTab() {
|
|
|
1195
1219
|
padding: 3,
|
|
1196
1220
|
hasRadius: true,
|
|
1197
1221
|
style: {
|
|
1198
|
-
border: publishStrategy === "
|
|
1199
|
-
backgroundColor: publishStrategy === "
|
|
1222
|
+
border: publishStrategy === "all" ? "2px solid #4945ff" : "1px solid #eaeaef",
|
|
1223
|
+
backgroundColor: publishStrategy === "all" ? "#f0f0ff" : "#ffffff",
|
|
1200
1224
|
cursor: "pointer"
|
|
1201
1225
|
},
|
|
1202
1226
|
onClick: () => {
|
|
1203
|
-
setPublishStrategy("
|
|
1204
|
-
setUnreviewedConsent(
|
|
1227
|
+
setPublishStrategy("all");
|
|
1228
|
+
setUnreviewedConsent(true);
|
|
1205
1229
|
},
|
|
1206
1230
|
children: /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "flex-start", children: [
|
|
1207
1231
|
/* @__PURE__ */ jsx(
|
|
1208
1232
|
"input",
|
|
1209
1233
|
{
|
|
1210
1234
|
type: "radio",
|
|
1211
|
-
id: "csv-strategy-
|
|
1235
|
+
id: "csv-strategy-all",
|
|
1212
1236
|
name: "csvPublishStrategy",
|
|
1213
|
-
checked: publishStrategy === "
|
|
1237
|
+
checked: publishStrategy === "all",
|
|
1214
1238
|
onChange: () => {
|
|
1215
|
-
setPublishStrategy("
|
|
1216
|
-
setUnreviewedConsent(
|
|
1239
|
+
setPublishStrategy("all");
|
|
1240
|
+
setUnreviewedConsent(true);
|
|
1217
1241
|
},
|
|
1218
1242
|
style: { marginTop: 3 }
|
|
1219
1243
|
}
|
|
1220
1244
|
),
|
|
1221
1245
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
1222
|
-
/* @__PURE__ */ jsx(Typography, { fontWeight: "bold", display: "block", children: "
|
|
1223
|
-
/* @__PURE__ */
|
|
1224
|
-
"Only publish records that are already approved & live (",
|
|
1225
|
-
preflightReport?.lifecycleStats?.published ?? 0,
|
|
1226
|
-
" record",
|
|
1227
|
-
(preflightReport?.lifecycleStats?.published ?? 0) === 1 ? "" : "s",
|
|
1228
|
-
"). The ",
|
|
1229
|
-
preflightReport?.lifecycleStats?.unreviewedCount ?? 0,
|
|
1230
|
-
" unreviewed or draft record(s) will receive the CSV update in Draft mode without publishing unreviewed draft revisions to visitors."
|
|
1231
|
-
] })
|
|
1246
|
+
/* @__PURE__ */ jsx(Typography, { fontWeight: "bold", textColor: "primary700", display: "block", children: "🚀 Publish All Imported Records Live (Immediate)" }),
|
|
1247
|
+
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Publish all affected records live to production immediately upon import execution." })
|
|
1232
1248
|
] })
|
|
1233
1249
|
] })
|
|
1234
1250
|
}
|
|
@@ -1239,56 +1255,32 @@ function CsvManagerTab() {
|
|
|
1239
1255
|
padding: 3,
|
|
1240
1256
|
hasRadius: true,
|
|
1241
1257
|
style: {
|
|
1242
|
-
border: publishStrategy === "
|
|
1243
|
-
backgroundColor: publishStrategy === "
|
|
1258
|
+
border: publishStrategy === "safe" ? "2px solid #4945ff" : "1px solid #eaeaef",
|
|
1259
|
+
backgroundColor: publishStrategy === "safe" ? "#f0f0ff" : "#ffffff",
|
|
1244
1260
|
cursor: "pointer"
|
|
1245
1261
|
},
|
|
1246
|
-
onClick: () =>
|
|
1262
|
+
onClick: () => {
|
|
1263
|
+
setPublishStrategy("safe");
|
|
1264
|
+
setUnreviewedConsent(false);
|
|
1265
|
+
},
|
|
1247
1266
|
children: /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "flex-start", children: [
|
|
1248
1267
|
/* @__PURE__ */ jsx(
|
|
1249
1268
|
"input",
|
|
1250
1269
|
{
|
|
1251
1270
|
type: "radio",
|
|
1252
|
-
id: "csv-strategy-
|
|
1271
|
+
id: "csv-strategy-safe",
|
|
1253
1272
|
name: "csvPublishStrategy",
|
|
1254
|
-
checked: publishStrategy === "
|
|
1255
|
-
onChange: () =>
|
|
1273
|
+
checked: publishStrategy === "safe",
|
|
1274
|
+
onChange: () => {
|
|
1275
|
+
setPublishStrategy("safe");
|
|
1276
|
+
setUnreviewedConsent(false);
|
|
1277
|
+
},
|
|
1256
1278
|
style: { marginTop: 3 }
|
|
1257
1279
|
}
|
|
1258
1280
|
),
|
|
1259
1281
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
1260
|
-
/* @__PURE__ */ jsx(Typography, { fontWeight: "bold",
|
|
1261
|
-
/* @__PURE__ */
|
|
1262
|
-
"Publish all affected records live to production immediately, pushing all pending unreviewed draft edits in the ",
|
|
1263
|
-
preflightReport?.lifecycleStats?.unreviewedCount ?? 0,
|
|
1264
|
-
" draft/modified record(s) live."
|
|
1265
|
-
] })
|
|
1266
|
-
] })
|
|
1267
|
-
] })
|
|
1268
|
-
}
|
|
1269
|
-
),
|
|
1270
|
-
publishStrategy === "all" && /* @__PURE__ */ jsx(
|
|
1271
|
-
Box,
|
|
1272
|
-
{
|
|
1273
|
-
padding: 3,
|
|
1274
|
-
background: "danger100",
|
|
1275
|
-
hasRadius: true,
|
|
1276
|
-
style: { border: "1px solid #f5c0b8" },
|
|
1277
|
-
children: /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "flex-start", children: [
|
|
1278
|
-
/* @__PURE__ */ jsx(
|
|
1279
|
-
Checkbox,
|
|
1280
|
-
{
|
|
1281
|
-
checked: unreviewedConsent,
|
|
1282
|
-
onValueChange: (checked) => setUnreviewedConsent(Boolean(checked))
|
|
1283
|
-
}
|
|
1284
|
-
),
|
|
1285
|
-
/* @__PURE__ */ jsxs(Box, { style: { cursor: "pointer" }, onClick: () => setUnreviewedConsent(!unreviewedConsent), children: [
|
|
1286
|
-
/* @__PURE__ */ jsx(Typography, { fontWeight: "bold", textColor: "danger700", display: "block", children: "Mandatory Risk Consent Required" }),
|
|
1287
|
-
/* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "danger700", children: [
|
|
1288
|
-
"I understand and explicitly consent that ",
|
|
1289
|
-
preflightReport?.lifecycleStats?.unreviewedCount ?? 0,
|
|
1290
|
-
" record(s) with unreviewed draft revisions or saved draft states will be published live to production visitors."
|
|
1291
|
-
] })
|
|
1282
|
+
/* @__PURE__ */ jsx(Typography, { fontWeight: "bold", display: "block", children: "🛡️ Safe Publishing (Only Publish Pre-Approved Live Records)" }),
|
|
1283
|
+
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Only publish records that are already approved & live. Any unreviewed or draft records will receive the CSV update in Draft mode without publishing to visitors." })
|
|
1292
1284
|
] })
|
|
1293
1285
|
] })
|
|
1294
1286
|
}
|
|
@@ -2356,6 +2348,511 @@ const BulkUpdatePreviewModal = ({
|
|
|
2356
2348
|
}
|
|
2357
2349
|
);
|
|
2358
2350
|
};
|
|
2351
|
+
const CheckerReviewModal = ({
|
|
2352
|
+
isOpen,
|
|
2353
|
+
onClose,
|
|
2354
|
+
batch,
|
|
2355
|
+
onApprove,
|
|
2356
|
+
onReject,
|
|
2357
|
+
approving,
|
|
2358
|
+
rejecting
|
|
2359
|
+
}) => {
|
|
2360
|
+
const { get } = useFetchClient();
|
|
2361
|
+
const { toggleNotification } = useNotification();
|
|
2362
|
+
const [loading, setLoading] = useState(false);
|
|
2363
|
+
const [error, setError] = useState(null);
|
|
2364
|
+
const [diffs, setDiffs] = useState([]);
|
|
2365
|
+
const [summary, setSummary] = useState({
|
|
2366
|
+
total: 0,
|
|
2367
|
+
willUpdate: 0,
|
|
2368
|
+
willCreate: 0,
|
|
2369
|
+
unchanged: 0,
|
|
2370
|
+
skipped: 0
|
|
2371
|
+
});
|
|
2372
|
+
const [filterTab, setFilterTab] = useState("all");
|
|
2373
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
2374
|
+
const [isExporting, setIsExporting] = useState(false);
|
|
2375
|
+
const [showRejectInput, setShowRejectInput] = useState(false);
|
|
2376
|
+
const [rejectionReason, setRejectionReason] = useState("");
|
|
2377
|
+
const fetchBatchDiffs = async () => {
|
|
2378
|
+
if (!batch || !batch.id) return;
|
|
2379
|
+
try {
|
|
2380
|
+
setLoading(true);
|
|
2381
|
+
setError(null);
|
|
2382
|
+
const res = await get(`/bulk-updater/batches/${batch.id}/diffs`);
|
|
2383
|
+
if (res.data && res.data.success) {
|
|
2384
|
+
setDiffs(res.data.diffs || []);
|
|
2385
|
+
setSummary({
|
|
2386
|
+
total: res.data.summary?.total ?? res.data.diffs?.length ?? 0,
|
|
2387
|
+
willUpdate: res.data.summary?.willUpdate ?? 0,
|
|
2388
|
+
willCreate: res.data.summary?.willCreate ?? 0,
|
|
2389
|
+
unchanged: res.data.summary?.unchanged ?? 0,
|
|
2390
|
+
skipped: res.data.summary?.skipped ?? 0
|
|
2391
|
+
});
|
|
2392
|
+
} else {
|
|
2393
|
+
setError(res.data?.error || "Failed to inspect pending batch changes.");
|
|
2394
|
+
}
|
|
2395
|
+
} catch (err) {
|
|
2396
|
+
setError(err.message || "An error occurred while fetching batch diffs.");
|
|
2397
|
+
} finally {
|
|
2398
|
+
setLoading(false);
|
|
2399
|
+
}
|
|
2400
|
+
};
|
|
2401
|
+
useEffect(() => {
|
|
2402
|
+
if (isOpen && batch) {
|
|
2403
|
+
setShowRejectInput(false);
|
|
2404
|
+
setRejectionReason("");
|
|
2405
|
+
setFilterTab("all");
|
|
2406
|
+
setSearchQuery("");
|
|
2407
|
+
fetchBatchDiffs();
|
|
2408
|
+
}
|
|
2409
|
+
}, [isOpen, batch?.id]);
|
|
2410
|
+
const handleExportCsv = async () => {
|
|
2411
|
+
if (!batch || !batch.id) return;
|
|
2412
|
+
try {
|
|
2413
|
+
setIsExporting(true);
|
|
2414
|
+
const res = await get(`/bulk-updater/batches/${batch.id}/diffs/export`);
|
|
2415
|
+
if (res.data && res.data.success && res.data.csvContent) {
|
|
2416
|
+
const safeContent = res.data.csvContent.startsWith("\uFEFF") ? res.data.csvContent : "\uFEFF" + res.data.csvContent;
|
|
2417
|
+
const blob = new Blob([safeContent], { type: "text/csv;charset=utf-8;" });
|
|
2418
|
+
const url = URL.createObjectURL(blob);
|
|
2419
|
+
const link = document.createElement("a");
|
|
2420
|
+
link.href = url;
|
|
2421
|
+
link.setAttribute("download", res.data.filename || `batch_${batch.id}_review.csv`);
|
|
2422
|
+
document.body.appendChild(link);
|
|
2423
|
+
link.click();
|
|
2424
|
+
document.body.removeChild(link);
|
|
2425
|
+
URL.revokeObjectURL(url);
|
|
2426
|
+
toggleNotification({
|
|
2427
|
+
type: "success",
|
|
2428
|
+
message: `Exported ${res.data.count || diffs.length} batch changes to audit CSV.`
|
|
2429
|
+
});
|
|
2430
|
+
} else {
|
|
2431
|
+
toggleNotification({
|
|
2432
|
+
type: "danger",
|
|
2433
|
+
message: "Failed to generate review CSV export."
|
|
2434
|
+
});
|
|
2435
|
+
}
|
|
2436
|
+
} catch (err) {
|
|
2437
|
+
toggleNotification({
|
|
2438
|
+
type: "danger",
|
|
2439
|
+
message: err.message || "Error downloading review CSV."
|
|
2440
|
+
});
|
|
2441
|
+
} finally {
|
|
2442
|
+
setIsExporting(false);
|
|
2443
|
+
}
|
|
2444
|
+
};
|
|
2445
|
+
const filteredDiffs = useMemo(() => {
|
|
2446
|
+
let items = diffs;
|
|
2447
|
+
if (filterTab === "changes") {
|
|
2448
|
+
items = items.filter((d) => d.hasChange || d.status === "will_create");
|
|
2449
|
+
} else if (filterTab === "will_update") {
|
|
2450
|
+
items = items.filter((d) => d.status === "will_update");
|
|
2451
|
+
} else if (filterTab === "will_create") {
|
|
2452
|
+
items = items.filter((d) => d.status === "will_create");
|
|
2453
|
+
} else if (filterTab === "unchanged") {
|
|
2454
|
+
items = items.filter((d) => d.status === "unchanged");
|
|
2455
|
+
} else if (filterTab === "will_skip") {
|
|
2456
|
+
items = items.filter((d) => d.status === "will_skip");
|
|
2457
|
+
}
|
|
2458
|
+
if (searchQuery.trim()) {
|
|
2459
|
+
const q = searchQuery.toLowerCase().trim();
|
|
2460
|
+
items = items.filter(
|
|
2461
|
+
(d) => d.label?.toLowerCase().includes(q) || d.documentId?.toLowerCase().includes(q) || d.collectionName?.toLowerCase().includes(q) || d.attribute?.toLowerCase().includes(q) || String(d.oldDisplay || "").toLowerCase().includes(q) || String(d.newDisplay || "").toLowerCase().includes(q)
|
|
2462
|
+
);
|
|
2463
|
+
}
|
|
2464
|
+
return items;
|
|
2465
|
+
}, [diffs, filterTab, searchQuery]);
|
|
2466
|
+
if (!isOpen || !batch) return null;
|
|
2467
|
+
const totalChanges = (summary.willUpdate || 0) + (summary.willCreate || 0);
|
|
2468
|
+
return /* @__PURE__ */ jsx(
|
|
2469
|
+
"div",
|
|
2470
|
+
{
|
|
2471
|
+
style: {
|
|
2472
|
+
position: "fixed",
|
|
2473
|
+
inset: 0,
|
|
2474
|
+
backgroundColor: "rgba(20, 20, 30, 0.75)",
|
|
2475
|
+
backdropFilter: "blur(4px)",
|
|
2476
|
+
display: "flex",
|
|
2477
|
+
alignItems: "center",
|
|
2478
|
+
justifyContent: "center",
|
|
2479
|
+
zIndex: 9999,
|
|
2480
|
+
padding: 16,
|
|
2481
|
+
boxSizing: "border-box"
|
|
2482
|
+
},
|
|
2483
|
+
onClick: onClose,
|
|
2484
|
+
children: /* @__PURE__ */ jsxs(
|
|
2485
|
+
Box,
|
|
2486
|
+
{
|
|
2487
|
+
background: "neutral0",
|
|
2488
|
+
hasRadius: true,
|
|
2489
|
+
shadow: "popupShadow",
|
|
2490
|
+
style: {
|
|
2491
|
+
maxWidth: 1160,
|
|
2492
|
+
width: "96%",
|
|
2493
|
+
maxHeight: "calc(100vh - 40px)",
|
|
2494
|
+
display: "flex",
|
|
2495
|
+
flexDirection: "column",
|
|
2496
|
+
overflow: "hidden",
|
|
2497
|
+
position: "relative",
|
|
2498
|
+
boxShadow: "0 24px 50px rgba(0, 0, 0, 0.4), 0 0 1px rgba(0, 0, 0, 0.25)"
|
|
2499
|
+
},
|
|
2500
|
+
onClick: (e) => e.stopPropagation(),
|
|
2501
|
+
children: [
|
|
2502
|
+
/* @__PURE__ */ jsx(
|
|
2503
|
+
Box,
|
|
2504
|
+
{
|
|
2505
|
+
padding: 5,
|
|
2506
|
+
background: "neutral100",
|
|
2507
|
+
style: {
|
|
2508
|
+
borderBottom: "1px solid rgba(120, 120, 150, 0.2)",
|
|
2509
|
+
flexShrink: 0
|
|
2510
|
+
},
|
|
2511
|
+
children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "flex-start", wrap: "wrap", gap: 3, children: [
|
|
2512
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
2513
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", marginBottom: 1, children: [
|
|
2514
|
+
/* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", children: "Checker Approval: Bulk Change Inspection" }),
|
|
2515
|
+
/* @__PURE__ */ jsx(Badge, { backgroundColor: "primary100", textColor: "primary700", children: batch.id }),
|
|
2516
|
+
(batch.payload?.action === "csv_import" || batch.id?.startsWith("csv_")) && /* @__PURE__ */ jsx(Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: "📊 CSV IMPORT" }),
|
|
2517
|
+
batch.payload?.action === "publish" && /* @__PURE__ */ jsx(Badge, { backgroundColor: "success100", textColor: "success700", children: "🚀 BULK PUBLISH" })
|
|
2518
|
+
] }),
|
|
2519
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", children: [
|
|
2520
|
+
"Submitted by ",
|
|
2521
|
+
/* @__PURE__ */ jsx("strong", { children: batch.createdBy?.email || "admin" }),
|
|
2522
|
+
" on ",
|
|
2523
|
+
new Date(batch.createdAt).toLocaleString()
|
|
2524
|
+
] }),
|
|
2525
|
+
batch.makerNotes && /* @__PURE__ */ jsx(Box, { marginTop: 2, padding: 2, background: "neutral150", hasRadius: true, style: { borderLeft: "3px solid #4945ff" }, children: /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral700", children: [
|
|
2526
|
+
/* @__PURE__ */ jsx("strong", { children: "Maker Notes:" }),
|
|
2527
|
+
" ",
|
|
2528
|
+
batch.makerNotes
|
|
2529
|
+
] }) })
|
|
2530
|
+
] }),
|
|
2531
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
|
|
2532
|
+
/* @__PURE__ */ jsx(
|
|
2533
|
+
Button,
|
|
2534
|
+
{
|
|
2535
|
+
variant: "tertiary",
|
|
2536
|
+
size: "S",
|
|
2537
|
+
startIcon: /* @__PURE__ */ jsx(Download, {}),
|
|
2538
|
+
onClick: handleExportCsv,
|
|
2539
|
+
loading: isExporting,
|
|
2540
|
+
children: "Export Audit CSV"
|
|
2541
|
+
}
|
|
2542
|
+
),
|
|
2543
|
+
/* @__PURE__ */ jsx(
|
|
2544
|
+
Button,
|
|
2545
|
+
{
|
|
2546
|
+
variant: "tertiary",
|
|
2547
|
+
size: "S",
|
|
2548
|
+
startIcon: /* @__PURE__ */ jsx(ArrowClockwise, {}),
|
|
2549
|
+
onClick: fetchBatchDiffs,
|
|
2550
|
+
loading,
|
|
2551
|
+
children: "Refresh"
|
|
2552
|
+
}
|
|
2553
|
+
)
|
|
2554
|
+
] })
|
|
2555
|
+
] })
|
|
2556
|
+
}
|
|
2557
|
+
),
|
|
2558
|
+
/* @__PURE__ */ jsxs(
|
|
2559
|
+
Box,
|
|
2560
|
+
{
|
|
2561
|
+
padding: 5,
|
|
2562
|
+
style: {
|
|
2563
|
+
flex: "1 1 auto",
|
|
2564
|
+
minHeight: 0,
|
|
2565
|
+
overflowY: "auto",
|
|
2566
|
+
WebkitOverflowScrolling: "touch"
|
|
2567
|
+
},
|
|
2568
|
+
children: [
|
|
2569
|
+
loading ? /* @__PURE__ */ jsx(Box, { padding: 8, style: { textAlign: "center" }, children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "center", gap: 3, children: [
|
|
2570
|
+
/* @__PURE__ */ jsx(Loader, { children: "Inspecting target records from database..." }),
|
|
2571
|
+
/* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "semiBold", children: "Generating Itemized Diffs..." }),
|
|
2572
|
+
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Comparing proposed values against current database records." })
|
|
2573
|
+
] }) }) : error ? /* @__PURE__ */ jsxs(Box, { padding: 6, style: { textAlign: "center" }, children: [
|
|
2574
|
+
/* @__PURE__ */ jsx(Typography, { variant: "delta", textColor: "danger700", fontWeight: "bold", children: "Failed to load change diffs" }),
|
|
2575
|
+
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral700", display: "block", marginTop: 1, children: error })
|
|
2576
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2577
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 3, marginBottom: 4, wrap: "wrap", children: [
|
|
2578
|
+
/* @__PURE__ */ jsxs(
|
|
2579
|
+
Box,
|
|
2580
|
+
{
|
|
2581
|
+
padding: 3,
|
|
2582
|
+
background: "neutral100",
|
|
2583
|
+
hasRadius: true,
|
|
2584
|
+
style: { flex: 1, minWidth: 130, border: "1px solid rgba(120, 120, 150, 0.15)" },
|
|
2585
|
+
children: [
|
|
2586
|
+
/* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", display: "block", children: "TOTAL TARGETS" }),
|
|
2587
|
+
/* @__PURE__ */ jsx(Typography, { variant: "beta", fontWeight: "bold", textColor: "neutral800", children: summary.total })
|
|
2588
|
+
]
|
|
2589
|
+
}
|
|
2590
|
+
),
|
|
2591
|
+
/* @__PURE__ */ jsxs(
|
|
2592
|
+
Box,
|
|
2593
|
+
{
|
|
2594
|
+
padding: 3,
|
|
2595
|
+
background: "warning100",
|
|
2596
|
+
hasRadius: true,
|
|
2597
|
+
style: { flex: 1, minWidth: 130, border: "1px solid #ffd591" },
|
|
2598
|
+
children: [
|
|
2599
|
+
/* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "warning700", display: "block", children: "WILL UPDATE" }),
|
|
2600
|
+
/* @__PURE__ */ jsx(Typography, { variant: "beta", fontWeight: "bold", textColor: "warning700", children: summary.willUpdate })
|
|
2601
|
+
]
|
|
2602
|
+
}
|
|
2603
|
+
),
|
|
2604
|
+
(summary.willCreate ?? 0) > 0 && /* @__PURE__ */ jsxs(
|
|
2605
|
+
Box,
|
|
2606
|
+
{
|
|
2607
|
+
padding: 3,
|
|
2608
|
+
background: "success100",
|
|
2609
|
+
hasRadius: true,
|
|
2610
|
+
style: { flex: 1, minWidth: 130, border: "1px solid #b7eb8f" },
|
|
2611
|
+
children: [
|
|
2612
|
+
/* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "success700", display: "block", children: "WILL CREATE (NEW)" }),
|
|
2613
|
+
/* @__PURE__ */ jsx(Typography, { variant: "beta", fontWeight: "bold", textColor: "success700", children: summary.willCreate })
|
|
2614
|
+
]
|
|
2615
|
+
}
|
|
2616
|
+
),
|
|
2617
|
+
/* @__PURE__ */ jsxs(
|
|
2618
|
+
Box,
|
|
2619
|
+
{
|
|
2620
|
+
padding: 3,
|
|
2621
|
+
background: "neutral100",
|
|
2622
|
+
hasRadius: true,
|
|
2623
|
+
style: { flex: 1, minWidth: 130, border: "1px solid rgba(120, 120, 150, 0.15)" },
|
|
2624
|
+
children: [
|
|
2625
|
+
/* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", display: "block", children: "UNCHANGED" }),
|
|
2626
|
+
/* @__PURE__ */ jsx(Typography, { variant: "beta", fontWeight: "bold", textColor: "neutral600", children: summary.unchanged })
|
|
2627
|
+
]
|
|
2628
|
+
}
|
|
2629
|
+
),
|
|
2630
|
+
summary.skipped > 0 && /* @__PURE__ */ jsxs(
|
|
2631
|
+
Box,
|
|
2632
|
+
{
|
|
2633
|
+
padding: 3,
|
|
2634
|
+
background: "danger100",
|
|
2635
|
+
hasRadius: true,
|
|
2636
|
+
style: { flex: 1, minWidth: 130, border: "1px solid #ffa39e" },
|
|
2637
|
+
children: [
|
|
2638
|
+
/* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "danger700", display: "block", children: "SKIPPED / ERRORS" }),
|
|
2639
|
+
/* @__PURE__ */ jsx(Typography, { variant: "beta", fontWeight: "bold", textColor: "danger700", children: summary.skipped })
|
|
2640
|
+
]
|
|
2641
|
+
}
|
|
2642
|
+
)
|
|
2643
|
+
] }),
|
|
2644
|
+
/* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 3, wrap: "wrap", gap: 3, children: [
|
|
2645
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 2, wrap: "wrap", children: [
|
|
2646
|
+
/* @__PURE__ */ jsxs(
|
|
2647
|
+
Button,
|
|
2648
|
+
{
|
|
2649
|
+
variant: filterTab === "all" ? "default" : "tertiary",
|
|
2650
|
+
size: "S",
|
|
2651
|
+
onClick: () => setFilterTab("all"),
|
|
2652
|
+
children: [
|
|
2653
|
+
"All Items (",
|
|
2654
|
+
diffs.length,
|
|
2655
|
+
")"
|
|
2656
|
+
]
|
|
2657
|
+
}
|
|
2658
|
+
),
|
|
2659
|
+
/* @__PURE__ */ jsxs(
|
|
2660
|
+
Button,
|
|
2661
|
+
{
|
|
2662
|
+
variant: filterTab === "changes" ? "default" : "tertiary",
|
|
2663
|
+
size: "S",
|
|
2664
|
+
onClick: () => setFilterTab("changes"),
|
|
2665
|
+
children: [
|
|
2666
|
+
"Changes Only (",
|
|
2667
|
+
totalChanges,
|
|
2668
|
+
")"
|
|
2669
|
+
]
|
|
2670
|
+
}
|
|
2671
|
+
),
|
|
2672
|
+
/* @__PURE__ */ jsxs(
|
|
2673
|
+
Button,
|
|
2674
|
+
{
|
|
2675
|
+
variant: filterTab === "will_update" ? "default" : "tertiary",
|
|
2676
|
+
size: "S",
|
|
2677
|
+
onClick: () => setFilterTab("will_update"),
|
|
2678
|
+
children: [
|
|
2679
|
+
"Updates (",
|
|
2680
|
+
summary.willUpdate,
|
|
2681
|
+
")"
|
|
2682
|
+
]
|
|
2683
|
+
}
|
|
2684
|
+
),
|
|
2685
|
+
(summary.willCreate ?? 0) > 0 && /* @__PURE__ */ jsxs(
|
|
2686
|
+
Button,
|
|
2687
|
+
{
|
|
2688
|
+
variant: filterTab === "will_create" ? "default" : "tertiary",
|
|
2689
|
+
size: "S",
|
|
2690
|
+
onClick: () => setFilterTab("will_create"),
|
|
2691
|
+
children: [
|
|
2692
|
+
"Creates (",
|
|
2693
|
+
summary.willCreate,
|
|
2694
|
+
")"
|
|
2695
|
+
]
|
|
2696
|
+
}
|
|
2697
|
+
),
|
|
2698
|
+
/* @__PURE__ */ jsxs(
|
|
2699
|
+
Button,
|
|
2700
|
+
{
|
|
2701
|
+
variant: filterTab === "unchanged" ? "default" : "tertiary",
|
|
2702
|
+
size: "S",
|
|
2703
|
+
onClick: () => setFilterTab("unchanged"),
|
|
2704
|
+
children: [
|
|
2705
|
+
"Unchanged (",
|
|
2706
|
+
summary.unchanged,
|
|
2707
|
+
")"
|
|
2708
|
+
]
|
|
2709
|
+
}
|
|
2710
|
+
),
|
|
2711
|
+
summary.skipped > 0 && /* @__PURE__ */ jsxs(
|
|
2712
|
+
Button,
|
|
2713
|
+
{
|
|
2714
|
+
variant: filterTab === "will_skip" ? "default" : "tertiary",
|
|
2715
|
+
size: "S",
|
|
2716
|
+
onClick: () => setFilterTab("will_skip"),
|
|
2717
|
+
children: [
|
|
2718
|
+
"Skipped (",
|
|
2719
|
+
summary.skipped,
|
|
2720
|
+
")"
|
|
2721
|
+
]
|
|
2722
|
+
}
|
|
2723
|
+
)
|
|
2724
|
+
] }),
|
|
2725
|
+
/* @__PURE__ */ jsx(Box, { style: { width: 260 }, children: /* @__PURE__ */ jsx(
|
|
2726
|
+
TextInput,
|
|
2727
|
+
{
|
|
2728
|
+
placeholder: "Search records or values...",
|
|
2729
|
+
size: "S",
|
|
2730
|
+
value: searchQuery,
|
|
2731
|
+
onChange: (e) => setSearchQuery(e.target.value)
|
|
2732
|
+
}
|
|
2733
|
+
) })
|
|
2734
|
+
] }),
|
|
2735
|
+
/* @__PURE__ */ jsx(
|
|
2736
|
+
Box,
|
|
2737
|
+
{
|
|
2738
|
+
hasRadius: true,
|
|
2739
|
+
style: {
|
|
2740
|
+
border: "1px solid #eaeaef",
|
|
2741
|
+
maxHeight: 380,
|
|
2742
|
+
overflowY: "auto",
|
|
2743
|
+
overflowX: "auto"
|
|
2744
|
+
},
|
|
2745
|
+
children: /* @__PURE__ */ jsxs(Table, { colCount: 6, rowCount: filteredDiffs.length + 1, style: { width: "100%", minWidth: 880 }, children: [
|
|
2746
|
+
/* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [
|
|
2747
|
+
/* @__PURE__ */ jsx(Th, { style: { width: 140 }, children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: "COLLECTION" }) }),
|
|
2748
|
+
/* @__PURE__ */ jsx(Th, { style: { minWidth: 180 }, children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: "RECORD IDENTIFIER" }) }),
|
|
2749
|
+
/* @__PURE__ */ jsx(Th, { style: { minWidth: 140 }, children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: "ATTRIBUTE" }) }),
|
|
2750
|
+
/* @__PURE__ */ jsx(Th, { style: { minWidth: 180 }, children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: "CURRENT VALUE (BEFORE)" }) }),
|
|
2751
|
+
/* @__PURE__ */ jsx(Th, { style: { minWidth: 180 }, children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: "PROPOSED VALUE (AFTER)" }) }),
|
|
2752
|
+
/* @__PURE__ */ jsx(Th, { style: { width: 130, textAlign: "right" }, children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: "IMPACT" }) })
|
|
2753
|
+
] }) }),
|
|
2754
|
+
/* @__PURE__ */ jsx(Tbody, { children: filteredDiffs.length === 0 ? /* @__PURE__ */ jsx(Tr, { children: /* @__PURE__ */ jsx(Td, { colSpan: 6, style: { textAlign: "center", padding: "24px" }, children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", children: "No records match the current filter or search criteria." }) }) }) : filteredDiffs.map((diff, idx) => /* @__PURE__ */ jsxs(Tr, { children: [
|
|
2755
|
+
/* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Badge, { backgroundColor: "neutral150", textColor: "neutral700", children: diff.collectionName }) }),
|
|
2756
|
+
/* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsxs(Box, { children: [
|
|
2757
|
+
/* @__PURE__ */ jsx(Typography, { fontWeight: "bold", display: "block", children: diff.label }),
|
|
2758
|
+
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", children: /* @__PURE__ */ jsx("code", { children: diff.documentId }) })
|
|
2759
|
+
] }) }),
|
|
2760
|
+
/* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "semiBold", children: diff.attribute }) }),
|
|
2761
|
+
/* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(
|
|
2762
|
+
Typography,
|
|
2763
|
+
{
|
|
2764
|
+
variant: "omega",
|
|
2765
|
+
style: {
|
|
2766
|
+
color: diff.hasChange ? "#9254de" : "#272635",
|
|
2767
|
+
textDecoration: diff.hasChange ? "line-through" : "none",
|
|
2768
|
+
wordBreak: "break-all"
|
|
2769
|
+
},
|
|
2770
|
+
children: diff.oldDisplay || "(empty)"
|
|
2771
|
+
}
|
|
2772
|
+
) }),
|
|
2773
|
+
/* @__PURE__ */ jsx(Td, { children: diff.status === "will_create" ? /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", textColor: "success700", style: { wordBreak: "break-all" }, children: diff.newDisplay || "(new value)" }) : diff.status === "will_skip" ? /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "warning700", children: "— Skipped —" }) : diff.hasChange ? /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", textColor: "success700", style: { wordBreak: "break-all" }, children: diff.newDisplay || "(empty)" }) : /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "neutral600", children: diff.newDisplay || "(matching)" }) }),
|
|
2774
|
+
/* @__PURE__ */ jsxs(Td, { style: { textAlign: "right" }, children: [
|
|
2775
|
+
diff.status === "will_create" && /* @__PURE__ */ jsx(Badge, { backgroundColor: "success100", textColor: "success700", children: "+ Will Create" }),
|
|
2776
|
+
diff.status === "will_update" && /* @__PURE__ */ jsx(Badge, { backgroundColor: "warning100", textColor: "warning700", children: "✎ Will Update" }),
|
|
2777
|
+
diff.status === "unchanged" && /* @__PURE__ */ jsx(Badge, { backgroundColor: "neutral150", textColor: "neutral600", children: "= Unchanged" }),
|
|
2778
|
+
diff.status === "will_skip" && /* @__PURE__ */ jsx(Badge, { backgroundColor: "danger100", textColor: "danger700", children: "⚠ Will Skip" })
|
|
2779
|
+
] })
|
|
2780
|
+
] }, `${diff.uid}-${diff.documentId}-${diff.attribute}-${idx}`)) })
|
|
2781
|
+
] })
|
|
2782
|
+
}
|
|
2783
|
+
)
|
|
2784
|
+
] }),
|
|
2785
|
+
showRejectInput && /* @__PURE__ */ jsxs(Box, { marginTop: 4, padding: 4, background: "danger100", hasRadius: true, style: { border: "1px solid #ffa39e" }, children: [
|
|
2786
|
+
/* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", textColor: "danger700", marginBottom: 2, children: "Reject Batch" }),
|
|
2787
|
+
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "danger700", marginBottom: 2, display: "block", children: "Please provide a rejection note explaining why this batch was declined. The Maker will see this message." }),
|
|
2788
|
+
/* @__PURE__ */ jsx(
|
|
2789
|
+
TextInput,
|
|
2790
|
+
{
|
|
2791
|
+
placeholder: "Reason for rejection (e.g., incorrect localized prices in CSV rows 4-8)...",
|
|
2792
|
+
value: rejectionReason,
|
|
2793
|
+
onChange: (e) => setRejectionReason(e.target.value),
|
|
2794
|
+
style: { marginBottom: 12 }
|
|
2795
|
+
}
|
|
2796
|
+
),
|
|
2797
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 2, justifyContent: "flex-end", children: [
|
|
2798
|
+
/* @__PURE__ */ jsx(Button, { variant: "tertiary", size: "S", onClick: () => setShowRejectInput(false), children: "Cancel" }),
|
|
2799
|
+
/* @__PURE__ */ jsx(
|
|
2800
|
+
Button,
|
|
2801
|
+
{
|
|
2802
|
+
variant: "danger",
|
|
2803
|
+
size: "S",
|
|
2804
|
+
loading: rejecting,
|
|
2805
|
+
onClick: () => onReject(batch.id, rejectionReason || "Declined by Checker"),
|
|
2806
|
+
children: "Confirm Rejection"
|
|
2807
|
+
}
|
|
2808
|
+
)
|
|
2809
|
+
] })
|
|
2810
|
+
] })
|
|
2811
|
+
]
|
|
2812
|
+
}
|
|
2813
|
+
),
|
|
2814
|
+
/* @__PURE__ */ jsx(
|
|
2815
|
+
Box,
|
|
2816
|
+
{
|
|
2817
|
+
padding: 4,
|
|
2818
|
+
background: "neutral100",
|
|
2819
|
+
style: {
|
|
2820
|
+
borderTop: "1px solid rgba(120, 120, 150, 0.2)",
|
|
2821
|
+
flexShrink: 0
|
|
2822
|
+
},
|
|
2823
|
+
children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: [
|
|
2824
|
+
/* @__PURE__ */ jsx(Button, { variant: "tertiary", onClick: onClose, disabled: approving || rejecting, children: "Close Preview" }),
|
|
2825
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 3, children: [
|
|
2826
|
+
!showRejectInput && /* @__PURE__ */ jsx(
|
|
2827
|
+
Button,
|
|
2828
|
+
{
|
|
2829
|
+
variant: "danger-light",
|
|
2830
|
+
onClick: () => setShowRejectInput(true),
|
|
2831
|
+
disabled: approving || rejecting,
|
|
2832
|
+
children: "Reject Batch"
|
|
2833
|
+
}
|
|
2834
|
+
),
|
|
2835
|
+
/* @__PURE__ */ jsx(
|
|
2836
|
+
Button,
|
|
2837
|
+
{
|
|
2838
|
+
variant: "success",
|
|
2839
|
+
startIcon: /* @__PURE__ */ jsx(Check, {}),
|
|
2840
|
+
onClick: () => onApprove(batch.id),
|
|
2841
|
+
loading: approving,
|
|
2842
|
+
disabled: rejecting,
|
|
2843
|
+
children: "Approve & Execute Batch"
|
|
2844
|
+
}
|
|
2845
|
+
)
|
|
2846
|
+
] })
|
|
2847
|
+
] })
|
|
2848
|
+
}
|
|
2849
|
+
)
|
|
2850
|
+
]
|
|
2851
|
+
}
|
|
2852
|
+
)
|
|
2853
|
+
}
|
|
2854
|
+
);
|
|
2855
|
+
};
|
|
2359
2856
|
function parseDynamicZonePath(path) {
|
|
2360
2857
|
if (!path) return { cleanPath: "", rootKey: "", subPath: "" };
|
|
2361
2858
|
const bracketMatch = path.match(/^([^.[]+)\[([^\]]+)\]\.(.+)$/);
|
|
@@ -3056,6 +3553,8 @@ const BulkUpdater = () => {
|
|
|
3056
3553
|
const [pendingBatchesLoading, setPendingBatchesLoading] = useState(false);
|
|
3057
3554
|
const [approvingBatchId, setApprovingBatchId] = useState(null);
|
|
3058
3555
|
const [rejectingBatchId, setRejectingBatchId] = useState(null);
|
|
3556
|
+
const [inspectingPendingBatch, setInspectingPendingBatch] = useState(null);
|
|
3557
|
+
const [exportingPendingBatchId, setExportingPendingBatchId] = useState(null);
|
|
3059
3558
|
const [makerNotesInput, setMakerNotesInput] = useState("");
|
|
3060
3559
|
const [publishOnApproval, setPublishOnApproval] = useState(true);
|
|
3061
3560
|
const [publishStrategy, setPublishStrategy] = useState("safe");
|
|
@@ -3751,7 +4250,7 @@ const BulkUpdater = () => {
|
|
|
3751
4250
|
return val.some((item) => typeof item === "object" ? testEntityObject(item) : testString(item));
|
|
3752
4251
|
}
|
|
3753
4252
|
if (typeof val === "object" && val !== null) {
|
|
3754
|
-
|
|
4253
|
+
return testEntityObject(val);
|
|
3755
4254
|
}
|
|
3756
4255
|
return testString(displayStr);
|
|
3757
4256
|
};
|
|
@@ -4344,6 +4843,7 @@ const BulkUpdater = () => {
|
|
|
4344
4843
|
type: "success",
|
|
4345
4844
|
message: res.data.message || `Batch ${batchId} approved and executed successfully!`
|
|
4346
4845
|
});
|
|
4846
|
+
setInspectingPendingBatch(null);
|
|
4347
4847
|
await fetchPendingBatches();
|
|
4348
4848
|
await fetchAuditHistory();
|
|
4349
4849
|
await fetchModels();
|
|
@@ -4362,8 +4862,8 @@ const BulkUpdater = () => {
|
|
|
4362
4862
|
setApprovingBatchId(null);
|
|
4363
4863
|
}
|
|
4364
4864
|
};
|
|
4365
|
-
const handleRejectBatch = async (batchId) => {
|
|
4366
|
-
const reason = window.prompt("Enter reason for rejection:", "Does not meet release criteria") || "Rejected by Checker";
|
|
4865
|
+
const handleRejectBatch = async (batchId, customReason) => {
|
|
4866
|
+
const reason = customReason || window.prompt("Enter reason for rejection:", "Does not meet release criteria") || "Rejected by Checker";
|
|
4367
4867
|
try {
|
|
4368
4868
|
setRejectingBatchId(batchId);
|
|
4369
4869
|
const res = await post(`/bulk-updater/batches/${batchId}/reject`, { reason });
|
|
@@ -4372,6 +4872,7 @@ const BulkUpdater = () => {
|
|
|
4372
4872
|
type: "info",
|
|
4373
4873
|
message: `Batch ${batchId} rejected: ${reason}`
|
|
4374
4874
|
});
|
|
4875
|
+
setInspectingPendingBatch(null);
|
|
4375
4876
|
await fetchPendingBatches();
|
|
4376
4877
|
} else {
|
|
4377
4878
|
toggleNotification({
|
|
@@ -4388,6 +4889,40 @@ const BulkUpdater = () => {
|
|
|
4388
4889
|
setRejectingBatchId(null);
|
|
4389
4890
|
}
|
|
4390
4891
|
};
|
|
4892
|
+
const handleExportPendingBatch = async (batchId) => {
|
|
4893
|
+
try {
|
|
4894
|
+
setExportingPendingBatchId(batchId);
|
|
4895
|
+
const res = await get(`/bulk-updater/batches/${batchId}/diffs/export`);
|
|
4896
|
+
if (res.data && res.data.success && res.data.csvContent) {
|
|
4897
|
+
const safeContent = res.data.csvContent.startsWith("\uFEFF") ? res.data.csvContent : "\uFEFF" + res.data.csvContent;
|
|
4898
|
+
const blob = new Blob([safeContent], { type: "text/csv;charset=utf-8;" });
|
|
4899
|
+
const url = URL.createObjectURL(blob);
|
|
4900
|
+
const link = document.createElement("a");
|
|
4901
|
+
link.href = url;
|
|
4902
|
+
link.setAttribute("download", res.data.filename || `batch_${batchId}_review.csv`);
|
|
4903
|
+
document.body.appendChild(link);
|
|
4904
|
+
link.click();
|
|
4905
|
+
document.body.removeChild(link);
|
|
4906
|
+
URL.revokeObjectURL(url);
|
|
4907
|
+
toggleNotification({
|
|
4908
|
+
type: "success",
|
|
4909
|
+
message: `Exported ${res.data.count || ""} batch diffs to CSV.`
|
|
4910
|
+
});
|
|
4911
|
+
} else {
|
|
4912
|
+
toggleNotification({
|
|
4913
|
+
type: "danger",
|
|
4914
|
+
message: "Failed to export batch diffs to CSV."
|
|
4915
|
+
});
|
|
4916
|
+
}
|
|
4917
|
+
} catch (err) {
|
|
4918
|
+
toggleNotification({
|
|
4919
|
+
type: "danger",
|
|
4920
|
+
message: err.message || "Error exporting batch diffs."
|
|
4921
|
+
});
|
|
4922
|
+
} finally {
|
|
4923
|
+
setExportingPendingBatchId(null);
|
|
4924
|
+
}
|
|
4925
|
+
};
|
|
4391
4926
|
const handleExecuteNow = async (batchId) => {
|
|
4392
4927
|
try {
|
|
4393
4928
|
setExecutingNowBatchId(batchId);
|
|
@@ -4681,9 +5216,14 @@ const BulkUpdater = () => {
|
|
|
4681
5216
|
/* @__PURE__ */ jsx(
|
|
4682
5217
|
Combobox,
|
|
4683
5218
|
{
|
|
4684
|
-
autocomplete: "
|
|
4685
|
-
placeholder: "
|
|
5219
|
+
autocomplete: { type: "list", filter: "contains" },
|
|
5220
|
+
placeholder: "Search Collection Type...",
|
|
4686
5221
|
value: selectedCollectionUid || void 0,
|
|
5222
|
+
clearLabel: "Clear collection selection",
|
|
5223
|
+
onClear: () => handleCollectionChange(""),
|
|
5224
|
+
onFocus: (e) => {
|
|
5225
|
+
e.target.select();
|
|
5226
|
+
},
|
|
4687
5227
|
onChange: (val) => {
|
|
4688
5228
|
if (val && typeof val === "string") {
|
|
4689
5229
|
handleCollectionChange(val);
|
|
@@ -4779,7 +5319,7 @@ const BulkUpdater = () => {
|
|
|
4779
5319
|
/* @__PURE__ */ jsx(
|
|
4780
5320
|
Combobox,
|
|
4781
5321
|
{
|
|
4782
|
-
autocomplete: "
|
|
5322
|
+
autocomplete: { type: "list", filter: "contains" },
|
|
4783
5323
|
placeholder: searchAttribute || `Same as Update (${selectedAttribute || "None"})`,
|
|
4784
5324
|
value: searchAttribute || "__default__",
|
|
4785
5325
|
clearLabel: "Reset search attribute",
|
|
@@ -4787,6 +5327,9 @@ const BulkUpdater = () => {
|
|
|
4787
5327
|
handleSearchAttributeChange("");
|
|
4788
5328
|
setSearchAttributeSearch("");
|
|
4789
5329
|
},
|
|
5330
|
+
onFocus: (e) => {
|
|
5331
|
+
e.target.select();
|
|
5332
|
+
},
|
|
4790
5333
|
onInputChange: (e) => {
|
|
4791
5334
|
setSearchAttributeSearch(e.target.value);
|
|
4792
5335
|
},
|
|
@@ -5576,7 +6119,7 @@ const BulkUpdater = () => {
|
|
|
5576
6119
|
/* @__PURE__ */ jsx(
|
|
5577
6120
|
Combobox,
|
|
5578
6121
|
{
|
|
5579
|
-
autocomplete: "
|
|
6122
|
+
autocomplete: { type: "list", filter: "contains" },
|
|
5580
6123
|
placeholder: "Search attribute by name or relation...",
|
|
5581
6124
|
value: selectedAttribute || void 0,
|
|
5582
6125
|
clearLabel: "Clear attribute selection",
|
|
@@ -5584,6 +6127,9 @@ const BulkUpdater = () => {
|
|
|
5584
6127
|
handleAttributeChange("");
|
|
5585
6128
|
setAttributeSearch("");
|
|
5586
6129
|
},
|
|
6130
|
+
onFocus: (e) => {
|
|
6131
|
+
e.target.select();
|
|
6132
|
+
},
|
|
5587
6133
|
onInputChange: (e) => {
|
|
5588
6134
|
setAttributeSearch(e.target.value);
|
|
5589
6135
|
},
|
|
@@ -6005,6 +6551,27 @@ const BulkUpdater = () => {
|
|
|
6005
6551
|
] })
|
|
6006
6552
|
] }) }),
|
|
6007
6553
|
/* @__PURE__ */ jsx(Td, { style: { textAlign: "right" }, children: /* @__PURE__ */ jsxs(Flex, { gap: 2, justifyContent: "flex-end", children: [
|
|
6554
|
+
/* @__PURE__ */ jsx(
|
|
6555
|
+
Button,
|
|
6556
|
+
{
|
|
6557
|
+
size: "S",
|
|
6558
|
+
variant: "secondary",
|
|
6559
|
+
startIcon: /* @__PURE__ */ jsx(Eye, {}),
|
|
6560
|
+
onClick: () => setInspectingPendingBatch(batch),
|
|
6561
|
+
children: "Review Changes"
|
|
6562
|
+
}
|
|
6563
|
+
),
|
|
6564
|
+
/* @__PURE__ */ jsx(
|
|
6565
|
+
Button,
|
|
6566
|
+
{
|
|
6567
|
+
size: "S",
|
|
6568
|
+
variant: "tertiary",
|
|
6569
|
+
startIcon: /* @__PURE__ */ jsx(Download, {}),
|
|
6570
|
+
onClick: () => handleExportPendingBatch(batch.id),
|
|
6571
|
+
loading: exportingPendingBatchId === batch.id,
|
|
6572
|
+
children: "Export CSV"
|
|
6573
|
+
}
|
|
6574
|
+
),
|
|
6008
6575
|
batch.status === "pending_approval" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6009
6576
|
/* @__PURE__ */ jsx(
|
|
6010
6577
|
Button,
|
|
@@ -6921,6 +7488,18 @@ const BulkUpdater = () => {
|
|
|
6921
7488
|
initialJob: dashboardJob,
|
|
6922
7489
|
isSubmitting: isSubmittingBatch
|
|
6923
7490
|
}
|
|
7491
|
+
),
|
|
7492
|
+
/* @__PURE__ */ jsx(
|
|
7493
|
+
CheckerReviewModal,
|
|
7494
|
+
{
|
|
7495
|
+
isOpen: Boolean(inspectingPendingBatch),
|
|
7496
|
+
onClose: () => setInspectingPendingBatch(null),
|
|
7497
|
+
batch: inspectingPendingBatch,
|
|
7498
|
+
onApprove: handleApproveBatch,
|
|
7499
|
+
onReject: handleRejectBatch,
|
|
7500
|
+
approving: Boolean(inspectingPendingBatch && approvingBatchId === inspectingPendingBatch.id),
|
|
7501
|
+
rejecting: Boolean(inspectingPendingBatch && rejectingBatchId === inspectingPendingBatch.id)
|
|
7502
|
+
}
|
|
6924
7503
|
)
|
|
6925
7504
|
]
|
|
6926
7505
|
}
|
|
@@ -6939,4 +7518,4 @@ export {
|
|
|
6939
7518
|
getSortOptionsForCollection,
|
|
6940
7519
|
parseDynamicZonePath
|
|
6941
7520
|
};
|
|
6942
|
-
//# sourceMappingURL=BulkUpdater-
|
|
7521
|
+
//# sourceMappingURL=BulkUpdater-By9Dd_YF.mjs.map
|