strapi-plugin-bulk-updater 1.4.3 → 1.4.9

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.
@@ -146,7 +146,7 @@ function CsvManagerTab() {
146
146
  const [collections, setCollections] = React.useState([]);
147
147
  const [loadingCollections, setLoadingCollections] = React.useState(true);
148
148
  const [selectedUid, setSelectedUid] = React.useState("");
149
- const [exportLimit, setExportLimit] = React.useState(1e3);
149
+ const [exportLimit, setExportLimit] = React.useState(0);
150
150
  const [exportOnlyViolations, setExportOnlyViolations] = React.useState(false);
151
151
  const [rules, setRules] = React.useState([]);
152
152
  const [selectedRuleId, setSelectedRuleId] = React.useState("");
@@ -154,9 +154,20 @@ function CsvManagerTab() {
154
154
  const [publishOnImport, setPublishOnImport] = React.useState(true);
155
155
  const [csvRawText, setCsvRawText] = React.useState("");
156
156
  const [fileName, setFileName] = React.useState("");
157
- const [parsedPreview, setParsedPreview] = React.useState([]);
157
+ const [allParsedRecords, setAllParsedRecords] = React.useState([]);
158
+ const [previewPage, setPreviewPage] = React.useState(1);
159
+ const [previewPageSize, setPreviewPageSize] = React.useState(10);
158
160
  const [importing, setImporting] = React.useState(false);
161
+ const [importProgress, setImportProgress] = React.useState(null);
159
162
  const [importResult, setImportResult] = React.useState(null);
163
+ const [executionWindow, setExecutionWindow] = React.useState(null);
164
+ const [preflightLoading, setPreflightLoading] = React.useState(false);
165
+ const [preflightReport, setPreflightReport] = React.useState(null);
166
+ const [showConfirmModal, setShowConfirmModal] = React.useState(false);
167
+ const [makerNotesInput, setMakerNotesInput] = React.useState("");
168
+ const [submittingBatch, setSubmittingBatch] = React.useState(false);
169
+ const [publishStrategy, setPublishStrategy] = React.useState("safe");
170
+ const [unreviewedConsent, setUnreviewedConsent] = React.useState(false);
160
171
  React.useEffect(() => {
161
172
  fetchInitialData();
162
173
  }, []);
@@ -167,6 +178,9 @@ function CsvManagerTab() {
167
178
  get("/bulk-updater/models"),
168
179
  get("/bulk-updater/validation-rules")
169
180
  ]);
181
+ if (modelsRes.data?.executionWindow) {
182
+ setExecutionWindow(modelsRes.data.executionWindow);
183
+ }
170
184
  if (modelsRes.data?.collections) {
171
185
  setCollections(modelsRes.data.collections);
172
186
  if (modelsRes.data.collections.length > 0) {
@@ -217,7 +231,7 @@ function CsvManagerTab() {
217
231
  downloadCsvFile(csvStr, filename);
218
232
  toggleNotification({
219
233
  type: "success",
220
- message: "Collection exported to CSV successfully"
234
+ message: `Collection exported successfully (${res.data?.count ?? 0} records)`
221
235
  });
222
236
  } catch (err) {
223
237
  if (ValidationRulesTab.isAbortError(err)) return;
@@ -270,34 +284,95 @@ function CsvManagerTab() {
270
284
  if (!file) return;
271
285
  setFileName(file.name);
272
286
  setImportResult(null);
287
+ setImportProgress(null);
273
288
  const reader = new FileReader();
274
289
  reader.onload = (event) => {
275
290
  const text = event.target?.result;
276
291
  setCsvRawText(text);
277
292
  try {
278
293
  const records = parseCsv(text);
279
- setParsedPreview(records.slice(0, 10));
294
+ setAllParsedRecords(records);
295
+ setPreviewPage(1);
280
296
  } catch (err) {
281
- setParsedPreview([]);
297
+ setAllParsedRecords([]);
282
298
  }
283
299
  };
284
300
  reader.readAsText(file);
285
301
  };
286
302
  const handleExecuteImport = async () => {
287
- if (!selectedUid || !csvRawText) return;
303
+ if (!selectedUid || allParsedRecords.length === 0) return;
304
+ const totalRows = allParsedRecords.length;
305
+ const CHUNK_SIZE = 100;
306
+ const totalChunks = Math.ceil(totalRows / CHUNK_SIZE);
307
+ const batchId = `csv_batch_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;
288
308
  try {
289
309
  setImporting(true);
290
310
  setImportResult(null);
291
- const res = await post("/bulk-updater/csv/import", {
292
- uid: selectedUid,
293
- csvText: csvRawText,
294
- mode: importMode,
295
- publish: publishOnImport
311
+ let liveCreated = 0;
312
+ let liveUpdated = 0;
313
+ let liveSkipped = 0;
314
+ let liveErrors = 0;
315
+ let finalResultData = null;
316
+ setImportProgress({
317
+ currentChunk: 0,
318
+ totalChunks,
319
+ percent: 0,
320
+ processedRows: 0,
321
+ totalRows,
322
+ liveCreated: 0,
323
+ liveUpdated: 0,
324
+ liveSkipped: 0,
325
+ liveErrors: 0
296
326
  });
297
- if (res.data && res.data.success) {
298
- setImportResult(res.data);
299
- const stats = res.data.stats;
300
- let notifMsg = `CSV Import complete: ${stats.created} created, ${stats.updated} updated.`;
327
+ for (let chunkIdx = 0; chunkIdx < totalChunks; chunkIdx++) {
328
+ const startIdx = chunkIdx * CHUNK_SIZE;
329
+ const endIdx = Math.min(startIdx + CHUNK_SIZE, totalRows);
330
+ const chunkRows = allParsedRecords.slice(startIdx, endIdx);
331
+ const isFirst = chunkIdx === 0;
332
+ const isLast = chunkIdx === totalChunks - 1;
333
+ const startRowNumber = startIdx + 2;
334
+ const res = await post("/bulk-updater/csv/import-chunk", {
335
+ uid: selectedUid,
336
+ rows: chunkRows,
337
+ batchId,
338
+ isFirstChunk: isFirst,
339
+ isLastChunk: isLast,
340
+ startRowNumber,
341
+ mode: importMode,
342
+ publish: publishOnImport,
343
+ publishStrategy,
344
+ unreviewedConsent
345
+ });
346
+ if (!res.data || !res.data.success) {
347
+ throw new Error(res.data?.error || `Chunk ${chunkIdx + 1} failed to process`);
348
+ }
349
+ const chunkStats = res.data.chunkStats;
350
+ if (chunkStats) {
351
+ liveCreated += chunkStats.created || 0;
352
+ liveUpdated += chunkStats.updated || 0;
353
+ liveSkipped += chunkStats.skipped || 0;
354
+ liveErrors += chunkStats.errors?.length || 0;
355
+ }
356
+ const currentProgressPercent = Math.round((chunkIdx + 1) / totalChunks * 100);
357
+ setImportProgress({
358
+ currentChunk: chunkIdx + 1,
359
+ totalChunks,
360
+ percent: currentProgressPercent,
361
+ processedRows: endIdx,
362
+ totalRows,
363
+ liveCreated,
364
+ liveUpdated,
365
+ liveSkipped,
366
+ liveErrors
367
+ });
368
+ if (isLast) {
369
+ finalResultData = res.data;
370
+ }
371
+ }
372
+ if (finalResultData && finalResultData.stats) {
373
+ setImportResult(finalResultData);
374
+ const stats = finalResultData.stats;
375
+ let notifMsg = `CSV Import complete: ${stats.created} created, ${stats.updated} updated across ${stats.total} records.`;
301
376
  if (stats.newWithoutDocId && stats.newWithoutDocId > 0) {
302
377
  notifMsg += ` (${stats.newWithoutDocId} new entries created due to omitted documentId)`;
303
378
  }
@@ -305,348 +380,1018 @@ function CsvManagerTab() {
305
380
  type: "success",
306
381
  message: notifMsg
307
382
  });
308
- } else {
309
- throw new Error(res.data?.error || "Import failed");
310
383
  }
311
384
  } catch (err) {
312
385
  if (ValidationRulesTab.isAbortError(err)) return;
313
386
  toggleNotification({
314
387
  type: "danger",
315
- message: err.message || "Error processing CSV import"
388
+ message: err.message || "Error processing CSV import batch"
316
389
  });
317
390
  } finally {
318
391
  setImporting(false);
319
392
  }
320
393
  };
394
+ const handleRunPreflight = async () => {
395
+ if (!selectedUid || allParsedRecords.length === 0) return;
396
+ try {
397
+ setPreflightLoading(true);
398
+ const res = await post("/bulk-updater/csv/validate-preflight", {
399
+ uid: selectedUid,
400
+ rows: allParsedRecords,
401
+ mode: importMode
402
+ });
403
+ if (res.data && res.data.success) {
404
+ setPreflightReport(res.data);
405
+ if (res.data.hasErrors) {
406
+ toggleNotification({
407
+ type: "warning",
408
+ message: `Pre-flight validation: ${res.data.totalErrorCount} error(s) and ${res.data.totalWarningCount} warning(s) detected.`
409
+ });
410
+ } else if (res.data.hasWarnings) {
411
+ toggleNotification({
412
+ type: "warning",
413
+ message: `Pre-flight validation passed with ${res.data.totalWarningCount} warning(s).`
414
+ });
415
+ } else {
416
+ toggleNotification({
417
+ type: "success",
418
+ message: `Pre-flight validation passed! All ${res.data.validRows} rows are ready for import.`
419
+ });
420
+ }
421
+ }
422
+ } catch (err) {
423
+ if (ValidationRulesTab.isAbortError(err)) return;
424
+ toggleNotification({
425
+ type: "danger",
426
+ message: err.message || "Failed running pre-flight validation"
427
+ });
428
+ } finally {
429
+ setPreflightLoading(false);
430
+ }
431
+ };
432
+ const submitBatchWithMode = async (executionMode, forceImmediate = false) => {
433
+ if (!selectedUid || allParsedRecords.length === 0) return;
434
+ if (executionMode === "immediate") {
435
+ setShowConfirmModal(false);
436
+ await handleExecuteImport();
437
+ return;
438
+ }
439
+ try {
440
+ setSubmittingBatch(true);
441
+ const res = await post("/bulk-updater/csv/submit-batch", {
442
+ uid: selectedUid,
443
+ rows: allParsedRecords,
444
+ executionMode,
445
+ makerNotes: makerNotesInput,
446
+ forceImmediate,
447
+ mode: importMode,
448
+ publish: publishOnImport,
449
+ publishStrategy,
450
+ unreviewedConsent,
451
+ unreviewedCount: preflightReport?.lifecycleStats?.unreviewedCount || 0
452
+ });
453
+ setShowConfirmModal(false);
454
+ if (res.data && res.data.pending) {
455
+ setMakerNotesInput("");
456
+ toggleNotification({
457
+ type: "success",
458
+ message: res.data.message || "CSV batch submitted for Checker authorization."
459
+ });
460
+ } else if (res.data && res.data.executeNow) {
461
+ await handleExecuteImport();
462
+ }
463
+ } catch (err) {
464
+ if (ValidationRulesTab.isAbortError(err)) return;
465
+ toggleNotification({
466
+ type: "danger",
467
+ message: err.message || "Failed submitting CSV batch"
468
+ });
469
+ } finally {
470
+ setSubmittingBatch(false);
471
+ }
472
+ };
321
473
  const selectedCol = collections.find((c) => c.uid === selectedUid);
322
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 6, background: "neutral0", hasRadius: true, shadow: "filterShadow", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 6, children: [
323
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
324
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
325
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", tag: "h2", fontWeight: "bold", children: "CSV Export & Import Center" }),
326
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.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." })
474
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 6, background: "neutral0", hasRadius: true, shadow: "filterShadow", children: [
475
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 6, children: [
476
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
477
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
478
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", tag: "h2", fontWeight: "bold", children: "CSV Export & Import Center" }),
479
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.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." })
480
+ ] }),
481
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "secondary", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowClockwise, {}), onClick: fetchInitialData, children: "Refresh" })
327
482
  ] }),
328
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "secondary", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowClockwise, {}), onClick: fetchInitialData, children: "Refresh" })
329
- ] }),
330
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 4, background: "neutral100", hasRadius: true, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, alignItems: "flex-end", children: [
331
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { flex: 1 }, children: [
332
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", marginBottom: 2, children: "Target Collection Type" }),
483
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 4, background: "neutral100", hasRadius: true, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, alignItems: "flex-end", children: [
484
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { flex: 1 }, children: [
485
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", marginBottom: 2, children: "Target Collection Type" }),
486
+ /* @__PURE__ */ jsxRuntime.jsx(
487
+ designSystem.SingleSelect,
488
+ {
489
+ value: selectedUid,
490
+ onChange: (val) => {
491
+ setSelectedUid(val);
492
+ setImportResult(null);
493
+ setImportProgress(null);
494
+ },
495
+ disabled: loadingCollections,
496
+ placeholder: "Select a collection...",
497
+ children: collections.map((col) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.SingleSelectOption, { value: col.uid, children: [
498
+ col.displayName,
499
+ " (",
500
+ col.totalEntries ?? 0,
501
+ " records)"
502
+ ] }, col.uid))
503
+ }
504
+ )
505
+ ] }),
333
506
  /* @__PURE__ */ jsxRuntime.jsx(
334
- designSystem.SingleSelect,
335
- {
336
- value: selectedUid,
337
- onChange: (val) => {
338
- setSelectedUid(val);
339
- setImportResult(null);
340
- },
341
- disabled: loadingCollections,
342
- placeholder: "Select a collection...",
343
- children: collections.map((col) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.SingleSelectOption, { value: col.uid, children: [
344
- col.displayName,
345
- " (",
346
- col.totalEntries ?? 0,
347
- " records)"
348
- ] }, col.uid))
349
- }
350
- )
351
- ] }),
352
- /* @__PURE__ */ jsxRuntime.jsx(
353
- designSystem.Button,
354
- {
355
- variant: "tertiary",
356
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Download, {}),
357
- onClick: handleDownloadTemplate,
358
- disabled: !selectedUid,
359
- children: "Download Sample CSV Template"
360
- }
361
- )
362
- ] }) }),
363
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 5, background: "neutral100", hasRadius: true, children: [
364
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h3", fontWeight: "bold", marginBottom: 2, children: "1. Export Data to CSV" }),
365
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", marginBottom: 4, children: "Extract entire collections or pinpoint records violating validation quotas." }),
366
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, wrap: "wrap", alignItems: "center", marginTop: 3, children: [
367
- /* @__PURE__ */ jsxRuntime.jsxs(
368
507
  designSystem.Button,
369
508
  {
370
- variant: "default",
509
+ variant: "tertiary",
371
510
  startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Download, {}),
372
- onClick: handleExportCollection,
511
+ onClick: handleDownloadTemplate,
373
512
  disabled: !selectedUid,
374
- children: [
375
- "Export All ",
376
- selectedCol?.displayName || "Collection",
377
- " Records (.csv)"
378
- ]
513
+ children: "Download Sample CSV Template"
379
514
  }
380
- ),
381
- rules.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", style: { marginLeft: "auto" }, children: [
382
- /* @__PURE__ */ jsxRuntime.jsx(
515
+ )
516
+ ] }) }),
517
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 5, background: "neutral100", hasRadius: true, children: [
518
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h3", fontWeight: "bold", marginBottom: 2, children: "1. Export Data to CSV" }),
519
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.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." }),
520
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, wrap: "wrap", alignItems: "center", marginTop: 3, children: [
521
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 220 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
383
522
  designSystem.SingleSelect,
384
523
  {
385
- value: selectedRuleId,
386
- onChange: (val) => setSelectedRuleId(val),
387
- placeholder: "Select Quota Rule...",
388
- children: rules.map((r) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.SingleSelectOption, { value: r.id, children: [
389
- "Rule: ",
390
- r.name
391
- ] }, r.id))
524
+ value: String(exportLimit),
525
+ onChange: (val) => setExportLimit(Number(val)),
526
+ placeholder: "Export Limit",
527
+ children: [
528
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "0", children: "All Records (No Limit)" }),
529
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "10000", children: "Up to 10,000 Records" }),
530
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "5000", children: "Up to 5,000 Records" }),
531
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "1000", children: "Up to 1,000 Records" })
532
+ ]
392
533
  }
393
- ),
394
- /* @__PURE__ */ jsxRuntime.jsx(
534
+ ) }),
535
+ /* @__PURE__ */ jsxRuntime.jsxs(
395
536
  designSystem.Button,
396
537
  {
397
- variant: "danger-light",
538
+ variant: "default",
398
539
  startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Download, {}),
399
- onClick: handleExportViolations,
400
- disabled: !selectedRuleId,
401
- children: "Export Violators CSV"
540
+ onClick: handleExportCollection,
541
+ disabled: !selectedUid,
542
+ children: [
543
+ "Export ",
544
+ exportLimit === 0 ? "All" : `${exportLimit}`,
545
+ " ",
546
+ selectedCol?.displayName || "Collection",
547
+ " Records (.csv)"
548
+ ]
402
549
  }
403
- )
404
- ] })
405
- ] })
406
- ] }),
407
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 5, background: "neutral100", hasRadius: true, children: [
408
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h3", fontWeight: "bold", marginBottom: 2, children: "2. Import & Batch Override CSV" }),
409
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", marginBottom: 4, children: "Upload an updated CSV file to bulk update existing records or insert new records." }),
410
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, wrap: "wrap", gap: 4, children: [
411
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
412
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", marginBottom: 1, display: "block", children: "Operation Mode" }),
413
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
414
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: "⚡ Smart Auto-Detect" }),
415
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
416
- "Automatically updates matching records by ",
417
- /* @__PURE__ */ jsxRuntime.jsx("code", { children: "documentId" }),
418
- " and creates new entries when ID is omitted."
419
- ] })
550
+ ),
551
+ rules.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", style: { marginLeft: "auto" }, children: [
552
+ /* @__PURE__ */ jsxRuntime.jsx(
553
+ designSystem.SingleSelect,
554
+ {
555
+ value: selectedRuleId,
556
+ onChange: (val) => setSelectedRuleId(val),
557
+ placeholder: "Select Quota Rule...",
558
+ children: rules.map((r) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.SingleSelectOption, { value: r.id, children: [
559
+ "Rule: ",
560
+ r.name
561
+ ] }, r.id))
562
+ }
563
+ ),
564
+ /* @__PURE__ */ jsxRuntime.jsx(
565
+ designSystem.Button,
566
+ {
567
+ variant: "danger-light",
568
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Download, {}),
569
+ onClick: handleExportViolations,
570
+ disabled: !selectedRuleId,
571
+ children: "Export Violators CSV"
572
+ }
573
+ )
420
574
  ] })
421
- ] }),
422
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
423
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", marginBottom: 1, display: "block", children: "Publish Options" }),
424
- /* @__PURE__ */ jsxRuntime.jsx(
425
- designSystem.Checkbox,
426
- {
427
- checked: publishOnImport,
428
- onValueChange: (val) => setPublishOnImport(val),
429
- children: "Auto-publish on import"
430
- }
431
- )
432
575
  ] })
433
576
  ] }),
434
- /* @__PURE__ */ jsxRuntime.jsxs(
435
- designSystem.Box,
436
- {
437
- padding: 6,
438
- background: "neutral0",
439
- hasRadius: true,
440
- style: {
441
- border: "2px dashed #dcdce4",
442
- textAlign: "center",
443
- cursor: "pointer"
444
- },
445
- children: [
577
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 5, background: "neutral100", hasRadius: true, children: [
578
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h3", fontWeight: "bold", marginBottom: 2, children: "2. Import & Batch Override CSV" }),
579
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.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." }),
580
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, wrap: "wrap", gap: 4, children: [
581
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
582
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", marginBottom: 1, display: "block", children: "Operation Mode" }),
583
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
584
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: "⚡ Smart Auto-Detect" }),
585
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
586
+ "Automatically updates matching records by ",
587
+ /* @__PURE__ */ jsxRuntime.jsx("code", { children: "documentId" }),
588
+ " and creates new entries when ID is omitted."
589
+ ] })
590
+ ] })
591
+ ] }),
592
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
593
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", marginBottom: 1, display: "block", children: "Publish Options" }),
446
594
  /* @__PURE__ */ jsxRuntime.jsx(
447
- "input",
595
+ designSystem.Checkbox,
448
596
  {
449
- type: "file",
450
- accept: ".csv",
451
- id: "csv-file-input",
452
- style: { display: "none" },
453
- onChange: handleFileUpload
597
+ checked: publishOnImport,
598
+ onValueChange: (val) => setPublishOnImport(val),
599
+ children: "Auto-publish on import"
454
600
  }
455
- ),
456
- /* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor: "csv-file-input", style: { cursor: "pointer", display: "block" }, children: [
457
- /* @__PURE__ */ jsxRuntime.jsx(icons.Upload, { style: { width: 32, height: 32, margin: "0 auto 8px", color: "#4945ff" } }),
458
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", fontWeight: "bold", display: "block", children: fileName ? `Selected: ${fileName}` : "Click here or drag a .csv file to upload" }),
459
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: "UTF-8 encoded CSV matching collection schema headers" })
460
- ] })
461
- ]
462
- }
463
- ),
464
- parsedPreview.length > 0 && (() => {
465
- const missingDocIdCount = parsedPreview.filter(
466
- (row) => !extractDocId(row)
467
- ).length;
468
- const headers = Object.keys(parsedPreview[0]);
469
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginTop: 4, background: "neutral0", padding: 4, hasRadius: true, children: [
470
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 3, children: [
471
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
472
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "sigma", textColor: "neutral700", children: [
473
- "FILE PREVIEW (FIRST ",
474
- parsedPreview.length,
475
- " ROWS)"
601
+ )
602
+ ] })
603
+ ] }),
604
+ /* @__PURE__ */ jsxRuntime.jsxs(
605
+ designSystem.Box,
606
+ {
607
+ padding: 6,
608
+ background: "neutral0",
609
+ hasRadius: true,
610
+ style: {
611
+ border: "2px dashed #dcdce4",
612
+ textAlign: "center",
613
+ cursor: "pointer"
614
+ },
615
+ children: [
616
+ /* @__PURE__ */ jsxRuntime.jsx(
617
+ "input",
618
+ {
619
+ type: "file",
620
+ accept: ".csv",
621
+ id: "csv-file-input",
622
+ style: { display: "none" },
623
+ onChange: handleFileUpload
624
+ }
625
+ ),
626
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor: "csv-file-input", style: { cursor: "pointer", display: "block" }, children: [
627
+ /* @__PURE__ */ jsxRuntime.jsx(icons.Upload, { style: { width: 32, height: 32, margin: "0 auto 8px", color: "#4945ff" } }),
628
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", fontWeight: "bold", display: "block", children: fileName ? `Selected: ${fileName}` : "Click here or drag a .csv file to upload" }),
629
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: "UTF-8 encoded CSV matching collection schema headers" })
630
+ ] })
631
+ ]
632
+ }
633
+ ),
634
+ allParsedRecords.length > 0 && (() => {
635
+ const totalRows = allParsedRecords.length;
636
+ const missingDocIdCount = allParsedRecords.filter((row) => !extractDocId(row)).length;
637
+ const updateCount = totalRows - missingDocIdCount;
638
+ const headers = Object.keys(allParsedRecords[0]);
639
+ const totalPages = Math.ceil(totalRows / previewPageSize) || 1;
640
+ const safePage = Math.min(Math.max(1, previewPage), totalPages);
641
+ const startIndex = (safePage - 1) * previewPageSize;
642
+ const endIndex = Math.min(startIndex + previewPageSize, totalRows);
643
+ const displayedRows = allParsedRecords.slice(startIndex, endIndex);
644
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginTop: 4, background: "neutral0", padding: 4, hasRadius: true, children: [
645
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 3, wrap: "wrap", gap: 2, children: [
646
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
647
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "sigma", textColor: "neutral700", children: [
648
+ "FILE PREVIEW — ",
649
+ totalRows.toLocaleString(),
650
+ " RECORDS DETECTED IN CSV"
651
+ ] }),
652
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral500", display: "block", children: [
653
+ totalRows.toLocaleString(),
654
+ " total row",
655
+ totalRows > 1 ? "s" : "",
656
+ " •",
657
+ " ",
658
+ updateCount > 0 ? `${updateCount.toLocaleString()} updates` : "",
659
+ updateCount > 0 && missingDocIdCount > 0 ? ", " : "",
660
+ missingDocIdCount > 0 ? `${missingDocIdCount.toLocaleString()} new entries (missing documentId)` : ""
661
+ ] })
476
662
  ] }),
477
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral500", display: "block", children: [
478
- parsedPreview.length,
479
- " previewed row",
480
- parsedPreview.length > 1 ? "s" : "",
481
- " •",
482
- " ",
483
- missingDocIdCount > 0 ? `${missingDocIdCount} row${missingDocIdCount > 1 ? "s" : ""} missing documentId (will be created as new entries)` : "All previewed rows contain document IDs"
663
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
664
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
665
+ "🔄 ",
666
+ updateCount.toLocaleString(),
667
+ " Updates"
668
+ ] }),
669
+ missingDocIdCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: [
670
+ "➕ ",
671
+ missingDocIdCount.toLocaleString(),
672
+ " New (No ID)"
673
+ ] })
484
674
  ] })
485
675
  ] }),
486
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: missingDocIdCount > 0 ? "warning100" : "success100", textColor: missingDocIdCount > 0 ? "warning700" : "success700", children: missingDocIdCount > 0 ? `${missingDocIdCount} New (No ID)` : "Ready to Import" })
487
- ] }),
488
- missingDocIdCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginBottom: 3, children: /* @__PURE__ */ jsxRuntime.jsx(
489
- designSystem.Alert,
490
- {
491
- variant: "default",
492
- title: "Smart Auto-Detect Active",
493
- children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "omega", children: [
494
- missingDocIdCount,
495
- " row",
496
- missingDocIdCount > 1 ? "s have" : " has",
497
- " no ",
498
- /* @__PURE__ */ jsxRuntime.jsx("code", { children: "documentId" }),
499
- ".",
500
- " ",
501
- "Because no documentId is provided, the engine will automatically treat them as new entries and generate new database IDs for them."
502
- ] })
503
- }
504
- ) }),
505
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { overflowX: "auto", maxWidth: "100%", border: "1px solid #e0e0e0", borderRadius: 4 }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: headers.length + 1, rowCount: parsedPreview.length + 1, children: [
506
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
507
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { width: 150, minWidth: 150 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Detected Action" }) }),
508
- headers.map((h) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { minWidth: 160 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: h }) }, h))
676
+ preflightReport && /* @__PURE__ */ jsxRuntime.jsx(
677
+ designSystem.Box,
678
+ {
679
+ marginBottom: 3,
680
+ padding: 4,
681
+ background: preflightReport.hasErrors ? "danger100" : preflightReport.hasWarnings ? "warning100" : "success100",
682
+ hasRadius: true,
683
+ style: { border: `1px solid ${preflightReport.hasErrors ? "#d9534f" : preflightReport.hasWarnings ? "#f0ad4e" : "#5cb85c"}` },
684
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, children: [
685
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 2, children: [
686
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.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` }),
687
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
688
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: [
689
+ "✓ ",
690
+ preflightReport.validRows,
691
+ " Valid Rows"
692
+ ] }),
693
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
694
+ "🔄 ",
695
+ preflightReport.estimatedUpdates,
696
+ " Updates"
697
+ ] }),
698
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: [
699
+ "➕ ",
700
+ preflightReport.estimatedCreates,
701
+ " New"
702
+ ] })
703
+ ] })
704
+ ] }),
705
+ preflightReport.hasErrors && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginTop: 2, background: "neutral0", padding: 3, hasRadius: true, style: { maxHeight: 160, overflowY: "auto" }, children: [
706
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: "danger700", display: "block", marginBottom: 1, children: [
707
+ "Validation Errors (",
708
+ preflightReport.totalErrorCount,
709
+ "):"
710
+ ] }),
711
+ preflightReport.errors.map((e, idx) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "danger700", display: "block", children: [
712
+ "• Row ",
713
+ e.row,
714
+ ": ",
715
+ e.error
716
+ ] }, idx))
717
+ ] }),
718
+ preflightReport.hasWarnings && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginTop: 2, background: "neutral0", padding: 3, hasRadius: true, style: { maxHeight: 140, overflowY: "auto" }, children: [
719
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: "warning700", display: "block", marginBottom: 1, children: [
720
+ "Warnings (",
721
+ preflightReport.totalWarningCount,
722
+ "):"
723
+ ] }),
724
+ preflightReport.warnings.map((w, idx) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "warning700", display: "block", children: [
725
+ "• Row ",
726
+ w.row,
727
+ ": ",
728
+ w.warning
729
+ ] }, idx))
730
+ ] })
731
+ ] })
732
+ }
733
+ ),
734
+ missingDocIdCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginBottom: 3, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Alert, { variant: "default", title: "Smart Auto-Detect Active", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "omega", children: [
735
+ missingDocIdCount.toLocaleString(),
736
+ " row",
737
+ missingDocIdCount > 1 ? "s have" : " has",
738
+ " no ",
739
+ /* @__PURE__ */ jsxRuntime.jsx("code", { children: "documentId" }),
740
+ ".",
741
+ " ",
742
+ "Because no documentId is provided, the engine will automatically treat them as new entries and generate new database IDs for them."
743
+ ] }) }) }),
744
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { overflowX: "auto", maxWidth: "100%", border: "1px solid #e0e0e0", borderRadius: 4 }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: headers.length + 1, rowCount: displayedRows.length + 1, children: [
745
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
746
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { width: 150, minWidth: 150 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Detected Action" }) }),
747
+ headers.map((h) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { minWidth: 160 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: h }) }, h))
748
+ ] }) }),
749
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: displayedRows.map((row, rIdx) => {
750
+ const hasDocId = !!extractDocId(row);
751
+ const actualRowIndex = startIndex + rIdx + 1;
752
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
753
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 1, children: [
754
+ hasDocId ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: "🔄 Update" }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: "➕ New Entry" }),
755
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: [
756
+ "Row #",
757
+ actualRowIndex
758
+ ] })
759
+ ] }) }),
760
+ headers.map((h) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { verticalAlign: "top", maxWidth: 300 }, children: (h === "documentId" || h === "id") && !row[h]?.trim() ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "warning700", fontWeight: "bold", children: "(Auto-generate ID)" }) : /* @__PURE__ */ jsxRuntime.jsx(
761
+ designSystem.Typography,
762
+ {
763
+ variant: "omega",
764
+ style: {
765
+ display: "-webkit-box",
766
+ WebkitLineClamp: 3,
767
+ WebkitBoxOrient: "vertical",
768
+ overflow: "hidden",
769
+ textOverflow: "ellipsis",
770
+ whiteSpace: "pre-wrap",
771
+ wordBreak: "break-word",
772
+ maxHeight: 65
773
+ },
774
+ title: row[h] || "",
775
+ children: row[h] || "-"
776
+ }
777
+ ) }, h))
778
+ ] }, rIdx);
779
+ }) })
509
780
  ] }) }),
