strapi-plugin-bulk-updater 1.4.3 → 1.4.6

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.
@@ -1,8 +1,8 @@
1
- import { jsx, jsxs, Fragment } from "react/jsx-runtime";
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
2
  import React__default, { useState, useEffect, useMemo, useRef } from "react";
3
3
  import { useFetchClient, useNotification } from "@strapi/strapi/admin";
4
- import { Box, Flex, Typography, Button, SingleSelect, SingleSelectOption, Badge, Checkbox, Alert, Table, Thead, Tr, Th, Tbody, Td, Loader, TextInput, ComboboxOption, Field, Combobox } from "@strapi/design-system";
5
- import { ArrowClockwise, Download, Upload, Check, Cog, Eye, Play } from "@strapi/icons";
4
+ import { Box, Flex, Typography, Button, SingleSelect, SingleSelectOption, Badge, Checkbox, Alert, Table, Thead, Tr, Th, Tbody, Td, TextInput, Loader, ComboboxOption, Field, Combobox } from "@strapi/design-system";
5
+ import { ArrowClockwise, Download, Upload, ChevronLeft, ChevronRight, Eye, Check, Cog, Play } from "@strapi/icons";
6
6
  import { i as isAbortError, V as ValidationRulesTab } from "./ValidationRulesTab-BtOCWK8E.mjs";
