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