510
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: parsedPreview.map((row, rIdx) => {
511
- const hasDocId = !!extractDocId(row);
512
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
513
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: hasDocId ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: "🔄 Match / Update" }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: "➕ New Entry" }) }),
514
- headers.map((h) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { verticalAlign: "top", maxWidth: 300 }, children: (h === "documentId" || h === "id") && !row[h]?.trim() ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "warning700", fontWeight: "bold", children: "(Auto-generate ID)" }) : /* @__PURE__ */ jsxRuntime.jsx(
515
- designSystem.Typography,
781
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginTop: 3, wrap: "wrap", gap: 3, children: [
782
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
783
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Rows per page:" }),
784
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 80 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
785
+ designSystem.SingleSelect,
516
786
  {
517
- variant: "omega",
518
- style: {
519
- display: "-webkit-box",
520
- WebkitLineClamp: 3,
521
- WebkitBoxOrient: "vertical",
522
- overflow: "hidden",
523
- textOverflow: "ellipsis",
524
- whiteSpace: "pre-wrap",
525
- wordBreak: "break-word",
526
- maxHeight: 65
787
+ value: String(previewPageSize),
788
+ onChange: (val) => {
789
+ setPreviewPageSize(Number(val));
790
+ setPreviewPage(1);
527
791
  },
528
- title: row[h] || "",
529
- children: row[h] || "-"
792
+ children: [
793
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "10", children: "10" }),
794
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "25", children: "25" }),
795
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "50", children: "50" }),
796
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "100", children: "100" })
797
+ ]
530
798
  }