7
7
  function parseCsvRows(csvText) {
8
8
  if (!csvText) return [];
@@ -142,7 +142,7 @@ function CsvManagerTab() {
142
142
  const [collections, setCollections] = useState([]);
143
143
  const [loadingCollections, setLoadingCollections] = useState(true);
144
144
  const [selectedUid, setSelectedUid] = useState("");
145
- const [exportLimit, setExportLimit] = useState(1e3);
145
+ const [exportLimit, setExportLimit] = useState(0);
146
146
  const [exportOnlyViolations, setExportOnlyViolations] = useState(false);
147
147
  const [rules, setRules] = useState([]);
148
148
  const [selectedRuleId, setSelectedRuleId] = useState("");
@@ -150,9 +150,18 @@ function CsvManagerTab() {
150
150
  const [publishOnImport, setPublishOnImport] = useState(true);
151
151
  const [csvRawText, setCsvRawText] = useState("");
152
152
  const [fileName, setFileName] = useState("");
153
- const [parsedPreview, setParsedPreview] = useState([]);
153
+ const [allParsedRecords, setAllParsedRecords] = useState([]);
154
+ const [previewPage, setPreviewPage] = useState(1);
155
+ const [previewPageSize, setPreviewPageSize] = useState(10);
154
156
  const [importing, setImporting] = useState(false);
157
+ const [importProgress, setImportProgress] = useState(null);
155
158
  const [importResult, setImportResult] = useState(null);
159
+ const [executionWindow, setExecutionWindow] = useState(null);
160
+ const [preflightLoading, setPreflightLoading] = useState(false);
161
+ const [preflightReport, setPreflightReport] = useState(null);
162
+ const [showConfirmModal, setShowConfirmModal] = useState(false);
163
+ const [makerNotesInput, setMakerNotesInput] = useState("");
164
+ const [submittingBatch, setSubmittingBatch] = useState(false);
156
165
  useEffect(() => {
157
166
  fetchInitialData();
158
167
  }, []);
@@ -163,6 +172,9 @@ function CsvManagerTab() {
163
172
  get("/bulk-updater/models"),
164
173
  get("/bulk-updater/validation-rules")
165
174
  ]);
175
+ if (modelsRes.data?.executionWindow) {
176
+ setExecutionWindow(modelsRes.data.executionWindow);
177
+ }
166
178
  if (modelsRes.data?.collections) {
167
179
  setCollections(modelsRes.data.collections);
168
180
  if (modelsRes.data.collections.length > 0) {
@@ -213,7 +225,7 @@ function CsvManagerTab() {
213
225
  downloadCsvFile(csvStr, filename);
214
226
  toggleNotification({
215
227
  type: "success",
216
- message: "Collection exported to CSV successfully"
228
+ message: `Collection exported successfully (${res.data?.count ?? 0} records)`
217
229
  });
218
230
  } catch (err) {
219
231
  if (isAbortError(err)) return;
@@ -266,34 +278,93 @@ function CsvManagerTab() {
266
278
  if (!file) return;
267
279
  setFileName(file.name);
268
280
  setImportResult(null);
281
+ setImportProgress(null);
269
282
  const reader = new FileReader();
270
283
  reader.onload = (event) => {
271
284
  const text = event.target?.result;
272
285
  setCsvRawText(text);
273
286
  try {
274
287
  const records = parseCsv(text);
275
- setParsedPreview(records.slice(0, 10));
288
+ setAllParsedRecords(records);
289
+ setPreviewPage(1);
276
290
  } catch (err) {
277
- setParsedPreview([]);
291
+ setAllParsedRecords([]);
278
292
  }
279
293
  };
280
294
  reader.readAsText(file);
281
295
  };
282
296
  const handleExecuteImport = async () => {
283
- if (!selectedUid || !csvRawText) return;
297
+ if (!selectedUid || allParsedRecords.length === 0) return;
298
+ const totalRows = allParsedRecords.length;
299
+ const CHUNK_SIZE = 100;
300
+ const totalChunks = Math.ceil(totalRows / CHUNK_SIZE);
301
+ const batchId = `csv_batch_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;
284
302
  try {
285
303
  setImporting(true);
286
304
  setImportResult(null);
287
- const res = await post("/bulk-updater/csv/import", {
288
- uid: selectedUid,
289
- csvText: csvRawText,
290
- mode: importMode,
291
- publish: publishOnImport
305
+ let liveCreated = 0;
306
+ let liveUpdated = 0;
307
+ let liveSkipped = 0;
308
+ let liveErrors = 0;
309
+ let finalResultData = null;
310
+ setImportProgress({
311
+ currentChunk: 0,
312
+ totalChunks,
313
+ percent: 0,
314
+ processedRows: 0,
315
+ totalRows,
316
+ liveCreated: 0,
317
+ liveUpdated: 0,
318
+ liveSkipped: 0,
319
+ liveErrors: 0
292
320
  });
293
- if (res.data && res.data.success) {
294
- setImportResult(res.data);
295
- const stats = res.data.stats;
296
- let notifMsg = `CSV Import complete: ${stats.created} created, ${stats.updated} updated.`;
321
+ for (let chunkIdx = 0; chunkIdx < totalChunks; chunkIdx++) {
322
+ const startIdx = chunkIdx * CHUNK_SIZE;
323
+ const endIdx = Math.min(startIdx + CHUNK_SIZE, totalRows);
324
+ const chunkRows = allParsedRecords.slice(startIdx, endIdx);
325
+ const isFirst = chunkIdx === 0;
326
+ const isLast = chunkIdx === totalChunks - 1;
327
+ const startRowNumber = startIdx + 2;
328
+ const res = await post("/bulk-updater/csv/import-chunk", {
329
+ uid: selectedUid,
330
+ rows: chunkRows,
331
+ batchId,
332
+ isFirstChunk: isFirst,
333
+ isLastChunk: isLast,
334
+ startRowNumber,
335
+ mode: importMode,
336
+ publish: publishOnImport
337
+ });
338
+ if (!res.data || !res.data.success) {
339
+ throw new Error(res.data?.error || `Chunk ${chunkIdx + 1} failed to process`);
340
+ }
341
+ const chunkStats = res.data.chunkStats;
342
+ if (chunkStats) {
343
+ liveCreated += chunkStats.created || 0;
344
+ liveUpdated += chunkStats.updated || 0;
345
+ liveSkipped += chunkStats.skipped || 0;
346
+ liveErrors += chunkStats.errors?.length || 0;
347
+ }
348
+ const currentProgressPercent = Math.round((chunkIdx + 1) / totalChunks * 100);
349
+ setImportProgress({
350
+ currentChunk: chunkIdx + 1,
351
+ totalChunks,
352
+ percent: currentProgressPercent,
353
+ processedRows: endIdx,
354
+ totalRows,
355
+ liveCreated,
356
+ liveUpdated,
357
+ liveSkipped,
358
+ liveErrors
359
+ });
360
+ if (isLast) {
361
+ finalResultData = res.data;
362
+ }
363
+ }
364
+ if (finalResultData && finalResultData.stats) {
365
+ setImportResult(finalResultData);
366
+ const stats = finalResultData.stats;
367
+ let notifMsg = `CSV Import complete: ${stats.created} created, ${stats.updated} updated across ${stats.total} records.`;
297
368
  if (stats.newWithoutDocId && stats.newWithoutDocId > 0) {
298
369
  notifMsg += ` (${stats.newWithoutDocId} new entries created due to omitted documentId)`;
299
370
  }
@@ -301,348 +372,864 @@ function CsvManagerTab() {
301
372
  type: "success",
302
373
  message: notifMsg
303
374
  });
304
- } else {
305
- throw new Error(res.data?.error || "Import failed");
306
375
  }
307
376
  } catch (err) {
308
377
  if (isAbortError(err)) return;
309
378
  toggleNotification({
310
379
  type: "danger",
311
- message: err.message || "Error processing CSV import"
380
+ message: err.message || "Error processing CSV import batch"
312
381
  });
313
382
  } finally {
314
383
  setImporting(false);
315
384
  }
316
385
  };
386
+ const handleRunPreflight = async () => {
387
+ if (!selectedUid || allParsedRecords.length === 0) return;
388
+ try {
389
+ setPreflightLoading(true);
390
+ const res = await post("/bulk-updater/csv/validate-preflight", {
391
+ uid: selectedUid,
392
+ rows: allParsedRecords,
393
+ mode: importMode
394
+ });
395
+ if (res.data && res.data.success) {
396
+ setPreflightReport(res.data);
397
+ if (res.data.hasErrors) {
398
+ toggleNotification({
399
+ type: "warning",
400
+ message: `Pre-flight validation: ${res.data.totalErrorCount} error(s) and ${res.data.totalWarningCount} warning(s) detected.`
401
+ });
402
+ } else if (res.data.hasWarnings) {
403
+ toggleNotification({
404
+ type: "warning",
405
+ message: `Pre-flight validation passed with ${res.data.totalWarningCount} warning(s).`
406
+ });
407
+ } else {
408
+ toggleNotification({
409
+ type: "success",
410
+ message: `Pre-flight validation passed! All ${res.data.validRows} rows are ready for import.`
411
+ });
412
+ }
413
+ }
414
+ } catch (err) {
415
+ if (isAbortError(err)) return;
416
+ toggleNotification({
417
+ type: "danger",
418
+ message: err.message || "Failed running pre-flight validation"
419
+ });
420
+ } finally {
421
+ setPreflightLoading(false);
422
+ }
423
+ };
424
+ const submitBatchWithMode = async (executionMode, forceImmediate = false) => {
425
+ if (!selectedUid || allParsedRecords.length === 0) return;
426
+ if (executionMode === "immediate") {
427
+ setShowConfirmModal(false);
428
+ await handleExecuteImport();
429
+ return;
430
+ }
431
+ try {
432
+ setSubmittingBatch(true);
433
+ const res = await post("/bulk-updater/csv/submit-batch", {
434
+ uid: selectedUid,
435
+ rows: allParsedRecords,
436
+ executionMode,
437
+ makerNotes: makerNotesInput,
438
+ forceImmediate,
439
+ mode: importMode,
440
+ publish: publishOnImport
441
+ });
442
+ setShowConfirmModal(false);
443
+ if (res.data && res.data.pending) {
444
+ setMakerNotesInput("");
445
+ toggleNotification({
446
+ type: "success",
447
+ message: res.data.message || "CSV batch submitted for Checker authorization."
448
+ });
449
+ } else if (res.data && res.data.executeNow) {
450
+ await handleExecuteImport();
451
+ }
452
+ } catch (err) {
453
+ if (isAbortError(err)) return;
454
+ toggleNotification({
455
+ type: "danger",
456
+ message: err.message || "Failed submitting CSV batch"
457
+ });
458
+ } finally {
459
+ setSubmittingBatch(false);
460
+ }
461
+ };
317
462
  const selectedCol = collections.find((c) => c.uid === selectedUid);
318
- return /* @__PURE__ */ jsx(Box, { padding: 6, background: "neutral0", hasRadius: true, shadow: "filterShadow", children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 6, children: [
319
- /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
320
- /* @__PURE__ */ jsxs(Box, { children: [
321
- /* @__PURE__ */ jsx(Typography, { variant: "beta", tag: "h2", fontWeight: "bold", children: "CSV Export & Import Center" }),
322
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Export all records, filtered subsets, or rule violations into CSV. Correct or add records in spreadsheets and re-upload with full schema & quota enforcement." })
463
+ return /* @__PURE__ */ jsxs(Box, { padding: 6, background: "neutral0", hasRadius: true, shadow: "filterShadow", children: [
464
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 6, children: [
465
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
466
+ /* @__PURE__ */ jsxs(Box, { children: [
467
+ /* @__PURE__ */ jsx(Typography, { variant: "beta", tag: "h2", fontWeight: "bold", children: "CSV Export & Import Center" }),
468
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Export all records (including 10,000+ datasets), filtered subsets, or rule violations into CSV. Correct or add records in spreadsheets and re-upload with high-performance chunked batch processing." })
469
+ ] }),
470
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", startIcon: /* @__PURE__ */ jsx(ArrowClockwise, {}), onClick: fetchInitialData, children: "Refresh" })
323
471
  ] }),
324
- /* @__PURE__ */ jsx(Button, { variant: "secondary", startIcon: /* @__PURE__ */ jsx(ArrowClockwise, {}), onClick: fetchInitialData, children: "Refresh" })
325
- ] }),
326
- /* @__PURE__ */ jsx(Box, { padding: 4, background: "neutral100", hasRadius: true, children: /* @__PURE__ */ jsxs(Flex, { gap: 4, alignItems: "flex-end", children: [
327
- /* @__PURE__ */ jsxs(Box, { style: { flex: 1 }, children: [
328
- /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", marginBottom: 2, children: "Target Collection Type" }),
472
+ /* @__PURE__ */ jsx(Box, { padding: 4, background: "neutral100", hasRadius: true, children: /* @__PURE__ */ jsxs(Flex, { gap: 4, alignItems: "flex-end", children: [
473
+ /* @__PURE__ */ jsxs(Box, { style: { flex: 1 }, children: [
474
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", marginBottom: 2, children: "Target Collection Type" }),
475
+ /* @__PURE__ */ jsx(
476
+ SingleSelect,
477
+ {
478
+ value: selectedUid,
479
+ onChange: (val) => {
480
+ setSelectedUid(val);
481
+ setImportResult(null);
482
+ setImportProgress(null);
483
+ },
484
+ disabled: loadingCollections,
485
+ placeholder: "Select a collection...",
486
+ children: collections.map((col) => /* @__PURE__ */ jsxs(SingleSelectOption, { value: col.uid, children: [
487
+ col.displayName,
488
+ " (",
489
+ col.totalEntries ?? 0,
490
+ " records)"
491
+ ] }, col.uid))
492
+ }
493
+ )
494
+ ] }),
329
495
  /* @__PURE__ */ jsx(
330
- SingleSelect,
331
- {
332
- value: selectedUid,
333
- onChange: (val) => {
334
- setSelectedUid(val);
335
- setImportResult(null);
336
- },
337
- disabled: loadingCollections,
338
- placeholder: "Select a collection...",
339
- children: collections.map((col) => /* @__PURE__ */ jsxs(SingleSelectOption, { value: col.uid, children: [
340
- col.displayName,
341
- " (",
342
- col.totalEntries ?? 0,
343
- " records)"
344
- ] }, col.uid))
345
- }
346
- )
347
- ] }),
348
- /* @__PURE__ */ jsx(
349
- Button,
350
- {
351
- variant: "tertiary",
352
- startIcon: /* @__PURE__ */ jsx(Download, {}),
353
- onClick: handleDownloadTemplate,
354
- disabled: !selectedUid,
355
- children: "Download Sample CSV Template"
356
- }
357
- )
358
- ] }) }),
359
- /* @__PURE__ */ jsxs(Box, { padding: 5, background: "neutral100", hasRadius: true, children: [
360
- /* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h3", fontWeight: "bold", marginBottom: 2, children: "1. Export Data to CSV" }),
361
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", marginBottom: 4, children: "Extract entire collections or pinpoint records violating validation quotas." }),
362
- /* @__PURE__ */ jsxs(Flex, { gap: 4, wrap: "wrap", alignItems: "center", marginTop: 3, children: [
363
- /* @__PURE__ */ jsxs(
364
496
  Button,
365
497
  {
366
- variant: "default",
498
+ variant: "tertiary",
367
499
  startIcon: /* @__PURE__ */ jsx(Download, {}),
368
- onClick: handleExportCollection,
500
+ onClick: handleDownloadTemplate,
369
501
  disabled: !selectedUid,
370
- children: [
371
- "Export All ",
372
- selectedCol?.displayName || "Collection",
373
- " Records (.csv)"
374
- ]
502
+ children: "Download Sample CSV Template"
375
503
  }
376
- ),
377
- rules.length > 0 && /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", style: { marginLeft: "auto" }, children: [
378
- /* @__PURE__ */ jsx(
504
+ )
505
+ ] }) }),
506
+ /* @__PURE__ */ jsxs(Box, { padding: 5, background: "neutral100", hasRadius: true, children: [
507
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h3", fontWeight: "bold", marginBottom: 2, children: "1. Export Data to CSV" }),
508
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", marginBottom: 4, children: "Extract entire collections without artificial limits (streams 10,000+ records in paged chunks) or pinpoint records violating validation quotas." }),
509
+ /* @__PURE__ */ jsxs(Flex, { gap: 4, wrap: "wrap", alignItems: "center", marginTop: 3, children: [
510
+ /* @__PURE__ */ jsx(Box, { style: { width: 220 }, children: /* @__PURE__ */ jsxs(
379
511
  SingleSelect,
380
512
  {
381
- value: selectedRuleId,
382
- onChange: (val) => setSelectedRuleId(val),
383
- placeholder: "Select Quota Rule...",
384
- children: rules.map((r) => /* @__PURE__ */ jsxs(SingleSelectOption, { value: r.id, children: [
385
- "Rule: ",
386
- r.name
387
- ] }, r.id))
513
+ value: String(exportLimit),
514
+ onChange: (val) => setExportLimit(Number(val)),
515
+ placeholder: "Export Limit",
516
+ children: [
517
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "0", children: "All Records (No Limit)" }),
518
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "10000", children: "Up to 10,000 Records" }),
519
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "5000", children: "Up to 5,000 Records" }),
520
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "1000", children: "Up to 1,000 Records" })
521
+ ]
388
522
  }
389
- ),
390
- /* @__PURE__ */ jsx(
523
+ ) }),
524
+ /* @__PURE__ */ jsxs(
391
525
  Button,
392
526
  {
393
- variant: "danger-light",
527
+ variant: "default",
394
528
  startIcon: /* @__PURE__ */ jsx(Download, {}),
395
- onClick: handleExportViolations,
396
- disabled: !selectedRuleId,
397
- children: "Export Violators CSV"
529
+ onClick: handleExportCollection,
530
+ disabled: !selectedUid,
531
+ children: [
532
+ "Export ",
533
+ exportLimit === 0 ? "All" : `${exportLimit}`,
534
+ " ",
535
+ selectedCol?.displayName || "Collection",
536
+ " Records (.csv)"
537
+ ]
398
538
  }
399
- )
400
- ] })
401
- ] })
402
- ] }),
403
- /* @__PURE__ */ jsxs(Box, { padding: 5, background: "neutral100", hasRadius: true, children: [
404
- /* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h3", fontWeight: "bold", marginBottom: 2, children: "2. Import & Batch Override CSV" }),
405
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", marginBottom: 4, children: "Upload an updated CSV file to bulk update existing records or insert new records." }),
406
- /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, wrap: "wrap", gap: 4, children: [
407
- /* @__PURE__ */ jsxs(Box, { children: [
408
- /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", marginBottom: 1, display: "block", children: "Operation Mode" }),
409
- /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
410
- /* @__PURE__ */ jsx(Badge, { backgroundColor: "primary100", textColor: "primary700", children: "⚡ Smart Auto-Detect" }),
411
- /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", children: [
412
- "Automatically updates matching records by ",
413
- /* @__PURE__ */ jsx("code", { children: "documentId" }),
414
- " and creates new entries when ID is omitted."
415
- ] })
539
+ ),
540
+ rules.length > 0 && /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", style: { marginLeft: "auto" }, children: [
541
+ /* @__PURE__ */ jsx(
542
+ SingleSelect,
543
+ {
544
+ value: selectedRuleId,
545
+ onChange: (val) => setSelectedRuleId(val),
546
+ placeholder: "Select Quota Rule...",
547
+ children: rules.map((r) => /* @__PURE__ */ jsxs(SingleSelectOption, { value: r.id, children: [
548
+ "Rule: ",
549
+ r.name
550
+ ] }, r.id))
551
+ }
552
+ ),
553
+ /* @__PURE__ */ jsx(
554
+ Button,
555
+ {
556
+ variant: "danger-light",
557
+ startIcon: /* @__PURE__ */ jsx(Download, {}),
558
+ onClick: handleExportViolations,
559
+ disabled: !selectedRuleId,
560
+ children: "Export Violators CSV"
561
+ }
562
+ )
416
563
  ] })
417
- ] }),
418
- /* @__PURE__ */ jsxs(Box, { children: [
419
- /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", marginBottom: 1, display: "block", children: "Publish Options" }),
420
- /* @__PURE__ */ jsx(
421
- Checkbox,
422
- {
423
- checked: publishOnImport,
424
- onValueChange: (val) => setPublishOnImport(val),
425
- children: "Auto-publish on import"
426
- }
427
- )
428
564
  ] })
429
565
  ] }),
430
- /* @__PURE__ */ jsxs(
431
- Box,
432
- {
433
- padding: 6,
434
- background: "neutral0",
435
- hasRadius: true,
436
- style: {
437
- border: "2px dashed #dcdce4",
438
- textAlign: "center",
439
- cursor: "pointer"
440
- },
441
- children: [
566
+ /* @__PURE__ */ jsxs(Box, { padding: 5, background: "neutral100", hasRadius: true, children: [
567
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h3", fontWeight: "bold", marginBottom: 2, children: "2. Import & Batch Override CSV" }),
568
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", marginBottom: 4, children: "Upload an updated CSV file (supports 10,000+ records via chunked streaming) to bulk update existing records or insert new records." }),
569
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, wrap: "wrap", gap: 4, children: [
570
+ /* @__PURE__ */ jsxs(Box, { children: [
571
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", marginBottom: 1, display: "block", children: "Operation Mode" }),
572
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
573
+ /* @__PURE__ */ jsx(Badge, { backgroundColor: "primary100", textColor: "primary700", children: "⚡ Smart Auto-Detect" }),
574
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", children: [
575
+ "Automatically updates matching records by ",
576
+ /* @__PURE__ */ jsx("code", { children: "documentId" }),
577
+ " and creates new entries when ID is omitted."
578
+ ] })
579
+ ] })
580
+ ] }),
581
+ /* @__PURE__ */ jsxs(Box, { children: [
582
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", marginBottom: 1, display: "block", children: "Publish Options" }),
442
583
  /* @__PURE__ */ jsx(
443
- "input",
584
+ Checkbox,
444
585
  {
445
- type: "file",
446
- accept: ".csv",
447
- id: "csv-file-input",
448
- style: { display: "none" },
449
- onChange: handleFileUpload
586
+ checked: publishOnImport,
587
+ onValueChange: (val) => setPublishOnImport(val),
588
+ children: "Auto-publish on import"
450
589
  }
451
- ),
452
- /* @__PURE__ */ jsxs("label", { htmlFor: "csv-file-input", style: { cursor: "pointer", display: "block" }, children: [
453
- /* @__PURE__ */ jsx(Upload, { style: { width: 32, height: 32, margin: "0 auto 8px", color: "#4945ff" } }),
454
- /* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", display: "block", children: fileName ? `Selected: ${fileName}` : "Click here or drag a .csv file to upload" }),
455
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", children: "UTF-8 encoded CSV matching collection schema headers" })
456
- ] })
457
- ]
458
- }
459
- ),
460
- parsedPreview.length > 0 && (() => {
461
- const missingDocIdCount = parsedPreview.filter(
462
- (row) => !extractDocId(row)
463
- ).length;
464
- const headers = Object.keys(parsedPreview[0]);
465
- return /* @__PURE__ */ jsxs(Box, { marginTop: 4, background: "neutral0", padding: 4, hasRadius: true, children: [
466
- /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 3, children: [
467
- /* @__PURE__ */ jsxs(Box, { children: [
468
- /* @__PURE__ */ jsxs(Typography, { variant: "sigma", textColor: "neutral700", children: [
469
- "FILE PREVIEW (FIRST ",
470
- parsedPreview.length,
471
- " ROWS)"
590
+ )
591
+ ] })
592
+ ] }),
593
+ /* @__PURE__ */ jsxs(
594
+ Box,
595
+ {
596
+ padding: 6,
597
+ background: "neutral0",
598
+ hasRadius: true,
599
+ style: {
600
+ border: "2px dashed #dcdce4",
601
+ textAlign: "center",
602
+ cursor: "pointer"
603
+ },
604
+ children: [
605
+ /* @__PURE__ */ jsx(
606
+ "input",
607
+ {
608
+ type: "file",
609
+ accept: ".csv",
610
+ id: "csv-file-input",
611
+ style: { display: "none" },
612
+ onChange: handleFileUpload
613
+ }
614
+ ),
615
+ /* @__PURE__ */ jsxs("label", { htmlFor: "csv-file-input", style: { cursor: "pointer", display: "block" }, children: [
616
+ /* @__PURE__ */ jsx(Upload, { style: { width: 32, height: 32, margin: "0 auto 8px", color: "#4945ff" } }),
617
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", display: "block", children: fileName ? `Selected: ${fileName}` : "Click here or drag a .csv file to upload" }),
618
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", children: "UTF-8 encoded CSV matching collection schema headers" })
619
+ ] })
620
+ ]
621
+ }
622
+ ),
623
+ allParsedRecords.length > 0 && (() => {
624
+ const totalRows = allParsedRecords.length;
625
+ const missingDocIdCount = allParsedRecords.filter((row) => !extractDocId(row)).length;
626
+ const updateCount = totalRows - missingDocIdCount;
627
+ const headers = Object.keys(allParsedRecords[0]);
628
+ const totalPages = Math.ceil(totalRows / previewPageSize) || 1;
629
+ const safePage = Math.min(Math.max(1, previewPage), totalPages);
630
+ const startIndex = (safePage - 1) * previewPageSize;
631
+ const endIndex = Math.min(startIndex + previewPageSize, totalRows);
632
+ const displayedRows = allParsedRecords.slice(startIndex, endIndex);
633
+ return /* @__PURE__ */ jsxs(Box, { marginTop: 4, background: "neutral0", padding: 4, hasRadius: true, children: [
634
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 3, wrap: "wrap", gap: 2, children: [
635
+ /* @__PURE__ */ jsxs(Box, { children: [
636
+ /* @__PURE__ */ jsxs(Typography, { variant: "sigma", textColor: "neutral700", children: [
637
+ "FILE PREVIEW — ",
638
+ totalRows.toLocaleString(),
639
+ " RECORDS DETECTED IN CSV"
640
+ ] }),
641
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral500", display: "block", children: [
642
+ totalRows.toLocaleString(),
643
+ " total row",
644
+ totalRows > 1 ? "s" : "",
645
+ " •",
646
+ " ",
647
+ updateCount > 0 ? `${updateCount.toLocaleString()} updates` : "",
648
+ updateCount > 0 && missingDocIdCount > 0 ? ", " : "",
649
+ missingDocIdCount > 0 ? `${missingDocIdCount.toLocaleString()} new entries (missing documentId)` : ""
650
+ ] })
472
651
  ] }),
473
- /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral500", display: "block", children: [
474
- parsedPreview.length,
475
- " previewed row",
476
- parsedPreview.length > 1 ? "s" : "",
477
- " •",
478
- " ",
479
- missingDocIdCount > 0 ? `${missingDocIdCount} row${missingDocIdCount > 1 ? "s" : ""} missing documentId (will be created as new entries)` : "All previewed rows contain document IDs"
652
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
653
+ /* @__PURE__ */ jsxs(Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
654
+ "🔄 ",
655
+ updateCount.toLocaleString(),
656
+ " Updates"
657
+ ] }),
658
+ missingDocIdCount > 0 && /* @__PURE__ */ jsxs(Badge, { backgroundColor: "warning100", textColor: "warning700", children: [
659
+ "➕ ",
660
+ missingDocIdCount.toLocaleString(),
661
+ " New (No ID)"
662
+ ] })
480
663
  ] })
481
664
  ] }),
482
- /* @__PURE__ */ jsx(Badge, { backgroundColor: missingDocIdCount > 0 ? "warning100" : "success100", textColor: missingDocIdCount > 0 ? "warning700" : "success700", children: missingDocIdCount > 0 ? `${missingDocIdCount} New (No ID)` : "Ready to Import" })
483
- ] }),
484
- missingDocIdCount > 0 && /* @__PURE__ */ jsx(Box, { marginBottom: 3, children: /* @__PURE__ */ jsx(
485
- Alert,
486
- {
487
- variant: "default",
488
- title: "Smart Auto-Detect Active",
489
- children: /* @__PURE__ */ jsxs(Typography, { variant: "omega", children: [
490
- missingDocIdCount,
491
- " row",
492
- missingDocIdCount > 1 ? "s have" : " has",
493
- " no ",
494
- /* @__PURE__ */ jsx("code", { children: "documentId" }),
495
- ".",
496
- " ",
497
- "Because no documentId is provided, the engine will automatically treat them as new entries and generate new database IDs for them."
498
- ] })
499
- }
500
- ) }),
501
- /* @__PURE__ */ jsx(Box, { style: { overflowX: "auto", maxWidth: "100%", border: "1px solid #e0e0e0", borderRadius: 4 }, children: /* @__PURE__ */ jsxs(Table, { colCount: headers.length + 1, rowCount: parsedPreview.length + 1, children: [
502
- /* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [
503
- /* @__PURE__ */ jsx(Th, { style: { width: 150, minWidth: 150 }, children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Detected Action" }) }),
504
- headers.map((h) => /* @__PURE__ */ jsx(Th, { style: { minWidth: 160 }, children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: h }) }, h))
665
+ preflightReport && /* @__PURE__ */ jsx(
666
+ Box,
667
+ {
668
+ marginBottom: 3,
669
+ padding: 4,
670
+ background: preflightReport.hasErrors ? "danger100" : preflightReport.hasWarnings ? "warning100" : "success100",
671
+ hasRadius: true,
672
+ style: { border: `1px solid ${preflightReport.hasErrors ? "#d9534f" : preflightReport.hasWarnings ? "#f0ad4e" : "#5cb85c"}` },
673
+ children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
674
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 2, children: [
675
+ /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", textColor: preflightReport.hasErrors ? "danger700" : preflightReport.hasWarnings ? "warning700" : "success700", children: preflightReport.hasErrors ? `⚠️ Pre-Flight Check: ${preflightReport.totalErrorCount} error(s) detected` : preflightReport.hasWarnings ? `⚡ Pre-Flight Check: ${preflightReport.validRows} valid rows with ${preflightReport.totalWarningCount} warning(s)` : `✅ Pre-Flight Check Passed: All ${preflightReport.validRows} rows are ready for import` }),
676
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
677
+ /* @__PURE__ */ jsxs(Badge, { backgroundColor: "success100", textColor: "success700", children: [
678
+ "✓ ",
679
+ preflightReport.validRows,
680
+ " Valid Rows"
681
+ ] }),
682
+ /* @__PURE__ */ jsxs(Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
683
+ "🔄 ",
684
+ preflightReport.estimatedUpdates,
685
+ " Updates"
686
+ ] }),
687
+ /* @__PURE__ */ jsxs(Badge, { backgroundColor: "warning100", textColor: "warning700", children: [
688
+ "➕ ",
689
+ preflightReport.estimatedCreates,
690
+ " New"
691
+ ] })
692
+ ] })
693
+ ] }),
694
+ preflightReport.hasErrors && /* @__PURE__ */ jsxs(Box, { marginTop: 2, background: "neutral0", padding: 3, hasRadius: true, style: { maxHeight: 160, overflowY: "auto" }, children: [
695
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", fontWeight: "bold", textColor: "danger700", display: "block", marginBottom: 1, children: [
696
+ "Validation Errors (",
697
+ preflightReport.totalErrorCount,
698
+ "):"
699
+ ] }),
700
+ preflightReport.errors.map((e, idx) => /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "danger700", display: "block", children: [
701
+ "• Row ",
702
+ e.row,
703
+ ": ",
704
+ e.error
705
+ ] }, idx))
706
+ ] }),
707
+ preflightReport.hasWarnings && /* @__PURE__ */ jsxs(Box, { marginTop: 2, background: "neutral0", padding: 3, hasRadius: true, style: { maxHeight: 140, overflowY: "auto" }, children: [
708
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", fontWeight: "bold", textColor: "warning700", display: "block", marginBottom: 1, children: [
709
+ "Warnings (",
710
+ preflightReport.totalWarningCount,
711
+ "):"
712
+ ] }),
713
+ preflightReport.warnings.map((w, idx) => /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "warning700", display: "block", children: [
714
+ "• Row ",
715
+ w.row,
716
+ ": ",
717
+ w.warning
718
+ ] }, idx))
719
+ ] })
720
+ ] })
721
+ }
722
+ ),
723
+ missingDocIdCount > 0 && /* @__PURE__ */ jsx(Box, { marginBottom: 3, children: /* @__PURE__ */ jsx(Alert, { variant: "default", title: "Smart Auto-Detect Active", children: /* @__PURE__ */ jsxs(Typography, { variant: "omega", children: [
724
+ missingDocIdCount.toLocaleString(),
725
+ " row",
726
+ missingDocIdCount > 1 ? "s have" : " has",
727
+ " no ",
728
+ /* @__PURE__ */ jsx("code", { children: "documentId" }),
729
+ ".",
730
+ " ",
731
+ "Because no documentId is provided, the engine will automatically treat them as new entries and generate new database IDs for them."
732
+ ] }) }) }),
733
+ /* @__PURE__ */ jsx(Box, { style: { overflowX: "auto", maxWidth: "100%", border: "1px solid #e0e0e0", borderRadius: 4 }, children: /* @__PURE__ */ jsxs(Table, { colCount: headers.length + 1, rowCount: displayedRows.length + 1, children: [
734
+ /* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [
735
+ /* @__PURE__ */ jsx(Th, { style: { width: 150, minWidth: 150 }, children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Detected Action" }) }),
736
+ headers.map((h) => /* @__PURE__ */ jsx(Th, { style: { minWidth: 160 }, children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: h }) }, h))
737
+ ] }) }),
738
+ /* @__PURE__ */ jsx(Tbody, { children: displayedRows.map((row, rIdx) => {
739
+ const hasDocId = !!extractDocId(row);
740
+ const actualRowIndex = startIndex + rIdx + 1;
741
+ return /* @__PURE__ */ jsxs(Tr, { children: [
742
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 1, children: [
743
+ hasDocId ? /* @__PURE__ */ jsx(Badge, { backgroundColor: "primary100", textColor: "primary700", children: "🔄 Update" }) : /* @__PURE__ */ jsx(Badge, { backgroundColor: "warning100", textColor: "warning700", children: "➕ New Entry" }),
744
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral500", children: [
745
+ "Row #",
746
+ actualRowIndex
747
+ ] })
748
+ ] }) }),
749
+ headers.map((h) => /* @__PURE__ */ jsx(Td, { style: { verticalAlign: "top", maxWidth: 300 }, children: (h === "documentId" || h === "id") && !row[h]?.trim() ? /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "warning700", fontWeight: "bold", children: "(Auto-generate ID)" }) : /* @__PURE__ */ jsx(
750
+ Typography,
751
+ {
752
+ variant: "omega",
753
+ style: {
754
+ display: "-webkit-box",
755
+ WebkitLineClamp: 3,
756
+ WebkitBoxOrient: "vertical",
757
+ overflow: "hidden",
758
+ textOverflow: "ellipsis",
759
+ whiteSpace: "pre-wrap",
760
+ wordBreak: "break-word",
761
+ maxHeight: 65
762
+ },
763
+ title: row[h] || "",
764
+ children: row[h] || "-"
765
+ }
766
+ ) }, h))
767
+ ] }, rIdx);
768
+ }) })
505
769
  ] }) }),
506
- /* @__PURE__ */ jsx(Tbody, { children: parsedPreview.map((row, rIdx) => {
507
- const hasDocId = !!extractDocId(row);
508
- return /* @__PURE__ */ jsxs(Tr, { children: [
509
- /* @__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" }) }),
510
- headers.map((h) => /* @__PURE__ */ jsx(Td, { style: { verticalAlign: "top", maxWidth: 300 }, children: (h === "documentId" || h === "id") && !row[h]?.trim() ? /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "warning700", fontWeight: "bold", children: "(Auto-generate ID)" }) : /* @__PURE__ */ jsx(
511
- Typography,
770
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginTop: 3, wrap: "wrap", gap: 3, children: [
771
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
772
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Rows per page:" }),
773
+ /* @__PURE__ */ jsx(Box, { style: { width: 80 }, children: /* @__PURE__ */ jsxs(
774
+ SingleSelect,
512
775
  {
513
- variant: "omega",
514
- style: {
515
- display: "-webkit-box",
516
- WebkitLineClamp: 3,
517
- WebkitBoxOrient: "vertical",
518
- overflow: "hidden",
519
- textOverflow: "ellipsis",
520
- whiteSpace: "pre-wrap",
521
- wordBreak: "break-word",
522
- maxHeight: 65
776
+ value: String(previewPageSize),
777
+ onChange: (val) => {
778
+ setPreviewPageSize(Number(val));
779
+ setPreviewPage(1);
523
780
  },
524
- title: row[h] || "",
525
- children: row[h] || "-"
781
+ children: [
782
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "10", children: "10" }),
783
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "25", children: "25" }),
784
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "50", children: "50" }),
785
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "100", children: "100" })
786
+ ]
526
787
  }
527
- ) }, h))
528
- ] }, rIdx);
529
- }) })
530
- ] }) }),
531
- /* @__PURE__ */ jsx(Flex, { justifyContent: "flex-end", marginTop: 4, children: /* @__PURE__ */ jsx(
532
- Button,
788
+ ) }),
789
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", children: [
790
+ "Showing ",
791
+ startIndex + 1,
792
+ "–",
793
+ endIndex,
794
+ " of ",
795
+ totalRows.toLocaleString(),
796
+ " rows"
797
+ ] })
798
+ ] }),
799
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
800
+ /* @__PURE__ */ jsx(
801
+ Button,
802
+ {
803
+ variant: "tertiary",
804
+ size: "S",
805
+ startIcon: /* @__PURE__ */ jsx(ChevronLeft, {}),
806
+ disabled: safePage <= 1 || importing,
807
+ onClick: () => setPreviewPage((p) => Math.max(1, p - 1)),
808
+ children: "Prev"
809
+ }
810
+ ),
811
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral700", children: [
812
+ "Page ",
813
+ safePage,
814
+ " of ",
815
+ totalPages
816
+ ] }),
817
+ /* @__PURE__ */ jsx(
818
+ Button,
819
+ {
820
+ variant: "tertiary",
821
+ size: "S",
822
+ endIcon: /* @__PURE__ */ jsx(ChevronRight, {}),
823
+ disabled: safePage >= totalPages || importing,
824
+ onClick: () => setPreviewPage((p) => Math.min(totalPages, p + 1)),
825
+ children: "Next"
826
+ }
827
+ )
828
+ ] })
829
+ ] }),
830
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginTop: 4, wrap: "wrap", gap: 3, children: [
831
+ /* @__PURE__ */ jsx(
832
+ Button,
833
+ {
834
+ variant: "secondary",
835
+ size: "L",
836
+ startIcon: /* @__PURE__ */ jsx(Eye, {}),
837
+ onClick: handleRunPreflight,
838
+ loading: preflightLoading,
839
+ disabled: importing,
840
+ children: "Run Pre-Flight Dry Run"
841
+ }
842
+ ),
843
+ /* @__PURE__ */ jsxs(
844
+ Button,
845
+ {
846
+ variant: "default",
847
+ size: "L",
848
+ startIcon: /* @__PURE__ */ jsx(Check, {}),
849
+ onClick: () => setShowConfirmModal(true),
850
+ loading: importing || submittingBatch,
851
+ children: [
852
+ "Review & Import (",
853
+ totalRows.toLocaleString(),
854
+ " Records)..."
855
+ ]
856
+ }
857
+ )
858
+ ] })
859
+ ] });
860
+ })(),
861
+ importProgress && /* @__PURE__ */ jsxs(Box, { marginTop: 4, padding: 4, background: "neutral0", hasRadius: true, style: { border: "1px solid #4945ff" }, children: [
862
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 2, children: [
863
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", textColor: "primary700", children: importing ? `Processing Batch Chunk ${importProgress.currentChunk} of ${importProgress.totalChunks} (${importProgress.percent}%)` : "Batch Import Complete" }),
864
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral700", children: [
865
+ importProgress.processedRows.toLocaleString(),
866
+ " / ",
867
+ importProgress.totalRows.toLocaleString(),
868
+ " rows processed"
869
+ ] })
870
+ ] }),
871
+ /* @__PURE__ */ jsx(Box, { style: { width: "100%", height: 10, background: "#e0e0e0", borderRadius: 5, overflow: "hidden", marginBottom: 12 }, children: /* @__PURE__ */ jsx(
872
+ Box,
533
873
  {
534
- variant: "default",
535
- size: "L",
536
- startIcon: /* @__PURE__ */ jsx(Check, {}),
537
- onClick: handleExecuteImport,
538
- loading: importing,
539
- children: "Execute Smart Import (Auto-Detect)"
874
+ style: {
875
+ width: `${importProgress.percent}%`,
876
+ height: "100%",
877
+ background: importProgress.percent === 100 ? "#2f6846" : "#4945ff",
878
+ transition: "width 0.2s ease-in-out"
879
+ }
540
880
  }
541
- ) })
542
- ] });
543
- })(),
544
- importResult && /* @__PURE__ */ jsx(Box, { marginTop: 4, children: /* @__PURE__ */ jsxs(
545
- Alert,
546
- {
547
- variant: importResult.stats.errors.length > 0 ? "warning" : "success",
548
- title: "CSV Import Results",
549
- children: [
550
- /* @__PURE__ */ jsxs(Typography, { variant: "omega", display: "block", marginBottom: 1, children: [
551
- "Successfully processed: ",
552
- /* @__PURE__ */ jsxs("strong", { children: [
553
- importResult.stats.created,
554
- " created"
555
- ] }),
556
- importResult.stats.newWithoutDocId > 0 && /* @__PURE__ */ jsxs("span", { children: [
557
- " (",
558
- importResult.stats.newWithoutDocId,
559
- " created as new entries because documentId was omitted)"
560
- ] }),
561
- ", ",
562
- /* @__PURE__ */ jsxs("strong", { children: [
563
- importResult.stats.updated,
564
- " updated"
565
- ] }),
566
- ",",
567
- " ",
568
- /* @__PURE__ */ jsxs("strong", { children: [
569
- importResult.stats.skipped,
570
- " skipped"
571
- ] }),
572
- " out of ",
573
- importResult.stats.total,
574
- " total rows."
881
+ ) }),
882
+ /* @__PURE__ */ jsxs(Flex, { gap: 3, wrap: "wrap", alignItems: "center", children: [
883
+ /* @__PURE__ */ jsxs(Badge, { backgroundColor: "success100", textColor: "success700", children: [
884
+ "Created: ",
885
+ importProgress.liveCreated.toLocaleString()
575
886
  ] }),
576
- importResult.stats.newWithoutDocId > 0 && /* @__PURE__ */ jsx(Box, { marginTop: 2, padding: 2, background: "neutral100", hasRadius: true, children: /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral700", children: [
577
- "ℹ️ ",
578
- /* @__PURE__ */ jsx("strong", { children: "Note on new entries:" }),
579
- " ",
580
- importResult.stats.newWithoutDocId,
581
- " row",
582
- importResult.stats.newWithoutDocId > 1 ? "s" : "",
583
- " had no ",
584
- /* @__PURE__ */ jsx("code", { children: "documentId" }),
585
- " 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."
586
- ] }) }),
587
- importResult.stats.errors.length > 0 && /* @__PURE__ */ jsxs(Box, { marginTop: 2, children: [
588
- /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "danger700", fontWeight: "bold", children: [
589
- "Errors encountered (",
590
- importResult.stats.errors.length,
591
- "):"
592
- ] }),
593
- /* @__PURE__ */ jsx("ul", { style: { margin: "4px 0 0 16px", padding: 0 }, children: importResult.stats.errors.slice(0, 10).map((err, idx) => /* @__PURE__ */ jsx("li", { style: { marginBottom: "2px" }, children: /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "danger600", children: [
594
- "Row ",
595
- err.row,
596
- ": ",
597
- err.error
598
- ] }) }, idx)) }),
599
- importResult.stats.errors.length > 10 && /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", style: { display: "block", marginTop: 4, fontStyle: "italic" }, children: [
600
- "...and ",
601
- importResult.stats.errors.length - 10,
602
- " more error",
603
- importResult.stats.errors.length - 10 > 1 ? "s" : ""
604
- ] })
887
+ /* @__PURE__ */ jsxs(Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
888
+ "Updated: ",
889
+ importProgress.liveUpdated.toLocaleString()
890
+ ] }),
891
+ /* @__PURE__ */ jsxs(Badge, { backgroundColor: "neutral150", textColor: "neutral700", children: [
892
+ "Skipped: ",
893
+ importProgress.liveSkipped.toLocaleString()
605
894
  ] }),
606
- importResult.stats.diffs && importResult.stats.diffs.length > 0 && /* @__PURE__ */ jsxs(Box, { marginTop: 3, padding: 3, background: "neutral0", hasRadius: true, style: { border: "1px solid #dcdce4" }, children: [
607
- /* @__PURE__ */ jsxs(Typography, { variant: "sigma", textColor: "neutral800", fontWeight: "bold", display: "block", marginBottom: 2, children: [
608
- "CHANGES APPLIED (",
609
- importResult.stats.diffs.length,
610
- "):"
895
+ importProgress.liveErrors > 0 && /* @__PURE__ */ jsxs(Badge, { backgroundColor: "danger100", textColor: "danger700", children: [
896
+ "Errors: ",
897
+ importProgress.liveErrors.toLocaleString()
898
+ ] })
899
+ ] })
900
+ ] }),
901
+ importResult && /* @__PURE__ */ jsx(Box, { marginTop: 4, children: /* @__PURE__ */ jsxs(
902
+ Alert,
903
+ {
904
+ variant: importResult.stats.errors.length > 0 ? "warning" : "success",
905
+ title: "CSV Import Results",
906
+ children: [
907
+ /* @__PURE__ */ jsxs(Typography, { variant: "omega", display: "block", marginBottom: 1, children: [
908
+ "Successfully processed: ",
909
+ /* @__PURE__ */ jsxs("strong", { children: [
910
+ importResult.stats.created.toLocaleString(),
911
+ " created"
912
+ ] }),
913
+ importResult.stats.newWithoutDocId > 0 && /* @__PURE__ */ jsxs("span", { children: [
914
+ " (",
915
+ importResult.stats.newWithoutDocId.toLocaleString(),
916
+ " created as new entries because documentId was omitted)"
917
+ ] }),
918
+ ", ",
919
+ /* @__PURE__ */ jsxs("strong", { children: [
920
+ importResult.stats.updated.toLocaleString(),
921
+ " updated"
922
+ ] }),
923
+ ",",
924
+ " ",
925
+ /* @__PURE__ */ jsxs("strong", { children: [
926
+ importResult.stats.skipped.toLocaleString(),
927
+ " skipped"
928
+ ] }),
929
+ " out of ",
930
+ importResult.stats.total.toLocaleString(),
931
+ " total rows."
611
932
  ] }),
612
- /* @__PURE__ */ jsx(Box, { style: { overflowX: "auto" }, children: /* @__PURE__ */ jsxs(Table, { colCount: 4, rowCount: importResult.stats.diffs.length + 1, children: [
613
- /* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [
614
- /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Entry / Document" }) }),
615
- /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Attribute" }) }),
616
- /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Previous Value" }) }),
617
- /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Applied Value" }) })
618
- ] }) }),
619
- /* @__PURE__ */ jsx(Tbody, { children: importResult.stats.diffs.slice(0, 10).map((d, idx) => {
620
- const oldDisplay = d.oldValue !== null && d.oldValue !== void 0 ? typeof d.oldValue === "object" ? JSON.stringify(d.oldValue) : String(d.oldValue) : "(empty)";
621
- const newDisplay = d.newValue !== null && d.newValue !== void 0 ? typeof d.newValue === "object" ? JSON.stringify(d.newValue) : String(d.newValue) : "(empty)";
622
- return /* @__PURE__ */ jsxs(Tr, { children: [
623
- /* @__PURE__ */ jsxs(Td, { children: [
624
- /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", children: d.label || d.documentId }),
625
- d.documentId && /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", display: "block", children: d.documentId })
626
- ] }),
627
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: d.attribute || "field" }) }),
628
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "danger600", children: oldDisplay }) }),
629
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "success600", fontWeight: "semiBold", children: newDisplay }) })
630
- ] }, idx);
631
- }) })
933
+ importResult.stats.newWithoutDocId > 0 && /* @__PURE__ */ jsx(Box, { marginTop: 2, padding: 2, background: "neutral100", hasRadius: true, children: /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral700", children: [
934
+ "ℹ️ ",
935
+ /* @__PURE__ */ jsx("strong", { children: "Note on new entries:" }),
936
+ " ",
937
+ importResult.stats.newWithoutDocId,
938
+ " row",
939
+ importResult.stats.newWithoutDocId > 1 ? "s" : "",
940
+ " had no ",
941
+ /* @__PURE__ */ jsx("code", { children: "documentId" }),
942
+ " 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."
632
943
  ] }) }),
633
- importResult.stats.diffs.length > 10 && /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", style: { display: "block", marginTop: 6, fontStyle: "italic" }, children: [
634
- "...and ",
635
- importResult.stats.diffs.length - 10,
636
- " more change",
637
- importResult.stats.diffs.length - 10 > 1 ? "s" : "",
638
- " recorded in Audit Trail & Version History."
944
+ importResult.stats.errors.length > 0 && /* @__PURE__ */ jsxs(Box, { marginTop: 2, children: [
945
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "danger700", fontWeight: "bold", children: [
946
+ "Errors encountered (",
947
+ importResult.stats.errors.length,
948
+ "):"
949
+ ] }),
950
+ /* @__PURE__ */ jsx("ul", { style: { margin: "4px 0 0 16px", padding: 0 }, children: importResult.stats.errors.slice(0, 10).map((err, idx) => /* @__PURE__ */ jsx("li", { style: { marginBottom: "2px" }, children: /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "danger600", children: [
951
+ "Row ",
952
+ err.row,
953
+ ": ",
954
+ err.error
955
+ ] }) }, idx)) }),
956
+ importResult.stats.errors.length > 10 && /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", style: { display: "block", marginTop: 4, fontStyle: "italic" }, children: [
957
+ "...and ",
958
+ importResult.stats.errors.length - 10,
959
+ " more error",
960
+ importResult.stats.errors.length - 10 > 1 ? "s" : ""
961
+ ] })
962
+ ] }),
963
+ importResult.stats.diffs && importResult.stats.diffs.length > 0 && /* @__PURE__ */ jsxs(Box, { marginTop: 3, padding: 3, background: "neutral0", hasRadius: true, style: { border: "1px solid #dcdce4" }, children: [
964
+ /* @__PURE__ */ jsxs(Typography, { variant: "sigma", textColor: "neutral800", fontWeight: "bold", display: "block", marginBottom: 2, children: [
965
+ "CHANGES APPLIED (",
966
+ importResult.stats.diffs.length.toLocaleString(),
967
+ "):"
968
+ ] }),
969
+ /* @__PURE__ */ jsx(Box, { style: { overflowX: "auto" }, children: /* @__PURE__ */ jsxs(Table, { colCount: 4, rowCount: importResult.stats.diffs.length + 1, children: [
970
+ /* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [
971
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Entry / Document" }) }),
972
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Attribute" }) }),
973
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Previous Value" }) }),
974
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Applied Value" }) })
975
+ ] }) }),
976
+ /* @__PURE__ */ jsx(Tbody, { children: importResult.stats.diffs.slice(0, 10).map((d, idx) => {
977
+ const oldDisplay = d.oldValue !== null && d.oldValue !== void 0 ? typeof d.oldValue === "object" ? JSON.stringify(d.oldValue) : String(d.oldValue) : "(empty)";
978
+ const newDisplay = d.newValue !== null && d.newValue !== void 0 ? typeof d.newValue === "object" ? JSON.stringify(d.newValue) : String(d.newValue) : "(empty)";
979
+ return /* @__PURE__ */ jsxs(Tr, { children: [
980
+ /* @__PURE__ */ jsxs(Td, { children: [
981
+ /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", children: d.label || d.documentId }),
982
+ d.documentId && /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", display: "block", children: d.documentId })
983
+ ] }),
984
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: d.attribute || "field" }) }),
985
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "danger600", children: oldDisplay }) }),
986
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "success600", fontWeight: "semiBold", children: newDisplay }) })
987
+ ] }, idx);
988
+ }) })
989
+ ] }) }),
990
+ importResult.stats.diffs.length > 10 && /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", style: { display: "block", marginTop: 6, fontStyle: "italic" }, children: [
991
+ "...and ",
992
+ importResult.stats.diffs.length - 10,
993
+ " more change",
994
+ importResult.stats.diffs.length - 10 > 1 ? "s" : "",
995
+ " recorded in Audit Trail & Version History."
996
+ ] })
639
997
  ] })
640
- ] })
641
- ]
642
- }
643
- ) })
644
- ] })
645
- ] }) });
998
+ ]
999
+ }
1000
+ ) })
1001
+ ] })
1002
+ ] }),
1003
+ showConfirmModal && /* @__PURE__ */ jsx(
1004
+ "div",
1005
+ {
1006
+ style: {
1007
+ position: "fixed",
1008
+ inset: 0,
1009
+ backgroundColor: "rgba(20, 20, 30, 0.65)",
1010
+ backdropFilter: "blur(2px)",
1011
+ display: "flex",
1012
+ alignItems: "center",
1013
+ justifyContent: "center",
1014
+ zIndex: 9999,
1015
+ padding: 16,
1016
+ boxSizing: "border-box",
1017
+ overflowY: "auto"
1018
+ },
1019
+ onClick: () => setShowConfirmModal(false),
1020
+ children: /* @__PURE__ */ jsxs(
1021
+ Box,
1022
+ {
1023
+ background: "neutral0",
1024
+ hasRadius: true,
1025
+ shadow: "popupShadow",
1026
+ style: {
1027
+ maxWidth: 680,
1028
+ width: "100%",
1029
+ maxHeight: "calc(100vh - 48px)",
1030
+ display: "flex",
1031
+ flexDirection: "column",
1032
+ overflow: "hidden",
1033
+ position: "relative",
1034
+ boxShadow: "0 24px 48px rgba(0, 0, 0, 0.32), 0 0 1px rgba(0, 0, 0, 0.2)"
1035
+ },
1036
+ onClick: (e) => e.stopPropagation(),
1037
+ children: [
1038
+ /* @__PURE__ */ jsx(
1039
+ Box,
1040
+ {
1041
+ padding: 5,
1042
+ background: "neutral100",
1043
+ style: {
1044
+ borderBottom: "1px solid rgba(120, 120, 150, 0.2)",
1045
+ flexShrink: 0
1046
+ },
1047
+ children: /* @__PURE__ */ jsx(Flex, { justifyContent: "space-between", alignItems: "center", children: /* @__PURE__ */ jsxs(Box, { children: [
1048
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", children: "Confirm CSV Bulk Import" }),
1049
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", display: "block", children: "Review import target, record actions, publishing mode, and execution window policies before proceeding." })
1050
+ ] }) })
1051
+ }
1052
+ ),
1053
+ /* @__PURE__ */ jsxs(
1054
+ Box,
1055
+ {
1056
+ padding: 5,
1057
+ style: {
1058
+ flex: "1 1 auto",
1059
+ minHeight: 0,
1060
+ overflowY: "auto",
1061
+ WebkitOverflowScrolling: "touch"
1062
+ },
1063
+ children: [
1064
+ /* @__PURE__ */ jsxs(Box, { padding: 4, background: "neutral100", hasRadius: true, marginBottom: 4, children: [
1065
+ /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", marginBottom: 2, display: "block", children: "IMPORT DETAILS" }),
1066
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
1067
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
1068
+ /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", children: "Target Collection:" }),
1069
+ /* @__PURE__ */ jsx(Badge, { backgroundColor: "primary100", textColor: "primary700", children: selectedCol?.displayName || selectedUid })
1070
+ ] }),
1071
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
1072
+ /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", children: "Total Detected Records:" }),
1073
+ /* @__PURE__ */ jsxs(Typography, { fontWeight: "bold", textColor: "primary600", children: [
1074
+ allParsedRecords.length.toLocaleString(),
1075
+ " records"
1076
+ ] })
1077
+ ] }),
1078
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
1079
+ /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", children: "Action Breakdown:" }),
1080
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
1081
+ /* @__PURE__ */ jsxs(Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
1082
+ "🔄 ",
1083
+ allParsedRecords.filter((r) => !!extractDocId(r)).length.toLocaleString(),
1084
+ " Updates"
1085
+ ] }),
1086
+ /* @__PURE__ */ jsxs(Badge, { backgroundColor: "warning100", textColor: "warning700", children: [
1087
+ "➕ ",
1088
+ allParsedRecords.filter((r) => !extractDocId(r)).length.toLocaleString(),
1089
+ " New (No ID)"
1090
+ ] })
1091
+ ] })
1092
+ ] }),
1093
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
1094
+ /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", children: "Import Mode:" }),
1095
+ /* @__PURE__ */ jsx(Badge, { backgroundColor: "neutral200", textColor: "neutral800", children: importMode === "update" ? "Update Only (Exact Document IDs)" : importMode === "create_or_override" ? "Create or Override" : "Smart Auto-Detect" })
1096
+ ] }),
1097
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
1098
+ /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", children: "Publishing Mode:" }),
1099
+ /* @__PURE__ */ jsx(
1100
+ Badge,
1101
+ {
1102
+ backgroundColor: publishOnImport ? "success100" : "warning100",
1103
+ textColor: publishOnImport ? "success700" : "warning700",
1104
+ children: publishOnImport ? "Publish Immediately" : "Keep as Draft"
1105
+ }
1106
+ )
1107
+ ] }),
1108
+ preflightReport && /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginTop: 2, paddingTop: 2, style: { borderTop: "1px solid #eaeaef" }, children: [
1109
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Pre-Flight Status:" }),
1110
+ /* @__PURE__ */ jsx(
1111
+ Typography,
1112
+ {
1113
+ variant: "pi",
1114
+ fontWeight: "bold",
1115
+ textColor: preflightReport.hasErrors ? "danger700" : preflightReport.hasWarnings ? "warning700" : "success700",
1116
+ children: preflightReport.hasErrors ? `⚠️ ${preflightReport.totalErrorCount} errors found` : preflightReport.hasWarnings ? `⚡ Passed with ${preflightReport.totalWarningCount} warnings` : "✅ 100% Passed"
1117
+ }
1118
+ )
1119
+ ] })
1120
+ ] })
1121
+ ] }),
1122
+ /* @__PURE__ */ jsx(Box, { padding: 4, hasRadius: true, marginBottom: 4, background: executionWindow?.isBusinessHours ? "warning100" : "success100", children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 1, children: [
1123
+ /* @__PURE__ */ jsx(Flex, { gap: 2, alignItems: "center", children: /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", textColor: executionWindow?.isBusinessHours ? "warning700" : "success700", children: executionWindow?.isBusinessHours ? "⏰ Peak Business Hours Restriction Active" : "✅ Server Off-Peak Window Active" }) }),
1124
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: executionWindow?.isBusinessHours ? "warning700" : "success700", children: executionWindow?.policyMessage || (executionWindow?.isBusinessHours ? "Server policy restricts bulk operations during peak business hours (09:00 - 18:00). Direct execution is paused to preserve API throughput." : "Currently outside peak business hours. Direct execution is permitted.") })
1125
+ ] }) }),
1126
+ /* @__PURE__ */ jsx(
1127
+ Box,
1128
+ {
1129
+ padding: 4,
1130
+ background: "neutral100",
1131
+ hasRadius: true,
1132
+ marginBottom: 4,
1133
+ style: { border: "1px solid rgba(120, 120, 150, 0.15)" },
1134
+ children: /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "flex-start", children: [
1135
+ /* @__PURE__ */ jsx(
1136
+ Checkbox,
1137
+ {
1138
+ checked: publishOnImport,
1139
+ onValueChange: (val) => setPublishOnImport(Boolean(val))
1140
+ }
1141
+ ),
1142
+ /* @__PURE__ */ jsxs(Box, { style: { cursor: "pointer" }, onClick: () => setPublishOnImport(!publishOnImport), children: [
1143
+ /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", display: "block", children: "Publish immediately upon checker approval / execution" }),
1144
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "When checked (default), content types with Draft & Publish enabled will have their updated documents published immediately upon execution. When unchecked, changes remain in Draft for further editorial review." })
1145
+ ] })
1146
+ ] })
1147
+ }
1148
+ ),
1149
+ /* @__PURE__ */ jsx(Box, { marginBottom: 2, children: /* @__PURE__ */ jsx(
1150
+ TextInput,
1151
+ {
1152
+ label: "Change Request / Release Notes (Optional)",
1153
+ name: "makerNotes",
1154
+ placeholder: "e.g. REL-5040: Bulk updating catalog attributes via CSV",
1155
+ value: makerNotesInput,
1156
+ onChange: (e) => setMakerNotesInput(e.target.value)
1157
+ }
1158
+ ) })
1159
+ ]
1160
+ }
1161
+ ),
1162
+ /* @__PURE__ */ jsx(
1163
+ Box,
1164
+ {
1165
+ padding: 4,
1166
+ background: "neutral100",
1167
+ style: {
1168
+ borderTop: "1px solid rgba(120, 120, 150, 0.2)",
1169
+ flexShrink: 0,
1170
+ position: "sticky",
1171
+ bottom: 0,
1172
+ zIndex: 20,
1173
+ boxShadow: "0 -4px 16px rgba(0, 0, 0, 0.08)"
1174
+ },
1175
+ children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", gap: 3, style: { width: "100%" }, children: [
1176
+ /* @__PURE__ */ jsx(Button, { variant: "tertiary", size: "M", onClick: () => setShowConfirmModal(false), children: "Cancel" }),
1177
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, wrap: "wrap", justifyContent: "flex-end", alignItems: "center", children: [
1178
+ /* @__PURE__ */ jsx(
1179
+ Button,
1180
+ {
1181
+ variant: executionWindow?.isBusinessHours ? "default" : "secondary",
1182
+ size: "M",
1183
+ onClick: () => submitBatchWithMode("maker_checker"),
1184
+ loading: submittingBatch,
1185
+ children: "Submit for Checker Approval"
1186
+ }
1187
+ ),
1188
+ executionWindow?.isBusinessHours ? /* @__PURE__ */ jsxs(Fragment, { children: [
1189
+ /* @__PURE__ */ jsxs(
1190
+ Button,
1191
+ {
1192
+ variant: "secondary",
1193
+ size: "M",
1194
+ onClick: () => submitBatchWithMode("queue_offpeak"),
1195
+ loading: submittingBatch,
1196
+ children: [
1197
+ "Queue for Off-Peak (",
1198
+ executionWindow?.offPeakStartTime || "18:00",
1199
+ ")"
1200
+ ]
1201
+ }
1202
+ ),
1203
+ /* @__PURE__ */ jsx(
1204
+ Button,
1205
+ {
1206
+ variant: "danger-light",
1207
+ size: "S",
1208
+ onClick: () => submitBatchWithMode("immediate", true),
1209
+ loading: importing,
1210
+ children: "Force Run Now"
1211
+ }
1212
+ )
1213
+ ] }) : /* @__PURE__ */ jsx(
1214
+ Button,
1215
+ {
1216
+ variant: "default",
1217
+ size: "M",
1218
+ onClick: () => submitBatchWithMode("immediate"),
1219
+ loading: importing,
1220
+ children: "Confirm & Execute Now"
1221
+ }
1222
+ )
1223
+ ] })
1224
+ ] })
1225
+ }
1226
+ )
1227
+ ]
1228
+ }
1229
+ )
1230
+ }
1231
+ )
1232
+ ] });
646
1233
  }
647
1234
  function formatVal(v) {
648
1235
  if (v === null || v === void 0) return "(empty)";
@@ -1624,7 +2211,7 @@ function formatComponentName(compUid) {
1624
2211
  const name = compUid.includes(".") ? compUid.split(".")[1] : compUid;
1625
2212
  return name.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
1626
2213
  }
1627
- function getAllNestedValues(obj, path) {
2214
+ function getAllNestedValues(obj, path, availableAttributes) {
1628
2215
  if (!obj || !path) return [];
1629
2216
  const parsed = parseDynamicZonePath(path);
1630
2217
  const parts = parsed.cleanPath.split(".");
@@ -1636,14 +2223,17 @@ function getAllNestedValues(obj, path) {
1636
2223
  if (!item || typeof item !== "object") return;
1637
2224
  if (parsed.targetIndex !== void 0 && idx !== parsed.targetIndex) return;
1638
2225
  if (parsed.targetComponentUid && item.__component !== parsed.targetComponentUid) return;
1639
- const subVal = tail.length > 0 ? getNestedValue(item, tail.join(".")) : item;
1640
- if (subVal !== void 0) {
2226
+ const subPath = tail.join(".");
2227
+ const scopedPath = item.__component ? `${head}[${item.__component}].${subPath}` : "";
2228
+ const isSupported = availableAttributes ? availableAttributes.includes(scopedPath) : false;
2229
+ const subVal = tail.length > 0 ? getNestedValue(item, subPath) : item;
2230
+ if (subVal !== void 0 || isSupported) {
1641
2231
  results.push({
1642
2232
  index: idx,
1643
2233
  componentUid: item.__component,
1644
2234
  componentName: formatComponentName(item.__component),
1645
- value: subVal,
1646
- path: `${head}[#${idx}${item.__component ? ` ${formatComponentName(item.__component)}` : ""}].${tail.join(".")}`
2235
+ value: subVal !== void 0 ? subVal : null,
2236
+ path: `${head}[#${idx}${item.__component ? ` ${formatComponentName(item.__component)}` : ""}].${subPath}`
1647
2237
  });
1648
2238
  }
1649
2239
  });
@@ -1918,7 +2508,7 @@ function renderCellContent(colDef, entry, col, selectedAttribute, searchAttribut
1918
2508
  ] });
