strapi-plugin-bulk-updater 1.2.3 → 1.2.7
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/dist/_chunks/{BulkUpdater-BdRJhVFh.js → BulkUpdater-2TTEWMXI.js} +1170 -163
- package/dist/_chunks/BulkUpdater-2TTEWMXI.js.map +1 -0
- package/dist/_chunks/{BulkUpdater-BYtO2SHc.mjs → BulkUpdater-DR8Z9z95.mjs} +1172 -165
- package/dist/_chunks/BulkUpdater-DR8Z9z95.mjs.map +1 -0
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +612 -104
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +612 -104
- package/dist/server/index.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/_chunks/BulkUpdater-BYtO2SHc.mjs.map +0 -1
- package/dist/_chunks/BulkUpdater-BdRJhVFh.js.map +0 -1
|
@@ -18,7 +18,7 @@ function CsvManagerTab() {
|
|
|
18
18
|
const [exportOnlyViolations, setExportOnlyViolations] = React.useState(false);
|
|
19
19
|
const [rules, setRules] = React.useState([]);
|
|
20
20
|
const [selectedRuleId, setSelectedRuleId] = React.useState("");
|
|
21
|
-
const [importMode, setImportMode] = React.useState("
|
|
21
|
+
const [importMode, setImportMode] = React.useState("auto");
|
|
22
22
|
const [publishOnImport, setPublishOnImport] = React.useState(true);
|
|
23
23
|
const [csvRawText, setCsvRawText] = React.useState("");
|
|
24
24
|
const [fileName, setFileName] = React.useState("");
|
|
@@ -171,9 +171,14 @@ function CsvManagerTab() {
|
|
|
171
171
|
});
|
|
172
172
|
if (res.data && res.data.success) {
|
|
173
173
|
setImportResult(res.data);
|
|
174
|
+
const stats = res.data.stats;
|
|
175
|
+
let notifMsg = `CSV Import complete: ${stats.created} created, ${stats.updated} updated.`;
|
|
176
|
+
if (stats.newWithoutDocId && stats.newWithoutDocId > 0) {
|
|
177
|
+
notifMsg += ` (${stats.newWithoutDocId} new entries created due to omitted documentId)`;
|
|
178
|
+
}
|
|
174
179
|
toggleNotification({
|
|
175
180
|
type: "success",
|
|
176
|
-
message:
|
|
181
|
+
message: notifMsg
|
|
177
182
|
});
|
|
178
183
|
} else {
|
|
179
184
|
throw new Error(res.data?.error || "Import failed");
|
|
@@ -279,7 +284,20 @@ function CsvManagerTab() {
|
|
|
279
284
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 6, marginBottom: 4, wrap: "wrap", children: [
|
|
280
285
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
281
286
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", marginBottom: 2, children: "Operation Mode" }),
|
|
282
|
-
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, children: [
|
|
287
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, wrap: "wrap", children: [
|
|
288
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", alignItems: "center", gap: 6, cursor: "pointer" }, children: [
|
|
289
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
290
|
+
"input",
|
|
291
|
+
{
|
|
292
|
+
type: "radio",
|
|
293
|
+
name: "importMode",
|
|
294
|
+
value: "auto",
|
|
295
|
+
checked: importMode === "auto",
|
|
296
|
+
onChange: () => setImportMode("auto")
|
|
297
|
+
}
|
|
298
|
+
),
|
|
299
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: importMode === "auto" ? "bold" : "normal", children: "Smart Auto-Detect (Auto-updates matching documentId, creates new if missing)" })
|
|
300
|
+
] }),
|
|
283
301
|
/* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", alignItems: "center", gap: 6, cursor: "pointer" }, children: [
|
|
284
302
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
285
303
|
"input",
|
|
@@ -291,7 +309,7 @@ function CsvManagerTab() {
|
|
|
291
309
|
onChange: () => setImportMode("update")
|
|
292
310
|
}
|
|
293
311
|
),
|
|
294
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: importMode === "update" ? "bold" : "normal", children: "Update Existing (
|
|
312
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: importMode === "update" ? "bold" : "normal", children: "Update Existing Only (Requires documentId)" })
|
|
295
313
|
] }),
|
|
296
314
|
/* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", alignItems: "center", gap: 6, cursor: "pointer" }, children: [
|
|
297
315
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -350,31 +368,73 @@ function CsvManagerTab() {
|
|
|
350
368
|
]
|
|
351
369
|
}
|
|
352
370
|
),
|
|
353
|
-
parsedPreview.length > 0 &&
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
371
|
+
parsedPreview.length > 0 && (() => {
|
|
372
|
+
const missingDocIdCount = parsedPreview.filter(
|
|
373
|
+
(row) => !(row.documentId || row.id || "").trim()
|
|
374
|
+
).length;
|
|
375
|
+
const headers = Object.keys(parsedPreview[0]);
|
|
376
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginTop: 4, background: "neutral0", padding: 4, hasRadius: true, children: [
|
|
377
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 3, children: [
|
|
378
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
379
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "sigma", textColor: "neutral700", children: [
|
|
380
|
+
"FILE PREVIEW (FIRST ",
|
|
381
|
+
parsedPreview.length,
|
|
382
|
+
" ROWS)"
|
|
383
|
+
] }),
|
|
384
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral500", display: "block", children: [
|
|
385
|
+
parsedPreview.length,
|
|
386
|
+
" previewed row",
|
|
387
|
+
parsedPreview.length > 1 ? "s" : "",
|
|
388
|
+
" •",
|
|
389
|
+
" ",
|
|
390
|
+
missingDocIdCount > 0 ? `${missingDocIdCount} row${missingDocIdCount > 1 ? "s" : ""} missing documentId (will be created as new entries)` : "All previewed rows contain document IDs"
|
|
391
|
+
] })
|
|
392
|
+
] }),
|
|
393
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: missingDocIdCount > 0 ? "warning100" : "success100", textColor: missingDocIdCount > 0 ? "warning700" : "success700", children: missingDocIdCount > 0 ? `${missingDocIdCount} New (No ID)` : "Ready to Import" })
|
|
359
394
|
] }),
|
|
360
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
395
|
+
missingDocIdCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginBottom: 3, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
396
|
+
designSystem.Alert,
|
|
397
|
+
{
|
|
398
|
+
variant: importMode === "update" ? "warning" : "default",
|
|
399
|
+
title: importMode === "update" ? "Warning: Missing Document IDs" : "Smart Auto-Detect Active",
|
|
400
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "omega", children: [
|
|
401
|
+
missingDocIdCount,
|
|
402
|
+
" row",
|
|
403
|
+
missingDocIdCount > 1 ? "s have" : " has",
|
|
404
|
+
" no ",
|
|
405
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "documentId" }),
|
|
406
|
+
".",
|
|
407
|
+
" ",
|
|
408
|
+
importMode === "auto" ? "Because no documentId is provided, the engine will automatically treat them as new entries and generate new database IDs for them." : importMode === "update" ? 'In "Update Existing Only" mode, rows without a documentId will be skipped. Switch to "Smart Auto-Detect" to automatically create them as new entries.' : "These will be created as new entries."
|
|
409
|
+
] })
|
|
410
|
+
}
|
|
411
|
+
) }),
|
|
412
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: headers.length + 1, rowCount: parsedPreview.length + 1, children: [
|
|
413
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
|
|
414
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { width: 140 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Detected Action" }) }),
|
|
415
|
+
headers.map((h) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: h }) }, h))
|
|
416
|
+
] }) }),
|
|
417
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: parsedPreview.map((row, rIdx) => {
|
|
418
|
+
const hasDocId = !!(row.documentId || row.id || "").trim();
|
|
419
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
|
|
420
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: hasDocId ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: "🔄 Match / Update" }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: "➕ New Entry" }) }),
|
|
421
|
+
headers.map((h) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: (h === "documentId" || h === "id") && !row[h]?.trim() ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "warning700", fontWeight: "bold", children: "(Auto-generate ID)" }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: row[h] || "-" }) }, h))
|
|
422
|
+
] }, rIdx);
|
|
423
|
+
}) })
|
|
424
|
+
] }),
|
|
425
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "flex-end", marginTop: 4, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
426
|
+
designSystem.Button,
|
|
427
|
+
{
|
|
428
|
+
variant: "default",
|
|
429
|
+
size: "L",
|
|
430
|
+
startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Check, {}),
|
|
431
|
+
onClick: handleExecuteImport,
|
|
432
|
+
loading: importing,
|
|
433
|
+
children: importMode === "auto" ? "Execute Smart Import (Auto-Detect)" : importMode === "update" ? "Execute Bulk Update" : "Execute Add / Override"
|
|
434
|
+
}
|
|
435
|
+
) })
|
|
436
|
+
] });
|
|
437
|
+
})(),
|
|
378
438
|
importResult && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
379
439
|
designSystem.Alert,
|
|
380
440
|
{
|
|
@@ -387,8 +447,12 @@ function CsvManagerTab() {
|
|
|
387
447
|
importResult.stats.created,
|
|
388
448
|
" created"
|
|
389
449
|
] }),
|
|
390
|
-
"
|
|
391
|
-
|
|
450
|
+
importResult.stats.newWithoutDocId > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
451
|
+
" (",
|
|
452
|
+
importResult.stats.newWithoutDocId,
|
|
453
|
+
" created as new entries because documentId was omitted)"
|
|
454
|
+
] }),
|
|
455
|
+
", ",
|
|
392
456
|
/* @__PURE__ */ jsxRuntime.jsxs("strong", { children: [
|
|
393
457
|
importResult.stats.updated,
|
|
394
458
|
" updated"
|
|
@@ -403,6 +467,17 @@ function CsvManagerTab() {
|
|
|
403
467
|
importResult.stats.total,
|
|
404
468
|
" total rows."
|
|
405
469
|
] }),
|
|
470
|
+
importResult.stats.newWithoutDocId > 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 2, padding: 2, background: "neutral100", hasRadius: true, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral700", children: [
|
|
471
|
+
"ℹ️ ",
|
|
472
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Note on new entries:" }),
|
|
473
|
+
" ",
|
|
474
|
+
importResult.stats.newWithoutDocId,
|
|
475
|
+
" row",
|
|
476
|
+
importResult.stats.newWithoutDocId > 1 ? "s" : "",
|
|
477
|
+
" had no ",
|
|
478
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "documentId" }),
|
|
479
|
+
" specified in the CSV. The engine generated brand-new database entries with assigned document IDs. If you export this collection now, you will see their generated document IDs."
|
|
480
|
+
] }) }),
|
|
406
481
|
importResult.stats.errors.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginTop: 2, children: [
|
|
407
482
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "danger700", fontWeight: "bold", children: [
|
|
408
483
|
"Errors encountered (",
|
|
@@ -422,6 +497,626 @@ function CsvManagerTab() {
|
|
|
422
497
|
] })
|
|
423
498
|
] }) });
|
|
424
499
|
}
|
|
500
|
+
function formatVal(v) {
|
|
501
|
+
if (v === null || v === void 0) return "(empty)";
|
|
502
|
+
if (typeof v === "object") {
|
|
503
|
+
if (v.display) return v.display;
|
|
504
|
+
if (v.label) return v.label;
|
|
505
|
+
return JSON.stringify(v);
|
|
506
|
+
}
|
|
507
|
+
return String(v);
|
|
508
|
+
}
|
|
509
|
+
function downloadFile(content, filename, mimeType = "text/csv;charset=utf-8;") {
|
|
510
|
+
const blob = new Blob([content], { type: mimeType });
|
|
511
|
+
const url = URL.createObjectURL(blob);
|
|
512
|
+
const a = document.createElement("a");
|
|
513
|
+
a.href = url;
|
|
514
|
+
a.download = filename;
|
|
515
|
+
document.body.appendChild(a);
|
|
516
|
+
a.click();
|
|
517
|
+
document.body.removeChild(a);
|
|
518
|
+
URL.revokeObjectURL(url);
|
|
519
|
+
}
|
|
520
|
+
function ProcessingDashboardModal({
|
|
521
|
+
isOpen,
|
|
522
|
+
onClose,
|
|
523
|
+
batchId,
|
|
524
|
+
initialJob,
|
|
525
|
+
isSubmitting = false
|
|
526
|
+
}) {
|
|
527
|
+
const { get } = admin.useFetchClient();
|
|
528
|
+
const { toggleNotification } = admin.useNotification();
|
|
529
|
+
const [job, setJob] = React.useState(null);
|
|
530
|
+
const [loading, setLoading] = React.useState(false);
|
|
531
|
+
const [filterStatus, setFilterStatus] = React.useState("all");
|
|
532
|
+
const [searchQuery, setSearchQuery] = React.useState("");
|
|
533
|
+
const [downloading, setDownloading] = React.useState(false);
|
|
534
|
+
const [copiedBatchId, setCopiedBatchId] = React.useState(false);
|
|
535
|
+
React.useEffect(() => {
|
|
536
|
+
if (initialJob && initialJob.id) {
|
|
537
|
+
setJob((prev) => ({
|
|
538
|
+
id: initialJob.id,
|
|
539
|
+
createdAt: initialJob.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
540
|
+
startedAt: initialJob.startedAt,
|
|
541
|
+
completedAt: initialJob.completedAt,
|
|
542
|
+
createdBy: initialJob.createdBy || { email: "admin@system.local" },
|
|
543
|
+
status: initialJob.status || (isSubmitting ? "processing" : "successful"),
|
|
544
|
+
executionMode: initialJob.executionMode || "immediate",
|
|
545
|
+
scheduledFor: initialJob.scheduledFor,
|
|
546
|
+
attribute: initialJob.attribute || "",
|
|
547
|
+
value: initialJob.value || "",
|
|
548
|
+
totalTargets: initialJob.totalTargets || 0,
|
|
549
|
+
processedCount: initialJob.processedCount || 0,
|
|
550
|
+
updatedCount: initialJob.updatedCount || 0,
|
|
551
|
+
skippedCount: initialJob.skippedCount || 0,
|
|
552
|
+
failedCount: initialJob.failedCount || 0,
|
|
553
|
+
isLive: Boolean(initialJob.isLive),
|
|
554
|
+
publishOnApproval: initialJob.publishOnApproval ?? true,
|
|
555
|
+
policyMessage: initialJob.policyMessage,
|
|
556
|
+
makerNotes: initialJob.makerNotes,
|
|
557
|
+
results: initialJob.results || [],
|
|
558
|
+
error: initialJob.error
|
|
559
|
+
}));
|
|
560
|
+
}
|
|
561
|
+
}, [initialJob, isSubmitting]);
|
|
562
|
+
const fetchStatus = async () => {
|
|
563
|
+
const targetId = batchId || initialJob?.id;
|
|
564
|
+
if (!targetId) return;
|
|
565
|
+
try {
|
|
566
|
+
setLoading(true);
|
|
567
|
+
const res = await get(`/bulk-updater/batches/${targetId}/status`);
|
|
568
|
+
if (res.data?.success && res.data.batch) {
|
|
569
|
+
setJob(res.data.batch);
|
|
570
|
+
}
|
|
571
|
+
} catch (err) {
|
|
572
|
+
} finally {
|
|
573
|
+
setLoading(false);
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
React.useEffect(() => {
|
|
577
|
+
if (isOpen && (batchId || initialJob?.id)) {
|
|
578
|
+
fetchStatus();
|
|
579
|
+
if (isSubmitting || job?.status === "processing") {
|
|
580
|
+
const interval = setInterval(fetchStatus, 2e3);
|
|
581
|
+
return () => clearInterval(interval);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}, [isOpen, batchId, initialJob?.id, isSubmitting, job?.status]);
|
|
585
|
+
const handleCopyId = () => {
|
|
586
|
+
const idToCopy = job?.id || batchId;
|
|
587
|
+
if (idToCopy) {
|
|
588
|
+
navigator.clipboard.writeText(idToCopy);
|
|
589
|
+
setCopiedBatchId(true);
|
|
590
|
+
setTimeout(() => setCopiedBatchId(false), 2e3);
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
const handleDownloadReport = async (format = "csv") => {
|
|
594
|
+
const targetId = job?.id || batchId;
|
|
595
|
+
if (!targetId) return;
|
|
596
|
+
try {
|
|
597
|
+
setDownloading(true);
|
|
598
|
+
const res = await get(`/bulk-updater/batches/${targetId}/report?format=${format}`);
|
|
599
|
+
if (res.data?.success) {
|
|
600
|
+
if (format === "csv" && res.data.csvContent) {
|
|
601
|
+
downloadFile(res.data.csvContent, res.data.filename);
|
|
602
|
+
} else if (format === "json") {
|
|
603
|
+
const jsonStr = JSON.stringify(res.data.batch || job, null, 2);
|
|
604
|
+
downloadFile(jsonStr, `bulk_update_report_${targetId}.json`, "application/json");
|
|
605
|
+
}
|
|
606
|
+
toggleNotification({
|
|
607
|
+
type: "success",
|
|
608
|
+
message: `Execution report downloaded (${format.toUpperCase()})`
|
|
609
|
+
});
|
|
610
|
+
} else {
|
|
611
|
+
throw new Error(res.data?.error || "Failed generating report");
|
|
612
|
+
}
|
|
613
|
+
} catch (err) {
|
|
614
|
+
if (job && job.results) {
|
|
615
|
+
const csvRows = [
|
|
616
|
+
"Batch ID,Collection Name,Document ID,Record Label,Attribute,Old Value,New Value,Status,Live On Site,Message",
|
|
617
|
+
...job.results.map(
|
|
618
|
+
(r) => [
|
|
619
|
+
job.id,
|
|
620
|
+
`"${r.collectionName}"`,
|
|
621
|
+
r.documentId,
|
|
622
|
+
`"${r.label.replace(/"/g, '""')}"`,
|
|
623
|
+
`"${r.attribute}"`,
|
|
624
|
+
`"${formatVal(r.oldValue).replace(/"/g, '""')}"`,
|
|
625
|
+
`"${formatVal(r.newValue).replace(/"/g, '""')}"`,
|
|
626
|
+
r.status,
|
|
627
|
+
r.isLive ? "YES" : "NO",
|
|
628
|
+
`"${r.message.replace(/"/g, '""')}"`
|
|
629
|
+
].join(",")
|
|
630
|
+
)
|
|
631
|
+
];
|
|
632
|
+
downloadFile(csvRows.join("\n"), `bulk_update_report_${targetId}.csv`);
|
|
633
|
+
toggleNotification({
|
|
634
|
+
type: "success",
|
|
635
|
+
message: "Execution report downloaded successfully"
|
|
636
|
+
});
|
|
637
|
+
} else {
|
|
638
|
+
toggleNotification({
|
|
639
|
+
type: "danger",
|
|
640
|
+
message: err.message || "Error downloading execution report"
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
} finally {
|
|
644
|
+
setDownloading(false);
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
const filteredResults = React.useMemo(() => {
|
|
648
|
+
if (!job?.results) return [];
|
|
649
|
+
return job.results.filter((item) => {
|
|
650
|
+
if (filterStatus !== "all" && item.status !== filterStatus) return false;
|
|
651
|
+
if (searchQuery.trim() !== "") {
|
|
652
|
+
const q = searchQuery.toLowerCase();
|
|
653
|
+
const matchesCol = item.collectionName?.toLowerCase().includes(q);
|
|
654
|
+
const matchesLabel = item.label?.toLowerCase().includes(q);
|
|
655
|
+
const matchesId = item.documentId?.toLowerCase().includes(q);
|
|
656
|
+
const matchesMsg = item.message?.toLowerCase().includes(q);
|
|
657
|
+
if (!matchesCol && !matchesLabel && !matchesId && !matchesMsg) return false;
|
|
658
|
+
}
|
|
659
|
+
return true;
|
|
660
|
+
});
|
|
661
|
+
}, [job?.results, filterStatus, searchQuery]);
|
|
662
|
+
if (!isOpen) return null;
|
|
663
|
+
const currentStatus = isSubmitting ? "processing" : job?.status || "processing";
|
|
664
|
+
const progressPercent = job && job.totalTargets > 0 ? Math.min(Math.round((job.updatedCount + job.skippedCount + job.failedCount) / job.totalTargets * 100), 100) : isSubmitting ? 50 : 100;
|
|
665
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
666
|
+
"div",
|
|
667
|
+
{
|
|
668
|
+
style: {
|
|
669
|
+
position: "fixed",
|
|
670
|
+
inset: 0,
|
|
671
|
+
backgroundColor: "rgba(20, 20, 30, 0.75)",
|
|
672
|
+
backdropFilter: "blur(3px)",
|
|
673
|
+
display: "flex",
|
|
674
|
+
alignItems: "center",
|
|
675
|
+
justifyContent: "center",
|
|
676
|
+
zIndex: 9999,
|
|
677
|
+
padding: 16,
|
|
678
|
+
boxSizing: "border-box",
|
|
679
|
+
overflowY: "auto"
|
|
680
|
+
},
|
|
681
|
+
onClick: onClose,
|
|
682
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
683
|
+
designSystem.Box,
|
|
684
|
+
{
|
|
685
|
+
background: "neutral0",
|
|
686
|
+
hasRadius: true,
|
|
687
|
+
shadow: "popupShadow",
|
|
688
|
+
style: {
|
|
689
|
+
maxWidth: 960,
|
|
690
|
+
width: "100%",
|
|
691
|
+
maxHeight: "calc(100vh - 40px)",
|
|
692
|
+
display: "flex",
|
|
693
|
+
flexDirection: "column",
|
|
694
|
+
overflow: "hidden",
|
|
695
|
+
position: "relative",
|
|
696
|
+
boxShadow: "0 24px 48px rgba(0, 0, 0, 0.35)"
|
|
697
|
+
},
|
|
698
|
+
onClick: (e) => e.stopPropagation(),
|
|
699
|
+
children: [
|
|
700
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
701
|
+
designSystem.Box,
|
|
702
|
+
{
|
|
703
|
+
padding: 5,
|
|
704
|
+
background: "neutral100",
|
|
705
|
+
style: { borderBottom: "1px solid #dcdce4", flexShrink: 0 },
|
|
706
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
|
|
707
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
708
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 3, children: [
|
|
709
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", fontWeight: "bold", children: "Bulk Update Processing Dashboard" }),
|
|
710
|
+
currentStatus === "processing" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: "🔄 PROCESSING" }),
|
|
711
|
+
currentStatus === "successful" && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: [
|
|
712
|
+
"🟢 CHANGES LIVE (",
|
|
713
|
+
job?.updatedCount,
|
|
714
|
+
" UPDATED)"
|
|
715
|
+
] }),
|
|
716
|
+
currentStatus === "partial_failure" && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: [
|
|
717
|
+
"⚠️ PARTIAL (",
|
|
718
|
+
job?.updatedCount,
|
|
719
|
+
" LIVE, ",
|
|
720
|
+
job?.failedCount,
|
|
721
|
+
" FAILED)"
|
|
722
|
+
] }),
|
|
723
|
+
currentStatus === "failed" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "danger100", textColor: "danger700", children: "🔴 FAILED" }),
|
|
724
|
+
currentStatus === "pending_approval" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: "⏳ PENDING CHECKER APPROVAL" }),
|
|
725
|
+
currentStatus === "queued_offpeak" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: "🕒 QUEUED FOR OFF-PEAK" })
|
|
726
|
+
] }),
|
|
727
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, marginTop: 1, children: [
|
|
728
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
|
|
729
|
+
"Batch ID: ",
|
|
730
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { style: { fontWeight: "bold" }, children: job?.id || batchId || "Generating..." })
|
|
731
|
+
] }),
|
|
732
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
733
|
+
"button",
|
|
734
|
+
{
|
|
735
|
+
type: "button",
|
|
736
|
+
onClick: handleCopyId,
|
|
737
|
+
style: {
|
|
738
|
+
background: "none",
|
|
739
|
+
border: "none",
|
|
740
|
+
padding: "0 4px",
|
|
741
|
+
cursor: "pointer",
|
|
742
|
+
fontSize: 12,
|
|
743
|
+
color: "#4945ff"
|
|
744
|
+
},
|
|
745
|
+
children: copiedBatchId ? "✓ Copied" : "Copy ID"
|
|
746
|
+
}
|
|
747
|
+
),
|
|
748
|
+
job?.createdAt && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: [
|
|
749
|
+
"• Submitted at ",
|
|
750
|
+
new Date(job.createdAt).toLocaleTimeString()
|
|
751
|
+
] }),
|
|
752
|
+
job?.createdBy?.email && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: [
|
|
753
|
+
"• Operator: ",
|
|
754
|
+
job.createdBy.email
|
|
755
|
+
] })
|
|
756
|
+
] })
|
|
757
|
+
] }),
|
|
758
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
|
|
759
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", size: "S", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowClockwise, {}), onClick: fetchStatus, loading, children: "Refresh" }),
|
|
760
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "secondary", size: "S", onClick: onClose, children: "Close" })
|
|
761
|
+
] })
|
|
762
|
+
] })
|
|
763
|
+
}
|
|
764
|
+
),
|
|
765
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 5, style: { flex: "1 1 auto", overflowY: "auto" }, children: [
|
|
766
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginBottom: 5, padding: 4, background: "neutral100", hasRadius: true, style: { border: "1px solid #eaeaef" }, children: [
|
|
767
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 2, children: [
|
|
768
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral700", children: "PROCESSING PIPELINE & VERIFICATION" }),
|
|
769
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: currentStatus === "successful" ? "success700" : "primary700", children: currentStatus === "processing" ? `In Progress... (${progressPercent}%)` : currentStatus === "successful" ? "100% Completed (Live on Server)" : currentStatus === "pending_approval" ? "Awaiting Checker Decision" : currentStatus === "queued_offpeak" ? "Scheduled for Off-Peak" : "Completed" })
|
|
770
|
+
] }),
|
|
771
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
772
|
+
"div",
|
|
773
|
+
{
|
|
774
|
+
style: {
|
|
775
|
+
width: "100%",
|
|
776
|
+
height: 8,
|
|
777
|
+
backgroundColor: "#dcdce4",
|
|
778
|
+
borderRadius: 4,
|
|
779
|
+
overflow: "hidden",
|
|
780
|
+
marginBottom: 16
|
|
781
|
+
},
|
|
782
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
783
|
+
"div",
|
|
784
|
+
{
|
|
785
|
+
style: {
|
|
786
|
+
height: "100%",
|
|
787
|
+
width: `${progressPercent}%`,
|
|
788
|
+
backgroundColor: currentStatus === "failed" ? "#d02b20" : currentStatus === "successful" ? "#2f6846" : "#4945ff",
|
|
789
|
+
transition: "width 0.4s ease"
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
)
|
|
793
|
+
}
|
|
794
|
+
),
|
|
795
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 2, children: [
|
|
796
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, children: [
|
|
797
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
798
|
+
"div",
|
|
799
|
+
{
|
|
800
|
+
style: {
|
|
801
|
+
width: 22,
|
|
802
|
+
height: 22,
|
|
803
|
+
borderRadius: "50%",
|
|
804
|
+
backgroundColor: "#2f6846",
|
|
805
|
+
color: "#fff",
|
|
806
|
+
display: "flex",
|
|
807
|
+
alignItems: "center",
|
|
808
|
+
justifyContent: "center",
|
|
809
|
+
fontSize: 12,
|
|
810
|
+
fontWeight: "bold"
|
|
811
|
+
},
|
|
812
|
+
children: "✓"
|
|
813
|
+
}
|
|
814
|
+
),
|
|
815
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: "1. Target Batch Queued" })
|
|
816
|
+
] }),
|
|
817
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, children: [
|
|
818
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
819
|
+
"div",
|
|
820
|
+
{
|
|
821
|
+
style: {
|
|
822
|
+
width: 22,
|
|
823
|
+
height: 22,
|
|
824
|
+
borderRadius: "50%",
|
|
825
|
+
backgroundColor: currentStatus === "failed" && !job?.updatedCount ? "#d02b20" : "#2f6846",
|
|
826
|
+
color: "#fff",
|
|
827
|
+
display: "flex",
|
|
828
|
+
alignItems: "center",
|
|
829
|
+
justifyContent: "center",
|
|
830
|
+
fontSize: 12,
|
|
831
|
+
fontWeight: "bold"
|
|
832
|
+
},
|
|
833
|
+
children: "✓"
|
|
834
|
+
}
|
|
835
|
+
),
|
|
836
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: "2. Schema & Quotas Validated" })
|
|
837
|
+
] }),
|
|
838
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, children: [
|
|
839
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
840
|
+
"div",
|
|
841
|
+
{
|
|
842
|
+
style: {
|
|
843
|
+
width: 22,
|
|
844
|
+
height: 22,
|
|
845
|
+
borderRadius: "50%",
|
|
846
|
+
backgroundColor: currentStatus === "processing" ? "#4945ff" : currentStatus === "failed" && !job?.updatedCount ? "#dcdce4" : "#2f6846",
|
|
847
|
+
color: "#fff",
|
|
848
|
+
display: "flex",
|
|
849
|
+
alignItems: "center",
|
|
850
|
+
justifyContent: "center",
|
|
851
|
+
fontSize: 12,
|
|
852
|
+
fontWeight: "bold"
|
|
853
|
+
},
|
|
854
|
+
children: currentStatus === "processing" ? "•" : "✓"
|
|
855
|
+
}
|
|
856
|
+
),
|
|
857
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: "3. Applied to Database" })
|
|
858
|
+
] }),
|
|
859
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, children: [
|
|
860
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
861
|
+
"div",
|
|
862
|
+
{
|
|
863
|
+
style: {
|
|
864
|
+
width: 22,
|
|
865
|
+
height: 22,
|
|
866
|
+
borderRadius: "50%",
|
|
867
|
+
backgroundColor: currentStatus === "successful" ? "#2f6846" : currentStatus === "pending_approval" ? "#f5c000" : currentStatus === "queued_offpeak" ? "#666687" : currentStatus === "processing" ? "#dcdce4" : "#d02b20",
|
|
868
|
+
color: "#fff",
|
|
869
|
+
display: "flex",
|
|
870
|
+
alignItems: "center",
|
|
871
|
+
justifyContent: "center",
|
|
872
|
+
fontSize: 12,
|
|
873
|
+
fontWeight: "bold"
|
|
874
|
+
},
|
|
875
|
+
children: currentStatus === "successful" ? "✓" : currentStatus === "pending_approval" ? "⏳" : "•"
|
|
876
|
+
}
|
|
877
|
+
),
|
|
878
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: "4. Live Verification & Audit" })
|
|
879
|
+
] })
|
|
880
|
+
] })
|
|
881
|
+
] }),
|
|
882
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginBottom: 5, children: [
|
|
883
|
+
currentStatus === "processing" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Alert, { variant: "default", title: "Bulk Update In Progress", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 3, children: [
|
|
884
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { small: true }),
|
|
885
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: "The engine is updating records across selected collections. Do not refresh or navigate away." })
|
|
886
|
+
] }) }),
|
|
887
|
+
currentStatus === "successful" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Alert, { variant: "success", title: "🟢 CHANGES ARE CURRENTLY LIVE ON SERVER", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "omega", display: "block", children: [
|
|
888
|
+
"All ",
|
|
889
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: job?.updatedCount || 0 }),
|
|
890
|
+
" matching records were successfully updated in the database",
|
|
891
|
+
job?.publishOnApproval ? " and auto-published" : "",
|
|
892
|
+
". Your changes are now live and accessible in Strapi Content Manager and public APIs."
|
|
893
|
+
] }) }),
|
|
894
|
+
currentStatus === "partial_failure" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Alert, { variant: "warning", title: "⚠️ PARTIAL EXECUTION (SOME RECORDS ENCOUNTERED ERRORS)", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "omega", display: "block", children: [
|
|
895
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: job?.updatedCount || 0 }),
|
|
896
|
+
" records were updated and are currently live, but",
|
|
897
|
+
" ",
|
|
898
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: job?.failedCount || 0 }),
|
|
899
|
+
" records failed. Inspect the itemized table below for exact error details."
|
|
900
|
+
] }) }),
|
|
901
|
+
currentStatus === "failed" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Alert, { variant: "danger", title: "🔴 EXECUTION FAILED (CHANGES NOT APPLIED)", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "omega", display: "block", children: [
|
|
902
|
+
"The bulk update failed: ",
|
|
903
|
+
job?.error || "One or more database mutations failed. No records were published."
|
|
904
|
+
] }) }),
|
|
905
|
+
currentStatus === "pending_approval" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Alert, { variant: "warning", title: "⏳ PENDING CHECKER APPROVAL (CHANGES NOT LIVE YET)", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "omega", display: "block", children: [
|
|
906
|
+
"This batch was submitted under the ",
|
|
907
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "4-Eyes Maker-Checker Policy" }),
|
|
908
|
+
". Changes will ",
|
|
909
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "not" }),
|
|
910
|
+
" go live until a Checker reviews and approves this batch in the",
|
|
911
|
+
" ",
|
|
912
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Audit Trail & Approvals" }),
|
|
913
|
+
" tab."
|
|
914
|
+
] }) }),
|
|
915
|
+
currentStatus === "queued_offpeak" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Alert, { variant: "default", title: `🕒 SCHEDULED FOR OFF-PEAK WINDOW (${job?.scheduledFor || "18:00"})`, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "omega", display: "block", children: [
|
|
916
|
+
"This batch was scheduled to avoid peak database load during business hours. The scheduled background worker will automatically execute and publish these changes at",
|
|
917
|
+
" ",
|
|
918
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: job?.scheduledFor || "off-peak" }),
|
|
919
|
+
"."
|
|
920
|
+
] }) })
|
|
921
|
+
] }),
|
|
922
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginBottom: 5, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
923
|
+
"div",
|
|
924
|
+
{
|
|
925
|
+
style: {
|
|
926
|
+
display: "grid",
|
|
927
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))",
|
|
928
|
+
gap: 16
|
|
929
|
+
},
|
|
930
|
+
children: [
|
|
931
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 4, background: "neutral100", hasRadius: true, style: { border: "1px solid #eaeaef" }, children: [
|
|
932
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", display: "block", children: "TOTAL TARGETS" }),
|
|
933
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", fontWeight: "bold", textColor: "neutral800", marginTop: 1, display: "block", children: job?.totalTargets || 0 }),
|
|
934
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: "Targeted entities" })
|
|
935
|
+
] }),
|
|
936
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 4, background: "success100", hasRadius: true, style: { border: "1px solid #b7eb8f" }, children: [
|
|
937
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "success700", display: "block", children: "LIVE / UPDATED" }),
|
|
938
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", fontWeight: "bold", textColor: "success700", marginTop: 1, display: "block", children: job?.updatedCount || 0 }),
|
|
939
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "success700", children: job?.isLive ? "✓ Active on site" : "Pending live" })
|
|
940
|
+
] }),
|
|
941
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 4, background: "neutral100", hasRadius: true, style: { border: "1px solid #eaeaef" }, children: [
|
|
942
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", display: "block", children: "SKIPPED" }),
|
|
943
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", fontWeight: "bold", textColor: "neutral800", marginTop: 1, display: "block", children: job?.skippedCount || 0 }),
|
|
944
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: "Attribute not in schema" })
|
|
945
|
+
] }),
|
|
946
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
947
|
+
designSystem.Box,
|
|
948
|
+
{
|
|
949
|
+
padding: 4,
|
|
950
|
+
background: (job?.failedCount || 0) > 0 ? "danger100" : "neutral100",
|
|
951
|
+
hasRadius: true,
|
|
952
|
+
style: { border: (job?.failedCount || 0) > 0 ? "1px solid #ffa39e" : "1px solid #eaeaef" },
|
|
953
|
+
children: [
|
|
954
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: (job?.failedCount || 0) > 0 ? "danger700" : "neutral600", display: "block", children: "FAILED / ERRORS" }),
|
|
955
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
956
|
+
designSystem.Typography,
|
|
957
|
+
{
|
|
958
|
+
variant: "alpha",
|
|
959
|
+
fontWeight: "bold",
|
|
960
|
+
textColor: (job?.failedCount || 0) > 0 ? "danger700" : "neutral800",
|
|
961
|
+
marginTop: 1,
|
|
962
|
+
display: "block",
|
|
963
|
+
children: job?.failedCount || 0
|
|
964
|
+
}
|
|
965
|
+
),
|
|
966
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: (job?.failedCount || 0) > 0 ? "danger700" : "neutral500", children: (job?.failedCount || 0) > 0 ? "Requires attention" : "0 errors" })
|
|
967
|
+
]
|
|
968
|
+
}
|
|
969
|
+
)
|
|
970
|
+
]
|
|
971
|
+
}
|
|
972
|
+
) }),
|
|
973
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginBottom: 4, padding: 4, background: "neutral100", hasRadius: true, style: { border: "1px solid #eaeaef" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: [
|
|
974
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
975
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", fontWeight: "bold", display: "block", children: "Execution Report & Verification Audit" }),
|
|
976
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Download an itemized verification report showing every record's old value, new value, and live deployment status." })
|
|
977
|
+
] }),
|
|
978
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
|
|
979
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
980
|
+
designSystem.Button,
|
|
981
|
+
{
|
|
982
|
+
variant: "default",
|
|
983
|
+
startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Download, {}),
|
|
984
|
+
onClick: () => handleDownloadReport("csv"),
|
|
985
|
+
loading: downloading,
|
|
986
|
+
children: "Download Execution Report (.csv)"
|
|
987
|
+
}
|
|
988
|
+
),
|
|
989
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
990
|
+
designSystem.Button,
|
|
991
|
+
{
|
|
992
|
+
variant: "secondary",
|
|
993
|
+
startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Download, {}),
|
|
994
|
+
onClick: () => handleDownloadReport("json"),
|
|
995
|
+
loading: downloading,
|
|
996
|
+
children: "JSON Report"
|
|
997
|
+
}
|
|
998
|
+
)
|
|
999
|
+
] })
|
|
1000
|
+
] }) }),
|
|
1001
|
+
job && job.results && job.results.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
1002
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 3, wrap: "wrap", gap: 2, children: [
|
|
1003
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "delta", fontWeight: "bold", children: [
|
|
1004
|
+
"Itemized Changes (",
|
|
1005
|
+
filteredResults.length,
|
|
1006
|
+
" / ",
|
|
1007
|
+
job.results.length,
|
|
1008
|
+
")"
|
|
1009
|
+
] }),
|
|
1010
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
|
|
1011
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1012
|
+
designSystem.Button,
|
|
1013
|
+
{
|
|
1014
|
+
variant: filterStatus === "all" ? "secondary" : "tertiary",
|
|
1015
|
+
size: "S",
|
|
1016
|
+
onClick: () => setFilterStatus("all"),
|
|
1017
|
+
children: [
|
|
1018
|
+
"All (",
|
|
1019
|
+
job.results.length,
|
|
1020
|
+
")"
|
|
1021
|
+
]
|
|
1022
|
+
}
|
|
1023
|
+
),
|
|
1024
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1025
|
+
designSystem.Button,
|
|
1026
|
+
{
|
|
1027
|
+
variant: filterStatus === "updated" ? "secondary" : "tertiary",
|
|
1028
|
+
size: "S",
|
|
1029
|
+
onClick: () => setFilterStatus("updated"),
|
|
1030
|
+
children: [
|
|
1031
|
+
"Live / Updated (",
|
|
1032
|
+
job.updatedCount,
|
|
1033
|
+
")"
|
|
1034
|
+
]
|
|
1035
|
+
}
|
|
1036
|
+
),
|
|
1037
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1038
|
+
designSystem.Button,
|
|
1039
|
+
{
|
|
1040
|
+
variant: filterStatus === "skipped" ? "secondary" : "tertiary",
|
|
1041
|
+
size: "S",
|
|
1042
|
+
onClick: () => setFilterStatus("skipped"),
|
|
1043
|
+
children: [
|
|
1044
|
+
"Skipped (",
|
|
1045
|
+
job.skippedCount,
|
|
1046
|
+
")"
|
|
1047
|
+
]
|
|
1048
|
+
}
|
|
1049
|
+
),
|
|
1050
|
+
job.failedCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1051
|
+
designSystem.Button,
|
|
1052
|
+
{
|
|
1053
|
+
variant: filterStatus === "error" ? "secondary" : "tertiary",
|
|
1054
|
+
size: "S",
|
|
1055
|
+
onClick: () => setFilterStatus("error"),
|
|
1056
|
+
children: [
|
|
1057
|
+
"Failed (",
|
|
1058
|
+
job.failedCount,
|
|
1059
|
+
")"
|
|
1060
|
+
]
|
|
1061
|
+
}
|
|
1062
|
+
)
|
|
1063
|
+
] })
|
|
1064
|
+
] }),
|
|
1065
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginBottom: 3, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1066
|
+
designSystem.TextInput,
|
|
1067
|
+
{
|
|
1068
|
+
placeholder: "Filter by collection name, title, or document ID...",
|
|
1069
|
+
value: searchQuery,
|
|
1070
|
+
onChange: (e) => setSearchQuery(e.target.value),
|
|
1071
|
+
size: "S"
|
|
1072
|
+
}
|
|
1073
|
+
) }),
|
|
1074
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: 6, rowCount: filteredResults.length + 1, children: [
|
|
1075
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
|
|
1076
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { width: 140 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Status" }) }),
|
|
1077
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Collection" }) }),
|
|
1078
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Record / Document ID" }) }),
|
|
1079
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Previous Value" }) }),
|
|
1080
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "New Live Value" }) }),
|
|
1081
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Details" }) })
|
|
1082
|
+
] }) }),
|
|
1083
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: filteredResults.map((item, idx) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
|
|
1084
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: item.status === "updated" ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: "🟢 Live / Updated" }) : item.status === "error" ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "danger100", textColor: "danger700", children: "❌ Failed" }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "neutral150", textColor: "neutral700", children: "⚠️ Skipped" }) }),
|
|
1085
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", children: item.collectionName }) }),
|
|
1086
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
1087
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", display: "block", children: item.label }),
|
|
1088
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: item.documentId }) })
|
|
1089
|
+
] }) }),
|
|
1090
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", children: formatVal(item.oldValue) }) }),
|
|
1091
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", textColor: "primary700", children: formatVal(item.newValue) }) }),
|
|
1092
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: item.status === "error" ? "danger700" : "neutral600", children: item.message }) })
|
|
1093
|
+
] }, idx)) })
|
|
1094
|
+
] })
|
|
1095
|
+
] })
|
|
1096
|
+
] }),
|
|
1097
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 4, background: "neutral100", style: { borderTop: "1px solid #dcdce4", flexShrink: 0 }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
|
|
1098
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: job?.completedAt ? `Finished at ${new Date(job.completedAt).toLocaleTimeString()}` : isSubmitting ? "Processing live batch..." : "Status active" }),
|
|
1099
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, children: [
|
|
1100
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1101
|
+
designSystem.Button,
|
|
1102
|
+
{
|
|
1103
|
+
variant: "default",
|
|
1104
|
+
size: "M",
|
|
1105
|
+
startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Download, {}),
|
|
1106
|
+
onClick: () => handleDownloadReport("csv"),
|
|
1107
|
+
loading: downloading,
|
|
1108
|
+
children: "Download Report (.csv)"
|
|
1109
|
+
}
|
|
1110
|
+
),
|
|
1111
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "secondary", size: "M", onClick: onClose, children: "Done" })
|
|
1112
|
+
] })
|
|
1113
|
+
] }) })
|
|
1114
|
+
]
|
|
1115
|
+
}
|
|
1116
|
+
)
|
|
1117
|
+
}
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
425
1120
|
function getNestedValue(obj, path) {
|
|
426
1121
|
if (!obj || !path) return void 0;
|
|
427
1122
|
if (!path.includes(".")) return obj[path];
|
|
@@ -612,7 +1307,21 @@ const BulkUpdater = () => {
|
|
|
612
1307
|
const [executingNowBatchId, setExecutingNowBatchId] = React.useState(null);
|
|
613
1308
|
const [makerNotesInput, setMakerNotesInput] = React.useState("");
|
|
614
1309
|
const [publishOnApproval, setPublishOnApproval] = React.useState(true);
|
|
615
|
-
const [
|
|
1310
|
+
const [showProcessingDashboard, setShowProcessingDashboard] = React.useState(false);
|
|
1311
|
+
const [dashboardBatchId, setDashboardBatchId] = React.useState("");
|
|
1312
|
+
const [dashboardJob, setDashboardJob] = React.useState(null);
|
|
1313
|
+
const [isSubmittingBatch, setIsSubmittingBatch] = React.useState(false);
|
|
1314
|
+
const MODELS_CACHE_KEY = "strapi_bulk_updater_models_cache";
|
|
1315
|
+
const LAST_ATTR_KEY = "strapi_bulk_updater_last_attr";
|
|
1316
|
+
const getInitialAttribute = () => {
|
|
1317
|
+
try {
|
|
1318
|
+
const stored = localStorage.getItem(LAST_ATTR_KEY);
|
|
1319
|
+
if (stored) return stored;
|
|
1320
|
+
} catch {
|
|
1321
|
+
}
|
|
1322
|
+
return "metaDescription";
|
|
1323
|
+
};
|
|
1324
|
+
const [selectedAttribute, setSelectedAttribute] = React.useState(getInitialAttribute);
|
|
616
1325
|
const [attributeValue, setAttributeValue] = React.useState("");
|
|
617
1326
|
const [selectedEntries, setSelectedEntries] = React.useState({});
|
|
618
1327
|
const [relationsMeta, setRelationsMeta] = React.useState({});
|
|
@@ -644,6 +1353,9 @@ const BulkUpdater = () => {
|
|
|
644
1353
|
const [collectionPages, setCollectionPages] = React.useState({});
|
|
645
1354
|
const [collectionSearch, setCollectionSearch] = React.useState("");
|
|
646
1355
|
const [visibleTypeUids, setVisibleTypeUids] = React.useState({});
|
|
1356
|
+
const [typeVisibilitySearch, setTypeVisibilitySearch] = React.useState("");
|
|
1357
|
+
const [attributeInfoExpanded, setAttributeInfoExpanded] = React.useState(false);
|
|
1358
|
+
const [attributeInfoSearch, setAttributeInfoSearch] = React.useState("");
|
|
647
1359
|
const [defaultPageSize, setDefaultPageSize] = React.useState(10);
|
|
648
1360
|
const [collectionSorts, setCollectionSorts] = React.useState({});
|
|
649
1361
|
const [collectionColumnVisibility, setCollectionColumnVisibility] = React.useState({});
|
|
@@ -682,47 +1394,72 @@ const BulkUpdater = () => {
|
|
|
682
1394
|
const [expandedDiffBatchId, setExpandedDiffBatchId] = React.useState(null);
|
|
683
1395
|
const [auditNotice, setAuditNotice] = React.useState(null);
|
|
684
1396
|
const latestActiveBatch = auditHistory.find((rec) => !rec.isRolledBack);
|
|
1397
|
+
const applyModelsData = (data) => {
|
|
1398
|
+
if (!data) return;
|
|
1399
|
+
const attrs = data.attributes || [];
|
|
1400
|
+
setAttributes(attrs);
|
|
1401
|
+
const cols = data.collections || [];
|
|
1402
|
+
setCollections(cols);
|
|
1403
|
+
setAuditEnabled(Boolean(data.auditEnabled));
|
|
1404
|
+
if (data.executionWindow) {
|
|
1405
|
+
setExecutionWindow(data.executionWindow);
|
|
1406
|
+
}
|
|
1407
|
+
const pages = {};
|
|
1408
|
+
const initialVisibility = {};
|
|
1409
|
+
const initialSorts = {};
|
|
1410
|
+
cols.forEach((col) => {
|
|
1411
|
+
const total = col.totalEntries !== void 0 ? col.totalEntries : col.entries ? col.entries.length : 0;
|
|
1412
|
+
const initialPageSize = col.isSingleType ? 1 : 10;
|
|
1413
|
+
pages[col.uid] = {
|
|
1414
|
+
page: 1,
|
|
1415
|
+
pageSize: initialPageSize,
|
|
1416
|
+
total,
|
|
1417
|
+
pageCount: col.isSingleType ? 1 : Math.ceil(total / initialPageSize) || 1,
|
|
1418
|
+
entries: (col.entries || []).slice(0, initialPageSize),
|
|
1419
|
+
loading: false
|
|
1420
|
+
};
|
|
1421
|
+
initialVisibility[col.uid] = true;
|
|
1422
|
+
initialSorts[col.uid] = { field: "createdAt", order: "desc" };
|
|
1423
|
+
});
|
|
1424
|
+
setCollectionPages(pages);
|
|
1425
|
+
setVisibleTypeUids(initialVisibility);
|
|
1426
|
+
setCollectionSorts(initialSorts);
|
|
1427
|
+
if (data.relationsMeta) {
|
|
1428
|
+
setRelationsMeta(data.relationsMeta);
|
|
1429
|
+
}
|
|
1430
|
+
let storedAttr = null;
|
|
1431
|
+
try {
|
|
1432
|
+
storedAttr = localStorage.getItem(LAST_ATTR_KEY);
|
|
1433
|
+
} catch {
|
|
1434
|
+
}
|
|
1435
|
+
const resolvedAttr = storedAttr && attrs.includes(storedAttr) ? storedAttr : attrs.includes(selectedAttribute) ? selectedAttribute : attrs[0] || "metaDescription";
|
|
1436
|
+
setSelectedAttribute(resolvedAttr);
|
|
1437
|
+
if (resolvedAttr && data.relationsMeta && data.relationsMeta[resolvedAttr]) {
|
|
1438
|
+
fetchRelationOptions(data.relationsMeta[resolvedAttr].targetUid);
|
|
1439
|
+
}
|
|
1440
|
+
};
|
|
685
1441
|
const fetchModels = async () => {
|
|
686
1442
|
try {
|
|
687
|
-
|
|
1443
|
+
const cached = sessionStorage.getItem(MODELS_CACHE_KEY);
|
|
1444
|
+
if (cached) {
|
|
1445
|
+
const parsed = JSON.parse(cached);
|
|
1446
|
+
if (parsed && parsed.data) {
|
|
1447
|
+
applyModelsData(parsed.data);
|
|
1448
|
+
setLoading(false);
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
} catch {
|
|
1452
|
+
}
|
|
1453
|
+
try {
|
|
1454
|
+
if (collections.length === 0) {
|
|
1455
|
+
setLoading(true);
|
|
1456
|
+
}
|
|
688
1457
|
const res = await get("/bulk-updater/models");
|
|
689
1458
|
if (res.data && res.data.success) {
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
if (res.data.executionWindow) {
|
|
695
|
-
setExecutionWindow(res.data.executionWindow);
|
|
696
|
-
}
|
|
697
|
-
const pages = {};
|
|
698
|
-
const initialVisibility = {};
|
|
699
|
-
const initialSorts = {};
|
|
700
|
-
cols.forEach((col) => {
|
|
701
|
-
const total = col.totalEntries !== void 0 ? col.totalEntries : col.entries ? col.entries.length : 0;
|
|
702
|
-
const initialPageSize = col.isSingleType ? 1 : 10;
|
|
703
|
-
pages[col.uid] = {
|
|
704
|
-
page: 1,
|
|
705
|
-
pageSize: initialPageSize,
|
|
706
|
-
total,
|
|
707
|
-
pageCount: col.isSingleType ? 1 : Math.ceil(total / initialPageSize) || 1,
|
|
708
|
-
entries: (col.entries || []).slice(0, initialPageSize),
|
|
709
|
-
loading: false
|
|
710
|
-
};
|
|
711
|
-
initialVisibility[col.uid] = true;
|
|
712
|
-
initialSorts[col.uid] = { field: "createdAt", order: "desc" };
|
|
713
|
-
});
|
|
714
|
-
setCollectionPages(pages);
|
|
715
|
-
setVisibleTypeUids(initialVisibility);
|
|
716
|
-
setCollectionSorts(initialSorts);
|
|
717
|
-
if (res.data.relationsMeta) {
|
|
718
|
-
setRelationsMeta(res.data.relationsMeta);
|
|
719
|
-
}
|
|
720
|
-
const activeAttr = selectedAttribute || res.data.attributes && res.data.attributes[0];
|
|
721
|
-
if (!selectedAttribute && activeAttr) {
|
|
722
|
-
setSelectedAttribute(activeAttr);
|
|
723
|
-
}
|
|
724
|
-
if (activeAttr && res.data.relationsMeta && res.data.relationsMeta[activeAttr]) {
|
|
725
|
-
fetchRelationOptions(res.data.relationsMeta[activeAttr].targetUid);
|
|
1459
|
+
applyModelsData(res.data);
|
|
1460
|
+
try {
|
|
1461
|
+
sessionStorage.setItem(MODELS_CACHE_KEY, JSON.stringify({ timestamp: Date.now(), data: res.data }));
|
|
1462
|
+
} catch {
|
|
726
1463
|
}
|
|
727
1464
|
}
|
|
728
1465
|
} catch (err) {
|
|
@@ -866,7 +1603,12 @@ const BulkUpdater = () => {
|
|
|
866
1603
|
if (isFilterActiveNow) {
|
|
867
1604
|
setSelectedEntries({});
|
|
868
1605
|
}
|
|
869
|
-
collections.
|
|
1606
|
+
const targetCols = collections.filter((col) => {
|
|
1607
|
+
if (visibleTypeUids[col.uid] === false) return false;
|
|
1608
|
+
if (filterOperator !== "none" && !col.attributes.includes(selectedAttribute)) return false;
|
|
1609
|
+
return true;
|
|
1610
|
+
});
|
|
1611
|
+
targetCols.forEach((col) => {
|
|
870
1612
|
loadCollectionPage(
|
|
871
1613
|
col.uid,
|
|
872
1614
|
1,
|
|
@@ -887,6 +1629,10 @@ const BulkUpdater = () => {
|
|
|
887
1629
|
const handleAttributeChange = (newAttr) => {
|
|
888
1630
|
if (newAttr !== selectedAttribute) {
|
|
889
1631
|
setSelectedAttribute(newAttr);
|
|
1632
|
+
try {
|
|
1633
|
+
localStorage.setItem(LAST_ATTR_KEY, newAttr);
|
|
1634
|
+
} catch {
|
|
1635
|
+
}
|
|
890
1636
|
setSelectedEntries({});
|
|
891
1637
|
setFilterOperator("none");
|
|
892
1638
|
setFilterQuery("");
|
|
@@ -898,7 +1644,10 @@ const BulkUpdater = () => {
|
|
|
898
1644
|
} else {
|
|
899
1645
|
setRelationOptions([]);
|
|
900
1646
|
}
|
|
901
|
-
collections.
|
|
1647
|
+
const collectionsToLoad = collections.filter(
|
|
1648
|
+
(col) => visibleTypeUids[col.uid] !== false && col.attributes.includes(newAttr) && (!collectionPages[col.uid]?.entries || collectionPages[col.uid].entries.length === 0)
|
|
1649
|
+
);
|
|
1650
|
+
collectionsToLoad.forEach((col) => {
|
|
902
1651
|
loadCollectionPage(col.uid, 1);
|
|
903
1652
|
});
|
|
904
1653
|
}
|
|
@@ -906,6 +1655,49 @@ const BulkUpdater = () => {
|
|
|
906
1655
|
const filteredCollections = collections.filter(
|
|
907
1656
|
(c) => visibleTypeUids[c.uid] !== false && (!collectionSearch.trim() || c.displayName.toLowerCase().includes(collectionSearch.toLowerCase().trim()) || c.uid.toLowerCase().includes(collectionSearch.toLowerCase().trim()))
|
|
908
1657
|
);
|
|
1658
|
+
const supportedCollections = React.useMemo(() => {
|
|
1659
|
+
return collections.filter((c) => c.attributes.includes(selectedAttribute));
|
|
1660
|
+
}, [collections, selectedAttribute]);
|
|
1661
|
+
const unsupportedCollections = React.useMemo(() => {
|
|
1662
|
+
return collections.filter((c) => !c.attributes.includes(selectedAttribute));
|
|
1663
|
+
}, [collections, selectedAttribute]);
|
|
1664
|
+
const handleFocusOnlySupported = () => {
|
|
1665
|
+
const newVis = {};
|
|
1666
|
+
collections.forEach((c) => {
|
|
1667
|
+
newVis[c.uid] = c.attributes.includes(selectedAttribute);
|
|
1668
|
+
});
|
|
1669
|
+
setVisibleTypeUids(newVis);
|
|
1670
|
+
toggleNotification({
|
|
1671
|
+
type: "info",
|
|
1672
|
+
message: `Workspace focused: Showing ${supportedCollections.length} of ${collections.length} collections supporting "${selectedAttribute}".`
|
|
1673
|
+
});
|
|
1674
|
+
};
|
|
1675
|
+
const handleShowAllTypes = () => {
|
|
1676
|
+
const newVis = {};
|
|
1677
|
+
collections.forEach((c) => {
|
|
1678
|
+
newVis[c.uid] = true;
|
|
1679
|
+
});
|
|
1680
|
+
setVisibleTypeUids(newVis);
|
|
1681
|
+
};
|
|
1682
|
+
const handleHideAllTypes = () => {
|
|
1683
|
+
const newVis = {};
|
|
1684
|
+
collections.forEach((c) => {
|
|
1685
|
+
newVis[c.uid] = false;
|
|
1686
|
+
});
|
|
1687
|
+
setVisibleTypeUids(newVis);
|
|
1688
|
+
};
|
|
1689
|
+
const memoizedComboboxOptions = React.useMemo(() => {
|
|
1690
|
+
return attributes.map((attr) => {
|
|
1691
|
+
const rel = relationsMeta[attr];
|
|
1692
|
+
return /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: attr, textValue: attr, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", width: "100%", gap: 2, children: [
|
|
1693
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: attr === selectedAttribute ? "bold" : "regular", children: attr }),
|
|
1694
|
+
rel ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", size: "S", children: [
|
|
1695
|
+
"relation ➔ ",
|
|
1696
|
+
rel.targetDisplayName
|
|
1697
|
+
] }) : attr.includes(".") ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "neutral150", textColor: "neutral700", size: "S", children: "component" }) : null
|
|
1698
|
+
] }) }, attr);
|
|
1699
|
+
});
|
|
1700
|
+
}, [attributes, relationsMeta, selectedAttribute]);
|
|
909
1701
|
const handleGlobalPageSizeChange = (newSize) => {
|
|
910
1702
|
const capped = Math.min(500, Math.max(1, newSize));
|
|
911
1703
|
setDefaultPageSize(capped);
|
|
@@ -1090,7 +1882,27 @@ const BulkUpdater = () => {
|
|
|
1090
1882
|
try {
|
|
1091
1883
|
setUpdating(true);
|
|
1092
1884
|
setShowConfirmModal(false);
|
|
1885
|
+
setShowProcessingDashboard(true);
|
|
1886
|
+
setIsSubmittingBatch(true);
|
|
1093
1887
|
setLastResults(null);
|
|
1888
|
+
const tempBatchId = `batch_${Date.now()}`;
|
|
1889
|
+
setDashboardBatchId(tempBatchId);
|
|
1890
|
+
setDashboardJob({
|
|
1891
|
+
id: tempBatchId,
|
|
1892
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1893
|
+
status: "processing",
|
|
1894
|
+
executionMode,
|
|
1895
|
+
attribute: selectedAttribute,
|
|
1896
|
+
value: attributeValue,
|
|
1897
|
+
totalTargets: targets.length,
|
|
1898
|
+
processedCount: 0,
|
|
1899
|
+
updatedCount: 0,
|
|
1900
|
+
skippedCount: 0,
|
|
1901
|
+
failedCount: 0,
|
|
1902
|
+
isLive: false,
|
|
1903
|
+
publishOnApproval,
|
|
1904
|
+
results: []
|
|
1905
|
+
});
|
|
1094
1906
|
const payload = {
|
|
1095
1907
|
attribute: selectedAttribute,
|
|
1096
1908
|
value: attributeValue,
|
|
@@ -1103,14 +1915,36 @@ const BulkUpdater = () => {
|
|
|
1103
1915
|
publishOnApproval
|
|
1104
1916
|
};
|
|
1105
1917
|
const res = await post("/bulk-updater/batch-update", payload);
|
|
1918
|
+
setIsSubmittingBatch(false);
|
|
1106
1919
|
if (res.data && res.data.success) {
|
|
1920
|
+
setSelectedEntries({});
|
|
1921
|
+
const finalBatchId = res.data.batchId;
|
|
1922
|
+
setDashboardBatchId(finalBatchId);
|
|
1107
1923
|
if (res.data.pending) {
|
|
1924
|
+
setDashboardJob({
|
|
1925
|
+
id: finalBatchId,
|
|
1926
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1927
|
+
status: res.data.status || "pending_approval",
|
|
1928
|
+
executionMode,
|
|
1929
|
+
scheduledFor: res.data.scheduledFor,
|
|
1930
|
+
attribute: selectedAttribute,
|
|
1931
|
+
value: attributeValue,
|
|
1932
|
+
totalTargets: targets.length,
|
|
1933
|
+
processedCount: 0,
|
|
1934
|
+
updatedCount: 0,
|
|
1935
|
+
skippedCount: 0,
|
|
1936
|
+
failedCount: 0,
|
|
1937
|
+
isLive: false,
|
|
1938
|
+
publishOnApproval,
|
|
1939
|
+
makerNotes: makerNotesInput,
|
|
1940
|
+
policyMessage: res.data.message,
|
|
1941
|
+
results: []
|
|
1942
|
+
});
|
|
1108
1943
|
toggleNotification({
|
|
1109
1944
|
type: "success",
|
|
1110
1945
|
message: res.data.message || "Batch submitted successfully."
|
|
1111
1946
|
});
|
|
1112
1947
|
await fetchPendingBatches();
|
|
1113
|
-
setActiveTab("audit");
|
|
1114
1948
|
} else {
|
|
1115
1949
|
setLastResults(res.data.results || []);
|
|
1116
1950
|
setUpdateSummary({
|
|
@@ -1118,6 +1952,23 @@ const BulkUpdater = () => {
|
|
|
1118
1952
|
skippedCount: res.data.skippedCount || 0,
|
|
1119
1953
|
batchId: res.data.batchId
|
|
1120
1954
|
});
|
|
1955
|
+
setDashboardJob({
|
|
1956
|
+
id: finalBatchId,
|
|
1957
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1958
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1959
|
+
status: res.data.status || "successful",
|
|
1960
|
+
executionMode: "immediate",
|
|
1961
|
+
attribute: selectedAttribute,
|
|
1962
|
+
value: attributeValue,
|
|
1963
|
+
totalTargets: res.data.totalTargets || targets.length,
|
|
1964
|
+
processedCount: res.data.results?.length || targets.length,
|
|
1965
|
+
updatedCount: res.data.updatedCount || 0,
|
|
1966
|
+
skippedCount: res.data.skippedCount || 0,
|
|
1967
|
+
failedCount: res.data.failedCount || 0,
|
|
1968
|
+
isLive: res.data.isLive !== false && res.data.updatedCount > 0,
|
|
1969
|
+
publishOnApproval,
|
|
1970
|
+
results: res.data.results || []
|
|
1971
|
+
});
|
|
1121
1972
|
toggleNotification({
|
|
1122
1973
|
type: "success",
|
|
1123
1974
|
message: `Bulk update complete! Batch ID: ${res.data.batchId}. Updated: ${res.data.updatedCount}, Skipped: ${res.data.skippedCount}`
|
|
@@ -1126,15 +1977,28 @@ const BulkUpdater = () => {
|
|
|
1126
1977
|
await fetchAuditHistory();
|
|
1127
1978
|
}
|
|
1128
1979
|
} else {
|
|
1980
|
+
setIsSubmittingBatch(false);
|
|
1981
|
+
setDashboardJob((prev) => ({
|
|
1982
|
+
...prev,
|
|
1983
|
+
status: "failed",
|
|
1984
|
+
error: res.data?.error || "Batch update encountered an error"
|
|
1985
|
+
}));
|
|
1129
1986
|
toggleNotification({
|
|
1130
1987
|
type: "danger",
|
|
1131
1988
|
message: res.data?.error || "Batch update encountered an error"
|
|
1132
1989
|
});
|
|
1133
1990
|
}
|
|
1134
1991
|
} catch (err) {
|
|
1992
|
+
setIsSubmittingBatch(false);
|
|
1993
|
+
const errMsg = err.response?.data?.error || err.message || "Batch update failed";
|
|
1994
|
+
setDashboardJob((prev) => ({
|
|
1995
|
+
...prev,
|
|
1996
|
+
status: "failed",
|
|
1997
|
+
error: errMsg
|
|
1998
|
+
}));
|
|
1135
1999
|
toggleNotification({
|
|
1136
2000
|
type: "danger",
|
|
1137
|
-
message:
|
|
2001
|
+
message: errMsg
|
|
1138
2002
|
});
|
|
1139
2003
|
} finally {
|
|
1140
2004
|
setUpdating(false);
|
|
@@ -1146,6 +2010,7 @@ const BulkUpdater = () => {
|
|
|
1146
2010
|
setApprovingBatchId(batchId);
|
|
1147
2011
|
const res = await post(`/bulk-updater/batches/${batchId}/approve`, { executeImmediately: true });
|
|
1148
2012
|
if (res.data && res.data.success) {
|
|
2013
|
+
setSelectedEntries({});
|
|
1149
2014
|
toggleNotification({
|
|
1150
2015
|
type: "success",
|
|
1151
2016
|
message: res.data.message || `Batch ${batchId} approved and executed successfully!`
|
|
@@ -1199,6 +2064,7 @@ const BulkUpdater = () => {
|
|
|
1199
2064
|
setExecutingNowBatchId(batchId);
|
|
1200
2065
|
const res = await post(`/bulk-updater/batches/${batchId}/execute-now`);
|
|
1201
2066
|
if (res.data && res.data.success) {
|
|
2067
|
+
setSelectedEntries({});
|
|
1202
2068
|
toggleNotification({
|
|
1203
2069
|
type: "success",
|
|
1204
2070
|
message: `Batch ${batchId} executed immediately!`
|
|
@@ -1328,16 +2194,29 @@ const BulkUpdater = () => {
|
|
|
1328
2194
|
] }),
|
|
1329
2195
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", children: "Cross-collection batch attribute management with declarative audit trail and one-click rollback." })
|
|
1330
2196
|
] }),
|
|
1331
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
2197
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
|
|
2198
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2199
|
+
designSystem.Button,
|
|
2200
|
+
{
|
|
2201
|
+
startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Download, {}),
|
|
2202
|
+
variant: "secondary",
|
|
2203
|
+
onClick: () => {
|
|
2204
|
+
setShowProcessingDashboard(true);
|
|
2205
|
+
},
|
|
2206
|
+
children: "Processing Tracker & Reports"
|
|
2207
|
+
}
|
|
2208
|
+
),
|
|
2209
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2210
|
+
designSystem.Button,
|
|
2211
|
+
{
|
|
2212
|
+
startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowClockwise, {}),
|
|
2213
|
+
variant: "tertiary",
|
|
2214
|
+
onClick: activeTab === "editor" ? fetchModels : fetchAuditHistory,
|
|
2215
|
+
loading: loading || auditLoading,
|
|
2216
|
+
children: "Refresh"
|
|
2217
|
+
}
|
|
2218
|
+
)
|
|
2219
|
+
] })
|
|
1341
2220
|
] }) }),
|
|
1342
2221
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, marginBottom: 4, wrap: "wrap", justifyContent: "space-between", alignItems: "center", children: [
|
|
1343
2222
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
|
|
@@ -1441,7 +2320,22 @@ const BulkUpdater = () => {
|
|
|
1441
2320
|
] })
|
|
1442
2321
|
}
|
|
1443
2322
|
),
|
|
1444
|
-
activeTab === "editor" && /* @__PURE__ */ jsxRuntime.
|
|
2323
|
+
activeTab === "editor" && loading && collections.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2324
|
+
designSystem.Box,
|
|
2325
|
+
{
|
|
2326
|
+
padding: 8,
|
|
2327
|
+
background: "neutral0",
|
|
2328
|
+
shadow: "filterShadow",
|
|
2329
|
+
hasRadius: true,
|
|
2330
|
+
marginBottom: 6,
|
|
2331
|
+
style: { width: "100%", textAlign: "center" },
|
|
2332
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "center", gap: 3, children: [
|
|
2333
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: "Discovering schema attributes and collections..." }),
|
|
2334
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", fontWeight: "semiBold", textColor: "neutral800", children: "Discovering Schema Attributes & Collections..." }),
|
|
2335
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Scanning collection types, components, and relations across your Strapi schema." })
|
|
2336
|
+
] })
|
|
2337
|
+
}
|
|
2338
|
+
) : activeTab === "editor" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1445
2339
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1446
2340
|
designSystem.Box,
|
|
1447
2341
|
{
|
|
@@ -1462,23 +2356,15 @@ const BulkUpdater = () => {
|
|
|
1462
2356
|
autocomplete: { type: "list", filter: "contains" },
|
|
1463
2357
|
placeholder: "Search attribute by name or relation...",
|
|
1464
2358
|
value: selectedAttribute,
|
|
2359
|
+
textValue: selectedAttribute,
|
|
2360
|
+
loading: loading && attributes.length === 0,
|
|
2361
|
+
loadingMessage: "Discovering schema attributes...",
|
|
1465
2362
|
onChange: (val) => {
|
|
1466
2363
|
if (val && typeof val === "string") {
|
|
1467
2364
|
handleAttributeChange(val);
|
|
1468
2365
|
}
|
|
1469
2366
|
},
|
|
1470
|
-
children:
|
|
1471
|
-
const rel = relationsMeta[attr];
|
|
1472
|
-
const cleanParts = attr.includes(".") ? attr.split(".").join(" ") : "";
|
|
1473
|
-
const labelText = `${attr} ${cleanParts} ${rel ? `relation ${rel.targetDisplayName}` : ""}`.trim();
|
|
1474
|
-
return /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: attr, textValue: labelText, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", width: "100%", gap: 2, children: [
|
|
1475
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: attr === selectedAttribute ? "bold" : "regular", children: attr }),
|
|
1476
|
-
rel ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", size: "S", children: [
|
|
1477
|
-
"relation ➔ ",
|
|
1478
|
-
rel.targetDisplayName
|
|
1479
|
-
] }) : attr.includes(".") ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "neutral150", textColor: "neutral700", size: "S", children: "component" }) : null
|
|
1480
|
-
] }) }, attr);
|
|
1481
|
-
})
|
|
2367
|
+
children: memoizedComboboxOptions
|
|
1482
2368
|
}
|
|
1483
2369
|
)
|
|
1484
2370
|
] }) }),
|
|
@@ -1593,30 +2479,85 @@ const BulkUpdater = () => {
|
|
|
1593
2479
|
}
|
|
1594
2480
|
) })
|
|
1595
2481
|
] }) }),
|
|
1596
|
-
selectedAttribute && /* @__PURE__ */ jsxRuntime.
|
|
1597
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
2482
|
+
selectedAttribute && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginTop: 4, padding: 4, background: "neutral100", hasRadius: true, style: { border: "1px solid #eaeaef" }, children: [
|
|
2483
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: [
|
|
2484
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
|
|
2485
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: "Attribute Info:" }),
|
|
2486
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", children: [
|
|
2487
|
+
"Targeting attribute ",
|
|
2488
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: selectedAttribute }) })
|
|
2489
|
+
] }),
|
|
2490
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: [
|
|
2491
|
+
"✓ Supported in ",
|
|
2492
|
+
supportedCollections.length,
|
|
2493
|
+
" of ",
|
|
2494
|
+
collections.length,
|
|
2495
|
+
" Collections"
|
|
2496
|
+
] }),
|
|
2497
|
+
unsupportedCollections.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "neutral200", textColor: "neutral700", children: [
|
|
2498
|
+
"— Not in schema: ",
|
|
2499
|
+
unsupportedCollections.length
|
|
2500
|
+
] })
|
|
2501
|
+
] }),
|
|
2502
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
|
|
2503
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2504
|
+
designSystem.Button,
|
|
2505
|
+
{
|
|
2506
|
+
variant: "secondary",
|
|
2507
|
+
size: "S",
|
|
2508
|
+
onClick: handleFocusOnlySupported,
|
|
2509
|
+
children: [
|
|
2510
|
+
"Focus Workspace: Show Supported Only (",
|
|
2511
|
+
supportedCollections.length,
|
|
2512
|
+
")"
|
|
2513
|
+
]
|
|
2514
|
+
}
|
|
2515
|
+
),
|
|
2516
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2517
|
+
designSystem.Button,
|
|
2518
|
+
{
|
|
2519
|
+
variant: "tertiary",
|
|
2520
|
+
size: "S",
|
|
2521
|
+
onClick: () => setAttributeInfoExpanded((prev) => !prev),
|
|
2522
|
+
children: attributeInfoExpanded ? "Hide Schema Breakdown ▲" : "Inspect Breakdown ▼"
|
|
2523
|
+
}
|
|
2524
|
+
)
|
|
2525
|
+
] })
|
|
1602
2526
|
] }),
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
designSystem.
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
2527
|
+
attributeInfoExpanded && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginTop: 3, paddingTop: 3, style: { borderTop: "1px solid #dcdce4" }, children: [
|
|
2528
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 3, wrap: "wrap", gap: 2, children: [
|
|
2529
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Showing collection types schema compatibility. Type to filter:" }),
|
|
2530
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 220 }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2531
|
+
designSystem.TextInput,
|
|
2532
|
+
{
|
|
2533
|
+
placeholder: "Filter collection name...",
|
|
2534
|
+
size: "S",
|
|
2535
|
+
value: attributeInfoSearch,
|
|
2536
|
+
onChange: (e) => setAttributeInfoSearch(e.target.value)
|
|
2537
|
+
}
|
|
2538
|
+
) })
|
|
2539
|
+
] }),
|
|
2540
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { wrap: "wrap", gap: 2, alignItems: "center", style: { maxHeight: 180, overflowY: "auto" }, children: collections.filter(
|
|
2541
|
+
(c) => !attributeInfoSearch.trim() || c.displayName.toLowerCase().includes(attributeInfoSearch.toLowerCase().trim()) || c.uid.toLowerCase().includes(attributeInfoSearch.toLowerCase().trim())
|
|
2542
|
+
).map((c) => {
|
|
2543
|
+
const hasAttr = c.attributes.includes(selectedAttribute);
|
|
2544
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2545
|
+
designSystem.Badge,
|
|
2546
|
+
{
|
|
2547
|
+
backgroundColor: hasAttr ? "success100" : "neutral200",
|
|
2548
|
+
textColor: hasAttr ? "success700" : "neutral600",
|
|
2549
|
+
size: "S",
|
|
2550
|
+
children: [
|
|
2551
|
+
c.displayName,
|
|
2552
|
+
": ",
|
|
2553
|
+
hasAttr ? "✓ Supported" : "— Not in schema"
|
|
2554
|
+
]
|
|
2555
|
+
},
|
|
2556
|
+
c.uid
|
|
2557
|
+
);
|
|
2558
|
+
}) })
|
|
2559
|
+
] })
|
|
2560
|
+
] })
|
|
1620
2561
|
]
|
|
1621
2562
|
}
|
|
1622
2563
|
),
|
|
@@ -1730,30 +2671,54 @@ const BulkUpdater = () => {
|
|
|
1730
2671
|
background: "neutral100",
|
|
1731
2672
|
hasRadius: true,
|
|
1732
2673
|
marginBottom: 4,
|
|
1733
|
-
style: { width: "100%", maxWidth: "100%", boxSizing: "border-box" },
|
|
2674
|
+
style: { width: "100%", maxWidth: "100%", boxSizing: "border-box", border: "1px solid #eaeaef" },
|
|
1734
2675
|
children: [
|
|
1735
|
-
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap:
|
|
1736
|
-
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
|
|
2676
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, marginBottom: 3, children: [
|
|
2677
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
|
|
1737
2678
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", children: "Filter Visible Content Types:" }),
|
|
1738
2679
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
|
|
1739
2680
|
filteredCollections.length,
|
|
1740
2681
|
" of ",
|
|
1741
2682
|
collections.length,
|
|
1742
2683
|
" visible"
|
|
2684
|
+
] }),
|
|
2685
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: [
|
|
2686
|
+
supportedCollections.filter((c) => visibleTypeUids[c.uid] !== false).length,
|
|
2687
|
+
" of ",
|
|
2688
|
+
supportedCollections.length,
|
|
2689
|
+
" supported visible"
|
|
1743
2690
|
] })
|
|
1744
2691
|
] }),
|
|
1745
|
-
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
|
|
2692
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
|
|
2693
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 220 }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2694
|
+
designSystem.TextInput,
|
|
2695
|
+
{
|
|
2696
|
+
placeholder: "Search types...",
|
|
2697
|
+
size: "S",
|
|
2698
|
+
value: typeVisibilitySearch,
|
|
2699
|
+
onChange: (e) => setTypeVisibilitySearch(e.target.value)
|
|
2700
|
+
}
|
|
2701
|
+
) }),
|
|
2702
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2703
|
+
designSystem.Button,
|
|
2704
|
+
{
|
|
2705
|
+
variant: "secondary",
|
|
2706
|
+
size: "S",
|
|
2707
|
+
onClick: handleFocusOnlySupported,
|
|
2708
|
+
children: [
|
|
2709
|
+
"Show Supported Only (",
|
|
2710
|
+
supportedCollections.length,
|
|
2711
|
+
")"
|
|
2712
|
+
]
|
|
2713
|
+
}
|
|
2714
|
+
),
|
|
1746
2715
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1747
2716
|
designSystem.Button,
|
|
1748
2717
|
{
|
|
1749
2718
|
variant: "tertiary",
|
|
1750
2719
|
size: "S",
|
|
1751
|
-
onClick:
|
|
1752
|
-
|
|
1753
|
-
collections.forEach((c) => allVis[c.uid] = true);
|
|
1754
|
-
setVisibleTypeUids(allVis);
|
|
1755
|
-
},
|
|
1756
|
-
children: "Show All Types"
|
|
2720
|
+
onClick: handleShowAllTypes,
|
|
2721
|
+
children: "Show All"
|
|
1757
2722
|
}
|
|
1758
2723
|
),
|
|
1759
2724
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1761,44 +2726,63 @@ const BulkUpdater = () => {
|
|
|
1761
2726
|
{
|
|
1762
2727
|
variant: "tertiary",
|
|
1763
2728
|
size: "S",
|
|
1764
|
-
onClick:
|
|
1765
|
-
|
|
1766
|
-
collections.forEach((c) => allVis[c.uid] = false);
|
|
1767
|
-
setVisibleTypeUids(allVis);
|
|
1768
|
-
},
|
|
1769
|
-
children: "Hide All Types"
|
|
2729
|
+
onClick: handleHideAllTypes,
|
|
2730
|
+
children: "Hide All"
|
|
1770
2731
|
}
|
|
1771
2732
|
)
|
|
1772
2733
|
] })
|
|
1773
2734
|
] }),
|
|
1774
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
|
|
1788
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1789
|
-
designSystem.Checkbox,
|
|
1790
|
-
{
|
|
1791
|
-
checked: isVisible,
|
|
1792
|
-
onCheckedChange: (checked) => setVisibleTypeUids((prev) => ({ ...prev, [col.uid]: Boolean(checked) })),
|
|
1793
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: isVisible ? "semiBold" : "regular", children: col.displayName })
|
|
1794
|
-
}
|
|
1795
|
-
),
|
|
1796
|
-
col.isSingleType ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", size: "S", children: "Single Type" }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "neutral150", textColor: "neutral600", size: "S", children: "Collection" })
|
|
1797
|
-
] })
|
|
2735
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2736
|
+
designSystem.Box,
|
|
2737
|
+
{
|
|
2738
|
+
style: {
|
|
2739
|
+
maxHeight: 220,
|
|
2740
|
+
overflowY: "auto",
|
|
2741
|
+
padding: 8,
|
|
2742
|
+
background: "#ffffff",
|
|
2743
|
+
borderRadius: 4,
|
|
2744
|
+
border: "1px solid #dcdce4",
|
|
2745
|
+
display: "grid",
|
|
2746
|
+
gridTemplateColumns: "repeat(auto-fill, minmax(260px, 1fr))",
|
|
2747
|
+
gap: 8
|
|
1798
2748
|
},
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
2749
|
+
children: collections.filter(
|
|
2750
|
+
(col) => !typeVisibilitySearch.trim() || col.displayName.toLowerCase().includes(typeVisibilitySearch.toLowerCase().trim()) || col.uid.toLowerCase().includes(typeVisibilitySearch.toLowerCase().trim())
|
|
2751
|
+
).map((col) => {
|
|
2752
|
+
const isVisible = visibleTypeUids[col.uid] !== false;
|
|
2753
|
+
const hasAttr = col.attributes.includes(selectedAttribute);
|
|
2754
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2755
|
+
designSystem.Box,
|
|
2756
|
+
{
|
|
2757
|
+
padding: 2,
|
|
2758
|
+
paddingLeft: 3,
|
|
2759
|
+
paddingRight: 3,
|
|
2760
|
+
background: isVisible ? "#f0f0ff" : "#f6f6f9",
|
|
2761
|
+
hasRadius: true,
|
|
2762
|
+
style: {
|
|
2763
|
+
border: isVisible ? "1px solid #4945ff" : "1px solid #eaeaef",
|
|
2764
|
+
transition: "all 0.15s ease"
|
|
2765
|
+
},
|
|
2766
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", gap: 2, children: [
|
|
2767
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2768
|
+
designSystem.Checkbox,
|
|
2769
|
+
{
|
|
2770
|
+
checked: isVisible,
|
|
2771
|
+
onCheckedChange: (checked) => setVisibleTypeUids((prev) => ({ ...prev, [col.uid]: Boolean(checked) })),
|
|
2772
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: isVisible ? "bold" : "regular", children: col.displayName })
|
|
2773
|
+
}
|
|
2774
|
+
),
|
|
2775
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, alignItems: "center", children: [
|
|
2776
|
+
col.isSingleType ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", size: "S", children: "Single" }) : null,
|
|
2777
|
+
hasAttr ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", size: "S", children: "✓ Supports" }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "neutral150", textColor: "neutral600", size: "S", children: "—" })
|
|
2778
|
+
] })
|
|
2779
|
+
] })
|
|
2780
|
+
},
|
|
2781
|
+
col.uid
|
|
2782
|
+
);
|
|
2783
|
+
})
|
|
2784
|
+
}
|
|
2785
|
+
)
|
|
1802
2786
|
]
|
|
1803
2787
|
}
|
|
1804
2788
|
),
|
|
@@ -2241,7 +3225,7 @@ const BulkUpdater = () => {
|
|
|
2241
3225
|
]
|
|
2242
3226
|
}
|
|
2243
3227
|
)
|
|
2244
|
-
] }),
|
|
3228
|
+
] }) : null,
|
|
2245
3229
|
activeTab === "audit" && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2246
3230
|
designSystem.Box,
|
|
2247
3231
|
{
|
|
@@ -2465,6 +3449,19 @@ const BulkUpdater = () => {
|
|
|
2465
3449
|
] })
|
|
2466
3450
|
] }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: "ACTIVE (APPLIED)" }) }),
|
|
2467
3451
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { minWidth: 160, textAlign: "right", whiteSpace: "nowrap" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, justifyContent: "flex-end", children: [
|
|
3452
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3453
|
+
designSystem.Button,
|
|
3454
|
+
{
|
|
3455
|
+
variant: "secondary",
|
|
3456
|
+
size: "S",
|
|
3457
|
+
startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Download, {}),
|
|
3458
|
+
onClick: () => {
|
|
3459
|
+
setDashboardBatchId(rec.batchId);
|
|
3460
|
+
setShowProcessingDashboard(true);
|
|
3461
|
+
},
|
|
3462
|
+
children: "Report"
|
|
3463
|
+
}
|
|
3464
|
+
),
|
|
2468
3465
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2469
3466
|
designSystem.Button,
|
|
2470
3467
|
{
|
|
@@ -2774,6 +3771,16 @@ const BulkUpdater = () => {
|
|
|
2774
3771
|
}
|
|
2775
3772
|
)
|
|
2776
3773
|
}
|
|
3774
|
+
),
|
|
3775
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3776
|
+
ProcessingDashboardModal,
|
|
3777
|
+
{
|
|
3778
|
+
isOpen: showProcessingDashboard,
|
|
3779
|
+
onClose: () => setShowProcessingDashboard(false),
|
|
3780
|
+
batchId: dashboardBatchId,
|
|
3781
|
+
initialJob: dashboardJob,
|
|
3782
|
+
isSubmitting: isSubmittingBatch
|
|
3783
|
+
}
|
|
2777
3784
|
)
|
|
2778
3785
|
]
|
|
2779
3786
|
}
|
|
@@ -2783,4 +3790,4 @@ exports.default = BulkUpdater;
|
|
|
2783
3790
|
exports.formatDisplayValue = formatDisplayValue;
|
|
2784
3791
|
exports.getAvailableColumnsForCollection = getAvailableColumnsForCollection;
|
|
2785
3792
|
exports.getNestedValue = getNestedValue;
|
|
2786
|
-
//# sourceMappingURL=BulkUpdater-
|
|
3793
|
+
//# sourceMappingURL=BulkUpdater-2TTEWMXI.js.map
|