531
- ) }, h))
532
- ] }, rIdx);
533
- }) })
534
- ] }) }),
535
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "flex-end", marginTop: 4, children: /* @__PURE__ */ jsxRuntime.jsx(
536
- designSystem.Button,
799
+ ) }),
800
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
801
+ "Showing ",
802
+ startIndex + 1,
803
+ "–",
804
+ endIndex,
805
+ " of ",
806
+ totalRows.toLocaleString(),
807
+ " rows"
808
+ ] })
809
+ ] }),
810
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
811
+ /* @__PURE__ */ jsxRuntime.jsx(
812
+ designSystem.Button,
813
+ {
814
+ variant: "tertiary",
815
+ size: "S",
816
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ChevronLeft, {}),
817
+ disabled: safePage <= 1 || importing,
818
+ onClick: () => setPreviewPage((p) => Math.max(1, p - 1)),
819
+ children: "Prev"
820
+ }
821
+ ),
822
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral700", children: [
823
+ "Page ",
824
+ safePage,
825
+ " of ",
826
+ totalPages
827
+ ] }),
828
+ /* @__PURE__ */ jsxRuntime.jsx(
829
+ designSystem.Button,
830
+ {
831
+ variant: "tertiary",
832
+ size: "S",
833
+ endIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ChevronRight, {}),
834
+ disabled: safePage >= totalPages || importing,
835
+ onClick: () => setPreviewPage((p) => Math.min(totalPages, p + 1)),
836
+ children: "Next"
837
+ }
838
+ )
839
+ ] })
840
+ ] }),
841
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginTop: 4, wrap: "wrap", gap: 3, children: [
842
+ /* @__PURE__ */ jsxRuntime.jsx(
843
+ designSystem.Button,
844
+ {
845
+ variant: "secondary",
846
+ size: "L",
847
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Eye, {}),
848
+ onClick: handleRunPreflight,
849
+ loading: preflightLoading,
850
+ disabled: importing,
851
+ children: "Run Pre-Flight Dry Run"
852
+ }
853
+ ),
854
+ /* @__PURE__ */ jsxRuntime.jsxs(
855
+ designSystem.Button,
856
+ {
857
+ variant: "default",
858
+ size: "L",
859
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Check, {}),
860
+ onClick: () => {
861
+ setPublishStrategy("safe");
862
+ setUnreviewedConsent(false);
863
+ setShowConfirmModal(true);
864
+ },
865
+ loading: importing || submittingBatch,
866
+ children: [
867
+ "Review & Import (",
868
+ totalRows.toLocaleString(),
869
+ " Records)..."
870
+ ]
871
+ }
872
+ )
873
+ ] })
874
+ ] });
875
+ })(),
876
+ importProgress && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginTop: 4, padding: 4, background: "neutral0", hasRadius: true, style: { border: "1px solid #4945ff" }, children: [
877
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 2, children: [
878
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", fontWeight: "bold", textColor: "primary700", children: importing ? `Processing Batch Chunk ${importProgress.currentChunk} of ${importProgress.totalChunks} (${importProgress.percent}%)` : "Batch Import Complete" }),
879
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral700", children: [
880
+ importProgress.processedRows.toLocaleString(),
881
+ " / ",
882
+ importProgress.totalRows.toLocaleString(),
883
+ " rows processed"
884
+ ] })
885
+ ] }),
886
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: "100%", height: 10, background: "#e0e0e0", borderRadius: 5, overflow: "hidden", marginBottom: 12 }, children: /* @__PURE__ */ jsxRuntime.jsx(
887
+ designSystem.Box,
537
888
  {
538
- variant: "default",
539
- size: "L",
540
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Check, {}),
541
- onClick: handleExecuteImport,
542
- loading: importing,
543
- children: "Execute Smart Import (Auto-Detect)"
889
+ style: {
890
+ width: `${importProgress.percent}%`,
891
+ height: "100%",
892
+ background: importProgress.percent === 100 ? "#2f6846" : "#4945ff",
893
+ transition: "width 0.2s ease-in-out"
894
+ }
544
895
  }
545
- ) })
546
- ] });
547
- })(),
548
- importResult && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(
549
- designSystem.Alert,
550
- {
551
- variant: importResult.stats.errors.length > 0 ? "warning" : "success",
552
- title: "CSV Import Results",
553
- children: [
554
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "omega", display: "block", marginBottom: 1, children: [
555
- "Successfully processed: ",
556
- /* @__PURE__ */ jsxRuntime.jsxs("strong", { children: [
557
- importResult.stats.created,
558
- " created"
559
- ] }),
560
- importResult.stats.newWithoutDocId > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
561
- " (",
562
- importResult.stats.newWithoutDocId,
563
- " created as new entries because documentId was omitted)"
564
- ] }),
565
- ", ",
566
- /* @__PURE__ */ jsxRuntime.jsxs("strong", { children: [
567
- importResult.stats.updated,
568
- " updated"
569
- ] }),
570
- ",",
571
- " ",
572
- /* @__PURE__ */ jsxRuntime.jsxs("strong", { children: [
573
- importResult.stats.skipped,
574
- " skipped"
575
- ] }),
576
- " out of ",
577
- importResult.stats.total,
578
- " total rows."
896
+ ) }),
897
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, wrap: "wrap", alignItems: "center", children: [
898
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: [
899
+ "Created: ",
900
+ importProgress.liveCreated.toLocaleString()
579
901
  ] }),
580
- importResult.stats.newWithoutDocId > 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 2, padding: 2, background: "neutral100", hasRadius: true, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral700", children: [
581
- "ℹ️ ",
582
- /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Note on new entries:" }),
583
- " ",
584
- importResult.stats.newWithoutDocId,
585
- " row",
586
- importResult.stats.newWithoutDocId > 1 ? "s" : "",
587
- " had no ",
588
- /* @__PURE__ */ jsxRuntime.jsx("code", { children: "documentId" }),
589
- " 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."
590
- ] }) }),
591
- importResult.stats.errors.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginTop: 2, children: [
592
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "danger700", fontWeight: "bold", children: [
593
- "Errors encountered (",
594
- importResult.stats.errors.length,
595
- "):"
596
- ] }),
597
- /* @__PURE__ */ jsxRuntime.jsx("ul", { style: { margin: "4px 0 0 16px", padding: 0 }, children: importResult.stats.errors.slice(0, 10).map((err, idx) => /* @__PURE__ */ jsxRuntime.jsx("li", { style: { marginBottom: "2px" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "danger600", children: [
598
- "Row ",
599
- err.row,
600
- ": ",
601
- err.error
602
- ] }) }, idx)) }),
603
- importResult.stats.errors.length > 10 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { display: "block", marginTop: 4, fontStyle: "italic" }, children: [
604
- "...and ",
605
- importResult.stats.errors.length - 10,
606
- " more error",
607
- importResult.stats.errors.length - 10 > 1 ? "s" : ""
608
- ] })
902
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
903
+ "Updated: ",
904
+ importProgress.liveUpdated.toLocaleString()
609
905
  ] }),
610
- importResult.stats.diffs && importResult.stats.diffs.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginTop: 3, padding: 3, background: "neutral0", hasRadius: true, style: { border: "1px solid #dcdce4" }, children: [
611
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "sigma", textColor: "neutral800", fontWeight: "bold", display: "block", marginBottom: 2, children: [
612
- "CHANGES APPLIED (",
613
- importResult.stats.diffs.length,
614
- "):"
906
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "neutral150", textColor: "neutral700", children: [
907
+ "Skipped: ",
908
+ importProgress.liveSkipped.toLocaleString()
909
+ ] }),
910
+ importProgress.liveErrors > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "danger100", textColor: "danger700", children: [
911
+ "Errors: ",
912
+ importProgress.liveErrors.toLocaleString()
913
+ ] })
914
+ ] })
915
+ ] }),
916
+ importResult && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(
917
+ designSystem.Alert,
918
+ {
919
+ variant: importResult.stats.errors.length > 0 ? "warning" : "success",
920
+ title: "CSV Import Results",
921
+ children: [
922
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "omega", display: "block", marginBottom: 1, children: [
923
+ "Successfully processed: ",
924
+ /* @__PURE__ */ jsxRuntime.jsxs("strong", { children: [
925
+ importResult.stats.created.toLocaleString(),
926
+ " created"
927
+ ] }),
928
+ importResult.stats.newWithoutDocId > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
929
+ " (",
930
+ importResult.stats.newWithoutDocId.toLocaleString(),
931
+ " created as new entries because documentId was omitted)"
932
+ ] }),
933
+ ", ",
934
+ /* @__PURE__ */ jsxRuntime.jsxs("strong", { children: [
935
+ importResult.stats.updated.toLocaleString(),
936
+ " updated"
937
+ ] }),
938
+ ",",
939
+ " ",
940
+ /* @__PURE__ */ jsxRuntime.jsxs("strong", { children: [
941
+ importResult.stats.skipped.toLocaleString(),
942
+ " skipped"
943
+ ] }),
944
+ " out of ",
945
+ importResult.stats.total.toLocaleString(),
946
+ " total rows."
615
947
  ] }),
616
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { overflowX: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: 4, rowCount: importResult.stats.diffs.length + 1, children: [
617
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
618
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Entry / Document" }) }),
619
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Attribute" }) }),
620
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Previous Value" }) }),
621
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Applied Value" }) })
622
- ] }) }),
623
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: importResult.stats.diffs.slice(0, 10).map((d, idx) => {
624
- const oldDisplay = d.oldValue !== null && d.oldValue !== void 0 ? typeof d.oldValue === "object" ? JSON.stringify(d.oldValue) : String(d.oldValue) : "(empty)";
625
- const newDisplay = d.newValue !== null && d.newValue !== void 0 ? typeof d.newValue === "object" ? JSON.stringify(d.newValue) : String(d.newValue) : "(empty)";
626
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
627
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Td, { children: [
628
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: d.label || d.documentId }),
629
- d.documentId && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", display: "block", children: d.documentId })
630
- ] }),
631
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: d.attribute || "field" }) }),
632
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "danger600", children: oldDisplay }) }),
633
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", fontWeight: "semiBold", children: newDisplay }) })
634
- ] }, idx);
635
- }) })
948
+ importResult.stats.newWithoutDocId > 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 2, padding: 2, background: "neutral100", hasRadius: true, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral700", children: [
949
+ "ℹ️ ",
950
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Note on new entries:" }),
951
+ " ",
952
+ importResult.stats.newWithoutDocId,
953
+ " row",
954
+ importResult.stats.newWithoutDocId > 1 ? "s" : "",
955
+ " had no ",
956
+ /* @__PURE__ */ jsxRuntime.jsx("code", { children: "documentId" }),
957
+ " 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."
636
958
  ] }) }),
637
- importResult.stats.diffs.length > 10 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { display: "block", marginTop: 6, fontStyle: "italic" }, children: [
638
- "...and ",
639
- importResult.stats.diffs.length - 10,
640
- " more change",
641
- importResult.stats.diffs.length - 10 > 1 ? "s" : "",
642
- " recorded in Audit Trail & Version History."
959
+ importResult.stats.errors.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginTop: 2, children: [
960
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "danger700", fontWeight: "bold", children: [
961
+ "Errors encountered (",
962
+ importResult.stats.errors.length,
963
+ "):"
964
+ ] }),
965
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { style: { margin: "4px 0 0 16px", padding: 0 }, children: importResult.stats.errors.slice(0, 10).map((err, idx) => /* @__PURE__ */ jsxRuntime.jsx("li", { style: { marginBottom: "2px" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "danger600", children: [
966
+ "Row ",
967
+ err.row,
968
+ ": ",
969
+ err.error
970
+ ] }) }, idx)) }),
971
+ importResult.stats.errors.length > 10 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { display: "block", marginTop: 4, fontStyle: "italic" }, children: [
972
+ "...and ",
973
+ importResult.stats.errors.length - 10,
974
+ " more error",
975
+ importResult.stats.errors.length - 10 > 1 ? "s" : ""
976
+ ] })
977
+ ] }),
978
+ importResult.stats.diffs && importResult.stats.diffs.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginTop: 3, padding: 3, background: "neutral0", hasRadius: true, style: { border: "1px solid #dcdce4" }, children: [
979
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "sigma", textColor: "neutral800", fontWeight: "bold", display: "block", marginBottom: 2, children: [
980
+ "CHANGES APPLIED (",
981
+ importResult.stats.diffs.length.toLocaleString(),
982
+ "):"
983
+ ] }),
984
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { overflowX: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: 4, rowCount: importResult.stats.diffs.length + 1, children: [
985
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
986
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Entry / Document" }) }),
987
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Attribute" }) }),
988
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Previous Value" }) }),
989
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Applied Value" }) })
990
+ ] }) }),
991
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: importResult.stats.diffs.slice(0, 10).map((d, idx) => {
992
+ const oldDisplay = d.oldValue !== null && d.oldValue !== void 0 ? typeof d.oldValue === "object" ? JSON.stringify(d.oldValue) : String(d.oldValue) : "(empty)";
993
+ const newDisplay = d.newValue !== null && d.newValue !== void 0 ? typeof d.newValue === "object" ? JSON.stringify(d.newValue) : String(d.newValue) : "(empty)";
994
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
995
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Td, { children: [
996
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: d.label || d.documentId }),
997
+ d.documentId && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", display: "block", children: d.documentId })
998
+ ] }),
999
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: d.attribute || "field" }) }),
1000
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "danger600", children: oldDisplay }) }),
1001
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", fontWeight: "semiBold", children: newDisplay }) })
1002
+ ] }, idx);
1003
+ }) })
1004
+ ] }) }),
1005
+ importResult.stats.diffs.length > 10 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { display: "block", marginTop: 6, fontStyle: "italic" }, children: [
1006
+ "...and ",
1007
+ importResult.stats.diffs.length - 10,
1008
+ " more change",
1009
+ importResult.stats.diffs.length - 10 > 1 ? "s" : "",
1010
+ " recorded in Audit Trail & Version History."
1011
+ ] })
643
1012
  ] })