1919
2509
  }
1920
2510
  case "search_attr": {
1921
- const matches = getAllNestedValues(entry, searchAttribute);
2511
+ const matches = getAllNestedValues(entry, searchAttribute, col?.attributes);
1922
2512
  if (matches.length === 0) {
1923
2513
  return /* @__PURE__ */ jsx(Typography, { textColor: "neutral500", style: { fontStyle: "italic" }, children: "(empty / not set)" });
1924
2514
  }
@@ -1986,7 +2576,7 @@ function renderCellContent(colDef, entry, col, selectedAttribute, searchAttribut
1986
2576
  }) });
1987
2577
  }
1988
2578
  case "target_attr": {
1989
- const matches = getAllNestedValues(entry, selectedAttribute);
2579
+ const matches = getAllNestedValues(entry, selectedAttribute, col?.attributes);
1990
2580
  if (matches.length === 0) {
1991
2581
  return /* @__PURE__ */ jsx(Typography, { textColor: "neutral500", style: { fontStyle: "italic" }, children: "(empty / not set)" });
1992
2582
  }
@@ -2896,7 +3486,11 @@ const BulkUpdater = () => {
2896
3486
  if (selectedEntries[key]) {
2897
3487
  const parts = key.split(KEY_SEP);
2898
3488
  if (parts.length === 2) {
2899
- targets.push({ uid: parts[0], documentId: parts[1] });
3489
+ const targetObj = { uid: parts[0], documentId: parts[1] };
3490
+ if (selectedDzBlocks[key] && selectedDzBlocks[key].length > 0) {
3491
+ targetObj.dynamicZoneIndexes = selectedDzBlocks[key];
3492
+ }
3493
+ targets.push(targetObj);
2900
3494
  }
2901
3495
  }
2902
3496
  });
