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