644
- ] })
645
- ]
646
- }
647
- ) })
648
- ] })
649
- ] }) });
1013
+ ]
1014
+ }
1015
+ ) })
1016
+ ] })
1017
+ ] }),
1018
+ showConfirmModal && /* @__PURE__ */ jsxRuntime.jsx(
1019
+ "div",
1020
+ {
1021
+ style: {
1022
+ position: "fixed",
1023
+ inset: 0,
1024
+ backgroundColor: "rgba(20, 20, 30, 0.65)",
1025
+ backdropFilter: "blur(2px)",
1026
+ display: "flex",
1027
+ alignItems: "center",
1028
+ justifyContent: "center",
1029
+ zIndex: 9999,
1030
+ padding: 16,
1031
+ boxSizing: "border-box",
1032
+ overflowY: "auto"
1033
+ },
1034
+ onClick: () => setShowConfirmModal(false),
1035
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
1036
+ designSystem.Box,
1037
+ {
1038
+ background: "neutral0",
1039
+ hasRadius: true,
1040
+ shadow: "popupShadow",
1041
+ style: {
1042
+ maxWidth: 680,
1043
+ width: "100%",
1044
+ maxHeight: "calc(100vh - 48px)",
1045
+ display: "flex",
1046
+ flexDirection: "column",
1047
+ overflow: "hidden",
1048
+ position: "relative",
1049
+ boxShadow: "0 24px 48px rgba(0, 0, 0, 0.32), 0 0 1px rgba(0, 0, 0, 0.2)"
1050
+ },
1051
+ onClick: (e) => e.stopPropagation(),
1052
+ children: [
1053
+ /* @__PURE__ */ jsxRuntime.jsx(
1054
+ designSystem.Box,
1055
+ {
1056
+ padding: 5,
1057
+ background: "neutral100",
1058
+ style: {
1059
+ borderBottom: "1px solid rgba(120, 120, 150, 0.2)",
1060
+ flexShrink: 0
1061
+ },
1062
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
1063
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", fontWeight: "bold", children: "Confirm CSV Bulk Import" }),
1064
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", display: "block", children: "Review import target, record actions, publishing mode, and execution window policies before proceeding." })
1065
+ ] }) })
1066
+ }
1067
+ ),
1068
+ /* @__PURE__ */ jsxRuntime.jsxs(
1069
+ designSystem.Box,
1070
+ {
1071
+ padding: 5,
1072
+ style: {
1073
+ flex: "1 1 auto",
1074
+ minHeight: 0,
1075
+ overflowY: "auto",
1076
+ WebkitOverflowScrolling: "touch"
1077
+ },
1078
+ children: [
1079
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 4, background: "neutral100", hasRadius: true, marginBottom: 4, children: [
1080
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", marginBottom: 2, display: "block", children: "IMPORT DETAILS" }),
1081
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, children: [
1082
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
1083
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: "Target Collection:" }),
1084
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: selectedCol?.displayName || selectedUid })
1085
+ ] }),
1086
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
1087
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: "Total Detected Records:" }),
1088
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { fontWeight: "bold", textColor: "primary600", children: [
1089
+ allParsedRecords.length.toLocaleString(),
1090
+ " records"
1091
+ ] })
1092
+ ] }),
1093
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
1094
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: "Action Breakdown:" }),
1095
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
1096
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
1097
+ "🔄 ",
1098
+ allParsedRecords.filter((r) => !!extractDocId(r)).length.toLocaleString(),
1099
+ " Updates"
1100
+ ] }),
1101
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: [
1102
+ "➕ ",
1103
+ allParsedRecords.filter((r) => !extractDocId(r)).length.toLocaleString(),
1104
+ " New (No ID)"
1105
+ ] })
1106
+ ] })
1107
+ ] }),
1108
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
1109
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: "Import Mode:" }),
1110
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "neutral200", textColor: "neutral800", children: importMode === "update" ? "Update Only (Exact Document IDs)" : importMode === "create_or_override" ? "Create or Override" : "Smart Auto-Detect" })
1111
+ ] }),
1112
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
1113
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: "Publishing Mode:" }),
1114
+ /* @__PURE__ */ jsxRuntime.jsx(
1115
+ designSystem.Badge,
1116
+ {
1117
+ backgroundColor: publishOnImport ? "success100" : "warning100",
1118
+ textColor: publishOnImport ? "success700" : "warning700",
1119
+ children: publishOnImport ? "Publish Immediately" : "Keep as Draft"
1120
+ }
1121
+ )
1122
+ ] }),
1123
+ preflightReport && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginTop: 2, paddingTop: 2, style: { borderTop: "1px solid #eaeaef" }, children: [
1124
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Pre-Flight Status:" }),
1125
+ /* @__PURE__ */ jsxRuntime.jsx(
1126
+ designSystem.Typography,
1127
+ {
1128
+ variant: "pi",
1129
+ fontWeight: "bold",
1130
+ textColor: preflightReport.hasErrors ? "danger700" : preflightReport.hasWarnings ? "warning700" : "success700",
1131
+ children: preflightReport.hasErrors ? `⚠️ ${preflightReport.totalErrorCount} errors found` : preflightReport.hasWarnings ? `⚡ Passed with ${preflightReport.totalWarningCount} warnings` : "✅ 100% Passed"
1132
+ }
1133
+ )
1134
+ ] })
1135
+ ] })
1136
+ ] }),
1137
+ preflightReport?.lifecycleStats && preflightReport.lifecycleStats.isDraftAndPublish && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginBottom: 4, padding: 3, background: "neutral100", hasRadius: true, style: { border: "1px solid rgba(120, 120, 150, 0.15)" }, children: [
1138
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "sigma", textColor: "neutral600", marginBottom: 2, display: "block", children: [
1139
+ "LIFECYCLE STATUS BREAKDOWN (",
1140
+ preflightReport.lifecycleStats.totalChecked,
1141
+ " Existing Records)"
1142
+ ] }),
1143
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, wrap: "wrap", children: [
1144
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: [
1145
+ "✓ Published / Approved: ",
1146
+ preflightReport.lifecycleStats.published
1147
+ ] }),
1148
+ preflightReport.lifecycleStats.modified > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
1149
+ "📝 Modified (Unreviewed Revisions): ",
1150
+ preflightReport.lifecycleStats.modified
1151
+ ] }),
1152
+ preflightReport.lifecycleStats.draft > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "neutral200", textColor: "neutral800", children: [
1153
+ "📄 Draft (Never Published): ",
1154
+ preflightReport.lifecycleStats.draft
1155
+ ] })
1156
+ ] })
1157
+ ] }),
1158
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 4, hasRadius: true, marginBottom: 4, background: executionWindow?.isBusinessHours ? "warning100" : "success100", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 1, children: [
1159
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, alignItems: "center", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", textColor: executionWindow?.isBusinessHours ? "warning700" : "success700", children: executionWindow?.isBusinessHours ? "⏰ Peak Business Hours Restriction Active" : "✅ Server Off-Peak Window Active" }) }),
1160
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.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.") })
1161
+ ] }) }),
1162
+ /* @__PURE__ */ jsxRuntime.jsx(
1163
+ designSystem.Box,
1164
+ {
1165
+ padding: 4,
1166
+ background: "neutral100",
1167
+ hasRadius: true,
1168
+ marginBottom: 4,
1169
+ style: { border: "1px solid rgba(120, 120, 150, 0.15)" },
1170
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "flex-start", children: [
1171
+ /* @__PURE__ */ jsxRuntime.jsx(
1172
+ designSystem.Checkbox,
1173
+ {
1174
+ checked: publishOnImport,
1175
+ onValueChange: (val) => setPublishOnImport(Boolean(val))
1176
+ }
1177
+ ),
1178
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { cursor: "pointer" }, onClick: () => setPublishOnImport(!publishOnImport), children: [
1179
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", display: "block", children: "Publish immediately upon checker approval / execution" }),
1180
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.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." })
1181
+ ] })
1182
+ ] })
1183
+ }
1184
+ ),
1185
+ publishOnImport && (preflightReport?.lifecycleStats?.unreviewedCount ?? 0) > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
1186
+ designSystem.Box,
1187
+ {
1188
+ padding: 4,
1189
+ background: "neutral100",
1190
+ hasRadius: true,
1191
+ marginBottom: 4,
1192
+ style: { border: "1px solid #d9d8ff" },
1193
+ children: [
1194
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", marginBottom: 3, display: "block", children: "PUBLISHING STRATEGY & GOVERNANCE CONSENT" }),
1195
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 3, children: [
1196
+ /* @__PURE__ */ jsxRuntime.jsx(
1197
+ designSystem.Box,
1198
+ {
1199
+ padding: 3,
1200
+ hasRadius: true,
1201
+ style: {
1202
+ border: publishStrategy === "safe" ? "2px solid #4945ff" : "1px solid #eaeaef",
1203
+ backgroundColor: publishStrategy === "safe" ? "#f0f0ff" : "#ffffff",
1204
+ cursor: "pointer"
1205
+ },
1206
+ onClick: () => {
1207
+ setPublishStrategy("safe");
1208
+ setUnreviewedConsent(false);
1209
+ },
1210
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "flex-start", children: [
1211
+ /* @__PURE__ */ jsxRuntime.jsx(
1212
+ "input",
1213
+ {
1214
+ type: "radio",
1215
+ id: "csv-strategy-safe",
1216
+ name: "csvPublishStrategy",
1217
+ checked: publishStrategy === "safe",
1218
+ onChange: () => {
1219
+ setPublishStrategy("safe");
1220
+ setUnreviewedConsent(false);
1221
+ },
1222
+ style: { marginTop: 3 }
1223
+ }
1224
+ ),
1225
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
1226
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", display: "block", children: "🛡️ Safe Publishing (Recommended)" }),
1227
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
1228
+ "Only publish records that are already approved & live (",
1229
+ preflightReport?.lifecycleStats?.published ?? 0,
1230
+ " record",
1231
+ (preflightReport?.lifecycleStats?.published ?? 0) === 1 ? "" : "s",
1232
+ "). The ",
1233
+ preflightReport?.lifecycleStats?.unreviewedCount ?? 0,
1234
+ " unreviewed or draft record(s) will receive the CSV update in Draft mode without publishing unreviewed draft revisions to visitors."
1235
+ ] })
1236
+ ] })
1237
+ ] })
1238
+ }
1239
+ ),
1240
+ /* @__PURE__ */ jsxRuntime.jsx(
1241
+ designSystem.Box,
1242
+ {
1243
+ padding: 3,
1244
+ hasRadius: true,
1245
+ style: {
1246
+ border: publishStrategy === "all" ? "2px solid #d02b20" : "1px solid #eaeaef",
1247
+ backgroundColor: publishStrategy === "all" ? "#fff5f5" : "#ffffff",
1248
+ cursor: "pointer"
1249
+ },
1250
+ onClick: () => setPublishStrategy("all"),
1251
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "flex-start", children: [
1252
+ /* @__PURE__ */ jsxRuntime.jsx(
1253
+ "input",
1254
+ {
1255
+ type: "radio",
1256
+ id: "csv-strategy-all",
1257
+ name: "csvPublishStrategy",
1258
+ checked: publishStrategy === "all",
1259
+ onChange: () => setPublishStrategy("all"),
1260
+ style: { marginTop: 3 }
1261
+ }
1262
+ ),
1263
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
1264
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", textColor: "danger700", display: "block", children: "⚠️ Publish All Imported Records (Includes Unreviewed Drafts)" }),
1265
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
1266
+ "Publish all affected records live to production immediately, pushing all pending unreviewed draft edits in the ",
1267
+ preflightReport?.lifecycleStats?.unreviewedCount ?? 0,
1268
+ " draft/modified record(s) live."
1269
+ ] })
1270
+ ] })
1271
+ ] })
1272
+ }
1273
+ ),
1274
+ publishStrategy === "all" && /* @__PURE__ */ jsxRuntime.jsx(
1275
+ designSystem.Box,
1276
+ {
1277
+ padding: 3,
1278
+ background: "danger100",
1279
+ hasRadius: true,
1280
+ style: { border: "1px solid #f5c0b8" },
1281
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "flex-start", children: [
1282
+ /* @__PURE__ */ jsxRuntime.jsx(
1283
+ designSystem.Checkbox,
1284
+ {
1285
+ checked: unreviewedConsent,
1286
+ onValueChange: (checked) => setUnreviewedConsent(Boolean(checked))
1287
+ }
1288
+ ),
1289
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { cursor: "pointer" }, onClick: () => setUnreviewedConsent(!unreviewedConsent), children: [
1290
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", textColor: "danger700", display: "block", children: "Mandatory Risk Consent Required" }),
1291
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "danger700", children: [
1292
+ "I understand and explicitly consent that ",
1293
+ preflightReport?.lifecycleStats?.unreviewedCount ?? 0,
1294
+ " record(s) with unreviewed draft revisions or saved draft states will be published live to production visitors."
1295
+ ] })
1296
+ ] })
1297
+ ] })
1298
+ }
1299
+ )
1300
+ ] })
1301
+ ]
1302
+ }
1303
+ ),
1304
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginBottom: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
1305
+ designSystem.TextInput,
1306
+ {
1307
+ label: "Change Request / Release Notes (Optional)",
1308
+ name: "makerNotes",
1309
+ placeholder: "e.g. REL-5040: Bulk updating catalog attributes via CSV",
1310
+ value: makerNotesInput,
1311
+ onChange: (e) => setMakerNotesInput(e.target.value)
1312
+ }
1313
+ ) })
1314
+ ]
1315
+ }
1316
+ ),
1317
+ (() => {
1318
+ const isConsentRequired = publishOnImport && (preflightReport?.lifecycleStats?.unreviewedCount ?? 0) > 0 && publishStrategy === "all" && !unreviewedConsent;
1319
+ return /* @__PURE__ */ jsxRuntime.jsx(
1320
+ designSystem.Box,
1321
+ {
1322
+ padding: 4,
1323
+ background: "neutral100",
1324
+ style: {
1325
+ borderTop: "1px solid rgba(120, 120, 150, 0.2)",
1326
+ flexShrink: 0,
1327
+ position: "sticky",
1328
+ bottom: 0,
1329
+ zIndex: 20,
1330
+ boxShadow: "0 -4px 16px rgba(0, 0, 0, 0.08)"
1331
+ },
1332
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", gap: 3, style: { width: "100%" }, children: [
1333
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", size: "M", onClick: () => setShowConfirmModal(false), children: "Cancel" }),
1334
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, wrap: "wrap", justifyContent: "flex-end", alignItems: "center", children: [
1335
+ /* @__PURE__ */ jsxRuntime.jsx(
1336
+ designSystem.Button,
1337
+ {
1338
+ variant: executionWindow?.isBusinessHours ? "default" : "secondary",
1339
+ size: "M",
1340
+ onClick: () => submitBatchWithMode("maker_checker"),
1341
+ loading: submittingBatch,
1342
+ disabled: isConsentRequired,
1343
+ children: "Submit for Checker Approval"
1344
+ }
1345
+ ),
1346
+ executionWindow?.isBusinessHours ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1347
+ /* @__PURE__ */ jsxRuntime.jsxs(
1348
+ designSystem.Button,
1349
+ {
1350
+ variant: "secondary",
1351
+ size: "M",
1352
+ onClick: () => submitBatchWithMode("queue_offpeak"),
1353
+ loading: submittingBatch,
1354
+ disabled: isConsentRequired,
1355
+ children: [
1356
+ "Queue for Off-Peak (",
1357
+ executionWindow?.offPeakStartTime || "18:00",
1358
+ ")"
1359
+ ]
1360
+ }
1361
+ ),
1362
+ /* @__PURE__ */ jsxRuntime.jsx(
1363
+ designSystem.Button,
1364
+ {
1365
+ variant: "danger-light",
1366
+ size: "S",
1367
+ onClick: () => submitBatchWithMode("immediate", true),
1368
+ loading: importing,
1369
+ disabled: isConsentRequired,
1370
+ children: "Force Run Now"
1371
+ }
1372
+ )
1373
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
1374
+ designSystem.Button,
1375
+ {
1376
+ variant: "default",
1377
+ size: "M",
1378
+ onClick: () => submitBatchWithMode("immediate"),
1379
+ loading: importing,
1380
+ disabled: isConsentRequired,
1381
+ children: "Confirm & Execute Now"
1382
+ }
1383
+ )
1384
+ ] })
1385
+ ] })
1386
+ }
1387
+ );
1388
+ })()
1389
+ ]
1390
+ }
1391
+ )
1392
+ }
1393
+ )
1394
+ ] });
650
1395
  }
651
1396
  function formatVal(v) {
652
1397
  if (v === null || v === void 0) return "(empty)";
@@ -1278,7 +2023,8 @@ const BulkUpdatePreviewModal = ({
1278
2023
  previewData,
1279
2024
  attribute,
1280
2025
  targetValueDisplay,
1281
- publishOnApproval = true
2026
+ publishOnApproval = true,
2027
+ lifecycleStats
1282
2028
  }) => {
1283
2029
  const [filterTab, setFilterTab] = React.useState("all");
1284
2030
  const [searchQuery, setSearchQuery] = React.useState("");
@@ -1381,6 +2127,17 @@ const BulkUpdatePreviewModal = ({
1381
2127
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", fontWeight: "semiBold", children: "Inspecting Target Records..." }),
1382
2128
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Computing proposed diffs and verifying against database schema." })
1383
2129
  ] }) }) : !previewData ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 6, style: { textAlign: "center" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: "No preview data available." }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2130