@@ -3046,6 +3640,20 @@ const BulkUpdater = () => {
3046
3640
  });
3047
3641
  await fetchModels();
3048
3642
  await fetchAuditHistory();
3643
+ if (selectedCollectionUid) {
3644
+ loadCollectionPage(
3645
+ selectedCollectionUid,
3646
+ collectionPages[selectedCollectionUid]?.page || 1,
3647
+ void 0,
3648
+ void 0,
3649
+ void 0,
3650
+ filterOperator,
3651
+ filterQuery,
3652
+ entrySearch,
3653
+ false,
3654
+ searchAttribute
3655
+ );
3656
+ }
3049
3657
  }
3050
3658
  } else {
3051
3659
  setIsSubmittingBatch(false);
@@ -4212,10 +4820,13 @@ const BulkUpdater = () => {
4212
4820
  isDzExpanded && dzFields.map((dzKey) => {
4213
4821
  const blocks = entry[dzKey] || [];
4214
4822
  const targetParsed = parseDynamicZonePath(selectedAttribute);
4215
- const allApplicableIndexes = blocks.map((_, i) => i).filter((i) => {
4823
+ const isBlockApplicable = (blockItem) => {
4216
4824
  if (!targetParsed.subPath) return true;
4217
- return getNestedValue(blocks[i], targetParsed.subPath) !== void 0;
4218
- });
4825
+ const scopedPath = `${dzKey}[${blockItem?.__component}].${targetParsed.subPath}`;
4826
+ const componentSupports = (col?.attributes || attributes || []).includes(scopedPath);
4827
+ return componentSupports || getNestedValue(blockItem, targetParsed.subPath) !== void 0;
4828
+ };
4829
+ const allApplicableIndexes = blocks.map((_, i) => i).filter((i) => isBlockApplicable(blocks[i]));
4219
4830
  return /* @__PURE__ */ jsx(Tr, { children: /* @__PURE__ */ jsx(Td, { colSpan: activeColumns.length, style: { padding: "12px 16px", backgroundColor: "rgba(73, 69, 255, 0.02)", borderBottom: "2px solid rgba(73, 69, 255, 0.15)" }, children: /* @__PURE__ */ jsxs(
4220
4831
  Box,
4221
4832
  {
@@ -4252,8 +4863,10 @@ const BulkUpdater = () => {
4252
4863
  /* @__PURE__ */ jsx(Tbody, { children: blocks.map((block, bIdx) => {
4253
4864
  const compUid = block.__component;
4254
4865
  const compLabel = formatComponentName(compUid);
4866
+ const scopedPath = `${dzKey}[${compUid}].${targetParsed.subPath}`;
4867
+ const componentSupports = (col?.attributes || attributes || []).includes(scopedPath);
4255
4868
  const blockVal = targetParsed.subPath ? getNestedValue(block, targetParsed.subPath) : void 0;
4256
- const hasTargetAttr = blockVal !== void 0;
4869
+ const hasTargetAttr = componentSupports || blockVal !== void 0;
4257
4870
  const isBlockChecked = hasTargetAttr && isSelected && (selectedDzBlocks[key] !== void 0 ? selectedDzBlocks[key].includes(bIdx) : true);
4258
4871
  return /* @__PURE__ */ jsxs(
4259
4872
  Tr,
@@ -4770,6 +5383,7 @@ const BulkUpdater = () => {
4770
5383
  '"'
4771
5384
  ] }),
4772
5385
  /* @__PURE__ */ jsxs(Flex, { gap: 1, alignItems: "center", marginTop: 1, children: [
5386
+ (batch.payload?.action === "csv_import" || batch.id?.startsWith("csv_")) && /* @__PURE__ */ jsx(Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: "📊 CSV IMPORT" }),
4773
5387
  /* @__PURE__ */ jsx(
4774
5388
  Badge,
4775
5389
  {
@@ -5521,4 +6135,4 @@ export {
5521
6135
  getNestedValue,
5522
6136
  parseDynamicZonePath
5523
6137
  };
5524
- //# sourceMappingURL=BulkUpdater-CbQk_OQG.mjs.map
6138
+ //# sourceMappingURL=BulkUpdater-CMkPdA9_.mjs.map