strapi-plugin-bulk-updater 1.0.0

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.
@@ -0,0 +1,2274 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const React = require("react");
5
+ const admin = require("@strapi/strapi/admin");
6
+ const designSystem = require("@strapi/design-system");
7
+ const icons = require("@strapi/icons");
8
+ const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
9
+ const React__default = /* @__PURE__ */ _interopDefault(React);
10
+ function getAvailableColumnsForCollection(col, selectedAttribute) {
11
+ const cols = [
12
+ {
13
+ key: "select",
14
+ label: "Select",
15
+ isPermanent: true,
16
+ defaultVisible: true
17
+ },
18
+ {
19
+ key: "identifier",
20
+ label: "Identifier",
21
+ sortable: !col.isSingleType,
22
+ defaultVisible: true
23
+ }
24
+ ];
25
+ const candidateAttrs = col.attributes || [];
26
+ candidateAttrs.forEach((attr) => {
27
+ if (attr === "name" || attr === "title" || attr === selectedAttribute) {
28
+ return;
29
+ }
30
+ const formattedLabel = attr.charAt(0).toUpperCase() + attr.slice(1);
31
+ cols.push({
32
+ key: `attr_${attr}`,
33
+ label: formattedLabel,
34
+ sortable: !col.isSingleType,
35
+ defaultVisible: ["department", "email", "location"].includes(attr)
36
+ });
37
+ });
38
+ cols.push({
39
+ key: "target_attr",
40
+ label: `Current "${selectedAttribute}"`,
41
+ sortable: !col.isSingleType && col.attributes.includes(selectedAttribute),
42
+ defaultVisible: true
43
+ });
44
+ cols.push({
45
+ key: "status",
46
+ label: "Status",
47
+ sortable: false,
48
+ defaultVisible: true
49
+ });
50
+ return cols;
51
+ }
52
+ function renderHeaderCell(colDef, col, currentSort, selectedAttribute, onSort) {
53
+ if (colDef.key === "select") {
54
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { width: 48 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Select" }) }, "select");
55
+ }
56
+ const isCurrentSorted = colDef.key === "identifier" && (currentSort.field === "name" || currentSort.field === "title") || colDef.key === "target_attr" && currentSort.field === selectedAttribute || colDef.key.startsWith("attr_") && currentSort.field === colDef.key.replace("attr_", "");
57
+ const sortArrow = isCurrentSorted ? currentSort.order === "asc" ? "▲" : "▼" : "⇅";
58
+ const handleClick = () => {
59
+ if (!colDef.sortable) return;
60
+ if (colDef.key === "identifier") {
61
+ const field = col.attributes.includes("name") ? "name" : col.attributes.includes("title") ? "title" : "createdAt";
62
+ onSort(col.uid, field);
63
+ } else if (colDef.key === "target_attr") {
64
+ onSort(col.uid, selectedAttribute);
65
+ } else if (colDef.key.startsWith("attr_")) {
66
+ onSort(col.uid, colDef.key.replace("attr_", ""));
67
+ }
68
+ };
69
+ return /* @__PURE__ */ jsxRuntime.jsx(
70
+ designSystem.Th,
71
+ {
72
+ style: { cursor: colDef.sortable ? "pointer" : "default", userSelect: "none" },
73
+ onClick: handleClick,
74
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, alignItems: "center", children: [
75
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: colDef.label }),
76
+ colDef.sortable && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: sortArrow })
77
+ ] })
78
+ },
79
+ colDef.key
80
+ );
81
+ }
82
+ function formatDisplayValue(val) {
83
+ if (val === void 0 || val === null || val === "") {
84
+ return "(empty / not set)";
85
+ }
86
+ if (Array.isArray(val)) {
87
+ if (val.length === 0) return "(empty / unlinked)";
88
+ return val.map(
89
+ (item) => typeof item === "object" && item !== null ? item.name || item.title || item.email || item.username || item.documentId || item.id : String(item)
90
+ ).join(", ");
91
+ }
92
+ if (typeof val === "object" && val !== null) {
93
+ if (val.isRelation && val.display) return val.display;
94
+ return val.name || val.title || val.email || val.username || val.documentId || val.id || JSON.stringify(val);
95
+ }
96
+ return String(val);
97
+ }
98
+ function renderCellContent(colDef, entry, col, selectedAttribute, isMatch, hasAttr, isSelected, onToggleSelect) {
99
+ switch (colDef.key) {
100
+ case "select":
101
+ return /* @__PURE__ */ jsxRuntime.jsx(
102
+ designSystem.Checkbox,
103
+ {
104
+ checked: isSelected,
105
+ onCheckedChange: onToggleSelect
106
+ }
107
+ );
108
+ case "identifier": {
109
+ const mainText = entry.name || entry.title || `Entry #${entry.id || entry.documentId}`;
110
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
111
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: mainText }),
112
+ entry.email && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", display: "block", children: entry.email })
113
+ ] });
114
+ }
115
+ case "target_attr": {
116
+ const val = entry[selectedAttribute];
117
+ const displayStr = formatDisplayValue(val);
118
+ const isUnset = displayStr === "(empty / not set)" || displayStr === "(empty / unlinked)";
119
+ return /* @__PURE__ */ jsxRuntime.jsx(
120
+ designSystem.Typography,
121
+ {
122
+ textColor: isUnset ? "neutral500" : "neutral800",
123
+ style: {
124
+ fontStyle: isUnset ? "italic" : "normal",
125
+ display: "inline-block",
126
+ maxWidth: 260,
127
+ overflow: "hidden",
128
+ textOverflow: "ellipsis",
129
+ whiteSpace: "nowrap"
130
+ },
131
+ children: displayStr
132
+ }
133
+ );
134
+ }
135
+ case "status": {
136
+ if (!hasAttr) {
137
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "neutral200", textColor: "neutral600", children: "Ignored" });
138
+ }
139
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, alignItems: "center", children: [
140
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: "Supported" }),
141
+ isMatch && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: "Matches Filter" })
142
+ ] });
143
+ }
144
+ default: {
145
+ const attrName = colDef.key.replace("attr_", "");
146
+ const rawVal = entry[attrName];
147
+ const displayStr = formatDisplayValue(rawVal);
148
+ return /* @__PURE__ */ jsxRuntime.jsx(
149
+ designSystem.Typography,
150
+ {
151
+ textColor: "neutral700",
152
+ style: { maxWidth: 200, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
153
+ children: displayStr === "(empty / not set)" ? "-" : displayStr
154
+ }
155
+ );
156
+ }
157
+ }
158
+ }
159
+ const BulkUpdater = () => {
160
+ const { get, post } = admin.useFetchClient();
161
+ const { toggleNotification } = admin.useNotification();
162
+ const getInitialTab = () => {
163
+ try {
164
+ const params = new URLSearchParams(window.location.search);
165
+ const t = params.get("tab");
166
+ if (t === "audit" || t === "editor") return t;
167
+ } catch {
168
+ }
169
+ return "editor";
170
+ };
171
+ const [activeTab, setActiveTabState] = React.useState(getInitialTab);
172
+ const setActiveTab = (tab) => {
173
+ setActiveTabState(tab);
174
+ try {
175
+ const url = new URL(window.location.href);
176
+ url.searchParams.set("tab", tab);
177
+ window.history.replaceState({}, "", url.toString());
178
+ } catch {
179
+ }
180
+ };
181
+ const [loading, setLoading] = React.useState(true);
182
+ const [updating, setUpdating] = React.useState(false);
183
+ const [attributes, setAttributes] = React.useState([]);
184
+ const [collections, setCollections] = React.useState([]);
185
+ const [license, setLicense] = React.useState(null);
186
+ const [auditEnabled, setAuditEnabled] = React.useState(false);
187
+ const [executionWindow, setExecutionWindow] = React.useState(null);
188
+ const [showConfirmModal, setShowConfirmModal] = React.useState(false);
189
+ const [pendingBatches, setPendingBatches] = React.useState([]);
190
+ const [pendingBatchesLoading, setPendingBatchesLoading] = React.useState(false);
191
+ const [approvingBatchId, setApprovingBatchId] = React.useState(null);
192
+ const [rejectingBatchId, setRejectingBatchId] = React.useState(null);
193
+ const [executingNowBatchId, setExecutingNowBatchId] = React.useState(null);
194
+ const [makerNotesInput, setMakerNotesInput] = React.useState("");
195
+ const [publishOnApproval, setPublishOnApproval] = React.useState(true);
196
+ const [selectedAttribute, setSelectedAttribute] = React.useState("metaDescription");
197
+ const [attributeValue, setAttributeValue] = React.useState("");
198
+ const [selectedEntries, setSelectedEntries] = React.useState({});
199
+ const [relationsMeta, setRelationsMeta] = React.useState({});
200
+ const [relationOptions, setRelationOptions] = React.useState([]);
201
+ const [relationOptionsLoading, setRelationOptionsLoading] = React.useState(false);
202
+ const [selectedRelationDocId, setSelectedRelationDocId] = React.useState("");
203
+ const [relationMode, setRelationMode] = React.useState("set");
204
+ const fetchRelationOptions = async (targetUid) => {
205
+ try {
206
+ setRelationOptionsLoading(true);
207
+ const res = await get(`/bulk-updater/relation-options?targetUid=${encodeURIComponent(targetUid)}`);
208
+ if (res.data && res.data.success) {
209
+ setRelationOptions(res.data.options || []);
210
+ }
211
+ } catch {
212
+ setRelationOptions([]);
213
+ } finally {
214
+ setRelationOptionsLoading(false);
215
+ }
216
+ };
217
+ const [useRegex, setUseRegex] = React.useState(false);
218
+ const [regexPattern, setRegexPattern] = React.useState("");
219
+ const [filterOperator, setFilterOperator] = React.useState("none");
220
+ const [filterQuery, setFilterQuery] = React.useState("");
221
+ const [hideNonMatching, setHideNonMatching] = React.useState(false);
222
+ const [lastResults, setLastResults] = React.useState(null);
223
+ const [updateSummary, setUpdateSummary] = React.useState(null);
224
+ const [collectionPages, setCollectionPages] = React.useState({});
225
+ const [collectionSearch, setCollectionSearch] = React.useState("");
226
+ const [visibleTypeUids, setVisibleTypeUids] = React.useState({});
227
+ const [defaultPageSize, setDefaultPageSize] = React.useState(10);
228
+ const [collectionSorts, setCollectionSorts] = React.useState({});
229
+ const [collectionColumnVisibility, setCollectionColumnVisibility] = React.useState({});
230
+ const [openColumnPickerUid, setOpenColumnPickerUid] = React.useState(null);
231
+ const toggleColumn = (colUid, colKey, availableCols) => {
232
+ setCollectionColumnVisibility((prev) => {
233
+ const current = prev[colUid] || availableCols.filter((c) => c.defaultVisible).map((c) => c.key);
234
+ let next;
235
+ if (current.includes(colKey)) {
236
+ const remaining = current.filter((k) => k !== colKey);
237
+ if (remaining.filter((k) => k !== "select").length === 0) {
238
+ return prev;
239
+ }
240
+ next = remaining;
241
+ } else {
242
+ next = [...current, colKey];
243
+ }
244
+ return { ...prev, [colUid]: next };
245
+ });
246
+ };
247
+ const showAllColumns = (colUid, availableCols) => {
248
+ setCollectionColumnVisibility((prev) => ({
249
+ ...prev,
250
+ [colUid]: availableCols.map((c) => c.key)
251
+ }));
252
+ };
253
+ const resetDefaultColumns = (colUid, availableCols) => {
254
+ setCollectionColumnVisibility((prev) => ({
255
+ ...prev,
256
+ [colUid]: availableCols.filter((c) => c.defaultVisible).map((c) => c.key)
257
+ }));
258
+ };
259
+ const [auditHistory, setAuditHistory] = React.useState([]);
260
+ const [auditLoading, setAuditLoading] = React.useState(false);
261
+ const [rollingBackBatchId, setRollingBackBatchId] = React.useState(null);
262
+ const [expandedDiffBatchId, setExpandedDiffBatchId] = React.useState(null);
263
+ const [auditNotice, setAuditNotice] = React.useState(null);
264
+ const latestActiveBatch = auditHistory.find((rec) => !rec.isRolledBack);
265
+ const fetchModels = async () => {
266
+ try {
267
+ setLoading(true);
268
+ const res = await get("/bulk-updater/models");
269
+ if (res.data && res.data.success) {
270
+ setAttributes(res.data.attributes || []);
271
+ const cols = res.data.collections || [];
272
+ setCollections(cols);
273
+ setLicense(res.data.license || null);
274
+ setAuditEnabled(Boolean(res.data.auditEnabled));
275
+ if (res.data.executionWindow) {
276
+ setExecutionWindow(res.data.executionWindow);
277
+ }
278
+ const pages = {};
279
+ const initialVisibility = {};
280
+ const initialSorts = {};
281
+ cols.forEach((col) => {
282
+ const total = col.totalEntries !== void 0 ? col.totalEntries : col.entries ? col.entries.length : 0;
283
+ const initialPageSize = col.isSingleType ? 1 : 10;
284
+ pages[col.uid] = {
285
+ page: 1,
286
+ pageSize: initialPageSize,
287
+ total,
288
+ pageCount: col.isSingleType ? 1 : Math.ceil(total / initialPageSize) || 1,
289
+ entries: (col.entries || []).slice(0, initialPageSize),
290
+ loading: false
291
+ };
292
+ initialVisibility[col.uid] = true;
293
+ initialSorts[col.uid] = { field: "createdAt", order: "desc" };
294
+ });
295
+ setCollectionPages(pages);
296
+ setVisibleTypeUids(initialVisibility);
297
+ setCollectionSorts(initialSorts);
298
+ if (res.data.relationsMeta) {
299
+ setRelationsMeta(res.data.relationsMeta);
300
+ }
301
+ const activeAttr = selectedAttribute || res.data.attributes && res.data.attributes[0];
302
+ if (!selectedAttribute && activeAttr) {
303
+ setSelectedAttribute(activeAttr);
304
+ }
305
+ if (activeAttr && res.data.relationsMeta && res.data.relationsMeta[activeAttr]) {
306
+ fetchRelationOptions(res.data.relationsMeta[activeAttr].targetUid);
307
+ }
308
+ }
309
+ } catch (err) {
310
+ toggleNotification({
311
+ type: "danger",
312
+ message: err.message || "Failed to load collections data"
313
+ });
314
+ } finally {
315
+ setLoading(false);
316
+ }
317
+ };
318
+ const loadCollectionPage = async (colUid, targetPage, customPageSize, customSortField, customSortOrder) => {
319
+ const currentPageState = collectionPages[colUid];
320
+ const size = customPageSize || currentPageState?.pageSize || defaultPageSize;
321
+ const currentSort = collectionSorts[colUid] || { field: "createdAt", order: "desc" };
322
+ const sortField = customSortField !== void 0 ? customSortField : currentSort.field;
323
+ const sortOrder = customSortOrder !== void 0 ? customSortOrder : currentSort.order;
324
+ setCollectionPages((prev) => ({
325
+ ...prev,
326
+ [colUid]: {
327
+ ...prev[colUid] || { page: 1, pageSize: size, total: 0, pageCount: 1, entries: [] },
328
+ loading: true
329
+ }
330
+ }));
331
+ try {
332
+ const params = new URLSearchParams({
333
+ page: String(targetPage),
334
+ pageSize: String(size)
335
+ });
336
+ if (selectedAttribute) params.set("attribute", selectedAttribute);
337
+ if (filterOperator && filterOperator !== "none") {
338
+ params.set("filterOperator", filterOperator);
339
+ if (filterQuery) params.set("filterQuery", filterQuery);
340
+ }
341
+ if (sortField) {
342
+ params.set("sortField", sortField);
343
+ params.set("sortOrder", sortOrder);
344
+ }
345
+ const res = await get(`/bulk-updater/collections/${colUid}/entries?${params.toString()}`);
346
+ if (res.data && res.data.success) {
347
+ setCollectionPages((prev) => ({
348
+ ...prev,
349
+ [colUid]: {
350
+ page: res.data.pagination.page,
351
+ pageSize: res.data.pagination.pageSize,
352
+ total: res.data.pagination.total,
353
+ pageCount: res.data.pagination.pageCount,
354
+ entries: res.data.entries || [],
355
+ loading: false
356
+ }
357
+ }));
358
+ }
359
+ } catch (err) {
360
+ toggleNotification({
361
+ type: "danger",
362
+ message: `Failed to load entries for ${colUid}: ${err.message}`
363
+ });
364
+ setCollectionPages((prev) => ({
365
+ ...prev,
366
+ [colUid]: {
367
+ ...prev[colUid] || {},
368
+ loading: false
369
+ }
370
+ }));
371
+ }
372
+ };
373
+ const fetchAuditHistory = async () => {
374
+ try {
375
+ setAuditLoading(true);
376
+ const res = await get("/bulk-updater/audit/history");
377
+ if (res.data) {
378
+ if (res.data.enabled) {
379
+ setAuditHistory(res.data.history || []);
380
+ setAuditNotice(null);
381
+ } else {
382
+ setAuditHistory([]);
383
+ setAuditNotice(res.data.message || "Audit trail is not configured.");
384
+ }
385
+ }
386
+ } catch (err) {
387
+ toggleNotification({
388
+ type: "danger",
389
+ message: err.message || "Failed to fetch audit history."
390
+ });
391
+ } finally {
392
+ setAuditLoading(false);
393
+ }
394
+ };
395
+ const fetchPendingBatches = async () => {
396
+ try {
397
+ setPendingBatchesLoading(true);
398
+ const res = await get("/bulk-updater/pending-batches");
399
+ if (res.data && res.data.success) {
400
+ setPendingBatches(res.data.batches || []);
401
+ if (res.data.executionWindow) {
402
+ setExecutionWindow(res.data.executionWindow);
403
+ }
404
+ }
405
+ } catch {
406
+ } finally {
407
+ setPendingBatchesLoading(false);
408
+ }
409
+ };
410
+ React.useEffect(() => {
411
+ fetchModels();
412
+ fetchAuditHistory();
413
+ fetchPendingBatches();
414
+ }, []);
415
+ React.useEffect(() => {
416
+ if (activeTab === "audit") {
417
+ fetchAuditHistory();
418
+ fetchPendingBatches();
419
+ }
420
+ }, [activeTab]);
421
+ const KEY_SEP = "@@@";
422
+ const handleAttributeChange = (newAttr) => {
423
+ if (newAttr !== selectedAttribute) {
424
+ setSelectedAttribute(newAttr);
425
+ setSelectedEntries({});
426
+ setFilterOperator("none");
427
+ setFilterQuery("");
428
+ setHideNonMatching(false);
429
+ setSelectedRelationDocId("");
430
+ setAttributeValue("");
431
+ if (relationsMeta[newAttr]) {
432
+ fetchRelationOptions(relationsMeta[newAttr].targetUid);
433
+ } else {
434
+ setRelationOptions([]);
435
+ }
436
+ collections.forEach((col) => {
437
+ loadCollectionPage(col.uid, 1);
438
+ });
439
+ }
440
+ };
441
+ const filteredCollections = collections.filter(
442
+ (c) => visibleTypeUids[c.uid] !== false && (!collectionSearch.trim() || c.displayName.toLowerCase().includes(collectionSearch.toLowerCase().trim()) || c.uid.toLowerCase().includes(collectionSearch.toLowerCase().trim()))
443
+ );
444
+ const handleGlobalPageSizeChange = (newSize) => {
445
+ const capped = Math.min(100, Math.max(1, newSize));
446
+ setDefaultPageSize(capped);
447
+ collections.forEach((col) => {
448
+ if (!col.isSingleType) {
449
+ loadCollectionPage(col.uid, 1, capped);
450
+ }
451
+ });
452
+ };
453
+ const handleCollectionPageSizeChange = (colUid, newSize) => {
454
+ const capped = Math.min(100, Math.max(1, newSize));
455
+ loadCollectionPage(colUid, 1, capped);
456
+ };
457
+ const handleCollectionSortChange = (colUid, field, order) => {
458
+ setCollectionSorts((prev) => ({
459
+ ...prev,
460
+ [colUid]: { field, order }
461
+ }));
462
+ loadCollectionPage(colUid, 1, void 0, field, order);
463
+ };
464
+ const handleHeaderSortClick = (colUid, field) => {
465
+ const current = collectionSorts[colUid] || { field: "createdAt", order: "desc" };
466
+ const nextOrder = current.field === field && current.order === "asc" ? "desc" : "asc";
467
+ handleCollectionSortChange(colUid, field, nextOrder);
468
+ };
469
+ const toggleEntry = (uid, documentId) => {
470
+ const key = `${uid}${KEY_SEP}${documentId}`;
471
+ setSelectedEntries((prev) => ({
472
+ ...prev,
473
+ [key]: !prev[key]
474
+ }));
475
+ };
476
+ const entryMatchesFilter = (entry, attr) => {
477
+ if (filterOperator === "none") return true;
478
+ const val = entry[attr];
479
+ const displayStr = formatDisplayValue(val);
480
+ 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)";
481
+ switch (filterOperator) {
482
+ case "empty":
483
+ return isValEmpty;
484
+ case "not_empty":
485
+ return !isValEmpty;
486
+ case "equals":
487
+ return !isValEmpty && displayStr.trim().toLowerCase() === filterQuery.trim().toLowerCase();
488
+ case "contains":
489
+ return !isValEmpty && displayStr.toLowerCase().includes(filterQuery.trim().toLowerCase());
490
+ case "starts_with":
491
+ return !isValEmpty && displayStr.trim().toLowerCase().startsWith(filterQuery.trim().toLowerCase());
492
+ default:
493
+ return true;
494
+ }
495
+ };
496
+ const getVisibleEntries = (col) => {
497
+ const pageState = collectionPages[col.uid];
498
+ const entries = pageState ? pageState.entries : col.entries || [];
499
+ if (!hideNonMatching || filterOperator === "none") {
500
+ return entries;
501
+ }
502
+ return entries.filter((e) => entryMatchesFilter(e, selectedAttribute));
503
+ };
504
+ const matchingEntriesCount = React__default.default.useMemo(() => {
505
+ if (filterOperator === "none") return 0;
506
+ let count = 0;
507
+ filteredCollections.forEach((col) => {
508
+ if (col.attributes.includes(selectedAttribute)) {
509
+ const pageState = collectionPages[col.uid];
510
+ const entries = pageState ? pageState.entries : col.entries || [];
511
+ entries.forEach((entry) => {
512
+ if (entryMatchesFilter(entry, selectedAttribute)) {
513
+ count++;
514
+ }
515
+ });
516
+ }
517
+ });
518
+ return count;
519
+ }, [filteredCollections, collectionPages, selectedAttribute, filterOperator, filterQuery]);
520
+ const selectAllMatching = () => {
521
+ const updated = { ...selectedEntries };
522
+ filteredCollections.forEach((col) => {
523
+ if (col.attributes.includes(selectedAttribute)) {
524
+ const pageState = collectionPages[col.uid];
525
+ const entries = pageState ? pageState.entries : col.entries || [];
526
+ entries.forEach((e) => {
527
+ if (entryMatchesFilter(e, selectedAttribute)) {
528
+ updated[`${col.uid}${KEY_SEP}${e.documentId}`] = true;
529
+ }
530
+ });
531
+ }
532
+ });
533
+ setSelectedEntries(updated);
534
+ };
535
+ const deselectAllMatching = () => {
536
+ const updated = { ...selectedEntries };
537
+ filteredCollections.forEach((col) => {
538
+ if (col.attributes.includes(selectedAttribute)) {
539
+ const pageState = collectionPages[col.uid];
540
+ const entries = pageState ? pageState.entries : col.entries || [];
541
+ entries.forEach((e) => {
542
+ if (entryMatchesFilter(e, selectedAttribute)) {
543
+ delete updated[`${col.uid}${KEY_SEP}${e.documentId}`];
544
+ }
545
+ });
546
+ }
547
+ });
548
+ setSelectedEntries(updated);
549
+ };
550
+ const clearFilter = () => {
551
+ setFilterOperator("none");
552
+ setFilterQuery("");
553
+ setHideNonMatching(false);
554
+ collections.forEach((col) => {
555
+ loadCollectionPage(col.uid, 1);
556
+ });
557
+ };
558
+ const isCollectionFullySelected = (col) => {
559
+ const entries = getVisibleEntries(col);
560
+ if (!entries.length) return false;
561
+ return entries.every((e) => selectedEntries[`${col.uid}${KEY_SEP}${e.documentId}`]);
562
+ };
563
+ const isCollectionPartiallySelected = (col) => {
564
+ const entries = getVisibleEntries(col);
565
+ if (!entries.length) return false;
566
+ const selectedCount = entries.filter(
567
+ (e) => selectedEntries[`${col.uid}${KEY_SEP}${e.documentId}`]
568
+ ).length;
569
+ return selectedCount > 0 && selectedCount < entries.length;
570
+ };
571
+ const toggleCollection = (col) => {
572
+ const entries = getVisibleEntries(col);
573
+ const willSelectAll = !isCollectionFullySelected(col);
574
+ const updated = { ...selectedEntries };
575
+ entries.forEach((e) => {
576
+ const key = `${col.uid}${KEY_SEP}${e.documentId}`;
577
+ if (willSelectAll) {
578
+ updated[key] = true;
579
+ } else {
580
+ delete updated[key];
581
+ }
582
+ });
583
+ setSelectedEntries(updated);
584
+ };
585
+ const selectAll = () => {
586
+ const updated = { ...selectedEntries };
587
+ filteredCollections.forEach((col) => {
588
+ const entries = getVisibleEntries(col);
589
+ entries.forEach((e) => {
590
+ updated[`${col.uid}${KEY_SEP}${e.documentId}`] = true;
591
+ });
592
+ });
593
+ setSelectedEntries(updated);
594
+ };
595
+ const deselectAll = () => {
596
+ setSelectedEntries({});
597
+ };
598
+ const getSelectedBreakdown = () => {
599
+ const breakdown = {};
600
+ Object.keys(selectedEntries).forEach((key) => {
601
+ if (selectedEntries[key]) {
602
+ const parts = key.split(KEY_SEP);
603
+ if (parts.length === 2) {
604
+ const uid = parts[0];
605
+ const col = collections.find((c) => c.uid === uid);
606
+ const name = col?.displayName || uid;
607
+ breakdown[name] = (breakdown[name] || 0) + 1;
608
+ }
609
+ }
610
+ });
611
+ return breakdown;
612
+ };
613
+ const handleOpenConfirmModal = () => {
614
+ if (!selectedAttribute) {
615
+ toggleNotification({
616
+ type: "warning",
617
+ message: "Please select an attribute from the dropdown"
618
+ });
619
+ return;
620
+ }
621
+ if (useRegex && !license?.isPro) {
622
+ toggleNotification({
623
+ type: "warning",
624
+ message: "Regex replacement is a Pro feature. Please activate your license in Settings."
625
+ });
626
+ return;
627
+ }
628
+ const totalSelected = Object.values(selectedEntries).filter(Boolean).length;
629
+ if (totalSelected === 0) {
630
+ toggleNotification({
631
+ type: "warning",
632
+ message: "Please select at least one collection type or entry to update"
633
+ });
634
+ return;
635
+ }
636
+ setShowConfirmModal(true);
637
+ };
638
+ const executeBatchWithMode = async (executionMode, forceImmediate = false) => {
639
+ const targets = [];
640
+ Object.keys(selectedEntries).forEach((key) => {
641
+ if (selectedEntries[key]) {
642
+ const parts = key.split(KEY_SEP);
643
+ if (parts.length === 2) {
644
+ targets.push({ uid: parts[0], documentId: parts[1] });
645
+ }
646
+ }
647
+ });
648
+ try {
649
+ setUpdating(true);
650
+ setShowConfirmModal(false);
651
+ setLastResults(null);
652
+ const payload = {
653
+ attribute: selectedAttribute,
654
+ value: attributeValue,
655
+ selectedEntries: targets,
656
+ isRegex: useRegex,
657
+ regexPattern,
658
+ executionMode,
659
+ makerNotes: makerNotesInput,
660
+ forceImmediate,
661
+ publishOnApproval
662
+ };
663
+ const res = await post("/bulk-updater/batch-update", payload);
664
+ if (res.data && res.data.success) {
665
+ if (res.data.pending) {
666
+ toggleNotification({
667
+ type: "success",
668
+ message: res.data.message || "Batch submitted successfully."
669
+ });
670
+ await fetchPendingBatches();
671
+ setActiveTab("audit");
672
+ } else {
673
+ setLastResults(res.data.results || []);
674
+ setUpdateSummary({
675
+ updatedCount: res.data.updatedCount || 0,
676
+ skippedCount: res.data.skippedCount || 0,
677
+ batchId: res.data.batchId
678
+ });
679
+ toggleNotification({
680
+ type: "success",
681
+ message: `Bulk update complete! Batch ID: ${res.data.batchId}. Updated: ${res.data.updatedCount}, Skipped: ${res.data.skippedCount}`
682
+ });
683
+ await fetchModels();
684
+ }
685
+ } else {
686
+ toggleNotification({
687
+ type: "danger",
688
+ message: res.data?.error || "Batch update encountered an error"
689
+ });
690
+ }
691
+ } catch (err) {
692
+ toggleNotification({
693
+ type: "danger",
694
+ message: err.response?.data?.error || err.message || "Batch update failed"
695
+ });
696
+ } finally {
697
+ setUpdating(false);
698
+ setMakerNotesInput("");
699
+ }
700
+ };
701
+ const handleApproveBatch = async (batchId) => {
702
+ try {
703
+ setApprovingBatchId(batchId);
704
+ const res = await post(`/bulk-updater/batches/${batchId}/approve`, { executeImmediately: true });
705
+ if (res.data && res.data.success) {
706
+ toggleNotification({
707
+ type: "success",
708
+ message: res.data.message || `Batch ${batchId} approved and executed successfully!`
709
+ });
710
+ await fetchPendingBatches();
711
+ await fetchAuditHistory();
712
+ await fetchModels();
713
+ } else {
714
+ toggleNotification({
715
+ type: "danger",
716
+ message: res.data?.error || "Failed to approve batch"
717
+ });
718
+ }
719
+ } catch (err) {
720
+ toggleNotification({
721
+ type: "danger",
722
+ message: err.response?.data?.error || err.message || "Failed to approve batch"
723
+ });
724
+ } finally {
725
+ setApprovingBatchId(null);
726
+ }
727
+ };
728
+ const handleRejectBatch = async (batchId) => {
729
+ const reason = window.prompt("Enter reason for rejection:", "Does not meet release criteria") || "Rejected by Checker";
730
+ try {
731
+ setRejectingBatchId(batchId);
732
+ const res = await post(`/bulk-updater/batches/${batchId}/reject`, { reason });
733
+ if (res.data && res.data.success) {
734
+ toggleNotification({
735
+ type: "info",
736
+ message: `Batch ${batchId} rejected: ${reason}`
737
+ });
738
+ await fetchPendingBatches();
739
+ } else {
740
+ toggleNotification({
741
+ type: "danger",
742
+ message: res.data?.error || "Failed to reject batch"
743
+ });
744
+ }
745
+ } catch (err) {
746
+ toggleNotification({
747
+ type: "danger",
748
+ message: err.response?.data?.error || err.message || "Failed to reject batch"
749
+ });
750
+ } finally {
751
+ setRejectingBatchId(null);
752
+ }
753
+ };
754
+ const handleExecuteNow = async (batchId) => {
755
+ try {
756
+ setExecutingNowBatchId(batchId);
757
+ const res = await post(`/bulk-updater/batches/${batchId}/execute-now`);
758
+ if (res.data && res.data.success) {
759
+ toggleNotification({
760
+ type: "success",
761
+ message: `Batch ${batchId} executed immediately!`
762
+ });
763
+ await fetchPendingBatches();
764
+ await fetchAuditHistory();
765
+ await fetchModels();
766
+ } else {
767
+ toggleNotification({
768
+ type: "danger",
769
+ message: res.data?.error || "Failed to execute batch"
770
+ });
771
+ }
772
+ } catch (err) {
773
+ toggleNotification({
774
+ type: "danger",
775
+ message: err.response?.data?.error || err.message || "Failed to execute batch"
776
+ });
777
+ } finally {
778
+ setExecutingNowBatchId(null);
779
+ }
780
+ };
781
+ const handleRollback = async (batchId) => {
782
+ if (!window.confirm(`Are you sure you want to roll back all changes made in batch "${batchId}"?`)) {
783
+ return;
784
+ }
785
+ try {
786
+ setRollingBackBatchId(batchId);
787
+ const res = await post(`/bulk-updater/audit/${batchId}/rollback`);
788
+ if (res.data && res.data.success) {
789
+ toggleNotification({
790
+ type: "success",
791
+ message: `Batch "${batchId}" rolled back successfully! Reverted ${res.data.revertedCount} entries.`
792
+ });
793
+ await fetchAuditHistory();
794
+ await fetchModels();
795
+ } else {
796
+ toggleNotification({
797
+ type: "danger",
798
+ message: res.data?.error || "Rollback failed."
799
+ });
800
+ }
801
+ } catch (err) {
802
+ toggleNotification({
803
+ type: "danger",
804
+ message: err.response?.data?.error || err.message || "Rollback encountered an error."
805
+ });
806
+ } finally {
807
+ setRollingBackBatchId(null);
808
+ }
809
+ };
810
+ const totalSelectedCount = Object.values(selectedEntries).filter(Boolean).length;
811
+ const rootRef = React__default.default.useRef(null);
812
+ React.useEffect(() => {
813
+ if (rootRef.current && rootRef.current.parentElement) {
814
+ rootRef.current.parentElement.style.minWidth = "0";
815
+ rootRef.current.parentElement.style.maxWidth = "100%";
816
+ }
817
+ }, []);
818
+ return /* @__PURE__ */ jsxRuntime.jsxs(
819
+ designSystem.Box,
820
+ {
821
+ ref: rootRef,
822
+ className: "bulk-updater-root",
823
+ padding: 8,
824
+ background: "neutral100",
825
+ style: { width: "100%", maxWidth: "100%", minWidth: 0, boxSizing: "border-box" },
826
+ children: [
827
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
828
+ /* Prevent page-level horizontal overflow under any circumstance */
829
+ html, body {
830
+ overflow-x: hidden !important;
831
+ max-width: 100vw !important;
832
+ }
833
+ div:has(> .bulk-updater-root) {
834
+ min-width: 0 !important;
835
+ max-width: 100% !important;
836
+ width: 100% !important;
837
+ flex: 1 1 0% !important;
838
+ overflow-x: clip !important;
839
+ }
840
+ .bulk-updater-root {
841
+ min-width: 0 !important;
842
+ max-width: 100% !important;
843
+ width: 100% !important;
844
+ box-sizing: border-box !important;
845
+ overflow-x: clip !important;
846
+ }
847
+ /* All tables stretch to take the full available container space */
848
+ .bulk-updater-root table {
849
+ width: 100% !important;
850
+ min-width: 0 !important;
851
+ }
852
+ .bulk-updater-root div:has(> table) {
853
+ width: 100% !important;
854
+ max-width: 100% !important;
855
+ min-width: 0 !important;
856
+ }
857
+ /* Table scroll container keeps horizontal scroll strictly at the table level */
858
+ .bulk-updater-table-container {
859
+ width: 100% !important;
860
+ max-width: 100% !important;
861
+ min-width: 0 !important;
862
+ overflow-x: auto !important;
863
+ -webkit-overflow-scrolling: touch;
864
+ }
865
+ .bulk-updater-table-container::-webkit-scrollbar {
866
+ height: 8px;
867
+ }
868
+ .bulk-updater-table-container::-webkit-scrollbar-track {
869
+ background: rgba(120, 120, 150, 0.1);
870
+ border-radius: 4px;
871
+ }
872
+ .bulk-updater-table-container::-webkit-scrollbar-thumb {
873
+ background: rgba(120, 120, 150, 0.4);
874
+ border-radius: 4px;
875
+ }
876
+ .bulk-updater-table-container::-webkit-scrollbar-thumb:hover {
877
+ background: rgba(120, 120, 150, 0.7);
878
+ }
879
+ ` }),
880
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "flex-start", gap: 2, marginBottom: 6, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", width: "100%", children: [
881
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
882
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "center", children: [
883
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", fontWeight: "bold", children: "Bulk Attribute Updater" }),
884
+ /* @__PURE__ */ jsxRuntime.jsx(
885
+ designSystem.Badge,
886
+ {
887
+ backgroundColor: license?.isPro ? "success100" : "neutral200",
888
+ textColor: license?.isPro ? "success700" : "neutral700",
889
+ children: license?.isPro ? `${license.tier.toUpperCase()} EDITION` : "COMMUNITY EDITION"
890
+ }
891
+ ),
892
+ auditEnabled && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: "AUDIT & ROLLBACK ACTIVE" })
893
+ ] }),
894
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", children: "Cross-collection batch attribute management with declarative audit trail and one-click rollback." })
895
+ ] }),
896
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
897
+ designSystem.Button,
898
+ {
899
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowClockwise, {}),
900
+ variant: "tertiary",
901
+ onClick: activeTab === "editor" ? fetchModels : fetchAuditHistory,
902
+ loading: loading || auditLoading,
903
+ children: "Refresh"
904
+ }
905
+ ) })
906
+ ] }) }),
907
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, marginBottom: 4, wrap: "wrap", justifyContent: "space-between", alignItems: "center", children: [
908
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
909
+ /* @__PURE__ */ jsxRuntime.jsx(
910
+ designSystem.Button,
911
+ {
912
+ variant: activeTab === "editor" ? "default" : "tertiary",
913
+ onClick: () => setActiveTab("editor"),
914
+ size: "M",
915
+ children: "Bulk Updater Workspace"
916
+ }
917
+ ),
918
+ /* @__PURE__ */ jsxRuntime.jsxs(
919
+ designSystem.Button,
920
+ {
921
+ variant: activeTab === "audit" ? "default" : "tertiary",
922
+ onClick: () => setActiveTab("audit"),
923
+ size: "M",
924
+ children: [
925
+ "Audit Trail & Approvals",
926
+ pendingBatches.filter((b) => b.status === "pending_approval" || b.status === "queued_offpeak").length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", style: { marginLeft: 8 }, children: [
927
+ pendingBatches.filter((b) => b.status === "pending_approval" || b.status === "queued_offpeak").length,
928
+ " PENDING"
929
+ ] }) : auditHistory.length > 0 ? ` (${auditHistory.length})` : ""
930
+ ]
931
+ }
932
+ )
933
+ ] }),
934
+ executionWindow && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, alignItems: "center", children: /* @__PURE__ */ jsxRuntime.jsx(
935
+ designSystem.Badge,
936
+ {
937
+ backgroundColor: executionWindow.isBusinessHours ? "warning100" : "success100",
938
+ textColor: executionWindow.isBusinessHours ? "warning700" : "success700",
939
+ children: executionWindow.isBusinessHours ? `PEAK HOURS (${executionWindow.businessHours?.start} - ${executionWindow.businessHours?.end} ${executionWindow.timeZone || ""}) • OFF-PEAK QUEUE ACTIVE` : `OFF-PEAK WINDOW ACTIVE (${executionWindow.timeZone || ""}) • DIRECT EXECUTION PERMITTED`
940
+ }
941
+ ) })
942
+ ] }),
943
+ activeTab === "editor" && latestActiveBatch && !updateSummary && /* @__PURE__ */ jsxRuntime.jsx(
944
+ designSystem.Box,
945
+ {
946
+ padding: 3,
947
+ paddingLeft: 4,
948
+ paddingRight: 4,
949
+ background: "neutral0",
950
+ shadow: "filterShadow",
951
+ hasRadius: true,
952
+ marginBottom: 4,
953
+ style: { borderLeft: "4px solid #4945ff" },
954
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 2, children: [
955
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
956
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: "ACTIVE BATCH" }),
957
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral700", children: [
958
+ /* @__PURE__ */ jsxRuntime.jsx("code", { children: latestActiveBatch.batchId }),
959
+ " (",
960
+ latestActiveBatch.recordCount,
961
+ " entries updated by ",
962
+ latestActiveBatch.userEmail,
963
+ ")"
964
+ ] })
965
+ ] }),
966
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
967
+ /* @__PURE__ */ jsxRuntime.jsx(
968
+ designSystem.Button,
969
+ {
970
+ variant: "danger-light",
971
+ size: "S",
972
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowClockwise, {}),
973
+ onClick: () => handleRollback(latestActiveBatch.batchId),
974
+ loading: rollingBackBatchId === latestActiveBatch.batchId,
975
+ children: "Rollback This Batch"
976
+ }
977
+ ),
978
+ /* @__PURE__ */ jsxRuntime.jsx(
979
+ designSystem.Button,
980
+ {
981
+ variant: "tertiary",
982
+ size: "S",
983
+ onClick: () => setActiveTab("audit"),
984
+ children: "View Rollback History →"
985
+ }
986
+ )
987
+ ] })
988
+ ] })
989
+ }
990
+ ),
991
+ activeTab === "editor" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
992
+ /* @__PURE__ */ jsxRuntime.jsxs(
993
+ designSystem.Box,
994
+ {
995
+ padding: 6,
996
+ background: "neutral0",
997
+ shadow: "filterShadow",
998
+ hasRadius: true,
999
+ marginBottom: 6,
1000
+ style: { width: "100%", maxWidth: "100%", minWidth: 0, boxSizing: "border-box" },
1001
+ children: [
1002
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", marginBottom: 4, fontWeight: "semiBold", children: "1. Configure Attribute Update" }),
1003
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, alignItems: "flex-end", wrap: "wrap", children: [
1004
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { minWidth: 260 }, children: /* @__PURE__ */ jsxRuntime.jsx(
1005
+ designSystem.SingleSelect,
1006
+ {
1007
+ label: "Attribute to Update",
1008
+ placeholder: "Select an attribute...",
1009
+ value: selectedAttribute,
1010
+ onChange: (val) => handleAttributeChange(String(val)),
1011
+ children: attributes.map((attr) => {
1012
+ const rel = relationsMeta[attr];
1013
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.SingleSelectOption, { value: attr, children: [
1014
+ attr,
1015
+ " ",
1016
+ rel ? `(relation ➔ ${rel.targetDisplayName})` : ""
1017
+ ] }, attr);
1018
+ })
1019
+ }
1020
+ ) }),
1021
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1, minWidth: 340 }, children: relationsMeta[selectedAttribute] ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 1, style: { width: "100%" }, children: [
1022
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
1023
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral700", children: [
1024
+ "Target ",
1025
+ relationsMeta[selectedAttribute].targetDisplayName,
1026
+ " Relation:"
1027
+ ] }),
1028
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: [
1029
+ relationsMeta[selectedAttribute].relationType,
1030
+ " → ",
1031
+ relationsMeta[selectedAttribute].targetDisplayName
1032
+ ] }),
1033
+ relationsMeta[selectedAttribute].isMulti && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: "Multi-Relation" })
1034
+ ] }),
1035
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", style: { width: "100%" }, children: [
1036
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1, minWidth: 200 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
1037
+ designSystem.SingleSelect,
1038
+ {
1039
+ label: `Choose ${relationsMeta[selectedAttribute].targetDisplayName}`,
1040
+ placeholder: relationOptionsLoading ? "Loading entities..." : `Pick a ${relationsMeta[selectedAttribute].targetDisplayName}...`,
1041
+ value: selectedRelationDocId,
1042
+ onChange: (val) => {
1043
+ const docId = String(val);
1044
+ setSelectedRelationDocId(docId);
1045
+ if (docId === "__clear__") {
1046
+ setAttributeValue(JSON.stringify({ mode: "clear", label: "(clear relation)" }));
1047
+ } else {
1048
+ const opt = relationOptions.find((o) => o.documentId === docId);
1049
+ setAttributeValue(JSON.stringify({ mode: relationMode, documentIds: [docId], label: opt?.label || docId }));
1050
+ }
1051
+ },
1052
+ children: [
1053
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "__clear__", children: "-- Clear / Unlink Relation --" }),
1054
+ relationOptions.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: opt.documentId, children: opt.label }, opt.documentId))
1055
+ ]
1056
+ }
1057
+ ) }),
1058
+ relationsMeta[selectedAttribute].isMulti && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 170 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
1059
+ designSystem.SingleSelect,
1060
+ {
1061
+ label: "Action Mode",
1062
+ value: relationMode,
1063
+ onChange: (val) => {
1064
+ const m = String(val);
1065
+ setRelationMode(m);
1066
+ if (selectedRelationDocId) {
1067
+ if (selectedRelationDocId === "__clear__" || m === "clear") {
1068
+ setAttributeValue(JSON.stringify({ mode: "clear", label: "(clear relation)" }));
1069
+ } else {
1070
+ const opt = relationOptions.find((o) => o.documentId === selectedRelationDocId);
1071
+ setAttributeValue(JSON.stringify({ mode: m, documentIds: [selectedRelationDocId], label: opt?.label || selectedRelationDocId }));
1072
+ }
1073
+ }
1074
+ },
1075
+ children: [
1076
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "set", children: "Set / Overwrite" }),
1077
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "connect", children: "Add / Connect" }),
1078
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "disconnect", children: "Remove / Disconnect" }),
1079
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "clear", children: "Clear All" })
1080
+ ]
1081
+ }
1082
+ ) })
1083
+ ] })
1084
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
1085
+ designSystem.TextInput,
1086
+ {
1087
+ label: useRegex ? "Replacement Value" : "New Attribute Value",
1088
+ name: "attributeValue",
1089
+ placeholder: useRegex ? "Replace matched pattern with..." : "Enter the value to apply...",
1090
+ value: attributeValue,
1091
+ onChange: (e) => setAttributeValue(e.target.value)
1092
+ }
1093
+ ) }),
1094
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { children: /* @__PURE__ */ jsxRuntime.jsxs(
1095
+ designSystem.Button,
1096
+ {
1097
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Play, {}),
1098
+ variant: "default",
1099
+ size: "L",
1100
+ onClick: handleOpenConfirmModal,
1101
+ loading: updating,
1102
+ disabled: loading || totalSelectedCount === 0,
1103
+ children: [
1104
+ "Update Selected (",
1105
+ totalSelectedCount,
1106
+ ")"
1107
+ ]
1108
+ }
1109
+ ) })
1110
+ ] }),
1111
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginTop: 4, padding: 4, background: "neutral100", hasRadius: true, children: [
1112
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: [
1113
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
1114
+ /* @__PURE__ */ jsxRuntime.jsx(
1115
+ designSystem.Checkbox,
1116
+ {
1117
+ checked: useRegex,
1118
+ onCheckedChange: (checked) => setUseRegex(Boolean(checked)),
1119
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children: "Enable Regex Find & Replace" })
1120
+ }
1121
+ ),
1122
+ /* @__PURE__ */ jsxRuntime.jsx(
1123
+ designSystem.Badge,
1124
+ {
1125
+ backgroundColor: license?.isPro ? "success100" : "warning100",
1126
+ textColor: license?.isPro ? "success700" : "warning700",
1127
+ children: license?.isPro ? "PRO UNLOCKED" : "PRO FEATURE"
1128
+ }
1129
+ )
1130
+ ] }),
1131
+ useRegex && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1, minWidth: 240 }, children: /* @__PURE__ */ jsxRuntime.jsx(
1132
+ designSystem.TextInput,
1133
+ {
1134
+ placeholder: "Regex pattern (e.g. \\b(OldName)\\b)...",
1135
+ value: regexPattern,
1136
+ onChange: (e) => setRegexPattern(e.target.value),
1137
+ disabled: !license?.isPro
1138
+ }
1139
+ ) })
1140
+ ] }),
1141
+ !license?.isPro && useRegex && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 2, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "warning700", children: "Regex replacement is locked to Pro license holders. Visit Settings > Bulk Updater to activate your key." }) })
1142
+ ] }),
1143
+ selectedAttribute && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 4, padding: 3, background: "neutral100", hasRadius: true, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
1144
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: "Attribute Info:" }),
1145
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", children: [
1146
+ "Targeting attribute ",
1147
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: selectedAttribute }),
1148
+ "."
1149
+ ] }),
1150
+ collections.map((c) => {
1151
+ const hasAttr = c.attributes.includes(selectedAttribute);
1152
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1153
+ designSystem.Badge,
1154
+ {
1155
+ backgroundColor: hasAttr ? "success100" : "neutral200",
1156
+ textColor: hasAttr ? "success700" : "neutral600",
1157
+ children: [
1158
+ c.displayName,
1159
+ ": ",
1160
+ hasAttr ? "Attribute exists" : "Not in schema"
1161
+ ]
1162
+ },
1163
+ c.uid
1164
+ );
1165
+ })
1166
+ ] }) })
1167
+ ]
1168
+ }
1169
+ ),
1170
+ /* @__PURE__ */ jsxRuntime.jsxs(
1171
+ designSystem.Box,
1172
+ {
1173
+ padding: 6,
1174
+ background: "neutral0",
1175
+ shadow: "filterShadow",
1176
+ hasRadius: true,
1177
+ marginBottom: 6,
1178
+ style: { width: "100%", maxWidth: "100%", minWidth: 0, boxSizing: "border-box" },
1179
+ children: [
1180
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, wrap: "wrap", gap: 3, children: [
1181
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
1182
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", fontWeight: "semiBold", children: "2. Select Targets" }),
1183
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Pick entire collection types or individual entries to apply the update to." })
1184
+ ] }),
1185
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
1186
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "secondary", size: "S", onClick: selectAll, children: "Select All" }),
1187
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", size: "S", onClick: deselectAll, children: "Deselect All" })
1188
+ ] })
1189
+ ] }),
1190
+ /* @__PURE__ */ jsxRuntime.jsx(
1191
+ designSystem.Box,
1192
+ {
1193
+ padding: 4,
1194
+ background: "neutral100",
1195
+ hasRadius: true,
1196
+ marginBottom: 5,
1197
+ style: { width: "100%", maxWidth: "100%", boxSizing: "border-box" },
1198
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 3, children: [
1199
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 2, children: [
1200
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
1201
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", children: "Find Targets by Current Value:" }),
1202
+ filterOperator !== "none" && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
1203
+ matchingEntriesCount,
1204
+ " matching ",
1205
+ matchingEntriesCount === 1 ? "entry" : "entries"
1206
+ ] })
1207
+ ] }),
1208
+ filterOperator !== "none" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "ghost", size: "S", onClick: clearFilter, children: "Clear Filter" })
1209
+ ] }),
1210
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "flex-end", wrap: "wrap", children: [
1211
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 240, minWidth: 200 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
1212
+ designSystem.SingleSelect,
1213
+ {
1214
+ label: `Filter condition for "${selectedAttribute}"`,
1215
+ value: filterOperator,
1216
+ onChange: (val) => setFilterOperator(String(val)),
1217
+ children: [
1218
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "none", children: "Show All (No Filter)" }),
1219
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "empty", children: "Is Empty or Null" }),
1220
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "not_empty", children: "Is Not Empty" }),
1221
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "equals", children: "Equals Exact Text" }),
1222
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "contains", children: "Contains Substring" }),
1223
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "starts_with", children: "Starts With" })
1224
+ ]
1225
+ }
1226
+ ) }),
1227
+ (filterOperator === "equals" || filterOperator === "contains" || filterOperator === "starts_with") && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1, minWidth: 220 }, children: /* @__PURE__ */ jsxRuntime.jsx(
1228
+ designSystem.TextInput,
1229
+ {
1230
+ label: "Value to Search",
1231
+ placeholder: "Type value to match...",
1232
+ value: filterQuery,
1233
+ onChange: (e) => setFilterQuery(e.target.value)
1234
+ }
1235
+ ) }),
1236
+ filterOperator !== "none" && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
1237
+ /* @__PURE__ */ jsxRuntime.jsxs(
1238
+ designSystem.Button,
1239
+ {
1240
+ variant: "default",
1241
+ size: "S",
1242
+ onClick: selectAllMatching,
1243
+ disabled: matchingEntriesCount === 0,
1244
+ children: [
1245
+ "Select Matching (",
1246
+ matchingEntriesCount,
1247
+ ")"
1248
+ ]
1249
+ }
1250
+ ),
1251
+ /* @__PURE__ */ jsxRuntime.jsx(
1252
+ designSystem.Button,
1253
+ {
1254
+ variant: "tertiary",
1255
+ size: "S",
1256
+ onClick: deselectAllMatching,
1257
+ disabled: matchingEntriesCount === 0,
1258
+ children: "Deselect Matching"
1259
+ }
1260
+ )
1261
+ ] })
1262
+ ] }),
1263
+ filterOperator !== "none" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, alignItems: "center", marginTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(
1264
+ designSystem.Checkbox,
1265
+ {
1266
+ checked: hideNonMatching,
1267
+ onCheckedChange: (checked) => setHideNonMatching(Boolean(checked)),
1268
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral700", children: "Only display matching entries in tables (hide non-matching)" })
1269
+ }
1270
+ ) })
1271
+ ] })
1272
+ }
1273
+ ),
1274
+ collections.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
1275
+ designSystem.Box,
1276
+ {
1277
+ padding: 4,
1278
+ background: "neutral100",
1279
+ hasRadius: true,
1280
+ marginBottom: 4,
1281
+ style: { width: "100%", maxWidth: "100%", boxSizing: "border-box" },
1282
+ children: [
1283
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 2, marginBottom: 3, children: [
1284
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
1285
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", children: "Filter Visible Content Types:" }),
1286
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
1287
+ filteredCollections.length,
1288
+ " of ",
1289
+ collections.length,
1290
+ " visible"
1291
+ ] })
1292
+ ] }),
1293
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
1294
+ /* @__PURE__ */ jsxRuntime.jsx(
1295
+ designSystem.Button,
1296
+ {
1297
+ variant: "tertiary",
1298
+ size: "S",
1299
+ onClick: () => {
1300
+ const allVis = {};
1301
+ collections.forEach((c) => allVis[c.uid] = true);
1302
+ setVisibleTypeUids(allVis);
1303
+ },
1304
+ children: "Show All Types"
1305
+ }
1306
+ ),
1307
+ /* @__PURE__ */ jsxRuntime.jsx(
1308
+ designSystem.Button,
1309
+ {
1310
+ variant: "tertiary",
1311
+ size: "S",
1312
+ onClick: () => {
1313
+ const allVis = {};
1314
+ collections.forEach((c) => allVis[c.uid] = false);
1315
+ setVisibleTypeUids(allVis);
1316
+ },
1317
+ children: "Hide All Types"
1318
+ }
1319
+ )
1320
+ ] })
1321
+ ] }),
1322
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { wrap: "wrap", gap: 2, alignItems: "center", children: collections.map((col) => {
1323
+ const isVisible = visibleTypeUids[col.uid] !== false;
1324
+ return /* @__PURE__ */ jsxRuntime.jsx(
1325
+ designSystem.Box,
1326
+ {
1327
+ padding: 2,
1328
+ paddingLeft: 3,
1329
+ paddingRight: 3,
1330
+ background: isVisible ? "neutral0" : "neutral200",
1331
+ hasRadius: true,
1332
+ borderStyle: "solid",
1333
+ borderWidth: "1px",
1334
+ borderColor: isVisible ? "primary200" : "neutral300",
1335
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
1336
+ /* @__PURE__ */ jsxRuntime.jsx(
1337
+ designSystem.Checkbox,
1338
+ {
1339
+ checked: isVisible,
1340
+ onCheckedChange: (checked) => setVisibleTypeUids((prev) => ({ ...prev, [col.uid]: Boolean(checked) })),
1341
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: isVisible ? "semiBold" : "regular", children: col.displayName })
1342
+ }
1343
+ ),
1344
+ col.isSingleType ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", size: "S", children: "Single Type" }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "neutral150", textColor: "neutral600", size: "S", children: "Collection" })
1345
+ ] })
1346
+ },
1347
+ col.uid
1348
+ );
1349
+ }) })
1350
+ ]
1351
+ }
1352
+ ),
1353
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, wrap: "wrap", gap: 3, children: [
1354
+ collections.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1, maxWidth: 360, minWidth: 220 }, children: /* @__PURE__ */ jsxRuntime.jsx(
1355
+ designSystem.TextInput,
1356
+ {
1357
+ label: "Search Content Types",
1358
+ placeholder: "Search by name or UID...",
1359
+ value: collectionSearch,
1360
+ onChange: (e) => setCollectionSearch(e.target.value)
1361
+ }
1362
+ ) }),
1363
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", style: { marginLeft: "auto" }, children: [
1364
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Default page size (max 100):" }),
1365
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 120 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
1366
+ designSystem.SingleSelect,
1367
+ {
1368
+ size: "S",
1369
+ value: String(defaultPageSize),
1370
+ onChange: (val) => handleGlobalPageSizeChange(Number(val)),
1371
+ children: [
1372
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "5", children: "5 / page" }),
1373
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "10", children: "10 / page" }),
1374
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "25", children: "25 / page" }),
1375
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "50", children: "50 / page" }),
1376
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "100", children: "100 / page" })
1377
+ ]
1378
+ }
1379
+ ) })
1380
+ ] })
1381
+ ] }),
1382
+ filteredCollections.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 6, background: "neutral100", hasRadius: true, style: { textAlign: "center" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "No content types match the visibility or search filters." }) }) : filteredCollections.map((col) => {
1383
+ const pageState = collectionPages[col.uid];
1384
+ const entries = pageState ? pageState.entries : col.entries || [];
1385
+ const totalEntries = pageState ? pageState.total : col.totalEntries !== void 0 ? col.totalEntries : entries.length;
1386
+ const page = pageState ? pageState.page : 1;
1387
+ const pageCount = pageState ? pageState.pageCount : 1;
1388
+ const pageSize = pageState ? pageState.pageSize : defaultPageSize;
1389
+ const hasAttr = col.attributes.includes(selectedAttribute);
1390
+ const visibleEntries = getVisibleEntries(col);
1391
+ const matchingColCount = entries.filter((e) => entryMatchesFilter(e, selectedAttribute)).length;
1392
+ const isFullySelected = isCollectionFullySelected(col);
1393
+ const isPartiallySelected = isCollectionPartiallySelected(col);
1394
+ const currentSort = collectionSorts[col.uid] || { field: "createdAt", order: "desc" };
1395
+ const availableColumns = getAvailableColumnsForCollection(col, selectedAttribute);
1396
+ const savedColKeys = collectionColumnVisibility[col.uid];
1397
+ const activeColumns = savedColKeys ? availableColumns.filter((c) => c.isPermanent || savedColKeys.includes(c.key)) : availableColumns.filter((c) => c.defaultVisible);
1398
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1399
+ designSystem.Box,
1400
+ {
1401
+ marginBottom: 5,
1402
+ padding: 4,
1403
+ hasRadius: true,
1404
+ borderStyle: "solid",
1405
+ borderWidth: "1px",
1406
+ borderColor: "neutral200",
1407
+ background: "neutral0",
1408
+ style: { width: "100%", maxWidth: "100%", minWidth: 0, boxSizing: "border-box" },
1409
+ children: [
1410
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 3, wrap: "wrap", gap: 2, children: [
1411
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "center", wrap: "wrap", children: [
1412
+ /* @__PURE__ */ jsxRuntime.jsx(
1413
+ designSystem.Checkbox,
1414
+ {
1415
+ checked: isFullySelected,
1416
+ indeterminate: isPartiallySelected ? true : void 0,
1417
+ onCheckedChange: () => toggleCollection(col),
1418
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "beta", fontWeight: "bold", children: [
1419
+ col.displayName,
1420
+ " ",
1421
+ col.isSingleType ? "(Single Type)" : "Collection"
1422
+ ] })
1423
+ }
1424
+ ),
1425
+ col.isSingleType ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: "Single Type" }) : /* @__PURE__ */ jsxRuntime.jsx(
1426
+ designSystem.Badge,
1427
+ {
1428
+ backgroundColor: hasAttr ? "success100" : "warning100",
1429
+ textColor: hasAttr ? "success700" : "warning700",
1430
+ children: hasAttr ? `Schema has "${selectedAttribute}"` : `Schema lacks "${selectedAttribute}" (will skip)`
1431
+ }
1432
+ )
1433
+ ] }),
1434
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "center", wrap: "wrap", children: [
1435
+ !col.isSingleType && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
1436
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Sort:" }),
1437
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 190 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
1438
+ designSystem.SingleSelect,
1439
+ {
1440
+ size: "S",
1441
+ value: `${currentSort.field}:${currentSort.order}`,
1442
+ onChange: (val) => {
1443
+ const [f, o] = String(val).split(":");
1444
+ handleCollectionSortChange(col.uid, f, o);
1445
+ },
1446
+ children: [
1447
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "createdAt:desc", children: "Created: Newest" }),
1448
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "createdAt:asc", children: "Created: Oldest" }),
1449
+ hasAttr && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1450
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.SingleSelectOption, { value: `${selectedAttribute}:asc`, children: [
1451
+ selectedAttribute,
1452
+ " (A → Z)"
1453
+ ] }),
1454
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.SingleSelectOption, { value: `${selectedAttribute}:desc`, children: [
1455
+ selectedAttribute,
1456
+ " (Z → A)"
1457
+ ] })
1458
+ ] }),
1459
+ col.attributes.includes("name") && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1460
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "name:asc", children: "Name (A → Z)" }),
1461
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "name:desc", children: "Name (Z → A)" })
1462
+ ] }),
1463
+ col.attributes.includes("title") && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1464
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "title:asc", children: "Title (A → Z)" }),
1465
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "title:desc", children: "Title (Z → A)" })
1466
+ ] })
1467
+ ]
1468
+ }
1469
+ ) })
1470
+ ] }),
1471
+ /* @__PURE__ */ jsxRuntime.jsxs(
1472
+ designSystem.Button,
1473
+ {
1474
+ variant: openColumnPickerUid === col.uid ? "secondary" : "tertiary",
1475
+ size: "S",
1476
+ onClick: () => setOpenColumnPickerUid(openColumnPickerUid === col.uid ? null : col.uid),
1477
+ children: [
1478
+ "Columns (",
1479
+ activeColumns.filter((c) => !c.isPermanent).length,
1480
+ "/",
1481
+ availableColumns.filter((c) => !c.isPermanent).length,
1482
+ ")"
1483
+ ]
1484
+ }
1485
+ ),
1486
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: col.isSingleType ? "1 single entry" : filterOperator !== "none" ? `${matchingColCount} matching of ${totalEntries} entries` : `${totalEntries} entries` })
1487
+ ] })
1488
+ ] }),
1489
+ openColumnPickerUid === col.uid && /* @__PURE__ */ jsxRuntime.jsx(
1490
+ designSystem.Box,
1491
+ {
1492
+ padding: 3,
1493
+ marginBottom: 3,
1494
+ background: "neutral100",
1495
+ hasRadius: true,
1496
+ borderColor: "neutral200",
1497
+ style: { border: "1px solid #dcdce4" },
1498
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: [
1499
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "center", wrap: "wrap", children: [
1500
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral700", children: [
1501
+ "Columns for ",
1502
+ col.displayName,
1503
+ ":"
1504
+ ] }),
1505
+ availableColumns.filter((c) => !c.isPermanent).map((c) => {
1506
+ const isChecked = activeColumns.some((ac) => ac.key === c.key);
1507
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(
1508
+ designSystem.Checkbox,
1509
+ {
1510
+ checked: isChecked,
1511
+ onCheckedChange: () => toggleColumn(col.uid, c.key, availableColumns),
1512
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: c.label })
1513
+ }
1514
+ ) }, c.key);
1515
+ })
1516
+ ] }),
1517
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
1518
+ /* @__PURE__ */ jsxRuntime.jsx(
1519
+ designSystem.Button,
1520
+ {
1521
+ variant: "tertiary",
1522
+ size: "S",
1523
+ onClick: () => showAllColumns(col.uid, availableColumns),
1524
+ children: "Show All"
1525
+ }
1526
+ ),
1527
+ /* @__PURE__ */ jsxRuntime.jsx(
1528
+ designSystem.Button,
1529
+ {
1530
+ variant: "tertiary",
1531
+ size: "S",
1532
+ onClick: () => resetDefaultColumns(col.uid, availableColumns),
1533
+ children: "Reset"
1534
+ }
1535
+ ),
1536
+ /* @__PURE__ */ jsxRuntime.jsx(
1537
+ designSystem.Button,
1538
+ {
1539
+ variant: "ghost",
1540
+ size: "S",
1541
+ onClick: () => setOpenColumnPickerUid(null),
1542
+ children: "Close"
1543
+ }
1544
+ )
1545
+ ] })
1546
+ ] })
1547
+ }
1548
+ ),
1549
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { className: "bulk-updater-table-container", style: { width: "100%", maxWidth: "100%", minWidth: 0, overflowX: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: activeColumns.length, rowCount: visibleEntries.length + 1, style: { width: "100%" }, children: [
1550
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: activeColumns.map(
1551
+ (colDef) => renderHeaderCell(colDef, col, currentSort, selectedAttribute, handleHeaderSortClick)
1552
+ ) }) }),
1553
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: visibleEntries.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { colSpan: activeColumns.length, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 4, style: { textAlign: "center" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: "No entries in this content type match the active filter criteria." }) }) }) }) : visibleEntries.map((entry) => {
1554
+ const key = `${col.uid}${KEY_SEP}${entry.documentId}`;
1555
+ const isSelected = Boolean(selectedEntries[key]);
1556
+ const isMatch = filterOperator !== "none" && entryMatchesFilter(entry, selectedAttribute);
1557
+ return /* @__PURE__ */ jsxRuntime.jsx(
1558
+ designSystem.Tr,
1559
+ {
1560
+ style: {
1561
+ backgroundColor: isSelected ? "rgba(73, 69, 255, 0.14)" : isMatch ? "rgba(46, 213, 115, 0.08)" : void 0,
1562
+ boxShadow: isSelected ? "inset 3px 0 0 0 #4945ff" : isMatch ? "inset 3px 0 0 0 #2ed573" : void 0
1563
+ },
1564
+ children: activeColumns.map((colDef) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: renderCellContent(
1565
+ colDef,
1566
+ entry,
1567
+ col,
1568
+ selectedAttribute,
1569
+ isMatch,
1570
+ hasAttr,
1571
+ isSelected,
1572
+ () => toggleEntry(col.uid, entry.documentId)
1573
+ ) }, colDef.key))
1574
+ },
1575
+ key
1576
+ );
1577
+ }) })
1578
+ ] }) }),
1579
+ !col.isSingleType && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginTop: 4, wrap: "wrap", gap: 3, children: [
1580
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "center", wrap: "wrap", children: [
1581
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
1582
+ "Showing ",
1583
+ totalEntries === 0 ? 0 : Math.min((page - 1) * pageSize + 1, totalEntries),
1584
+ " to ",
1585
+ Math.min(page * pageSize, totalEntries),
1586
+ " of ",
1587
+ totalEntries,
1588
+ " entries"
1589
+ ] }),
1590
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, alignItems: "center", children: [
1591
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Show:" }),
1592
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 80 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
1593
+ designSystem.SingleSelect,
1594
+ {
1595
+ size: "S",
1596
+ value: String(pageSize),
1597
+ onChange: (val) => handleCollectionPageSizeChange(col.uid, Number(val)),
1598
+ children: [
1599
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "5", children: "5" }),
1600
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "10", children: "10" }),
1601
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "25", children: "25" }),
1602
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "50", children: "50" }),
1603
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "100", children: "100" })
1604
+ ]
1605
+ }
1606
+ ) }),
1607
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: "per page" })
1608
+ ] })
1609
+ ] }),
1610
+ pageCount > 1 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
1611
+ /* @__PURE__ */ jsxRuntime.jsx(
1612
+ designSystem.Button,
1613
+ {
1614
+ variant: "tertiary",
1615
+ size: "S",
1616
+ disabled: page <= 1 || pageState?.loading,
1617
+ onClick: () => loadCollectionPage(col.uid, page - 1),
1618
+ children: "← Previous"
1619
+ }
1620
+ ),
1621
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", fontWeight: "bold", textColor: "neutral800", children: [
1622
+ "Page ",
1623
+ page,
1624
+ " of ",
1625
+ pageCount
1626
+ ] }),
1627
+ /* @__PURE__ */ jsxRuntime.jsx(
1628
+ designSystem.Button,
1629
+ {
1630
+ variant: "tertiary",
1631
+ size: "S",
1632
+ disabled: page >= pageCount || pageState?.loading,
1633
+ onClick: () => loadCollectionPage(col.uid, page + 1),
1634
+ children: "Next →"
1635
+ }
1636
+ )
1637
+ ] })
1638
+ ] })
1639
+ ]
1640
+ },
1641
+ col.uid
1642
+ );
1643
+ })
1644
+ ]
1645
+ }
1646
+ ),
1647
+ updateSummary && /* @__PURE__ */ jsxRuntime.jsxs(
1648
+ designSystem.Box,
1649
+ {
1650
+ padding: 6,
1651
+ background: "neutral0",
1652
+ shadow: "filterShadow",
1653
+ hasRadius: true,
1654
+ marginBottom: 6,
1655
+ children: [
1656
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", fontWeight: "semiBold", marginBottom: 3, children: "3. Execution Results" }),
1657
+ /* @__PURE__ */ jsxRuntime.jsx(
1658
+ designSystem.Alert,
1659
+ {
1660
+ title: "Batch Update Completed",
1661
+ variant: updateSummary.updatedCount > 0 ? "success" : "default",
1662
+ closeLabel: "Close",
1663
+ onClose: () => setUpdateSummary(null),
1664
+ marginBottom: 4,
1665
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", gap: 3, width: "100%", children: [
1666
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "omega", children: [
1667
+ "Successfully updated ",
1668
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: updateSummary.updatedCount }),
1669
+ " records. Skipped ",
1670
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: updateSummary.skippedCount }),
1671
+ " records.",
1672
+ updateSummary.batchId && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1673
+ " ",
1674
+ "Transaction Batch ID: ",
1675
+ /* @__PURE__ */ jsxRuntime.jsx("code", { children: updateSummary.batchId }),
1676
+ " (logged to audit trail)."
1677
+ ] })
1678
+ ] }),
1679
+ updateSummary.batchId && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, marginTop: 1, children: [
1680
+ /* @__PURE__ */ jsxRuntime.jsx(
1681
+ designSystem.Button,
1682
+ {
1683
+ variant: "danger-light",
1684
+ size: "S",
1685
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowClockwise, {}),
1686
+ onClick: () => handleRollback(updateSummary.batchId),
1687
+ loading: rollingBackBatchId === updateSummary.batchId,
1688
+ children: "Rollback This Batch"
1689
+ }
1690
+ ),
1691
+ /* @__PURE__ */ jsxRuntime.jsx(
1692
+ designSystem.Button,
1693
+ {
1694
+ variant: "tertiary",
1695
+ size: "S",
1696
+ onClick: () => setActiveTab("audit"),
1697
+ children: "View in Audit Trail & Rollback History →"
1698
+ }
1699
+ )
1700
+ ] })
1701
+ ] })
1702
+ }
1703
+ ),
1704
+ lastResults && lastResults.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: 4, rowCount: lastResults.length + 1, children: [
1705
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
1706
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Collection" }) }),
1707
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Entry" }) }),
1708
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Status" }) }),
1709
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Details" }) })
1710
+ ] }) }),
1711
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: lastResults.map((res, idx) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
1712
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children: res.collectionName }) }),
1713
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: res.label }) }),
1714
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Td, { children: [
1715
+ res.status === "updated" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: "Updated" }),
1716
+ res.status === "skipped" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: "Skipped" }),
1717
+ res.status === "error" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "danger100", textColor: "danger700", children: "Error" })
1718
+ ] }),
1719
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral700", children: res.message }) })
1720
+ ] }, idx)) })
1721
+ ] })
1722
+ ]
1723
+ }
1724
+ )
1725
+ ] }),
1726
+ activeTab === "audit" && /* @__PURE__ */ jsxRuntime.jsxs(
1727
+ designSystem.Box,
1728
+ {
1729
+ padding: 6,
1730
+ background: "neutral0",
1731
+ shadow: "filterShadow",
1732
+ hasRadius: true,
1733
+ style: { width: "100%", maxWidth: "100%", minWidth: 0, boxSizing: "border-box" },
1734
+ children: [
1735
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, children: [
1736
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
1737
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", fontWeight: "semiBold", children: "Audit Trail & Version History" }),
1738
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Deterministic record of all batch updates, timestamps, admin authorship, and one-click rollback." })
1739
+ ] }),
1740
+ /* @__PURE__ */ jsxRuntime.jsx(
1741
+ designSystem.Button,
1742
+ {
1743
+ variant: "tertiary",
1744
+ size: "S",
1745
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowClockwise, {}),
1746
+ onClick: () => {
1747
+ fetchAuditHistory();
1748
+ fetchPendingBatches();
1749
+ },
1750
+ loading: auditLoading || pendingBatchesLoading,
1751
+ children: "Refresh History"
1752
+ }
1753
+ )
1754
+ ] }),
1755
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginBottom: 6, padding: 5, background: "neutral100", hasRadius: true, style: { border: "1px solid #e6e6ef" }, children: [
1756
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 3, children: [
1757
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
1758
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
1759
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", fontWeight: "bold", children: "Pending Approvals & Queued Batches" }),
1760
+ pendingBatches.filter((b) => b.status === "pending_approval" || b.status === "queued_offpeak").length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: [
1761
+ pendingBatches.filter((b) => b.status === "pending_approval" || b.status === "queued_offpeak").length,
1762
+ " Action Required"
1763
+ ] })
1764
+ ] }),
1765
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Four-Eyes Maker-Checker authorization queue and off-peak scheduled maintenance batches." })
1766
+ ] }),
1767
+ /* @__PURE__ */ jsxRuntime.jsx(
1768
+ designSystem.Button,
1769
+ {
1770
+ variant: "tertiary",
1771
+ size: "S",
1772
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowClockwise, {}),
1773
+ onClick: fetchPendingBatches,
1774
+ loading: pendingBatchesLoading,
1775
+ children: "Refresh Queue"
1776
+ }
1777
+ )
1778
+ ] }),
1779
+ pendingBatches.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 4, background: "neutral0", hasRadius: true, style: { textAlign: "center" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "No batches pending approval or queued for off-peak execution." }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { className: "bulk-updater-table-container", style: { width: "100%", maxWidth: "100%", minWidth: 0, overflowX: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: 6, rowCount: pendingBatches.length + 1, style: { width: "100%" }, children: [
1780
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
1781
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { minWidth: 160 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Batch ID & Created" }) }),
1782
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { minWidth: 130 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Maker" }) }),
1783
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { minWidth: 200 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Target Details" }) }),
1784
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { minWidth: 150 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Status" }) }),
1785
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { minWidth: 180 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Notes" }) }),
1786
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { minWidth: 180, textAlign: "right" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Actions" }) })
1787
+ ] }) }),
1788
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: pendingBatches.map((batch) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
1789
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Td, { children: [
1790
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children: batch.id }),
1791
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", display: "block", children: new Date(batch.createdAt).toLocaleString() })
1792
+ ] }),
1793
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: batch.createdBy?.email || "admin" }) }),
1794
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Td, { children: [
1795
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "omega", fontWeight: "semiBold", children: [
1796
+ batch.summary.attribute,
1797
+ ': "',
1798
+ formatDisplayValue(batch.summary.value),
1799
+ '"'
1800
+ ] }),
1801
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, alignItems: "center", marginTop: 1, children: [
1802
+ /* @__PURE__ */ jsxRuntime.jsx(
1803
+ designSystem.Badge,
1804
+ {
1805
+ backgroundColor: batch.summary.publishOnApproval === false ? "warning100" : "success100",
1806
+ textColor: batch.summary.publishOnApproval === false ? "warning700" : "success700",
1807
+ children: batch.summary.publishOnApproval === false ? "DRAFT ONLY" : "AUTO-PUBLISH"
1808
+ }
1809
+ ),
1810
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
1811
+ "• ",
1812
+ batch.summary.totalTargets,
1813
+ " records across ",
1814
+ Object.keys(batch.summary.collectionBreakdown || {}).length,
1815
+ " collections"
1816
+ ] })
1817
+ ] })
1818
+ ] }),
1819
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Td, { children: [
1820
+ batch.status === "pending_approval" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: "PENDING CHECKER APPROVAL" }),
1821
+ batch.status === "queued_offpeak" && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
1822
+ "QUEUED OFF-PEAK (",
1823
+ batch.scheduledFor || "18:00",
1824
+ ")"
1825
+ ] }),
1826
+ batch.status === "approved" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: "APPROVED" }),
1827
+ batch.status === "rejected" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "danger100", textColor: "danger700", children: "REJECTED" }),
1828
+ batch.status === "executed" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "neutral200", textColor: "neutral700", children: "EXECUTED" })
1829
+ ] }),
1830
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral700", children: [
1831
+ batch.makerNotes || "—",
1832
+ batch.approval?.rejectionReason && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { color: "#d9534f", display: "block" }, children: [
1833
+ "Rejection Reason: ",
1834
+ batch.approval.rejectionReason
1835
+ ] })
1836
+ ] }) }),
1837
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { textAlign: "right" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, justifyContent: "flex-end", children: [
1838
+ batch.status === "pending_approval" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1839
+ /* @__PURE__ */ jsxRuntime.jsx(
1840
+ designSystem.Button,
1841
+ {
1842
+ size: "S",
1843
+ variant: "success-light",
1844
+ onClick: () => handleApproveBatch(batch.id),
1845
+ loading: approvingBatchId === batch.id,
1846
+ children: "Approve & Execute"
1847
+ }
1848
+ ),
1849
+ /* @__PURE__ */ jsxRuntime.jsx(
1850
+ designSystem.Button,
1851
+ {
1852
+ size: "S",
1853
+ variant: "danger-light",
1854
+ onClick: () => handleRejectBatch(batch.id),
1855
+ loading: rejectingBatchId === batch.id,
1856
+ children: "Reject"
1857
+ }
1858
+ )
1859
+ ] }),
1860
+ batch.status === "queued_offpeak" && /* @__PURE__ */ jsxRuntime.jsx(
1861
+ designSystem.Button,
1862
+ {
1863
+ size: "S",
1864
+ variant: "default",
1865
+ onClick: () => handleExecuteNow(batch.id),
1866
+ loading: executingNowBatchId === batch.id,
1867
+ children: "Run Now"
1868
+ }
1869
+ )
1870
+ ] }) })
1871
+ ] }, batch.id)) })
1872
+ ] }) })
1873
+ ] }),
1874
+ auditNotice && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Alert, { variant: "warning", title: "Audit Configuration Status", marginBottom: 4, children: auditNotice }),
1875
+ auditHistory.length === 0 && !auditLoading && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 8, background: "neutral100", hasRadius: true, style: { textAlign: "center" }, children: [
1876
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", textColor: "neutral600", children: "No batch operations recorded in the audit trail yet." }),
1877
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", display: "block", marginTop: 2, children: "When you execute a bulk update in the editor, its transactional diff will be logged here." })
1878
+ ] }),
1879
+ auditHistory.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { className: "bulk-updater-table-container", style: { width: "100%", maxWidth: "100%", minWidth: 0, overflowX: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: 6, rowCount: auditHistory.length + 1, style: { width: "100%" }, children: [
1880
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
1881
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { minWidth: 140 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Date & Time" }) }),
1882
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { minWidth: 130 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Performed By" }) }),
1883
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { minWidth: 160 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Attribute & Value" }) }),
1884
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { minWidth: 90 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Impact" }) }),
1885
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { minWidth: 120 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Status" }) }),
1886
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { minWidth: 160, textAlign: "right" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Actions" }) })
1887
+ ] }) }),
1888
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: auditHistory.map((rec) => {
1889
+ const isExpanded = expandedDiffBatchId === rec.batchId;
1890
+ const dateStr = new Date(rec.executedAt).toLocaleString();
1891
+ return /* @__PURE__ */ jsxRuntime.jsxs(React__default.default.Fragment, { children: [
1892
+ /* @__PURE__ */ jsxRuntime.jsxs(
1893
+ designSystem.Tr,
1894
+ {
1895
+ style: {
1896
+ backgroundColor: rec.isRolledBack ? "rgba(255, 255, 255, 0.04)" : void 0,
1897
+ opacity: rec.isRolledBack ? 0.85 : 1
1898
+ },
1899
+ children: [
1900
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Td, { style: { minWidth: 140 }, children: [
1901
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: dateStr }),
1902
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral500", display: "block", style: { wordBreak: "break-all" }, children: [
1903
+ "Batch ID: ",
1904
+ rec.batchId
1905
+ ] })
1906
+ ] }),
1907
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Td, { style: { minWidth: 130 }, children: [
1908
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: rec.userName }),
1909
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", display: "block", style: { wordBreak: "break-all" }, children: rec.userEmail })
1910
+ ] }),
1911
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { minWidth: 160 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
1912
+ designSystem.Typography,
1913
+ {
1914
+ style: {
1915
+ display: "inline-block",
1916
+ maxWidth: "100%",
1917
+ overflow: "hidden",
1918
+ textOverflow: "ellipsis",
1919
+ whiteSpace: "nowrap"
1920
+ },
1921
+ title: `${rec.attribute} → "${formatDisplayValue(rec.value)}"`,
1922
+ children: [
1923
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: rec.attribute }),
1924
+ " → “",
1925
+ formatDisplayValue(rec.value),
1926
+ "”"
1927
+ ]
1928
+ }
1929
+ ) }),
1930
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Td, { style: { minWidth: 90, whiteSpace: "nowrap" }, children: [
1931
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: [
1932
+ rec.updatedCount,
1933
+ " updated"
1934
+ ] }),
1935
+ rec.skippedCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", style: { marginLeft: 6 }, children: [
1936
+ rec.skippedCount,
1937
+ " skipped"
1938
+ ] })
1939
+ ] }),
1940
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { minWidth: 120 }, children: rec.isRolledBack ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
1941
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "neutral200", textColor: "neutral700", children: "ROLLED BACK" }),
1942
+ rec.rolledBackAt && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral500", display: "block", style: { wordBreak: "break-word", marginTop: 2 }, children: [
1943
+ new Date(rec.rolledBackAt).toLocaleTimeString(),
1944
+ " by ",
1945
+ rec.rolledBackBy || "Admin"
1946
+ ] })
1947
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: "ACTIVE (APPLIED)" }) }),
1948
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { minWidth: 160, textAlign: "right", whiteSpace: "nowrap" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, justifyContent: "flex-end", children: [
1949
+ /* @__PURE__ */ jsxRuntime.jsx(
1950
+ designSystem.Button,
1951
+ {
1952
+ variant: "tertiary",
1953
+ size: "S",
1954
+ onClick: () => setExpandedDiffBatchId(isExpanded ? null : rec.batchId),
1955
+ children: isExpanded ? "Hide Diffs" : "View Diffs"
1956
+ }
1957
+ ),
1958
+ !rec.isRolledBack && /* @__PURE__ */ jsxRuntime.jsx(
1959
+ designSystem.Button,
1960
+ {
1961
+ variant: "danger",
1962
+ size: "S",
1963
+ loading: rollingBackBatchId === rec.batchId,
1964
+ onClick: () => handleRollback(rec.batchId),
1965
+ children: "Rollback"
1966
+ }
1967
+ )
1968
+ ] }) })
1969
+ ]
1970
+ }
1971
+ ),
1972
+ isExpanded && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { colSpan: 6, style: { background: "rgba(0, 0, 0, 0.06)", padding: 16 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
1973
+ designSystem.Box,
1974
+ {
1975
+ className: "bulk-updater-table-container",
1976
+ padding: 3,
1977
+ background: "neutral0",
1978
+ hasRadius: true,
1979
+ borderStyle: "solid",
1980
+ borderWidth: "1px",
1981
+ borderColor: "neutral200",
1982
+ style: { width: "100%", maxWidth: "100%", minWidth: 0, overflowX: "auto" },
1983
+ children: [
1984
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "delta", fontWeight: "bold", marginBottom: 2, children: [
1985
+ "Detailed Before/After Snapshots for Batch ",
1986
+ rec.batchId,
1987
+ ":"
1988
+ ] }),
1989
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: 4, rowCount: rec.diffs.length + 1, children: [
1990
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
1991
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Collection" }) }),
1992
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Document / Entry" }) }),
1993
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Previous Value" }) }),
1994
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Applied Value" }) })
1995
+ ] }) }),
1996
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: rec.diffs.map((d, dIdx) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
1997
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: d.collectionName }) }),
1998
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: d.label }) }),
1999
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "danger600", children: formatDisplayValue(d.oldValue) }) }),
2000
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", children: formatDisplayValue(d.newValue) }) })
2001
+ ] }, dIdx)) })
2002
+ ] })
2003
+ ]
2004
+ }
2005
+ ) }) })
2006
+ ] }, rec.batchId);
2007
+ }) })
2008
+ ] }) })
2009
+ ]
2010
+ }
2011
+ ),
2012
+ showConfirmModal && /* @__PURE__ */ jsxRuntime.jsx(
2013
+ "div",
2014
+ {
2015
+ style: {
2016
+ position: "fixed",
2017
+ inset: 0,
2018
+ backgroundColor: "rgba(20, 20, 30, 0.65)",
2019
+ backdropFilter: "blur(2px)",
2020
+ display: "flex",
2021
+ alignItems: "center",
2022
+ justifyContent: "center",
2023
+ zIndex: 9999,
2024
+ padding: 16,
2025
+ boxSizing: "border-box",
2026
+ overflowY: "auto"
2027
+ },
2028
+ onClick: () => setShowConfirmModal(false),
2029
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
2030
+ designSystem.Box,
2031
+ {
2032
+ background: "neutral0",
2033
+ hasRadius: true,
2034
+ shadow: "popupShadow",
2035
+ style: {
2036
+ maxWidth: 680,
2037
+ width: "100%",
2038
+ maxHeight: "calc(100vh - 48px)",
2039
+ display: "flex",
2040
+ flexDirection: "column",
2041
+ overflow: "hidden",
2042
+ position: "relative",
2043
+ boxShadow: "0 24px 48px rgba(0, 0, 0, 0.32), 0 0 1px rgba(0, 0, 0, 0.2)"
2044
+ },
2045
+ onClick: (e) => e.stopPropagation(),
2046
+ children: [
2047
+ /* @__PURE__ */ jsxRuntime.jsx(
2048
+ designSystem.Box,
2049
+ {
2050
+ padding: 5,
2051
+ background: "neutral100",
2052
+ style: {
2053
+ borderBottom: "1px solid rgba(120, 120, 150, 0.2)",
2054
+ flexShrink: 0
2055
+ },
2056
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
2057
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
2058
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", fontWeight: "bold", children: "Confirm Bulk Update" }),
2059
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", display: "block", children: "Review target attributes, affected counts, and server execution policy before proceeding." })
2060
+ ] }),
2061
+ /* @__PURE__ */ jsxRuntime.jsx(
2062
+ designSystem.Badge,
2063
+ {
2064
+ backgroundColor: license?.tier === "enterprise" ? "primary100" : "neutral200",
2065
+ textColor: license?.tier === "enterprise" ? "primary700" : "neutral700",
2066
+ children: license?.tier === "enterprise" ? "ENTERPRISE 4-EYES" : "STANDARD"
2067
+ }
2068
+ )
2069
+ ] })
2070
+ }
2071
+ ),
2072
+ /* @__PURE__ */ jsxRuntime.jsxs(
2073
+ designSystem.Box,
2074
+ {
2075
+ padding: 5,
2076
+ style: {
2077
+ flex: "1 1 auto",
2078
+ minHeight: 0,
2079
+ overflowY: "auto",
2080
+ WebkitOverflowScrolling: "touch"
2081
+ },
2082
+ children: [
2083
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 4, background: "neutral100", hasRadius: true, marginBottom: 4, children: [
2084
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", marginBottom: 2, display: "block", children: "UPDATE DETAILS" }),
2085
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, children: [
2086
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", children: [
2087
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: "Target Attribute:" }),
2088
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: selectedAttribute })
2089
+ ] }),
2090
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
2091
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: "Target Value:" }),
2092
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", style: { wordBreak: "break-all", maxWidth: 360 }, children: relationsMeta[selectedAttribute] ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: selectedRelationDocId === "__clear__" ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "danger100", textColor: "danger700", children: "Clear / Unlink Relation" }) : /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: [
2093
+ relationsMeta[selectedAttribute].isMulti ? `[${relationMode.toUpperCase()}] ` : "",
2094
+ relationOptions.find((o) => o.documentId === selectedRelationDocId)?.label || selectedRelationDocId || "(none selected)"
2095
+ ] }) }) : useRegex ? /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2096
+ "Replace ",
2097
+ /* @__PURE__ */ jsxRuntime.jsxs("code", { children: [
2098
+ "/",
2099
+ regexPattern,
2100
+ "/g"
2101
+ ] }),
2102
+ " with ",
2103
+ /* @__PURE__ */ jsxRuntime.jsxs("strong", { children: [
2104
+ '"',
2105
+ attributeValue,
2106
+ '"'
2107
+ ] })
2108
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("strong", { children: [
2109
+ '"',
2110
+ attributeValue || "(empty string)",
2111
+ '"'
2112
+ ] }) })
2113
+ ] }),
2114
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", children: [
2115
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: "Total Selected Records:" }),
2116
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { fontWeight: "bold", textColor: "primary600", children: [
2117
+ totalSelectedCount,
2118
+ " records"
2119
+ ] })
2120
+ ] }),
2121
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
2122
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: "Publishing Mode:" }),
2123
+ /* @__PURE__ */ jsxRuntime.jsx(
2124
+ designSystem.Badge,
2125
+ {
2126
+ backgroundColor: publishOnApproval ? "success100" : "warning100",
2127
+ textColor: publishOnApproval ? "success700" : "warning700",
2128
+ children: publishOnApproval ? "Publish Immediately" : "Keep as Draft"
2129
+ }
2130
+ )
2131
+ ] })
2132
+ ] })
2133
+ ] }),
2134
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginBottom: 4, children: [
2135
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", marginBottom: 2, display: "block", children: "COLLECTION BREAKDOWN" }),
2136
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 1, children: Object.entries(getSelectedBreakdown()).map(([name, count]) => /* @__PURE__ */ jsxRuntime.jsxs(
2137
+ designSystem.Flex,
2138
+ {
2139
+ justifyContent: "space-between",
2140
+ padding: 2,
2141
+ background: "neutral100",
2142
+ hasRadius: true,
2143
+ style: { border: "1px solid rgba(120, 120, 150, 0.15)" },
2144
+ children: [
2145
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: name }),
2146
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "neutral200", textColor: "neutral800", children: [
2147
+ count,
2148
+ " ",
2149
+ count === 1 ? "record" : "records"
2150
+ ] })
2151
+ ]
2152
+ },
2153
+ name
2154
+ )) })
2155
+ ] }),
2156
+ /* @__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: [
2157
+ /* @__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" }) }),
2158
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: executionWindow?.isBusinessHours ? "warning700" : "success700", children: executionWindow?.policyMessage || (executionWindow?.isBusinessHours ? "Server policy restricts bulk updates 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.") })
2159
+ ] }) }),
2160
+ /* @__PURE__ */ jsxRuntime.jsx(
2161
+ designSystem.Box,
2162
+ {
2163
+ padding: 4,
2164
+ background: "neutral100",
2165
+ hasRadius: true,
2166
+ marginBottom: 4,
2167
+ style: { border: "1px solid rgba(120, 120, 150, 0.15)" },
2168
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "flex-start", children: [
2169
+ /* @__PURE__ */ jsxRuntime.jsx(
2170
+ designSystem.Checkbox,
2171
+ {
2172
+ name: "publishOnApproval",
2173
+ checked: publishOnApproval,
2174
+ onCheckedChange: (checked) => setPublishOnApproval(Boolean(checked))
2175
+ }
2176
+ ),
2177
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { cursor: "pointer" }, onClick: () => setPublishOnApproval(!publishOnApproval), children: [
2178
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", display: "block", children: "Publish immediately upon checker approval / execution" }),
2179
+ /* @__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." })
2180
+ ] })
2181
+ ] })
2182
+ }
2183
+ ),
2184
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginBottom: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
2185
+ designSystem.TextInput,
2186
+ {
2187
+ label: "Change Request / Release Notes (Optional)",
2188
+ name: "makerNotes",
2189
+ placeholder: "e.g. REL-4021: Updating summer pricing & promo attributes",
2190
+ value: makerNotesInput,
2191
+ onChange: (e) => setMakerNotesInput(e.target.value)
2192
+ }
2193
+ ) })
2194
+ ]
2195
+ }
2196
+ ),
2197
+ /* @__PURE__ */ jsxRuntime.jsx(
2198
+ designSystem.Box,
2199
+ {
2200
+ padding: 4,
2201
+ background: "neutral100",
2202
+ style: {
2203
+ borderTop: "1px solid rgba(120, 120, 150, 0.2)",
2204
+ flexShrink: 0,
2205
+ position: "sticky",
2206
+ bottom: 0,
2207
+ zIndex: 20,
2208
+ boxShadow: "0 -4px 16px rgba(0, 0, 0, 0.08)"
2209
+ },
2210
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", gap: 3, style: { width: "100%" }, children: [
2211
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", size: "M", onClick: () => setShowConfirmModal(false), children: "Cancel" }),
2212
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, wrap: "wrap", justifyContent: "flex-end", alignItems: "center", children: [
2213
+ (license?.tier === "enterprise" || license?.features?.includes("Maker-Checker Approval Workflow (4-Eyes Principle)")) && /* @__PURE__ */ jsxRuntime.jsx(
2214
+ designSystem.Button,
2215
+ {
2216
+ variant: executionWindow?.isBusinessHours ? "default" : "secondary",
2217
+ size: "M",
2218
+ onClick: () => executeBatchWithMode("maker_checker"),
2219
+ loading: updating,
2220
+ children: "Submit for Checker Approval"
2221
+ }
2222
+ ),
2223
+ executionWindow?.isBusinessHours ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2224
+ /* @__PURE__ */ jsxRuntime.jsxs(
2225
+ designSystem.Button,
2226
+ {
2227
+ variant: license?.tier === "enterprise" ? "secondary" : "default",
2228
+ size: "M",
2229
+ onClick: () => executeBatchWithMode("queue_offpeak"),
2230
+ loading: updating,
2231
+ children: [
2232
+ "Queue for Off-Peak (",
2233
+ executionWindow?.offPeakStartTime || "18:00",
2234
+ ")"
2235
+ ]
2236
+ }
2237
+ ),
2238
+ /* @__PURE__ */ jsxRuntime.jsx(
2239
+ designSystem.Button,
2240
+ {
2241
+ variant: "danger-light",
2242
+ size: "S",
2243
+ onClick: () => executeBatchWithMode("immediate", true),
2244
+ loading: updating,
2245
+ children: "Force Run Now"
2246
+ }
2247
+ )
2248
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
2249
+ designSystem.Button,
2250
+ {
2251
+ variant: "default",
2252
+ size: "M",
2253
+ onClick: () => executeBatchWithMode("immediate"),
2254
+ loading: updating,
2255
+ children: "Confirm & Execute Now"
2256
+ }
2257
+ )
2258
+ ] })
2259
+ ] })
2260
+ }
2261
+ )
2262
+ ]
2263
+ }
2264
+ )
2265
+ }
2266
+ )
2267
+ ]
2268
+ }
2269
+ );
2270
+ };
2271
+ exports.default = BulkUpdater;
2272
+ exports.formatDisplayValue = formatDisplayValue;
2273
+ exports.getAvailableColumnsForCollection = getAvailableColumnsForCollection;
2274
+ //# sourceMappingURL=BulkUpdater-CooMo-Hc.js.map