+ publishOnApproval && lifecycleStats && lifecycleStats.unreviewedCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginBottom: 4, padding: 3, background: "warning100", hasRadius: true, style: { border: "1px solid #f5c0b8" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
2131
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: "warning700", children: "⚠️ Safe Publishing Active:" }),
2132
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "warning700", children: [
2133
+ lifecycleStats.unreviewedCount,
2134
+ " target record(s) contain unreviewed draft changes (",
2135
+ lifecycleStats.draft,
2136
+ " Draft, ",
2137
+ lifecycleStats.modified,
2138
+ " Modified). By default, these will be updated as drafts without publishing unreviewed revisions to production visitors."
2139
+ ] })
2140
+ ] }) }),
1384
2141
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, marginBottom: 4, wrap: "wrap", children: [
1385
2142
  /* @__PURE__ */ jsxRuntime.jsxs(
1386
2143
  designSystem.Box,
@@ -1628,26 +2385,29 @@ function formatComponentName(compUid) {
1628
2385
  const name = compUid.includes(".") ? compUid.split(".")[1] : compUid;
1629
2386
  return name.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
1630
2387
  }
1631
- function getAllNestedValues(obj, path) {
2388
+ function getAllNestedValues(obj, path, availableAttributes) {
1632
2389
  if (!obj || !path) return [];
1633
2390
  const parsed = parseDynamicZonePath(path);
1634
2391
  const parts = parsed.cleanPath.split(".");
1635
2392
  const [head, ...tail] = parts;
1636
- const rootTarget = obj[head];
2393
+ const rootTarget = getPropertyValue(obj, head);
1637
2394
  if (Array.isArray(rootTarget)) {
1638
2395
  const results = [];
1639
2396
  rootTarget.forEach((item, idx) => {
1640
2397
  if (!item || typeof item !== "object") return;
1641
2398
  if (parsed.targetIndex !== void 0 && idx !== parsed.targetIndex) return;
1642
2399
  if (parsed.targetComponentUid && item.__component !== parsed.targetComponentUid) return;
1643
- const subVal = tail.length > 0 ? getNestedValue(item, tail.join(".")) : item;
1644
- if (subVal !== void 0) {
2400
+ const subPath = tail.join(".");
2401
+ const scopedPath = item.__component ? `${head}[${item.__component}].${subPath}` : "";
2402
+ const isSupported = availableAttributes ? availableAttributes.includes(scopedPath) : false;
2403
+ const subVal = tail.length > 0 ? getNestedValue(item, subPath) : item;
2404
+ if (subVal !== void 0 || isSupported) {
1645
2405
  results.push({
1646
2406
  index: idx,
1647
2407
  componentUid: item.__component,
1648
2408
  componentName: formatComponentName(item.__component),
1649
- value: subVal,
1650
- path: `${head}[#${idx}${item.__component ? ` ${formatComponentName(item.__component)}` : ""}].${tail.join(".")}`
2409
+ value: subVal !== void 0 ? subVal : null,
2410
+ path: `${head}[#${idx}${item.__component ? ` ${formatComponentName(item.__component)}` : ""}].${subPath}`
1651
2411
  });
1652
2412
  }
1653
2413
  });
@@ -1665,6 +2425,27 @@ function getAllNestedValues(obj, path) {
1665
2425
  }
1666
2426
  return [];
1667
2427
  }
2428
+ function getPropertyValue(target, key) {
2429
+ if (!target || typeof target !== "object" || !key) return void 0;
2430
+ if (target[key] !== void 0 && target[key] !== null) return target[key];
2431
+ const camelKey = key.replace(/_([a-z0-9])/g, (_, letter) => letter.toUpperCase());
2432
+ if (target[camelKey] !== void 0 && target[camelKey] !== null) return target[camelKey];
2433
+ const snakeKey = key.replace(/([A-Z])/g, "_$1").toLowerCase();
2434
+ if (target[snakeKey] !== void 0 && target[snakeKey] !== null) return target[snakeKey];
2435
+ const lowerKey = key.toLowerCase();
2436
+ const lowerNoUnderscore = lowerKey.replace(/_/g, "");
2437
+ const matchedKey = Object.keys(target).find((k) => {
2438
+ const kLower = k.toLowerCase();
2439
+ return kLower === lowerKey || kLower.replace(/_/g, "") === lowerNoUnderscore;
2440
+ });
2441
+ if (matchedKey && target[matchedKey] !== void 0 && target[matchedKey] !== null) {
2442
+ return target[matchedKey];
2443
+ }
2444
+ if (key in target) return target[key];
2445
+ if (camelKey in target) return target[camelKey];
2446
+ if (snakeKey in target) return target[snakeKey];
2447
+ return void 0;
2448
+ }
1668
2449
  function getNestedValue(obj, path) {
1669
2450
  if (!obj || !path) return void 0;
1670
2451
  const parsed = parseDynamicZonePath(path);
@@ -1681,12 +2462,12 @@ function getNestedValue(obj, path) {
1681
2462
  const item = current[idx];
1682
2463
  if (parsed.targetIndex !== void 0 && idx !== parsed.targetIndex) continue;
1683
2464
  if (parsed.targetComponentUid && item?.__component !== parsed.targetComponentUid) continue;
1684
- const found = extract(item?.[head], tail);
2465
+ const found = extract(getPropertyValue(item, head), tail);
1685
2466
  if (found !== void 0 && found !== null) return found;
1686
2467
  }
1687
2468
  return void 0;
1688
2469
  }
1689
- return extract(current[head], tail);
2470
+ return extract(getPropertyValue(current, head), tail);
1690
2471
  }
1691
2472
  return extract(obj, parts);
1692
2473
  }
@@ -1711,24 +2492,59 @@ function getAvailableColumnsForCollection(col, selectedAttribute, searchAttribut
1711
2492
  return;
1712
2493
  }
1713
2494
  let formattedLabel = attr;
1714
- const bracketMatch = attr.match(/^([^.[]+)\[([^\]]+)\]\.(.+)$/);
1715
- if (bracketMatch) {
1716
- const root = bracketMatch[1].charAt(0).toUpperCase() + bracketMatch[1].slice(1);
1717
- const comp = formatComponentName(bracketMatch[2]);
1718
- const sub = bracketMatch[3].split(".").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" > ");
1719
- formattedLabel = `${root} [${comp}] > ${sub}`;
1720
- } else if (attr.includes(".")) {
1721
- formattedLabel = attr.split(".").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" > ");
2495
+ if (attr === "updated_at" || attr === "updatedAt") {
2496
+ formattedLabel = "Updated At";
2497
+ } else if (attr === "created_at" || attr === "createdAt") {
2498
+ formattedLabel = "Created At";
2499
+ } else if (attr === "published_at" || attr === "publishedAt") {
2500
+ formattedLabel = "Published At";
1722
2501
  } else {
1723
- formattedLabel = attr.charAt(0).toUpperCase() + attr.slice(1);
2502
+ const bracketMatch = attr.match(/^([^.[]+)\[([^\]]+)\]\.(.+)$/);
2503
+ if (bracketMatch) {
2504
+ const root = bracketMatch[1].charAt(0).toUpperCase() + bracketMatch[1].slice(1);
2505
+ const comp = formatComponentName(bracketMatch[2]);
2506
+ const sub = bracketMatch[3].split(".").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" > ");
2507
+ formattedLabel = `${root} [${comp}] > ${sub}`;
2508
+ } else if (attr.includes(".")) {
2509
+ formattedLabel = attr.split(".").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" > ");
2510
+ } else {
2511
+ formattedLabel = attr.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
2512
+ }
1724
2513
  }
1725
2514
  cols.push({
1726
2515
  key: `attr_${attr}`,
1727
2516
  label: formattedLabel,
1728
2517
  sortable: !col.isSingleType,
1729
- defaultVisible: ["department", "email", "location"].includes(attr)
2518
+ defaultVisible: ["department", "email", "location", "updated_at", "updatedAt"].includes(attr)
1730
2519
  });
1731
2520
  });
2521
+ const hasUpdatedAt = candidateAttrs.some((a) => a === "updatedAt" || a === "updated_at");
2522
+ const hasCreatedAt = candidateAttrs.some((a) => a === "createdAt" || a === "created_at");
2523
+ const hasPublishedAt = candidateAttrs.some((a) => a === "publishedAt" || a === "published_at");
2524
+ if (!hasUpdatedAt) {
2525
+ cols.push({
2526
+ key: "attr_updatedAt",
2527
+ label: "Updated At",
2528
+ sortable: !col.isSingleType,
2529
+ defaultVisible: false
2530
+ });
2531
+ }
2532
+ if (!hasCreatedAt) {
2533
+ cols.push({
2534
+ key: "attr_createdAt",
2535
+ label: "Created At",
2536
+ sortable: !col.isSingleType,
2537
+ defaultVisible: false
2538
+ });
2539
+ }
2540
+ if (!hasPublishedAt && col.draftAndPublish) {
2541
+ cols.push({
2542
+ key: "attr_publishedAt",
2543
+ label: "Published At",
2544
+ sortable: !col.isSingleType,
2545
+ defaultVisible: false
2546
+ });
2547
+ }
1732
2548
  if (searchAttribute && searchAttribute !== selectedAttribute) {
1733
2549
  let formattedSearchLabel = searchAttribute;
1734
2550
  const bracketMatch = searchAttribute.match(/^([^.[]+)\[([^\]]+)\]\.(.+)$/);
@@ -1774,11 +2590,136 @@ function getAvailableColumnsForCollection(col, selectedAttribute, searchAttribut
1774
2590
  });
1775
2591
  return cols;
1776
2592
  }
2593
+ function getActiveSortLabel(currentSort, col) {
2594
+ const f = currentSort.field;
2595
+ const o = currentSort.order;
2596
+ const dir = o === "asc" ? "▲" : "▼";
2597
+ if (f === "updatedAt" || f === "updated_at") {
2598
+ return `Updated: ${o === "desc" ? "Newest" : "Oldest"} ${dir}`;
2599
+ }
2600
+ if (f === "createdAt" || f === "created_at") {
2601
+ return `Created: ${o === "desc" ? "Newest" : "Oldest"} ${dir}`;
2602
+ }
2603
+ if (f === "publishedAt" || f === "published_at") {
2604
+ return `Published: ${o === "desc" ? "Newest" : "Oldest"} ${dir}`;
2605
+ }
2606
+ if (f === "id") {
2607
+ return `ID: ${o === "desc" ? "Highest" : "Lowest"} ${dir}`;
2608
+ }
2609
+ let attrLabel = f;
2610
+ if (f.includes(".")) {
2611
+ attrLabel = f.split(".").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" > ");
2612
+ } else {
2613
+ attrLabel = f.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
2614
+ }
2615
+ return `${attrLabel} (${o === "asc" ? "A → Z" : "Z → A"} ${dir})`;
2616
+ }
2617
+ function getSortOptionsForCollection(col, selectedAttribute, searchAttribute) {
2618
+ const options = [];
2619
+ const hasCustomUpdatedAt = col.attributes?.includes("updated_at");
2620
+ const updatedField = hasCustomUpdatedAt ? "updated_at" : "updatedAt";
2621
+ options.push({
2622
+ field: updatedField,
2623
+ order: "desc",
2624
+ label: "Updated At (Newest First ↓)",
2625
+ columnLabel: "Updated At",
2626
+ group: "timestamps"
2627
+ });
2628
+ options.push({
2629
+ field: updatedField,
2630
+ order: "asc",
2631
+ label: "Updated At (Oldest First ↑)",
2632
+ columnLabel: "Updated At",
2633
+ group: "timestamps"
2634
+ });
2635
+ const hasCustomCreatedAt = col.attributes?.includes("created_at");
2636
+ const createdField = hasCustomCreatedAt ? "created_at" : "createdAt";
2637
+ options.push({
2638
+ field: createdField,
2639
+ order: "desc",
2640
+ label: "Created At (Newest First ↓)",
2641
+ columnLabel: "Created At",
2642
+ group: "timestamps"
2643
+ });
2644
+ options.push({
2645
+ field: createdField,
2646
+ order: "asc",
2647
+ label: "Created At (Oldest First ↑)",
2648
+ columnLabel: "Created At",
2649
+ group: "timestamps"
2650
+ });
2651
+ if (col.draftAndPublish) {
2652
+ const hasCustomPublishedAt = col.attributes?.includes("published_at");
2653
+ const publishedField = hasCustomPublishedAt ? "published_at" : "publishedAt";
2654
+ options.push({
2655
+ field: publishedField,
2656
+ order: "desc",
2657
+ label: "Published At (Newest First ↓)",
2658
+ columnLabel: "Published At",
2659
+ group: "timestamps"
2660
+ });
2661
+ options.push({
2662
+ field: publishedField,
2663
+ order: "asc",
2664
+ label: "Published At (Oldest First ↑)",
2665
+ columnLabel: "Published At",
2666
+ group: "timestamps"
2667
+ });
2668
+ }
2669
+ options.push({
2670
+ field: "id",
2671
+ order: "desc",
2672
+ label: "Entry ID (Highest First ↓)",
2673
+ columnLabel: "ID",
2674
+ group: "system"
2675
+ });
2676
+ options.push({
2677
+ field: "id",
2678
+ order: "asc",
2679
+ label: "Entry ID (Lowest First ↑)",
2680
+ columnLabel: "ID",
2681
+ group: "system"
2682
+ });
2683
+ const systemKeywords = [
2684
+ "createdat",
2685
+ "created_at",
2686
+ "updatedat",
2687
+ "updated_at",
2688
+ "publishedat",
2689
+ "published_at",
2690
+ "id",
2691
+ "documentid"
2692
+ ];
2693
+ const attrs = (col.attributes || []).filter((a) => !systemKeywords.includes(a.toLowerCase()));
2694
+ attrs.forEach((attr) => {
2695
+ let formattedLabel = attr;
2696
+ if (attr.includes(".")) {
2697
+ formattedLabel = attr.split(".").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" > ");
2698
+ } else {
2699
+ formattedLabel = attr.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
2700
+ }
2701
+ options.push({
2702
+ field: attr,
2703
+ order: "asc",
2704
+ label: `${formattedLabel} (A → Z / Lowest ↑)`,
2705
+ columnLabel: formattedLabel,
2706
+ group: "attributes"
2707
+ });
2708
+ options.push({
2709
+ field: attr,
2710
+ order: "desc",
2711
+ label: `${formattedLabel} (Z → A / Highest ↓)`,
2712
+ columnLabel: formattedLabel,
2713
+ group: "attributes"
2714
+ });
2715
+ });
2716
+ return options;
2717
+ }
1777
2718
  function renderHeaderCell(colDef, col, currentSort, selectedAttribute, searchAttribute, onSort) {
1778
2719
  if (colDef.key === "select") {
1779
2720
  return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { width: 48 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Select" }) }, "select");
1780
2721
  }
1781
- const isCurrentSorted = colDef.key === "identifier" && (currentSort.field === "name" || currentSort.field === "title") || colDef.key === "search_attr" && currentSort.field === searchAttribute || colDef.key === "target_attr" && currentSort.field === selectedAttribute || colDef.key.startsWith("attr_") && currentSort.field === colDef.key.replace("attr_", "");
2722
+ const isCurrentSorted = colDef.key === "identifier" && (currentSort.field === "name" || currentSort.field === "title" || currentSort.field === "id") || colDef.key === "search_attr" && currentSort.field === searchAttribute || colDef.key === "target_attr" && currentSort.field === selectedAttribute || colDef.key.startsWith("attr_") && (currentSort.field === colDef.key.replace("attr_", "") || currentSort.field === "updatedAt" && colDef.key === "attr_updated_at" || currentSort.field === "updated_at" && colDef.key === "attr_updatedAt" || currentSort.field === "createdAt" && colDef.key === "attr_created_at" || currentSort.field === "created_at" && colDef.key === "attr_createdAt" || currentSort.field === "publishedAt" && colDef.key === "attr_published_at" || currentSort.field === "published_at" && colDef.key === "attr_publishedAt");
1782
2723
  const sortArrow = isCurrentSorted ? currentSort.order === "asc" ? "▲" : "▼" : "⇅";
1783
2724
  const handleClick = () => {
1784
2725
  if (!colDef.sortable) return;
@@ -1810,6 +2751,15 @@ function formatDisplayValue(val) {
1810
2751
  if (val === void 0 || val === null || val === "") {
1811
2752
  return "(empty / not set)";
1812
2753
  }
2754
+ if (val instanceof Date) {
2755
+ return isNaN(val.getTime()) ? "(invalid date)" : val.toLocaleString();
2756
+ }
2757
+ if (typeof val === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(val)) {
2758
+ const d = new Date(val);
2759
+ if (!isNaN(d.getTime())) {
2760
+ return d.toLocaleString();
2761
+ }
2762
+ }
1813
2763
  if (Array.isArray(val)) {
1814
2764
  if (val.length === 0) return "(empty / unlinked)";
1815
2765
  return val.map(
@@ -1922,7 +2872,7 @@ function renderCellContent(colDef, entry, col, selectedAttribute, searchAttribut
1922
2872
  ] });
1923
2873
  }
1924
2874
  case "search_attr": {
1925
- const matches = getAllNestedValues(entry, searchAttribute);
2875
+ const matches = getAllNestedValues(entry, searchAttribute, col?.attributes);
1926
2876
  if (matches.length === 0) {
1927
2877
  return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral500", style: { fontStyle: "italic" }, children: "(empty / not set)" });
1928
2878
  }
@@ -1990,7 +2940,7 @@ function renderCellContent(colDef, entry, col, selectedAttribute, searchAttribut
1990
2940
  }) });
1991
2941
  }
