strapi-plugin-bulk-updater 1.4.10 → 1.5.2
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-BiSkkAHF.mjs} +673 -147
- package/dist/_chunks/BulkUpdater-BiSkkAHF.mjs.map +1 -0
- package/dist/_chunks/{BulkUpdater-C0uPoso2.js → BulkUpdater-JZW74XM4.js} +672 -146
- package/dist/_chunks/BulkUpdater-JZW74XM4.js.map +1 -0
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +687 -231
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +687 -231
- package/dist/server/index.mjs.map +1 -1
- package/package.json +1 -1
- 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");
|
|
@@ -3528,8 +4027,6 @@ const BulkUpdater = () => {
|
|
|
3528
4027
|
const filteredCollections = useMemo(() => {
|
|
3529
4028
|
return selectedCol ? [selectedCol] : [];
|
|
3530
4029
|
}, [selectedCol]);
|
|
3531
|
-
const [attributeSearch, setAttributeSearch] = useState("");
|
|
3532
|
-
const [searchAttributeSearch, setSearchAttributeSearch] = useState("");
|
|
3533
4030
|
const getAttrDepth = (attr) => attributesMeta[attr]?.depth ?? (attr.includes(".") ? attr.split(".").length - 1 : 0);
|
|
3534
4031
|
const availableColAttributes = useMemo(() => {
|
|
3535
4032
|
return selectedCol?.attributes || attributes || [];
|
|
@@ -3537,48 +4034,20 @@ const BulkUpdater = () => {
|
|
|
3537
4034
|
const depthFilteredAttributes = useMemo(() => {
|
|
3538
4035
|
return availableColAttributes.filter((attr) => getAttrDepth(attr) <= componentDepth);
|
|
3539
4036
|
}, [availableColAttributes, componentDepth, attributesMeta]);
|
|
3540
|
-
const filteredAttributes = useMemo(() => {
|
|
3541
|
-
const q = attributeSearch.toLowerCase().trim();
|
|
3542
|
-
if (!q || q === (selectedAttribute || "").toLowerCase().trim()) {
|
|
3543
|
-
return depthFilteredAttributes;
|
|
3544
|
-
}
|
|
3545
|
-
const cleanQ = q.replace(/[^a-z0-9]/g, "");
|
|
3546
|
-
return depthFilteredAttributes.filter((attr) => {
|
|
3547
|
-
const lower = attr.toLowerCase();
|
|
3548
|
-
const cleanLower = lower.replace(/[^a-z0-9]/g, "");
|
|
3549
|
-
const rel = relationsMeta[attr];
|
|
3550
|
-
const relTarget = (rel?.targetDisplayName || "").toLowerCase();
|
|
3551
|
-
return lower.includes(q) || cleanLower.includes(cleanQ) || relTarget.includes(q);
|
|
3552
|
-
});
|
|
3553
|
-
}, [depthFilteredAttributes, attributeSearch, selectedAttribute, relationsMeta]);
|
|
3554
|
-
const filteredSearchAttributes = useMemo(() => {
|
|
3555
|
-
const q = searchAttributeSearch.toLowerCase().trim();
|
|
3556
|
-
if (!q || q === (searchAttribute || "").toLowerCase().trim()) {
|
|
3557
|
-
return depthFilteredAttributes;
|
|
3558
|
-
}
|
|
3559
|
-
const cleanQ = q.replace(/[^a-z0-9]/g, "");
|
|
3560
|
-
return depthFilteredAttributes.filter((attr) => {
|
|
3561
|
-
const lower = attr.toLowerCase();
|
|
3562
|
-
const cleanLower = lower.replace(/[^a-z0-9]/g, "");
|
|
3563
|
-
const rel = relationsMeta[attr];
|
|
3564
|
-
const relTarget = (rel?.targetDisplayName || "").toLowerCase();
|
|
3565
|
-
return lower.includes(q) || cleanLower.includes(cleanQ) || relTarget.includes(q);
|
|
3566
|
-
});
|
|
3567
|
-
}, [depthFilteredAttributes, searchAttributeSearch, searchAttribute, relationsMeta]);
|
|
3568
4037
|
const memoizedComboboxOptions = useMemo(() => {
|
|
3569
|
-
if (
|
|
3570
|
-
return /* @__PURE__ */ jsx(ComboboxOption, { value: "__none__", disabled: true, textValue: "No
|
|
3571
|
-
|
|
3572
|
-
attributeSearch,
|
|
3573
|
-
'" (Depth L',
|
|
4038
|
+
if (depthFilteredAttributes.length === 0) {
|
|
4039
|
+
return /* @__PURE__ */ jsx(ComboboxOption, { value: "__none__", disabled: true, textValue: "No attributes available", children: /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", children: [
|
|
4040
|
+
"No attributes available for this collection type (Depth L",
|
|
3574
4041
|
componentDepth,
|
|
3575
4042
|
")"
|
|
3576
4043
|
] }) });
|
|
3577
4044
|
}
|
|
3578
|
-
return
|
|
4045
|
+
return depthFilteredAttributes.map((attr) => {
|
|
3579
4046
|
const rel = relationsMeta[attr];
|
|
3580
4047
|
const depth = getAttrDepth(attr);
|
|
3581
|
-
|
|
4048
|
+
const badgeText = rel ? `relation ${rel.targetDisplayName}` : depth > 0 ? `L${depth} component` : "";
|
|
4049
|
+
const textValue = badgeText ? `${attr} ${badgeText}` : attr;
|
|
4050
|
+
return /* @__PURE__ */ jsx(ComboboxOption, { value: attr, textValue, children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", width: "100%", gap: 2, children: [
|
|
3582
4051
|
/* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: attr === selectedAttribute ? "bold" : "regular", children: attr }),
|
|
3583
4052
|
rel ? /* @__PURE__ */ jsxs(Badge, { backgroundColor: "secondary100", textColor: "secondary700", size: "S", children: [
|
|
3584
4053
|
"relation ➔ ",
|
|
@@ -3590,14 +4059,14 @@ const BulkUpdater = () => {
|
|
|
3590
4059
|
] }) : null
|
|
3591
4060
|
] }) }, attr);
|
|
3592
4061
|
});
|
|
3593
|
-
}, [
|
|
4062
|
+
}, [depthFilteredAttributes, relationsMeta, selectedAttribute, componentDepth, attributesMeta]);
|
|
3594
4063
|
const memoizedSearchComboboxOptions = useMemo(() => {
|
|
3595
4064
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3596
4065
|
/* @__PURE__ */ jsx(
|
|
3597
4066
|
ComboboxOption,
|
|
3598
4067
|
{
|
|
3599
4068
|
value: "__default__",
|
|
3600
|
-
textValue: `Same as Update Attribute (${selectedAttribute || "None"})`,
|
|
4069
|
+
textValue: `Same as Update Attribute (${selectedAttribute || "None"}) default`,
|
|
3601
4070
|
children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", width: "100%", gap: 2, children: [
|
|
3602
4071
|
/* @__PURE__ */ jsxs(Typography, { variant: "omega", fontStyle: "italic", textColor: "neutral600", children: [
|
|
3603
4072
|
"-- Same as Update Attribute (",
|
|
@@ -3608,10 +4077,12 @@ const BulkUpdater = () => {
|
|
|
3608
4077
|
] })
|
|
3609
4078
|
}
|
|
3610
4079
|
),
|
|
3611
|
-
|
|
4080
|
+
depthFilteredAttributes.map((attr) => {
|
|
3612
4081
|
const rel = relationsMeta[attr];
|
|
3613
4082
|
const depth = getAttrDepth(attr);
|
|
3614
|
-
|
|
4083
|
+
const badgeText = rel ? `relation ${rel.targetDisplayName}` : depth > 0 ? `L${depth} component` : "";
|
|
4084
|
+
const textValue = badgeText ? `${attr} ${badgeText}` : attr;
|
|
4085
|
+
return /* @__PURE__ */ jsx(ComboboxOption, { value: attr, textValue, children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", width: "100%", gap: 2, children: [
|
|
3615
4086
|
/* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: attr === searchAttribute ? "bold" : "regular", children: attr }),
|
|
3616
4087
|
rel ? /* @__PURE__ */ jsxs(Badge, { backgroundColor: "secondary100", textColor: "secondary700", size: "S", children: [
|
|
3617
4088
|
"relation ➔ ",
|
|
@@ -3624,7 +4095,7 @@ const BulkUpdater = () => {
|
|
|
3624
4095
|
] }) }, `search_${attr}`);
|
|
3625
4096
|
})
|
|
3626
4097
|
] });
|
|
3627
|
-
}, [
|
|
4098
|
+
}, [depthFilteredAttributes, relationsMeta, searchAttribute, selectedAttribute, componentDepth, attributesMeta]);
|
|
3628
4099
|
const handleSearchAttributeChange = (newAttr) => {
|
|
3629
4100
|
const finalAttr = newAttr === "__default__" ? "" : newAttr;
|
|
3630
4101
|
setSearchAttribute(finalAttr);
|
|
@@ -3751,7 +4222,7 @@ const BulkUpdater = () => {
|
|
|
3751
4222
|
return val.some((item) => typeof item === "object" ? testEntityObject(item) : testString(item));
|
|
3752
4223
|
}
|
|
3753
4224
|
if (typeof val === "object" && val !== null) {
|
|
3754
|
-
|
|
4225
|
+
return testEntityObject(val);
|
|
3755
4226
|
}
|
|
3756
4227
|
return testString(displayStr);
|
|
3757
4228
|
};
|
|
@@ -3799,7 +4270,6 @@ const BulkUpdater = () => {
|
|
|
3799
4270
|
};
|
|
3800
4271
|
const clearFilter = () => {
|
|
3801
4272
|
setSearchAttribute("");
|
|
3802
|
-
setSearchAttributeSearch("");
|
|
3803
4273
|
setFilterOperator("none");
|
|
3804
4274
|
setFilterQuery("");
|
|
3805
4275
|
setEntrySearch("");
|
|
@@ -4344,6 +4814,7 @@ const BulkUpdater = () => {
|
|
|
4344
4814
|
type: "success",
|
|
4345
4815
|
message: res.data.message || `Batch ${batchId} approved and executed successfully!`
|
|
4346
4816
|
});
|
|
4817
|
+
setInspectingPendingBatch(null);
|
|
4347
4818
|
await fetchPendingBatches();
|
|
4348
4819
|
await fetchAuditHistory();
|
|
4349
4820
|
await fetchModels();
|
|
@@ -4362,8 +4833,8 @@ const BulkUpdater = () => {
|
|
|
4362
4833
|
setApprovingBatchId(null);
|
|
4363
4834
|
}
|
|
4364
4835
|
};
|
|
4365
|
-
const handleRejectBatch = async (batchId) => {
|
|
4366
|
-
const reason = window.prompt("Enter reason for rejection:", "Does not meet release criteria") || "Rejected by Checker";
|
|
4836
|
+
const handleRejectBatch = async (batchId, customReason) => {
|
|
4837
|
+
const reason = customReason || window.prompt("Enter reason for rejection:", "Does not meet release criteria") || "Rejected by Checker";
|
|
4367
4838
|
try {
|
|
4368
4839
|
setRejectingBatchId(batchId);
|
|
4369
4840
|
const res = await post(`/bulk-updater/batches/${batchId}/reject`, { reason });
|
|
@@ -4372,6 +4843,7 @@ const BulkUpdater = () => {
|
|
|
4372
4843
|
type: "info",
|
|
4373
4844
|
message: `Batch ${batchId} rejected: ${reason}`
|
|
4374
4845
|
});
|
|
4846
|
+
setInspectingPendingBatch(null);
|
|
4375
4847
|
await fetchPendingBatches();
|
|
4376
4848
|
} else {
|
|
4377
4849
|
toggleNotification({
|
|
@@ -4388,6 +4860,40 @@ const BulkUpdater = () => {
|
|
|
4388
4860
|
setRejectingBatchId(null);
|
|
4389
4861
|
}
|
|
4390
4862
|
};
|
|
4863
|
+
const handleExportPendingBatch = async (batchId) => {
|
|
4864
|
+
try {
|
|
4865
|
+
setExportingPendingBatchId(batchId);
|
|
4866
|
+
const res = await get(`/bulk-updater/batches/${batchId}/diffs/export`);
|
|
4867
|
+
if (res.data && res.data.success && res.data.csvContent) {
|
|
4868
|
+
const safeContent = res.data.csvContent.startsWith("\uFEFF") ? res.data.csvContent : "\uFEFF" + res.data.csvContent;
|
|
4869
|
+
const blob = new Blob([safeContent], { type: "text/csv;charset=utf-8;" });
|
|
4870
|
+
const url = URL.createObjectURL(blob);
|
|
4871
|
+
const link = document.createElement("a");
|
|
4872
|
+
link.href = url;
|
|
4873
|
+
link.setAttribute("download", res.data.filename || `batch_${batchId}_review.csv`);
|
|
4874
|
+
document.body.appendChild(link);
|
|
4875
|
+
link.click();
|
|
4876
|
+
document.body.removeChild(link);
|
|
4877
|
+
URL.revokeObjectURL(url);
|
|
4878
|
+
toggleNotification({
|
|
4879
|
+
type: "success",
|
|
4880
|
+
message: `Exported ${res.data.count || ""} batch diffs to CSV.`
|
|
4881
|
+
});
|
|
4882
|
+
} else {
|
|
4883
|
+
toggleNotification({
|
|
4884
|
+
type: "danger",
|
|
4885
|
+
message: "Failed to export batch diffs to CSV."
|
|
4886
|
+
});
|
|
4887
|
+
}
|
|
4888
|
+
} catch (err) {
|
|
4889
|
+
toggleNotification({
|
|
4890
|
+
type: "danger",
|
|
4891
|
+
message: err.message || "Error exporting batch diffs."
|
|
4892
|
+
});
|
|
4893
|
+
} finally {
|
|
4894
|
+
setExportingPendingBatchId(null);
|
|
4895
|
+
}
|
|
4896
|
+
};
|
|
4391
4897
|
const handleExecuteNow = async (batchId) => {
|
|
4392
4898
|
try {
|
|
4393
4899
|
setExecutingNowBatchId(batchId);
|
|
@@ -4681,9 +5187,14 @@ const BulkUpdater = () => {
|
|
|
4681
5187
|
/* @__PURE__ */ jsx(
|
|
4682
5188
|
Combobox,
|
|
4683
5189
|
{
|
|
4684
|
-
autocomplete: "
|
|
4685
|
-
placeholder: "
|
|
5190
|
+
autocomplete: { type: "list", filter: "contains" },
|
|
5191
|
+
placeholder: "Search Collection Type...",
|
|
4686
5192
|
value: selectedCollectionUid || void 0,
|
|
5193
|
+
clearLabel: "Clear collection selection",
|
|
5194
|
+
onClear: () => handleCollectionChange(""),
|
|
5195
|
+
onFocus: (e) => {
|
|
5196
|
+
e.target.select();
|
|
5197
|
+
},
|
|
4687
5198
|
onChange: (val) => {
|
|
4688
5199
|
if (val && typeof val === "string") {
|
|
4689
5200
|
handleCollectionChange(val);
|
|
@@ -4779,26 +5290,17 @@ const BulkUpdater = () => {
|
|
|
4779
5290
|
/* @__PURE__ */ jsx(
|
|
4780
5291
|
Combobox,
|
|
4781
5292
|
{
|
|
4782
|
-
autocomplete: "
|
|
5293
|
+
autocomplete: { type: "list", filter: "contains" },
|
|
4783
5294
|
placeholder: searchAttribute || `Same as Update (${selectedAttribute || "None"})`,
|
|
4784
5295
|
value: searchAttribute || "__default__",
|
|
4785
5296
|
clearLabel: "Reset search attribute",
|
|
4786
|
-
onClear: () =>
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
},
|
|
4790
|
-
onInputChange: (e) => {
|
|
4791
|
-
setSearchAttributeSearch(e.target.value);
|
|
4792
|
-
},
|
|
4793
|
-
onOpenChange: (isOpen) => {
|
|
4794
|
-
if (!isOpen) {
|
|
4795
|
-
setSearchAttributeSearch("");
|
|
4796
|
-
}
|
|
5297
|
+
onClear: () => handleSearchAttributeChange(""),
|
|
5298
|
+
onFocus: (e) => {
|
|
5299
|
+
e.target.select();
|
|
4797
5300
|
},
|
|
4798
5301
|
onChange: (val) => {
|
|
4799
5302
|
if (val && typeof val === "string" && val !== "__none__") {
|
|
4800
5303
|
handleSearchAttributeChange(val);
|
|
4801
|
-
setSearchAttributeSearch("");
|
|
4802
5304
|
}
|
|
4803
5305
|
},
|
|
4804
5306
|
children: memoizedSearchComboboxOptions
|
|
@@ -5576,28 +6078,19 @@ const BulkUpdater = () => {
|
|
|
5576
6078
|
/* @__PURE__ */ jsx(
|
|
5577
6079
|
Combobox,
|
|
5578
6080
|
{
|
|
5579
|
-
autocomplete: "
|
|
6081
|
+
autocomplete: { type: "list", filter: "contains" },
|
|
5580
6082
|
placeholder: "Search attribute by name or relation...",
|
|
5581
6083
|
value: selectedAttribute || void 0,
|
|
5582
6084
|
clearLabel: "Clear attribute selection",
|
|
5583
|
-
onClear: () =>
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
},
|
|
5587
|
-
onInputChange: (e) => {
|
|
5588
|
-
setAttributeSearch(e.target.value);
|
|
5589
|
-
},
|
|
5590
|
-
onOpenChange: (isOpen) => {
|
|
5591
|
-
if (!isOpen) {
|
|
5592
|
-
setAttributeSearch("");
|
|
5593
|
-
}
|
|
6085
|
+
onClear: () => handleAttributeChange(""),
|
|
6086
|
+
onFocus: (e) => {
|
|
6087
|
+
e.target.select();
|
|
5594
6088
|
},
|
|
5595
6089
|
loading: loading && attributes.length === 0,
|
|
5596
6090
|
loadingMessage: "Discovering schema attributes...",
|
|
5597
6091
|
onChange: (val) => {
|
|
5598
6092
|
if (val && typeof val === "string" && val !== "__none__") {
|
|
5599
6093
|
handleAttributeChange(val);
|
|
5600
|
-
setAttributeSearch("");
|
|
5601
6094
|
}
|
|
5602
6095
|
},
|
|
5603
6096
|
children: memoizedComboboxOptions
|
|
@@ -6005,6 +6498,27 @@ const BulkUpdater = () => {
|
|
|
6005
6498
|
] })
|
|
6006
6499
|
] }) }),
|
|
6007
6500
|
/* @__PURE__ */ jsx(Td, { style: { textAlign: "right" }, children: /* @__PURE__ */ jsxs(Flex, { gap: 2, justifyContent: "flex-end", children: [
|
|
6501
|
+
/* @__PURE__ */ jsx(
|
|
6502
|
+
Button,
|
|
6503
|
+
{
|
|
6504
|
+
size: "S",
|
|
6505
|
+
variant: "secondary",
|
|
6506
|
+
startIcon: /* @__PURE__ */ jsx(Eye, {}),
|
|
6507
|
+
onClick: () => setInspectingPendingBatch(batch),
|
|
6508
|
+
children: "Review Changes"
|
|
6509
|
+
}
|
|
6510
|
+
),
|
|
6511
|
+
/* @__PURE__ */ jsx(
|
|
6512
|
+
Button,
|
|
6513
|
+
{
|
|
6514
|
+
size: "S",
|
|
6515
|
+
variant: "tertiary",
|
|
6516
|
+
startIcon: /* @__PURE__ */ jsx(Download, {}),
|
|
6517
|
+
onClick: () => handleExportPendingBatch(batch.id),
|
|
6518
|
+
loading: exportingPendingBatchId === batch.id,
|
|
6519
|
+
children: "Export CSV"
|
|
6520
|
+
}
|
|
6521
|
+
),
|
|
6008
6522
|
batch.status === "pending_approval" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6009
6523
|
/* @__PURE__ */ jsx(
|
|
6010
6524
|
Button,
|
|
@@ -6921,6 +7435,18 @@ const BulkUpdater = () => {
|
|
|
6921
7435
|
initialJob: dashboardJob,
|
|
6922
7436
|
isSubmitting: isSubmittingBatch
|
|
6923
7437
|
}
|
|
7438
|
+
),
|
|
7439
|
+
/* @__PURE__ */ jsx(
|
|
7440
|
+
CheckerReviewModal,
|
|
7441
|
+
{
|
|
7442
|
+
isOpen: Boolean(inspectingPendingBatch),
|
|
7443
|
+
onClose: () => setInspectingPendingBatch(null),
|
|
7444
|
+
batch: inspectingPendingBatch,
|
|
7445
|
+
onApprove: handleApproveBatch,
|
|
7446
|
+
onReject: handleRejectBatch,
|
|
7447
|
+
approving: Boolean(inspectingPendingBatch && approvingBatchId === inspectingPendingBatch.id),
|
|
7448
|
+
rejecting: Boolean(inspectingPendingBatch && rejectingBatchId === inspectingPendingBatch.id)
|
|
7449
|
+
}
|
|
6924
7450
|
)
|
|
6925
7451
|
]
|
|
6926
7452
|
}
|
|
@@ -6939,4 +7465,4 @@ export {
|
|
|
6939
7465
|
getSortOptionsForCollection,
|
|
6940
7466
|
parseDynamicZonePath
|
|
6941
7467
|
};
|
|
6942
|
-
//# sourceMappingURL=BulkUpdater-
|
|
7468
|
+
//# sourceMappingURL=BulkUpdater-BiSkkAHF.mjs.map
|