1992
2942
  case "target_attr": {
1993
- const matches = getAllNestedValues(entry, selectedAttribute);
2943
+ const matches = getAllNestedValues(entry, selectedAttribute, col?.attributes);
1994
2944
  if (matches.length === 0) {
1995
2945
  return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral500", style: { fontStyle: "italic" }, children: "(empty / not set)" });
1996
2946
  }
@@ -2110,9 +3060,11 @@ const BulkUpdater = () => {
2110
3060
  const [pendingBatchesLoading, setPendingBatchesLoading] = React.useState(false);
2111
3061
  const [approvingBatchId, setApprovingBatchId] = React.useState(null);
2112
3062
  const [rejectingBatchId, setRejectingBatchId] = React.useState(null);
2113
- const [executingNowBatchId, setExecutingNowBatchId] = React.useState(null);
2114
3063
  const [makerNotesInput, setMakerNotesInput] = React.useState("");
2115
3064
  const [publishOnApproval, setPublishOnApproval] = React.useState(true);
3065
+ const [publishStrategy, setPublishStrategy] = React.useState("safe");
3066
+ const [unreviewedConsent, setUnreviewedConsent] = React.useState(false);
3067
+ const [publishConsent, setPublishConsent] = React.useState(false);
2116
3068
  const [showProcessingDashboard, setShowProcessingDashboard] = React.useState(false);
2117
3069
  const [dashboardBatchId, setDashboardBatchId] = React.useState("");
2118
3070
  const [dashboardJob, setDashboardJob] = React.useState(null);
@@ -2189,10 +3141,32 @@ const BulkUpdater = () => {
2189
3141
  } catch {
2190
3142
  }
2191
3143
  };
3144
+ const [searchRelationOptions, setSearchRelationOptions] = React.useState([]);
3145
+ const [searchRelationOptionsLoading, setSearchRelationOptionsLoading] = React.useState(false);
3146
+ const fetchSearchRelationOptions = async (targetUid) => {
3147
+ try {
3148
+ setSearchRelationOptionsLoading(true);
3149
+ const res = await get(`/bulk-updater/relation-options?targetUid=${encodeURIComponent(targetUid)}`);
3150
+ if (res.data && res.data.success) {
3151
+ setSearchRelationOptions(res.data.options || []);
3152
+ }
3153
+ } catch {
3154
+ setSearchRelationOptions([]);
3155
+ } finally {
3156
+ setSearchRelationOptionsLoading(false);
3157
+ }
3158
+ };
2192
3159
  const [searchAttribute, setSearchAttribute] = React.useState("");
2193
3160
  const [searchAttributeInput, setSearchAttributeInput] = React.useState("");
2194
3161
  const [attributesMeta, setAttributesMeta] = React.useState({});
2195
3162
  const effectiveSearchAttribute = searchAttribute || selectedAttribute;
3163
+ React.useEffect(() => {
3164
+ if (effectiveSearchAttribute && relationsMeta[effectiveSearchAttribute]) {
3165
+ fetchSearchRelationOptions(relationsMeta[effectiveSearchAttribute].targetUid);
3166
+ } else {
3167
+ setSearchRelationOptions([]);
3168
+ }
3169
+ }, [effectiveSearchAttribute, relationsMeta]);
2196
3170
  const [filterOperator, setFilterOperator] = React.useState("none");
2197
3171
  const [filterQuery, setFilterQuery] = React.useState("");
2198
3172
  const [entrySearch, setEntrySearch] = React.useState("");
@@ -2211,6 +3185,8 @@ const BulkUpdater = () => {
2211
3185
  const [collectionColumnVisibility, setCollectionColumnVisibility] = React.useState({});
2212
3186
  const [openColumnPickerUid, setOpenColumnPickerUid] = React.useState(null);
2213
3187
  const [columnSearchTerm, setColumnSearchTerm] = React.useState("");
3188
+ const [openSortPickerUid, setOpenSortPickerUid] = React.useState(null);
3189
+ const [sortSearchTerm, setSortSearchTerm] = React.useState("");
2214
3190
  const toggleColumn = (colUid, colKey, availableCols) => {
2215
3191
  setCollectionColumnVisibility((prev) => {
2216
3192
  const current = prev[colUid] || availableCols.filter((c) => c.defaultVisible).map((c) => c.key);
@@ -2743,21 +3719,45 @@ const BulkUpdater = () => {
2743
3719
  }
2744
3720
  const checkMatch = (val) => {
2745
3721
  const displayStr = formatDisplayValue(val);
2746
- const isValEmpty = val === void 0 || val === null || displayStr.trim() === "" || displayStr.trim().toLowerCase() === "(not set)" || displayStr.trim().toLowerCase() === "(empty / not set)" || displayStr.trim().toLowerCase() === "(empty / unlinked)";
2747
- switch (filterOperator) {
2748
- case "empty":
2749
- return isValEmpty;
2750
- case "not_empty":
2751
- return !isValEmpty;
2752
- case "equals":
2753
- return !isValEmpty && displayStr.trim().toLowerCase() === filterQuery.trim().toLowerCase();
2754
- case "contains":
2755
- return !isValEmpty && displayStr.toLowerCase().includes(filterQuery.trim().toLowerCase());
2756
- case "starts_with":
2757
- return !isValEmpty && displayStr.trim().toLowerCase().startsWith(filterQuery.trim().toLowerCase());
2758
- default:
2759
- return true;
3722
+ const isValEmpty = val === void 0 || val === null || Array.isArray(val) && val.length === 0 || displayStr.trim() === "" || displayStr.trim().toLowerCase() === "(not set)" || displayStr.trim().toLowerCase() === "(empty / not set)" || displayStr.trim().toLowerCase() === "(empty / unlinked)";
3723
+ if (filterOperator === "empty") return isValEmpty;
3724
+ if (filterOperator === "not_empty") return !isValEmpty;
3725
+ if (isValEmpty) return false;
3726
+ const q = filterQuery.trim().toLowerCase();
3727
+ if (!q) return true;
3728
+ const testString = (str) => {
3729
+ if (str === void 0 || str === null) return false;
3730
+ const s = String(str).trim().toLowerCase();
3731
+ switch (filterOperator) {
3732
+ case "equals":
3733
+ return s === q;
3734
+ case "contains":
3735
+ return s.includes(q);
3736
+ case "starts_with":
3737
+ return s.startsWith(q);
3738
+ default:
3739
+ return true;
3740
+ }
3741
+ };
3742
+ const testEntityObject = (obj) => {
3743
+ if (!obj || typeof obj !== "object") return false;
3744
+ if (testString(obj.name)) return true;
3745
+ if (testString(obj.title)) return true;
3746
+ if (testString(obj.email)) return true;
3747
+ if (testString(obj.username)) return true;
3748
+ if (testString(obj.documentId)) return true;
3749
+ if (testString(String(obj.id))) return true;
3750
+ if (testString(obj.slug)) return true;
3751
+ if (testString(formatDisplayValue(obj))) return true;
3752
+ return false;
3753
+ };
3754
+ if (Array.isArray(val)) {
3755
+ return val.some((item) => typeof item === "object" ? testEntityObject(item) : testString(item));
2760
3756
  }
3757
+ if (typeof val === "object" && val !== null) {
3758
+ if (testEntityObject(val)) return true;
3759
+ }
3760
+ return testString(displayStr);
2761
3761
  };
2762
3762
  if (filterOperator === "empty") {
2763
3763
  return matches.every((m) => checkMatch(m.value));
@@ -2869,6 +3869,40 @@ const BulkUpdater = () => {
2869
3869
  });
2870
3870
  return breakdown;
2871
3871
  };
3872
+ const selectedLifecycleStats = React.useMemo(() => {
3873
+ let published = 0;
3874
+ let modified = 0;
3875
+ let draft = 0;
3876
+ let pendingReview = 0;
3877
+ Object.keys(selectedEntries).forEach((key) => {
3878
+ if (!selectedEntries[key]) return;
3879
+ const parts = key.split(KEY_SEP);
3880
+ if (parts.length !== 2) return;
3881
+ const [uid, docId] = parts;
3882
+ const col = collections.find((c) => c.uid === uid) || null;
3883
+ const entry = collectionPages[uid]?.entries?.find((e) => e.documentId === docId || String(e.id) === docId) || col?.entries?.find((e) => e.documentId === docId || String(e.id) === docId);
3884
+ if (col && col.draftAndPublish === false) {
3885
+ published++;
3886
+ } else if (entry) {
3887
+ const { status } = getEntryLifecycleStatus(entry, col, pendingBatches);
3888
+ if (status === "published") published++;
3889
+ else if (status === "modified") modified++;
3890
+ else if (status === "draft") draft++;
3891
+ else if (status === "pending_review") pendingReview++;
3892
+ } else {
3893
+ draft++;
3894
+ }
3895
+ });
3896
+ const unreviewedCount = draft + modified + pendingReview;
3897
+ return {
3898
+ published,
3899
+ modified,
3900
+ draft,
3901
+ pendingReview,
3902
+ unreviewedCount,
3903
+ total: published + modified + draft + pendingReview
3904
+ };
3905
+ }, [selectedEntries, collections, collectionPages, pendingBatches]);
2872
3906
  const handleOpenConfirmModal = () => {
2873
3907
  if (!selectedAttribute) {
2874
3908
  toggleNotification({
@@ -2885,6 +3919,8 @@ const BulkUpdater = () => {
2885
3919
  });
2886
3920
  return;
2887
3921
  }
3922
+ setPublishStrategy("safe");
3923
+ setUnreviewedConsent(false);
2888
3924
  setShowConfirmModal(true);
2889
3925
  };
2890
3926
  const handleOpenPreviewModal = async () => {
@@ -2900,7 +3936,11 @@ const BulkUpdater = () => {
2900
3936
  if (selectedEntries[key]) {
2901
3937
  const parts = key.split(KEY_SEP);
2902
3938
  if (parts.length === 2) {
2903
- targets.push({ uid: parts[0], documentId: parts[1] });
3939
+ const targetObj = { uid: parts[0], documentId: parts[1] };
3940
+ if (selectedDzBlocks[key] && selectedDzBlocks[key].length > 0) {
3941
+ targetObj.dynamicZoneIndexes = selectedDzBlocks[key];
3942
+ }
3943
+ targets.push(targetObj);
2904
3944
  }
2905
3945
  }
2906
3946
  });
@@ -2975,6 +4015,9 @@ const BulkUpdater = () => {
2975
4015
  failedCount: 0,
2976
4016
  isLive: false,
2977
4017
  publishOnApproval,
4018
+ publishStrategy,
4019
+ unreviewedConsent,
4020
+ unreviewedCount: selectedLifecycleStats.unreviewedCount,
2978
4021
  results: []
2979
4022
  });
2980
4023
  const payload = {
@@ -2986,7 +4029,10 @@ const BulkUpdater = () => {
2986
4029
  executionMode,
2987
4030
  makerNotes: makerNotesInput,
2988
4031
  forceImmediate,
2989
- publishOnApproval
4032
+ publishOnApproval,
4033
+ publishStrategy,
4034
+ unreviewedConsent,
4035
+ unreviewedCount: selectedLifecycleStats.unreviewedCount
2990
4036
  };
2991
4037
  const res = await post("/bulk-updater/batch-update", payload);
2992
4038
  setIsSubmittingBatch(false);
@@ -3050,6 +4096,20 @@ const BulkUpdater = () => {
3050
4096
  });
3051
4097
  await fetchModels();
3052
4098
  await fetchAuditHistory();
4099
+ if (selectedCollectionUid) {
4100
+ loadCollectionPage(
4101
+ selectedCollectionUid,
4102
+ collectionPages[selectedCollectionUid]?.page || 1,
4103
+ void 0,
4104
+ void 0,
4105
+ void 0,
4106
+ filterOperator,
4107
+ filterQuery,
4108
+ entrySearch,
4109
+ false,
4110
+ searchAttribute
4111
+ );
4112
+ }
3053
4113
  }
3054
4114
  } else {
3055
4115
  setIsSubmittingBatch(false);
@@ -3140,6 +4200,7 @@ const BulkUpdater = () => {
3140
4200
  });
3141
4201
  return;
3142
4202
  }
4203
+ setPublishConsent(false);
3143
4204
  setShowPublishConfirmModal(true);
3144
4205
  };
3145
4206
  const executeBatchPublish = async (executionMode, forceImmediate = false) => {
@@ -3174,13 +4235,17 @@ const BulkUpdater = () => {
3174
4235
  failedCount: 0,
3175
4236
  isLive: true,
3176
4237
  publishOnApproval: true,
4238
+ unreviewedConsent: publishConsent,
4239
+ unreviewedCount: selectedLifecycleStats.unreviewedCount,
3177
4240
  results: []
3178
4241
  });
3179
4242
  const payload = {
3180
4243
  selectedEntries: targets,
3181
4244
  executionMode,
3182
4245
  makerNotes: publishMakerNotesInput,
3183
- forceImmediate
4246
+ forceImmediate,
4247
+ unreviewedConsent: publishConsent,
4248
+ unreviewedCount: selectedLifecycleStats.unreviewedCount
3184
4249
  };
3185
4250
  const res = await post("/bulk-updater/batch-publish", payload);
3186
4251
  setIsSubmittingBatch(false);
@@ -3761,7 +4826,27 @@ const BulkUpdater = () => {
3761
4826
  ]
3762
4827
  }
3763
4828
  ) }),
3764
- (filterOperator === "equals" || filterOperator === "contains" || filterOperator === "starts_with") && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1, minWidth: 180 }, children: /* @__PURE__ */ jsxRuntime.jsx(
4829
+ (filterOperator === "equals" || filterOperator === "contains" || filterOperator === "starts_with") && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1, minWidth: 200 }, children: effectiveSearchAttribute && relationsMeta[effectiveSearchAttribute] ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
4830
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: `Target ${relationsMeta[effectiveSearchAttribute].targetDisplayName} Relation` }),
4831
+ /* @__PURE__ */ jsxRuntime.jsx(
4832
+ designSystem.Combobox,
4833
+ {
4834
+ autocomplete: { type: "list", filter: "contains" },
4835
+ placeholder: searchRelationOptionsLoading ? "Loading entities..." : `Search & pick ${relationsMeta[effectiveSearchAttribute].targetDisplayName} or type text...`,
4836
+ value: filterQuery,
4837
+ onClear: () => setFilterQuery(""),
4838
+ onChange: (val) => {
4839
+ if (typeof val === "string") {
4840
+ setFilterQuery(val);
4841
+ }
4842
+ },
4843
+ onInputChange: (e) => {
4844
+ setFilterQuery(e.target.value);
4845
+ },
4846
+ children: searchRelationOptions.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: opt.label, textValue: opt.label, children: opt.label }, opt.documentId))
4847
+ }
4848
+ )
4849
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
3765
4850
  designSystem.TextInput,
3766
4851
  {
3767
4852
  label: effectiveSearchAttribute ? `Value for "${effectiveSearchAttribute}"` : "Value to match",
@@ -3911,41 +4996,127 @@ const BulkUpdater = () => {
3911
4996
  ) : null
3912
4997
  ] }),
3913
4998
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "center", wrap: "wrap", children: [
3914
- !col.isSingleType && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
3915
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Sort:" }),
3916
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 190 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
3917
- designSystem.SingleSelect,
4999
+ !col.isSingleType && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { position: "relative", display: "inline-block" }, children: [
5000
+ /* @__PURE__ */ jsxRuntime.jsx(
5001
+ designSystem.Button,
3918
5002
  {
5003
+ variant: openSortPickerUid === col.uid ? "secondary" : "tertiary",
3919
5004
  size: "S",
3920
- value: `${currentSort.field}:${currentSort.order}`,
3921
- onChange: (val) => {
3922
- const [f, o] = String(val).split(":");
3923
- handleCollectionSortChange(col.uid, f, o);
5005
+ onClick: () => {
5006
+ setOpenSortPickerUid(openSortPickerUid === col.uid ? null : col.uid);
5007
+ setSortSearchTerm("");
5008
+ if (openColumnPickerUid === col.uid) setOpenColumnPickerUid(null);
5009
+ },
5010
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, alignItems: "center", children: [
5011
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", fontWeight: "bold", children: "Sort:" }),
5012
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "semiBold", children: getActiveSortLabel(currentSort) })
5013
+ ] })
5014
+ }
5015
+ ),
5016
+ openSortPickerUid === col.uid && /* @__PURE__ */ jsxRuntime.jsxs(
5017
+ designSystem.Box,
5018
+ {
5019
+ style: {
5020
+ position: "absolute",
5021
+ top: "calc(100% + 6px)",
5022
+ left: 0,
5023
+ zIndex: 220,
5024
+ width: 330,
5025
+ maxHeight: 440,
5026
+ background: "#ffffff",
5027
+ borderRadius: 8,
5028
+ boxShadow: "0 10px 25px rgba(33, 33, 52, 0.18)",
5029
+ border: "1px solid #dcdce4",
5030
+ padding: 12,
5031
+ display: "flex",
5032
+ flexDirection: "column",
5033
+ gap: 8
3924
5034
  },
3925
5035
  children: [
3926
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "createdAt:desc", children: "Created: Newest" }),
3927
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "createdAt:asc", children: "Created: Oldest" }),
3928
- hasAttr && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3929
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.SingleSelectOption, { value: `${selectedAttribute}:asc`, children: [
3930
- selectedAttribute,
3931
- " (A → Z)"
3932
- ] }),
3933
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.SingleSelectOption, { value: `${selectedAttribute}:desc`, children: [
3934
- selectedAttribute,
3935
- " (Z → A)"
3936
- ] })
3937
- ] }),
3938
- col.attributes.includes("name") && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3939
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "name:asc", children: "Name (A → Z)" }),
3940
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "name:desc", children: "Name (Z → A)" })
5036
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
5037
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: "Sort by Column" }),
5038
+ /* @__PURE__ */ jsxRuntime.jsx(
5039
+ designSystem.Button,
5040
+ {
5041
+ variant: "ghost",
5042
+ size: "S",
5043
+ onClick: () => setOpenSortPickerUid(null),
5044
+ style: { padding: "2px 6px", height: "auto", minWidth: "unset" },
5045
+ children: "✕"
5046
+ }
5047
+ )
3941
5048
  ] }),
3942
- col.attributes.includes("title") && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3943
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "title:asc", children: "Title (A → Z)" }),
3944
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "title:desc", children: "Title (Z → A)" })
3945
- ] })
5049
+ /* @__PURE__ */ jsxRuntime.jsx(
5050
+ designSystem.TextInput,
5051
+ {
5052
+ size: "S",
5053
+ placeholder: "Search columns to sort...",
5054
+ value: sortSearchTerm,
5055
+ onChange: (e) => setSortSearchTerm(e.target.value)
5056
+ }
5057
+ ),
5058
+ /* @__PURE__ */ jsxRuntime.jsx(
5059
+ designSystem.Box,
5060
+ {
5061
+ style: {
5062
+ maxHeight: 320,
5063
+ overflowY: "auto",
5064
+ display: "flex",
5065
+ flexDirection: "column",
5066
+ gap: 4
5067
+ },
5068
+ children: getSortOptionsForCollection(col).filter((opt) => {
5069
+ if (!sortSearchTerm.trim()) return true;
5070
+ const term = sortSearchTerm.toLowerCase().trim();
5071
+ return opt.label.toLowerCase().includes(term) || opt.field.toLowerCase().includes(term);
5072
+ }).map((opt) => {
5073
+ const isSelected = (currentSort.field === opt.field || currentSort.field === "updatedAt" && opt.field === "updated_at" || currentSort.field === "updated_at" && opt.field === "updatedAt" || currentSort.field === "createdAt" && opt.field === "created_at" || currentSort.field === "created_at" && opt.field === "createdAt") && currentSort.order === opt.order;
5074
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5075
+ designSystem.Box,
5076
+ {
5077
+ onClick: () => {
5078
+ handleCollectionSortChange(col.uid, opt.field, opt.order);
5079
+ setOpenSortPickerUid(null);
5080
+ setSortSearchTerm("");
5081
+ },
5082
+ style: {
5083
+ padding: "6px 10px",
5084
+ borderRadius: 6,
5085
+ cursor: "pointer",
5086
+ background: isSelected ? "rgba(73, 69, 255, 0.12)" : "transparent",
5087
+ border: isSelected ? "1px solid #4945ff" : "1px solid transparent",
5088
+ display: "flex",
5089
+ alignItems: "center",
5090
+ justifyContent: "space-between",
5091
+ transition: "background 0.15s ease"
5092
+ },
5093
+ onMouseEnter: (e) => {
5094
+ if (!isSelected) e.currentTarget.style.background = "#f6f6f9";
5095
+ },
5096
+ onMouseLeave: (e) => {
5097
+ if (!isSelected) e.currentTarget.style.background = "transparent";
5098
+ },
5099
+ children: [
5100
+ /* @__PURE__ */ jsxRuntime.jsx(
5101
+ designSystem.Typography,
5102
+ {
5103
+ variant: "omega",
5104
+ fontWeight: isSelected ? "bold" : "normal",
5105
+ textColor: isSelected ? "primary700" : "neutral800",
5106
+ children: opt.label
5107
+ }
5108
+ ),
5109
+ isSelected && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { size: "S", backgroundColor: "primary100", textColor: "primary700", children: "Active" })
5110
+ ]
5111
+ },
5112
+ `${opt.field}:${opt.order}`
5113
+ );
5114
+ })
5115
+ }
5116
+ )
3946
5117
  ]
3947
5118
  }
3948
- ) })
5119
+ )
3949
5120
  ] }),
3950
5121
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { position: "relative", display: "inline-block" }, children: [
3951
5122
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -3957,6 +5128,7 @@ const BulkUpdater = () => {
3957
5128
  onClick: () => {
3958
5129
  setOpenColumnPickerUid(openColumnPickerUid === col.uid ? null : col.uid);
3959
5130
  setColumnSearchTerm("");
5131
+ if (openSortPickerUid === col.uid) setOpenSortPickerUid(null);
3960
5132
  },
3961
5133
  children: [
3962
5134
  "Columns (",
@@ -4216,10 +5388,13 @@ const BulkUpdater = () => {
4216
5388
  isDzExpanded && dzFields.map((dzKey) => {
4217
5389
  const blocks = entry[dzKey] || [];
4218
5390
  const targetParsed = parseDynamicZonePath(selectedAttribute);
4219
- const allApplicableIndexes = blocks.map((_, i) => i).filter((i) => {
5391
+ const isBlockApplicable = (blockItem) => {
4220
5392
  if (!targetParsed.subPath) return true;
4221
- return getNestedValue(blocks[i], targetParsed.subPath) !== void 0;
4222
- });
5393
+ const scopedPath = `${dzKey}[${blockItem?.__component}].${targetParsed.subPath}`;
5394
+ const componentSupports = (col?.attributes || attributes || []).includes(scopedPath);
5395
+ return componentSupports || getNestedValue(blockItem, targetParsed.subPath) !== void 0;
5396
+ };
5397
+ const allApplicableIndexes = blocks.map((_, i) => i).filter((i) => isBlockApplicable(blocks[i]));
4223
5398
  return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.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__ */ jsxRuntime.jsxs(
4224
5399
  designSystem.Box,
4225
5400
  {
@@ -4256,8 +5431,10 @@ const BulkUpdater = () => {
4256
5431
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: blocks.map((block, bIdx) => {
4257
5432
  const compUid = block.__component;
4258
5433
  const compLabel = formatComponentName(compUid);
5434
+ const scopedPath = `${dzKey}[${compUid}].${targetParsed.subPath}`;
5435
+ const componentSupports = (col?.attributes || attributes || []).includes(scopedPath);
4259
5436
  const blockVal = targetParsed.subPath ? getNestedValue(block, targetParsed.subPath) : void 0;
4260
- const hasTargetAttr = blockVal !== void 0;
5437
+ const hasTargetAttr = componentSupports || blockVal !== void 0;
4261
5438
  const isBlockChecked = hasTargetAttr && isSelected && (selectedDzBlocks[key] !== void 0 ? selectedDzBlocks[key].includes(bIdx) : true);
4262
5439
  return /* @__PURE__ */ jsxRuntime.jsxs(
4263
5440
  designSystem.Tr,
@@ -4577,27 +5754,42 @@ const BulkUpdater = () => {
4577
5754
  )
4578
5755
  ] })
4579
5756
  ] }),
4580
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 4, padding: 4, background: "neutral100", hasRadius: true, style: { border: "1px solid #eaeaef" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: [
4581
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
4582
- /* @__PURE__ */ jsxRuntime.jsx(
4583
- designSystem.Checkbox,
4584
- {
4585
- checked: publishOnApproval,
4586
- onCheckedChange: (checked) => setPublishOnApproval(Boolean(checked)),
4587
- children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children: "Auto-Publish after Update" })
4588
- }
4589
- ),
4590
- /* @__PURE__ */ jsxRuntime.jsx(
4591
- designSystem.Badge,
4592
- {
4593
- backgroundColor: publishOnApproval ? "success100" : "warning100",
4594
- textColor: publishOnApproval ? "success700" : "warning700",
4595
- children: publishOnApproval ? "✓ LIVE PUBLISH" : "DRAFT ONLY"
4596
- }
4597
- )
5757
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginTop: 4, padding: 4, background: "neutral100", hasRadius: true, style: { border: "1px solid #eaeaef" }, children: [
5758
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: [
5759
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
5760
+ /* @__PURE__ */ jsxRuntime.jsx(
5761
+ designSystem.Checkbox,
5762
+ {
5763
+ checked: publishOnApproval,
5764
+ onCheckedChange: (checked) => setPublishOnApproval(Boolean(checked)),
5765
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children: "Auto-Publish after Update" })
5766
+ }
5767
+ ),
5768
+ /* @__PURE__ */ jsxRuntime.jsx(
5769
+ designSystem.Badge,
5770
+ {
5771
+ backgroundColor: publishOnApproval ? "success100" : "warning100",
5772
+ textColor: publishOnApproval ? "success700" : "warning700",
5773
+ children: publishOnApproval ? "✓ LIVE PUBLISH" : "DRAFT ONLY"
5774
+ }
5775
+ )
5776
+ ] }),
5777
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: publishOnApproval ? "When enabled, entries will be published live immediately upon batch execution for draft-enabled content types." : "When disabled, entries will be saved as draft versions without publishing to live visitors." })
4598
5778
  ] }),
4599
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: publishOnApproval ? "When enabled, entries will be published live immediately upon batch execution for draft-enabled content types." : "When disabled, entries will be saved as draft versions without publishing to live visitors." })
4600
- ] }) }),
5779
+ publishOnApproval && selectedLifecycleStats.unreviewedCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 3, padding: 3, background: "warning100", hasRadius: true, style: { border: "1px solid #f5c0b8" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "flex-start", children: [
5780
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: "warning700", children: "⚠️ Unreviewed Content Alert:" }),
5781
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "warning700", children: [
5782
+ selectedLifecycleStats.unreviewedCount,
5783
+ " of ",
5784
+ totalSelectedCount,
5785
+ " selected record(s) contain unreviewed draft changes (",
5786
+ selectedLifecycleStats.draft,
5787
+ " Draft, ",
5788
+ selectedLifecycleStats.modified,
5789
+ " Modified). Publishing them live will push all unreviewed draft content across all fields to live visitors. You can select safe publishing (recommended) or provide explicit consent at review time."
5790
+ ] })
5791
+ ] }) })
5792
+ ] }),
4601
5793
  selectedCol && selectedAttribute && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 4, padding: 3, background: "neutral100", hasRadius: true, style: { border: "1px solid #eaeaef" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
4602
5794
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: "Target Selection:" }),
4603
5795
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
@@ -4774,6 +5966,7 @@ const BulkUpdater = () => {
4774
5966
  '"'
4775
5967
  ] }),
4776
5968
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, alignItems: "center", marginTop: 1, children: [
5969
+ (batch.payload?.action === "csv_import" || batch.id?.startsWith("csv_")) && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: "📊 CSV IMPORT" }),
4777
5970
  /* @__PURE__ */ jsxRuntime.jsx(
4778
5971
  designSystem.Badge,
4779
5972
  {
@@ -4782,6 +5975,12 @@ const BulkUpdater = () => {
4782
5975
  children: batch.summary.publishOnApproval === false ? "DRAFT ONLY" : "AUTO-PUBLISH"
4783
5976
  }
4784
5977
  ),
5978
+ batch.summary.publishStrategy === "safe" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: "SAFE PUBLISH" }),
5979
+ batch.summary.publishStrategy === "all" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: "PUBLISH ALL (CONSENTED)" }),
5980
+ Boolean(batch.summary.unreviewedCount) && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "neutral150", textColor: "neutral700", children: [
5981
+ "Unreviewed: ",
5982
+ batch.summary.unreviewedCount
5983
+ ] }),
4785
5984
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
4786
5985
  "• ",
4787
5986
  batch.summary.totalTargets,
@@ -4929,7 +6128,9 @@ const BulkUpdater = () => {
4929
6128
  rec.skippedCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", style: { marginLeft: 6 }, children: [
4930
6129
  rec.skippedCount,
4931
6130
  " skipped"
4932
- ] })
6131
+ ] }),
6132
+ rec.publishStrategy === "safe" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", style: { marginLeft: 6 }, children: "Safe Publish" }),
6133
+ rec.publishStrategy === "all" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", style: { marginLeft: 6 }, children: "Publish All (Consented)" })
4933
6134
  ] }),
4934
6135
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { minWidth: 120 }, children: rec.isRolledBack ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
4935
6136
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "neutral200", textColor: "neutral700", children: "ROLLED BACK" }),
@@ -5148,6 +6349,31 @@ const BulkUpdater = () => {
5148
6349
  ] })
5149
6350
  ] })
5150
6351
  ] }),
6352
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginBottom: 4, padding: 3, background: "neutral100", hasRadius: true, style: { border: "1px solid rgba(120, 120, 150, 0.15)" }, children: [
6353
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "sigma", textColor: "neutral600", marginBottom: 2, display: "block", children: [
6354
+ "LIFECYCLE STATUS BREAKDOWN (",
6355
+ totalSelectedCount,
6356
+ " Selected)"
6357
+ ] }),
6358
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, wrap: "wrap", children: [
6359
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: [
6360
+ "✓ Published / Approved: ",
6361
+ selectedLifecycleStats.published
6362
+ ] }),
6363
+ selectedLifecycleStats.modified > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
6364
+ "📝 Modified (Unreviewed Revisions): ",
6365
+ selectedLifecycleStats.modified
6366
+ ] }),
6367
+ selectedLifecycleStats.draft > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "neutral200", textColor: "neutral800", children: [
6368
+ "📄 Draft (Never Published): ",
6369
+ selectedLifecycleStats.draft
6370
+ ] }),
6371
+ selectedLifecycleStats.pendingReview > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: [
6372
+ "⏳ Pending Review: ",
6373
+ selectedLifecycleStats.pendingReview
6374
+ ] })
6375
+ ] })
6376
+ ] }),
5151
6377
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginBottom: 4, children: [
5152
6378
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", marginBottom: 2, display: "block", children: "COLLECTION BREAKDOWN" }),
5153
6379
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 1, children: Object.entries(getSelectedBreakdown()).map(([name, count]) => /* @__PURE__ */ jsxRuntime.jsxs(
@@ -5198,6 +6424,128 @@ const BulkUpdater = () => {
5198
6424
  ] })
5199
6425
  }
5200
6426
  ),
6427
+ publishOnApproval && selectedLifecycleStats.unreviewedCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
6428
+ designSystem.Box,
6429
+ {
6430
+ padding: 4,
6431
+ background: "neutral100",
6432
+ hasRadius: true,
6433
+ marginBottom: 4,
6434
+ style: { border: "1px solid #d9d8ff" },
6435
+ children: [
6436
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", marginBottom: 3, display: "block", children: "PUBLISHING STRATEGY & GOVERNANCE CONSENT" }),
6437
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 3, children: [
6438
+ /* @__PURE__ */ jsxRuntime.jsx(
6439
+ designSystem.Box,
6440
+ {
6441
+ padding: 3,
6442
+ hasRadius: true,
6443
+ style: {
6444
+ border: publishStrategy === "safe" ? "2px solid #4945ff" : "1px solid #eaeaef",
6445
+ backgroundColor: publishStrategy === "safe" ? "#f0f0ff" : "#ffffff",
6446
+ cursor: "pointer"
6447
+ },
6448
+ onClick: () => {
6449
+ setPublishStrategy("safe");
6450
+ setUnreviewedConsent(false);
6451
+ },
6452
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "flex-start", children: [
6453
+ /* @__PURE__ */ jsxRuntime.jsx(
6454
+ "input",
6455
+ {
6456
+ type: "radio",
6457
+ id: "strategy-safe",
6458
+ name: "publishStrategy",
6459
+ checked: publishStrategy === "safe",
6460
+ onChange: () => {
6461
+ setPublishStrategy("safe");
6462
+ setUnreviewedConsent(false);
6463
+ },
6464
+ style: { marginTop: 3 }
6465
+ }
6466
+ ),
6467
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
6468
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", display: "block", children: "🛡️ Safe Publishing (Recommended)" }),
6469
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
6470
+ "Only publish records that are already approved & live (",
6471
+ selectedLifecycleStats.published,
6472
+ " record",
6473
+ selectedLifecycleStats.published === 1 ? "" : "s",
6474
+ "). The ",
6475
+ selectedLifecycleStats.unreviewedCount,
6476
+ " unreviewed or draft record(s) will receive the bulk update in Draft mode without publishing unreviewed draft revisions to visitors."
6477
+ ] })
6478
+ ] })
6479
+ ] })
6480
+ }
6481
+ ),
6482
+ /* @__PURE__ */ jsxRuntime.jsx(
6483
+ designSystem.Box,
6484
+ {
6485
+ padding: 3,
6486
+ hasRadius: true,
6487
+ style: {
6488
+ border: publishStrategy === "all" ? "2px solid #d02b20" : "1px solid #eaeaef",
6489
+ backgroundColor: publishStrategy === "all" ? "#fff5f5" : "#ffffff",
6490
+ cursor: "pointer"
6491
+ },
6492
+ onClick: () => setPublishStrategy("all"),
6493
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "flex-start", children: [
6494
+ /* @__PURE__ */ jsxRuntime.jsx(
6495
+ "input",
6496
+ {
6497
+ type: "radio",
6498
+ id: "strategy-all",
6499
+ name: "publishStrategy",
6500
+ checked: publishStrategy === "all",
6501
+ onChange: () => setPublishStrategy("all"),
6502
+ style: { marginTop: 3 }
6503
+ }
6504
+ ),
6505
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
6506
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", textColor: "danger700", display: "block", children: "⚠️ Publish All Selected Records (Includes Unreviewed Drafts)" }),
6507
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
6508
+ "Publish all ",
6509
+ totalSelectedCount,
6510
+ " records live to production immediately, pushing all pending unreviewed draft edits in the ",
6511
+ selectedLifecycleStats.unreviewedCount,
6512
+ " draft/modified record(s) live."
6513
+ ] })
6514
+ ] })
6515
+ ] })
6516
+ }
6517
+ ),
6518
+ publishStrategy === "all" && /* @__PURE__ */ jsxRuntime.jsx(
6519
+ designSystem.Box,
6520
+ {
6521
+ padding: 3,
6522
+ background: "danger100",
6523
+ hasRadius: true,
6524
+ style: { border: "1px solid #f5c0b8" },
6525
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "flex-start", children: [
6526
+ /* @__PURE__ */ jsxRuntime.jsx(
6527
+ designSystem.Checkbox,
6528
+ {
6529
+ name: "unreviewedConsent",
6530
+ checked: unreviewedConsent,
6531
+ onCheckedChange: (checked) => setUnreviewedConsent(Boolean(checked))
6532
+ }
6533
+ ),
6534
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { cursor: "pointer" }, onClick: () => setUnreviewedConsent(!unreviewedConsent), children: [
6535
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", textColor: "danger700", display: "block", children: "Mandatory Risk Consent Required" }),
6536
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "danger700", children: [
6537
+ "I understand and explicitly consent that ",
6538
+ selectedLifecycleStats.unreviewedCount,
6539
+ " record(s) with unreviewed draft revisions or saved draft states will be published live to production visitors."
6540
+ ] })
6541
+ ] })
6542
+ ] })
6543
+ }
6544
+ )
6545
+ ] })
6546
+ ]
6547
+ }
6548
+ ),
5201
6549
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginBottom: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
5202
6550
  designSystem.TextInput,
5203
6551
  {
@@ -5224,54 +6572,61 @@ const BulkUpdater = () => {
5224
6572
  zIndex: 20,
5225
6573
  boxShadow: "0 -4px 16px rgba(0, 0, 0, 0.08)"
5226
6574
  },
5227
- children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", gap: 3, style: { width: "100%" }, children: [
5228
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", size: "M", onClick: () => setShowConfirmModal(false), children: "Cancel" }),
5229
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, wrap: "wrap", justifyContent: "flex-end", alignItems: "center", children: [
5230
- /* @__PURE__ */ jsxRuntime.jsx(
5231
- designSystem.Button,
5232
- {
5233
- variant: executionWindow?.isBusinessHours ? "default" : "secondary",
5234
- size: "M",
5235
- onClick: () => executeBatchWithMode("maker_checker"),
5236
- loading: updating,
5237
- children: "Submit for Checker Approval"
5238
- }
5239
- ),
5240
- executionWindow?.isBusinessHours ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5241
- /* @__PURE__ */ jsxRuntime.jsxs(
6575
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, style: { width: "100%" }, children: [
6576
+ publishOnApproval && selectedLifecycleStats.unreviewedCount > 0 && publishStrategy === "all" && !unreviewedConsent && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "danger700", fontWeight: "bold", children: "⚠️ Explicit consent is required before publishing records with unreviewed draft revisions." }),
6577
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", gap: 3, style: { width: "100%" }, children: [
6578
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", size: "M", onClick: () => setShowConfirmModal(false), children: "Cancel" }),
6579
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, wrap: "wrap", justifyContent: "flex-end", alignItems: "center", children: [
6580
+ /* @__PURE__ */ jsxRuntime.jsx(
5242
6581
  designSystem.Button,
5243
6582
  {
5244
- variant: "secondary",
6583
+ variant: executionWindow?.isBusinessHours ? "default" : "secondary",
5245
6584
  size: "M",
5246
- onClick: () => executeBatchWithMode("queue_offpeak"),
6585
+ onClick: () => executeBatchWithMode("maker_checker"),
5247
6586
  loading: updating,
5248
- children: [
5249
- "Queue for Off-Peak (",
5250
- executionWindow?.offPeakStartTime || "18:00",
5251
- ")"
5252
- ]
6587
+ disabled: updating || publishOnApproval && selectedLifecycleStats.unreviewedCount > 0 && publishStrategy === "all" && !unreviewedConsent,
6588
+ children: "Submit for Checker Approval"
5253
6589
  }
5254
6590
  ),
5255
- /* @__PURE__ */ jsxRuntime.jsx(
6591
+ executionWindow?.isBusinessHours ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6592
+ /* @__PURE__ */ jsxRuntime.jsxs(
6593
+ designSystem.Button,
6594
+ {
6595
+ variant: "secondary",
6596
+ size: "M",
6597
+ onClick: () => executeBatchWithMode("queue_offpeak"),
6598
+ loading: updating,
6599
+ disabled: updating || publishOnApproval && selectedLifecycleStats.unreviewedCount > 0 && publishStrategy === "all" && !unreviewedConsent,
6600
+ children: [
6601
+ "Queue for Off-Peak (",
6602
+ executionWindow?.offPeakStartTime || "18:00",
6603
+ ")"
6604
+ ]
6605
+ }
6606
+ ),
6607
+ /* @__PURE__ */ jsxRuntime.jsx(
6608
+ designSystem.Button,
6609
+ {
6610
+ variant: "danger-light",
6611
+ size: "S",
6612
+ onClick: () => executeBatchWithMode("immediate", true),
6613
+ loading: updating,
6614
+ disabled: updating || publishOnApproval && selectedLifecycleStats.unreviewedCount > 0 && publishStrategy === "all" && !unreviewedConsent,
6615
+ children: "Force Run Now"
6616
+ }
6617
+ )
6618
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
5256
6619
  designSystem.Button,
5257
6620
  {
5258
- variant: "danger-light",
5259
- size: "S",
5260
- onClick: () => executeBatchWithMode("immediate", true),
6621
+ variant: "default",
6622
+ size: "M",
6623
+ onClick: () => executeBatchWithMode("immediate"),
5261
6624
  loading: updating,
5262
- children: "Force Run Now"
6625
+ disabled: updating || publishOnApproval && selectedLifecycleStats.unreviewedCount > 0 && publishStrategy === "all" && !unreviewedConsent,
6626
+ children: "Confirm & Execute Now"
5263
6627
  }
5264
6628
  )
5265
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(
5266
- designSystem.Button,
5267
- {
5268
- variant: "default",
5269
- size: "M",
5270
- onClick: () => executeBatchWithMode("immediate"),
5271
- loading: updating,
5272
- children: "Confirm & Execute Now"
5273
- }
5274
- )
6629
+ ] })
5275
6630
  ] })
5276
6631
  ] })
5277
6632
  }
@@ -5346,6 +6701,27 @@ const BulkUpdater = () => {
5346
6701
  gap: 16
5347
6702
  },
5348
6703
  children: [
6704
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 3, background: "neutral100", hasRadius: true, style: { border: "1px solid #eaeaef" }, children: [
6705
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "sigma", textColor: "neutral600", marginBottom: 2, display: "block", children: [
6706
+ "LIFECYCLE STATUS BREAKDOWN (",
6707
+ totalSelectedCount,
6708
+ " Selected)"
6709
+ ] }),
6710
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, wrap: "wrap", children: [
6711
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: [
6712
+ "✓ Published: ",
6713
+ selectedLifecycleStats.published
6714
+ ] }),
6715
+ selectedLifecycleStats.modified > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
6716
+ "📝 Modified (Unreviewed Revisions): ",
6717
+ selectedLifecycleStats.modified
6718
+ ] }),
6719
+ selectedLifecycleStats.draft > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "neutral200", textColor: "neutral800", children: [
6720
+ "📄 Draft (Never Published): ",
6721
+ selectedLifecycleStats.draft
6722
+ ] })
6723
+ ] })
6724
+ ] }),
5349
6725
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 4, background: "neutral100", hasRadius: true, style: { border: "1px solid #eaeaef" }, children: [
5350
6726
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", children: "Collection Breakdown:" }),
5351
6727
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 2, children: collections.filter(
@@ -5384,6 +6760,37 @@ const BulkUpdater = () => {
5384
6760
  );
5385
6761
  }) })
5386
6762
  ] }),
6763
+ selectedLifecycleStats.unreviewedCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(
6764
+ designSystem.Box,
6765
+ {
6766
+ padding: 4,
6767
+ background: "danger100",
6768
+ hasRadius: true,
6769
+ style: { border: "1px solid #f5c0b8" },
6770
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "flex-start", children: [
6771
+ /* @__PURE__ */ jsxRuntime.jsx(
6772
+ designSystem.Checkbox,
6773
+ {
6774
+ name: "publishConsent",
6775
+ checked: publishConsent,
6776
+ onCheckedChange: (checked) => setPublishConsent(Boolean(checked))
6777
+ }
6778
+ ),
6779
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { cursor: "pointer" }, onClick: () => setPublishConsent(!publishConsent), children: [
6780
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", textColor: "danger700", display: "block", children: "Mandatory Risk Consent Required" }),
6781
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "danger700", children: [
6782
+ "I understand and explicitly consent that ",
6783
+ selectedLifecycleStats.unreviewedCount,
6784
+ " record(s) contain unreviewed draft changes (",
6785
+ selectedLifecycleStats.draft,
6786
+ " Draft, ",
6787
+ selectedLifecycleStats.modified,
6788
+ " Modified) and will be published live to production visitors."
6789
+ ] })
6790
+ ] })
6791
+ ] })
6792
+ }
6793
+ ),
5387
6794
  executionWindow?.isBusinessHours && /* @__PURE__ */ jsxRuntime.jsxs(
5388
6795
  designSystem.Box,
5389
6796
  {
@@ -5428,54 +6835,61 @@ const BulkUpdater = () => {
5428
6835
  flexShrink: 0,
5429
6836
  boxShadow: "0 -4px 16px rgba(0, 0, 0, 0.08)"
5430
6837
  },
5431
- children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", gap: 3, style: { width: "100%" }, children: [
5432
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", size: "M", onClick: () => setShowPublishConfirmModal(false), children: "Cancel" }),
5433
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, wrap: "wrap", justifyContent: "flex-end", alignItems: "center", children: [
5434
- /* @__PURE__ */ jsxRuntime.jsx(
5435
- designSystem.Button,
5436
- {
5437
- variant: executionWindow?.isBusinessHours ? "default" : "secondary",
5438
- size: "M",
5439
- onClick: () => executeBatchPublish("maker_checker"),
5440
- loading: publishing,
5441
- children: "Submit for Checker Approval"
5442
- }
5443
- ),
5444
- executionWindow?.isBusinessHours ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5445
- /* @__PURE__ */ jsxRuntime.jsxs(
6838
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, style: { width: "100%" }, children: [
6839
+ selectedLifecycleStats.unreviewedCount > 0 && !publishConsent && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "danger700", fontWeight: "bold", children: "⚠️ Please check the mandatory consent box above to confirm publishing unreviewed draft records." }),
6840
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", gap: 3, style: { width: "100%" }, children: [
6841
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", size: "M", onClick: () => setShowPublishConfirmModal(false), children: "Cancel" }),
6842
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, wrap: "wrap", justifyContent: "flex-end", alignItems: "center", children: [
6843
+ /* @__PURE__ */ jsxRuntime.jsx(
5446
6844
  designSystem.Button,
5447
6845
  {
5448
- variant: "secondary",
6846
+ variant: executionWindow?.isBusinessHours ? "default" : "secondary",
5449
6847
  size: "M",
5450
- onClick: () => executeBatchPublish("queue_offpeak"),
6848
+ onClick: () => executeBatchPublish("maker_checker"),
5451
6849
  loading: publishing,
5452
- children: [
5453
- "Queue for Off-Peak (",
5454
- executionWindow?.offPeakStartTime || "18:00",
5455
- ")"
5456
- ]
6850
+ disabled: publishing || selectedLifecycleStats.unreviewedCount > 0 && !publishConsent,
6851
+ children: "Submit for Checker Approval"
5457
6852
  }
5458
6853
  ),
5459
- /* @__PURE__ */ jsxRuntime.jsx(
6854
+ executionWindow?.isBusinessHours ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6855
+ /* @__PURE__ */ jsxRuntime.jsxs(
6856
+ designSystem.Button,
6857
+ {
6858
+ variant: "secondary",
6859
+ size: "M",
6860
+ onClick: () => executeBatchPublish("queue_offpeak"),
6861
+ loading: publishing,
6862
+ disabled: publishing || selectedLifecycleStats.unreviewedCount > 0 && !publishConsent,
6863
+ children: [
6864
+ "Queue for Off-Peak (",
6865
+ executionWindow?.offPeakStartTime || "18:00",
6866
+ ")"
6867
+ ]
6868
+ }
6869
+ ),
6870
+ /* @__PURE__ */ jsxRuntime.jsx(
6871
+ designSystem.Button,
6872
+ {
6873
+ variant: "danger-light",
6874
+ size: "S",
6875
+ onClick: () => executeBatchPublish("immediate", true),
6876
+ loading: publishing,
6877
+ disabled: publishing || selectedLifecycleStats.unreviewedCount > 0 && !publishConsent,
6878
+ children: "Force Publish Now"
6879
+ }
6880
+ )
6881
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
5460
6882
  designSystem.Button,
5461
6883
  {
5462
- variant: "danger-light",
5463
- size: "S",
5464
- onClick: () => executeBatchPublish("immediate", true),
6884
+ variant: "default",
6885
+ size: "M",
6886
+ onClick: () => executeBatchPublish("immediate"),
5465
6887
  loading: publishing,
5466
- children: "Force Publish Now"
6888
+ disabled: publishing || selectedLifecycleStats.unreviewedCount > 0 && !publishConsent,
6889
+ children: "Confirm & Publish Now"
5467
6890
  }
5468
6891
  )
5469
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(
5470
- designSystem.Button,
5471
- {
5472
- variant: "default",
5473
- size: "M",
5474
- onClick: () => executeBatchPublish("immediate"),
5475
- loading: publishing,
5476
- children: "Confirm & Publish Now"
5477
- }
5478
- )
6892
+ ] })
5479
6893
  ] })
5480
6894
  ] })
5481
6895
  }
@@ -5498,6 +6912,7 @@ const BulkUpdater = () => {
5498
6912
  previewData,
5499
6913
  attribute: selectedAttribute,
5500
6914
  publishOnApproval,
6915
+ lifecycleStats: selectedLifecycleStats,
5501
6916
  targetValueDisplay: relationsMeta[selectedAttribute] ? selectedRelationDocId === "__clear__" ? "Clear / Unlink Relation" : relationOptions.find((o) => o.documentId === selectedRelationDocId)?.label || selectedRelationDocId || "(none)" : useRegex ? `Replace /${regexPattern}/g with "${attributeValue}"` : attributeValue
5502
6917
  }
5503
6918
  ),
@@ -5518,9 +6933,12 @@ const BulkUpdater = () => {
5518
6933
  exports.default = BulkUpdater;
5519
6934
  exports.formatComponentName = formatComponentName;
5520
6935
  exports.formatDisplayValue = formatDisplayValue;
6936
+ exports.getActiveSortLabel = getActiveSortLabel;
5521
6937
  exports.getAllNestedValues = getAllNestedValues;
5522
6938
  exports.getAvailableColumnsForCollection = getAvailableColumnsForCollection;
5523
6939
  exports.getEntryLifecycleStatus = getEntryLifecycleStatus;
5524
6940
  exports.getNestedValue = getNestedValue;
6941
+ exports.getPropertyValue = getPropertyValue;
6942
+ exports.getSortOptionsForCollection = getSortOptionsForCollection;
5525
6943
  exports.parseDynamicZonePath = parseDynamicZonePath;
5526
- //# sourceMappingURL=BulkUpdater-mSvmiLwc.js.map
6944
+ //# sourceMappingURL=BulkUpdater-C0uPoso2.js.map