strapi-plugin-bulk-updater 1.3.8 → 1.3.10

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.
Files changed (29) hide show
  1. package/dist/_chunks/{BulkUpdater-BosdSbGF.js → BulkUpdater-DRHQBZZV.js} +20 -4
  2. package/dist/_chunks/BulkUpdater-DRHQBZZV.js.map +1 -0
  3. package/dist/_chunks/{BulkUpdater-DUrmamj-.mjs → BulkUpdater-J21ZgJWx.mjs} +20 -4
  4. package/dist/_chunks/BulkUpdater-J21ZgJWx.mjs.map +1 -0
  5. package/dist/_chunks/{ScopedQuotaSelectInput-CyGlYa9x.js → ScopedQuotaSelectInput-BsBpmwun.js} +79 -11
  6. package/dist/_chunks/ScopedQuotaSelectInput-BsBpmwun.js.map +1 -0
  7. package/dist/_chunks/{ScopedQuotaSelectInput-Cy3eEj9-.mjs → ScopedQuotaSelectInput-YKdQgrPt.mjs} +79 -11
  8. package/dist/_chunks/ScopedQuotaSelectInput-YKdQgrPt.mjs.map +1 -0
  9. package/dist/_chunks/{SettingsPage-Bdse5MP4.mjs → SettingsPage-BDObdCIY.mjs} +2 -2
  10. package/dist/_chunks/{SettingsPage-Bdse5MP4.mjs.map → SettingsPage-BDObdCIY.mjs.map} +1 -1
  11. package/dist/_chunks/{SettingsPage-B6Ia7SVS.js → SettingsPage-M-HQ8Jtr.js} +2 -2
  12. package/dist/_chunks/{SettingsPage-B6Ia7SVS.js.map → SettingsPage-M-HQ8Jtr.js.map} +1 -1
  13. package/dist/_chunks/{ValidationRulesTab-BqGz1VLo.js → ValidationRulesTab-DuvL-K4F.js} +21 -8
  14. package/dist/_chunks/ValidationRulesTab-DuvL-K4F.js.map +1 -0
  15. package/dist/_chunks/{ValidationRulesTab-Dqkson67.mjs → ValidationRulesTab-VeRr-vxa.mjs} +21 -8
  16. package/dist/_chunks/ValidationRulesTab-VeRr-vxa.mjs.map +1 -0
  17. package/dist/admin/index.js +3 -3
  18. package/dist/admin/index.mjs +3 -3
  19. package/dist/server/index.js +223 -42
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/server/index.mjs +223 -42
  22. package/dist/server/index.mjs.map +1 -1
  23. package/package.json +1 -1
  24. package/dist/_chunks/BulkUpdater-BosdSbGF.js.map +0 -1
  25. package/dist/_chunks/BulkUpdater-DUrmamj-.mjs.map +0 -1
  26. package/dist/_chunks/ScopedQuotaSelectInput-Cy3eEj9-.mjs.map +0 -1
  27. package/dist/_chunks/ScopedQuotaSelectInput-CyGlYa9x.js.map +0 -1
  28. package/dist/_chunks/ValidationRulesTab-BqGz1VLo.js.map +0 -1
  29. package/dist/_chunks/ValidationRulesTab-Dqkson67.mjs.map +0 -1
@@ -31,16 +31,80 @@ const DEFAULT_HEADING_ITEMS = [
31
31
  { value: "h5", label: "H5 - Card / Item Heading", maxAllowed: null },
32
32
  { value: "h6", label: "H6 - Minor Heading", maxAllowed: null }
33
33
  ];
34
- function computeCrossComponentUsage(data, targetFieldLeafName, excludePath, includeRichTextHtml = false) {
34
+ function cleanKey(str) {
35
+ return (str || "").replace(/[-_]/g, "").toLowerCase().trim();
36
+ }
37
+ function matchesPath(normalizedPath, leafKey, targetField) {
38
+ if (!targetField) return false;
39
+ const pClean = cleanKey(normalizedPath);
40
+ const kClean = cleanKey(leafKey);
41
+ const tClean = cleanKey(targetField);
42
+ if (kClean === tClean || pClean === tClean) return true;
43
+ const targetSegments = targetField.replace(/^api::/i, "").split(".").map(cleanKey).filter(Boolean);
44
+ const pathSegments = normalizedPath.split(".").map(cleanKey).filter(Boolean);
45
+ if (targetSegments.length === 0 || pathSegments.length === 0) return false;
46
+ if (targetSegments.length === 1) {
47
+ const single = targetSegments[0];
48
+ return kClean === single || pathSegments[pathSegments.length - 1] === single;
49
+ }
50
+ let isSuffix = true;
51
+ for (let i = 0; i < targetSegments.length; i++) {
52
+ const tSeg = targetSegments[targetSegments.length - 1 - i];
53
+ const pSeg = pathSegments[pathSegments.length - 1 - i];
54
+ if (tSeg !== "*" && tSeg !== pSeg) {
55
+ isSuffix = false;
56
+ break;
57
+ }
58
+ }
59
+ if (isSuffix) return true;
60
+ for (let startIdx = 1; startIdx < targetSegments.length; startIdx++) {
61
+ const subTarget = targetSegments.slice(startIdx);
62
+ if (subTarget.length > pathSegments.length) continue;
63
+ let subSuffix = true;
64
+ for (let i = 0; i < subTarget.length; i++) {
65
+ const tSeg = subTarget[subTarget.length - 1 - i];
66
+ const pSeg = pathSegments[pathSegments.length - 1 - i];
67
+ if (tSeg !== "*" && tSeg !== pSeg) {
68
+ subSuffix = false;
69
+ break;
70
+ }
71
+ }
72
+ if (subSuffix) return true;
73
+ }
74
+ let pIdx = 0;
75
+ let matchesInOrder = true;
76
+ let tStart = 0;
77
+ if (targetSegments.length > 2 && targetSegments[0] !== pathSegments[0] && targetSegments[0] !== "*") {
78
+ tStart = 1;
79
+ }
80
+ for (let tIdx = tStart; tIdx < targetSegments.length; tIdx++) {
81
+ const tSeg = targetSegments[tIdx];
82
+ if (tSeg === "*") continue;
83
+ let found = false;
84
+ while (pIdx < pathSegments.length) {
85
+ if (pathSegments[pIdx] === tSeg) {
86
+ found = true;
87
+ pIdx++;
88
+ break;
89
+ }
90
+ pIdx++;
91
+ }
92
+ if (!found) {
93
+ matchesInOrder = false;
94
+ break;
95
+ }
96
+ }
97
+ return matchesInOrder;
98
+ }
99
+ function computeCrossComponentUsage(data, targetFieldRule, fieldLeafName, excludePath, includeRichTextHtml = false) {
35
100
  const counts = {};
36
- const targetLower = targetFieldLeafName.toLowerCase().trim();
37
101
  function traverse(current, currentPath) {
38
102
  if (!current || typeof current !== "object") return;
39
103
  for (const key of Object.keys(current)) {
40
104
  const val = current[key];
41
105
  const fullPath = currentPath ? `${currentPath}.${key}` : key;
42
- const keyLower = key.toLowerCase().trim();
43
- const isTargetMatch = keyLower === targetLower || targetLower.endsWith(`.${keyLower}`) || targetLower.split(".").pop() === keyLower;
106
+ const normalizedPath = fullPath.replace(/\.\d+/g, "");
107
+ const isTargetMatch = matchesPath(normalizedPath, key, targetFieldRule || fieldLeafName);
44
108
  if (isTargetMatch && fullPath !== excludePath && typeof val === "string" && val.trim()) {
45
109
  const valNorm = val.toLowerCase().trim();
46
110
  counts[valNorm] = (counts[valNorm] || 0) + 1;
@@ -80,17 +144,15 @@ function ScopedQuotaSelectInput({
80
144
  const { get } = admin.useFetchClient();
81
145
  const formValues = strapiAdmin.useForm("ScopedQuotaSelectInput", (state) => state.values) || {};
82
146
  const fieldLeafName = name.split(".").pop() || name;
147
+ const normalizedFieldPath = name.replace(/\.\d+/g, "");
83
148
  const [activeUiRule, setActiveUiRule] = React__namespace.useState(null);
84
149
  React__namespace.useEffect(() => {
85
150
  let isMounted = true;
86
151
  get("/bulk-updater/validation-rules").then((res) => {
87
152
  if (isMounted && res.data && res.data.success && Array.isArray(res.data.rules)) {
88
- const leafLower = fieldLeafName.toLowerCase().trim();
89
- const nameLower = name.toLowerCase().trim();
90
153
  const matching = res.data.rules.find((r) => {
91
154
  if (!r.enabled || !r.targetField) return false;
92
- const targetLower = r.targetField.toLowerCase().trim();
93
- return targetLower === leafLower || targetLower === nameLower || targetLower.endsWith(`.${leafLower}`) || targetLower.split(".").pop() === leafLower || nameLower.endsWith(`.${targetLower}`);
155
+ return matchesPath(normalizedFieldPath, fieldLeafName, r.targetField);
94
156
  });
95
157
  if (matching) {
96
158
  setActiveUiRule(matching);
@@ -101,7 +163,7 @@ function ScopedQuotaSelectInput({
101
163
  return () => {
102
164
  isMounted = false;
103
165
  };
104
- }, [fieldLeafName, name]);
166
+ }, [fieldLeafName, normalizedFieldPath]);
105
167
  const optionsConfig = attribute?.options || {};
106
168
  const items = React__namespace.useMemo(() => {
107
169
  const rawItems = optionsConfig.items && optionsConfig.items.length > 0 ? optionsConfig.items : [];
@@ -135,7 +197,13 @@ function ScopedQuotaSelectInput({
135
197
  }, [optionsConfig.items, activeUiRule]);
136
198
  const disabledBehavior = activeUiRule?.exceededAction || optionsConfig.disabledBehavior || "disable";
137
199
  const otherUsage = React__namespace.useMemo(() => {
138
- return computeCrossComponentUsage(formValues, fieldLeafName, name, Boolean(activeUiRule?.includeRichTextHtml));
200
+ return computeCrossComponentUsage(
201
+ formValues,
202
+ activeUiRule?.targetField || fieldLeafName,
203
+ fieldLeafName,
204
+ name,
205
+ Boolean(activeUiRule?.includeRichTextHtml)
206
+ );
139
207
  }, [formValues, fieldLeafName, name, activeUiRule]);
140
208
  const processedOptions = React__namespace.useMemo(() => {
141
209
  return items.map((item) => {
@@ -225,4 +293,4 @@ function ScopedQuotaSelectInput({
225
293
  ] });
226
294
  }
227
295
  exports.default = ScopedQuotaSelectInput;
228
- //# sourceMappingURL=ScopedQuotaSelectInput-CyGlYa9x.js.map
296
+ //# sourceMappingURL=ScopedQuotaSelectInput-BsBpmwun.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScopedQuotaSelectInput-BsBpmwun.js","sources":["../../admin/src/components/ScopedQuotaSelectInput.tsx"],"sourcesContent":["import * as React from 'react';\nimport {\n Field,\n SingleSelect,\n SingleSelectOption,\n Box,\n Typography,\n} from '@strapi/design-system';\nimport { useForm } from '@strapi/admin/strapi-admin';\nimport { useFetchClient } from '@strapi/strapi/admin';\n\nexport interface QuotaItem {\n value: string;\n label: string;\n maxAllowed?: number | null; // e.g. 1 for H1, 3 for H2, null for unlimited\n}\n\nexport interface ScopedQuotaSelectProps {\n name: string;\n value?: string | null;\n onChange: (e: { target: { name: string; value: string } }) => void;\n attribute?: {\n type?: string;\n customField?: string;\n options?: {\n scope?: 'entry' | 'zone';\n disabledBehavior?: 'disable' | 'hide';\n items?: QuotaItem[];\n };\n };\n disabled?: boolean;\n required?: boolean;\n placeholder?: string;\n label?: React.ReactNode;\n hint?: React.ReactNode;\n}\n\n// Default fallback options if none configured on the attribute\nconst DEFAULT_HEADING_ITEMS: QuotaItem[] = [\n { value: 'h1', label: 'H1 - Main Page Title', maxAllowed: 1 },\n { value: 'h2', label: 'H2 - Primary Section Heading', maxAllowed: 3 },\n { value: 'h3', label: 'H3 - Sub-section Heading', maxAllowed: null },\n { value: 'h4', label: 'H4 - Component Title', maxAllowed: null },\n { value: 'h5', label: 'H5 - Card / Item Heading', maxAllowed: null },\n { value: 'h6', label: 'H6 - Minor Heading', maxAllowed: null },\n];\n\nfunction cleanKey(str: string): string {\n return (str || '').replace(/[-_]/g, '').toLowerCase().trim();\n}\n\nfunction matchesPath(normalizedPath: string, leafKey: string, targetField: string): boolean {\n if (!targetField) return false;\n const pClean = cleanKey(normalizedPath);\n const kClean = cleanKey(leafKey);\n const tClean = cleanKey(targetField);\n\n if (kClean === tClean || pClean === tClean) return true;\n\n const targetSegments = targetField\n .replace(/^api::/i, '')\n .split('.')\n .map(cleanKey)\n .filter(Boolean);\n\n const pathSegments = normalizedPath\n .split('.')\n .map(cleanKey)\n .filter(Boolean);\n\n if (targetSegments.length === 0 || pathSegments.length === 0) return false;\n\n if (targetSegments.length === 1) {\n const single = targetSegments[0];\n return kClean === single || pathSegments[pathSegments.length - 1] === single;\n }\n\n let isSuffix = true;\n for (let i = 0; i < targetSegments.length; i++) {\n const tSeg = targetSegments[targetSegments.length - 1 - i];\n const pSeg = pathSegments[pathSegments.length - 1 - i];\n if (tSeg !== '*' && tSeg !== pSeg) {\n isSuffix = false;\n break;\n }\n }\n if (isSuffix) return true;\n\n for (let startIdx = 1; startIdx < targetSegments.length; startIdx++) {\n const subTarget = targetSegments.slice(startIdx);\n if (subTarget.length > pathSegments.length) continue;\n let subSuffix = true;\n for (let i = 0; i < subTarget.length; i++) {\n const tSeg = subTarget[subTarget.length - 1 - i];\n const pSeg = pathSegments[pathSegments.length - 1 - i];\n if (tSeg !== '*' && tSeg !== pSeg) {\n subSuffix = false;\n break;\n }\n }\n if (subSuffix) return true;\n }\n\n let pIdx = 0;\n let matchesInOrder = true;\n let tStart = 0;\n if (targetSegments.length > 2 && targetSegments[0] !== pathSegments[0] && targetSegments[0] !== '*') {\n tStart = 1;\n }\n for (let tIdx = tStart; tIdx < targetSegments.length; tIdx++) {\n const tSeg = targetSegments[tIdx];\n if (tSeg === '*') continue;\n let found = false;\n while (pIdx < pathSegments.length) {\n if (pathSegments[pIdx] === tSeg) {\n found = true;\n pIdx++;\n break;\n }\n pIdx++;\n }\n if (!found) {\n matchesInOrder = false;\n break;\n }\n }\n\n return matchesInOrder;\n}\n\n/**\n * Recursively traverses the entire form data tree to count occurrences of target field values,\n * excluding the current input's exact field path.\n */\nfunction computeCrossComponentUsage(\n data: any,\n targetFieldRule: string,\n fieldLeafName: string,\n excludePath: string,\n includeRichTextHtml: boolean = false\n): Record<string, number> {\n const counts: Record<string, number> = {};\n\n function traverse(current: any, currentPath: string) {\n if (!current || typeof current !== 'object') return;\n\n for (const key of Object.keys(current)) {\n const val = current[key];\n const fullPath = currentPath ? `${currentPath}.${key}` : key;\n const normalizedPath = fullPath.replace(/\\.\\d+/g, '');\n\n const isTargetMatch = matchesPath(normalizedPath, key, targetFieldRule || fieldLeafName);\n\n if (isTargetMatch && fullPath !== excludePath && typeof val === 'string' && val.trim()) {\n const valNorm = val.toLowerCase().trim();\n counts[valNorm] = (counts[valNorm] || 0) + 1;\n }\n\n // If rule checks rich text HTML, scan strings with HTML or Markdown headings\n if (includeRichTextHtml && typeof val === 'string' && (val.includes('<h') || val.includes('#'))) {\n for (let level = 1; level <= 6; level++) {\n const tag = `h${level}`;\n const htmlMatches = val.match(new RegExp(`<${tag}\\\\b[^>]*>`, 'gi'));\n if (htmlMatches) {\n counts[tag] = (counts[tag] || 0) + htmlMatches.length;\n }\n const mdMatches = val.match(new RegExp(`(?:^|\\\\n)${'#'.repeat(level)}\\\\s+[^\\\\n]+`, 'g'));\n if (mdMatches) {\n counts[tag] = (counts[tag] || 0) + mdMatches.length;\n }\n }\n }\n\n if (typeof val === 'object' && val !== null) {\n traverse(val, fullPath);\n }\n }\n }\n\n traverse(data, '');\n return counts;\n}\n\nexport default function ScopedQuotaSelectInput({\n name,\n value,\n onChange,\n attribute,\n disabled = false,\n required = false,\n placeholder,\n label,\n hint,\n}: ScopedQuotaSelectProps) {\n const { get } = useFetchClient();\n\n // Subscribe to live Formik/Form values in Strapi v5 Admin Edit View\n const formValues = useForm('ScopedQuotaSelectInput', (state) => state.values) || {};\n\n // Extract the leaf property name (e.g., 'titleStyle' from 'blocks.0.titleStyle')\n const fieldLeafName = name.split('.').pop() || name;\n const normalizedFieldPath = name.replace(/\\.\\d+/g, '');\n\n // Active UI-configured rule state\n const [activeUiRule, setActiveUiRule] = React.useState<any>(null);\n\n React.useEffect(() => {\n let isMounted = true;\n get('/bulk-updater/validation-rules')\n .then((res: any) => {\n if (isMounted && res.data && res.data.success && Array.isArray(res.data.rules)) {\n const matching = res.data.rules.find((r: any) => {\n if (!r.enabled || !r.targetField) return false;\n return matchesPath(normalizedFieldPath, fieldLeafName, r.targetField);\n });\n\n if (matching) {\n setActiveUiRule(matching);\n }\n }\n })\n .catch(() => {\n // Fall back gracefully to schema options on network/auth failure\n });\n return () => {\n isMounted = false;\n };\n }, [fieldLeafName, normalizedFieldPath]);\n\n const optionsConfig = attribute?.options || {};\n\n // Dynamically merge items: include items from attribute options, UI rules, or default headings\n const items: QuotaItem[] = React.useMemo(() => {\n const rawItems: QuotaItem[] = optionsConfig.items && optionsConfig.items.length > 0\n ? optionsConfig.items\n : [];\n\n const map = new Map<string, QuotaItem>();\n rawItems.forEach((it) => {\n map.set((it.value || '').toLowerCase().trim(), { ...it });\n });\n\n if (activeUiRule?.quotas) {\n for (const [k, max] of Object.entries(activeUiRule.quotas)) {\n const lowerK = k.toLowerCase().trim();\n if (map.has(lowerK)) {\n const existing = map.get(lowerK)!;\n map.set(lowerK, {\n ...existing,\n maxAllowed: typeof max === 'number' ? max : existing.maxAllowed,\n });\n } else {\n // If option not in schema items, add it preserving the user-defined casing\n map.set(lowerK, {\n value: k, // preserve original casing from rule, e.g. \"H1\"\n label: k.toUpperCase(),\n maxAllowed: typeof max === 'number' ? max : null,\n });\n }\n }\n }\n\n if (map.size === 0) {\n DEFAULT_HEADING_ITEMS.forEach((it) => map.set(it.value.toLowerCase().trim(), { ...it }));\n }\n\n return Array.from(map.values());\n }, [optionsConfig.items, activeUiRule]);\n\n const disabledBehavior = activeUiRule?.exceededAction || optionsConfig.disabledBehavior || 'disable';\n\n // Compute other components' usage across the entire entry (and rich text HTML if rule enabled)\n const otherUsage = React.useMemo(() => {\n return computeCrossComponentUsage(\n formValues,\n activeUiRule?.targetField || fieldLeafName,\n fieldLeafName,\n name,\n Boolean(activeUiRule?.includeRichTextHtml)\n );\n }, [formValues, fieldLeafName, name, activeUiRule]);\n\n // Compute processed options: evaluate quotas and apply disable or hide rules\n const processedOptions = React.useMemo(() => {\n return items\n .map((item) => {\n const itemValLower = (item.value || '').toLowerCase().trim();\n const usedCount = otherUsage[itemValLower] || 0;\n const max = item.maxAllowed;\n\n // Quota is exhausted if other components have already reached maxAllowed\n const isQuotaReached = typeof max === 'number' && max > 0 && usedCount >= max;\n\n // Check if current field already holds this value (case-insensitively)\n const isCurrentValue = (value || '').toLowerCase().trim() === itemValLower;\n const isDisabled = isQuotaReached && !isCurrentValue;\n\n let quotaNotice = '';\n if (typeof max === 'number' && max > 0) {\n quotaNotice = `(${usedCount}/${max} used)`;\n }\n\n return {\n ...item,\n isDisabled,\n quotaNotice,\n isQuotaReached,\n };\n })\n .filter((item) => {\n if (disabledBehavior === 'hide' && item.isDisabled) {\n return false;\n }\n return true;\n });\n }, [items, otherUsage, value, disabledBehavior]);\n\n // Identify options whose quota has been exhausted by other components\n const exhaustedItems = React.useMemo(() => {\n return items.filter((i) => {\n const iValLower = (i.value || '').toLowerCase().trim();\n const isCurrentValue = (value || '').toLowerCase().trim() === iValLower;\n return (\n typeof i.maxAllowed === 'number' &&\n i.maxAllowed > 0 &&\n (otherUsage[iValLower] || 0) >= i.maxAllowed &&\n !isCurrentValue\n );\n });\n }, [items, otherUsage, value]);\n\n const handleChange = (selectedVal: string) => {\n if (onChange) {\n onChange({ target: { name, value: selectedVal } });\n }\n };\n\n const displayLabel = label || activeUiRule?.name || (fieldLeafName.charAt(0).toUpperCase() + fieldLeafName.slice(1));\n const displayPlaceholder = placeholder || `Select ${displayLabel}...`;\n const matchedItemValue = items.find((it) => (it.value || '').toLowerCase().trim() === (value || '').toLowerCase().trim())?.value || (value || '');\n\n return (\n <Field.Root name={name} required={required}>\n {displayLabel && <Field.Label>{displayLabel}</Field.Label>}\n <SingleSelect\n placeholder={displayPlaceholder}\n value={matchedItemValue}\n onChange={handleChange}\n disabled={disabled}\n >\n {processedOptions.map((opt) => (\n <SingleSelectOption\n key={opt.value}\n value={opt.value}\n disabled={opt.isDisabled}\n >\n {opt.label}{' '}\n {opt.quotaNotice && (\n <span style={{ opacity: 0.65, fontSize: '0.85em', marginLeft: 6 }}>\n [{opt.quotaNotice}]\n </span>\n )}\n </SingleSelectOption>\n ))}\n </SingleSelect>\n {hint && <Field.Hint>{hint}</Field.Hint>}\n\n {exhaustedItems.length > 0 && (\n <Box paddingTop={1}>\n {exhaustedItems.map((item) => (\n <Typography\n key={item.value}\n variant=\"pi\"\n textColor=\"neutral600\"\n style={{ display: 'block', marginTop: 2 }}\n >\n ℹ️ <strong>{item.label}</strong> quota reached (Max {item.maxAllowed} per entry).\n </Typography>\n ))}\n </Box>\n )}\n </Field.Root>\n );\n}\n"],"names":["useFetchClient","useForm","React","jsxs","Field","jsx","SingleSelect","SingleSelectOption","Box","Typography"],"mappings":"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"}
@@ -11,16 +11,80 @@ const DEFAULT_HEADING_ITEMS = [
11
11
  { value: "h5", label: "H5 - Card / Item Heading", maxAllowed: null },
12
12
  { value: "h6", label: "H6 - Minor Heading", maxAllowed: null }
13
13
  ];
14
- function computeCrossComponentUsage(data, targetFieldLeafName, excludePath, includeRichTextHtml = false) {
14
+ function cleanKey(str) {
15
+ return (str || "").replace(/[-_]/g, "").toLowerCase().trim();
16
+ }
17
+ function matchesPath(normalizedPath, leafKey, targetField) {
18
+ if (!targetField) return false;
19
+ const pClean = cleanKey(normalizedPath);
20
+ const kClean = cleanKey(leafKey);
21
+ const tClean = cleanKey(targetField);
22
+ if (kClean === tClean || pClean === tClean) return true;
23
+ const targetSegments = targetField.replace(/^api::/i, "").split(".").map(cleanKey).filter(Boolean);
24
+ const pathSegments = normalizedPath.split(".").map(cleanKey).filter(Boolean);
25
+ if (targetSegments.length === 0 || pathSegments.length === 0) return false;
26
+ if (targetSegments.length === 1) {
27
+ const single = targetSegments[0];
28
+ return kClean === single || pathSegments[pathSegments.length - 1] === single;
29
+ }
30
+ let isSuffix = true;
31
+ for (let i = 0; i < targetSegments.length; i++) {
32
+ const tSeg = targetSegments[targetSegments.length - 1 - i];
33
+ const pSeg = pathSegments[pathSegments.length - 1 - i];
34
+ if (tSeg !== "*" && tSeg !== pSeg) {
35
+ isSuffix = false;
36
+ break;
37
+ }
38
+ }
39
+ if (isSuffix) return true;
40
+ for (let startIdx = 1; startIdx < targetSegments.length; startIdx++) {
41
+ const subTarget = targetSegments.slice(startIdx);
42
+ if (subTarget.length > pathSegments.length) continue;
43
+ let subSuffix = true;
44
+ for (let i = 0; i < subTarget.length; i++) {
45
+ const tSeg = subTarget[subTarget.length - 1 - i];
46
+ const pSeg = pathSegments[pathSegments.length - 1 - i];
47
+ if (tSeg !== "*" && tSeg !== pSeg) {
48
+ subSuffix = false;
49
+ break;
50
+ }
51
+ }
52
+ if (subSuffix) return true;
53
+ }
54
+ let pIdx = 0;
55
+ let matchesInOrder = true;
56
+ let tStart = 0;
57
+ if (targetSegments.length > 2 && targetSegments[0] !== pathSegments[0] && targetSegments[0] !== "*") {
58
+ tStart = 1;
59
+ }
60
+ for (let tIdx = tStart; tIdx < targetSegments.length; tIdx++) {
61
+ const tSeg = targetSegments[tIdx];
62
+ if (tSeg === "*") continue;
63
+ let found = false;
64
+ while (pIdx < pathSegments.length) {
65
+ if (pathSegments[pIdx] === tSeg) {
66
+ found = true;
67
+ pIdx++;
68
+ break;
69
+ }
70
+ pIdx++;
71
+ }
72
+ if (!found) {
73
+ matchesInOrder = false;
74
+ break;
75
+ }
76
+ }
77
+ return matchesInOrder;
78
+ }
79
+ function computeCrossComponentUsage(data, targetFieldRule, fieldLeafName, excludePath, includeRichTextHtml = false) {
15
80
  const counts = {};
16
- const targetLower = targetFieldLeafName.toLowerCase().trim();
17
81
  function traverse(current, currentPath) {
18
82
  if (!current || typeof current !== "object") return;
19
83
  for (const key of Object.keys(current)) {
20
84
  const val = current[key];
21
85
  const fullPath = currentPath ? `${currentPath}.${key}` : key;
22
- const keyLower = key.toLowerCase().trim();
23
- const isTargetMatch = keyLower === targetLower || targetLower.endsWith(`.${keyLower}`) || targetLower.split(".").pop() === keyLower;
86
+ const normalizedPath = fullPath.replace(/\.\d+/g, "");
87
+ const isTargetMatch = matchesPath(normalizedPath, key, targetFieldRule || fieldLeafName);
24
88
  if (isTargetMatch && fullPath !== excludePath && typeof val === "string" && val.trim()) {
25
89
  const valNorm = val.toLowerCase().trim();
26
90
  counts[valNorm] = (counts[valNorm] || 0) + 1;
@@ -60,17 +124,15 @@ function ScopedQuotaSelectInput({
60
124
  const { get } = useFetchClient();
61
125
  const formValues = useForm("ScopedQuotaSelectInput", (state) => state.values) || {};
62
126
  const fieldLeafName = name.split(".").pop() || name;
127
+ const normalizedFieldPath = name.replace(/\.\d+/g, "");
63
128
  const [activeUiRule, setActiveUiRule] = React.useState(null);
64
129
  React.useEffect(() => {
65
130
  let isMounted = true;
66
131
  get("/bulk-updater/validation-rules").then((res) => {
67
132
  if (isMounted && res.data && res.data.success && Array.isArray(res.data.rules)) {
68
- const leafLower = fieldLeafName.toLowerCase().trim();
69
- const nameLower = name.toLowerCase().trim();
70
133
  const matching = res.data.rules.find((r) => {
71
134
  if (!r.enabled || !r.targetField) return false;
72
- const targetLower = r.targetField.toLowerCase().trim();
73
- return targetLower === leafLower || targetLower === nameLower || targetLower.endsWith(`.${leafLower}`) || targetLower.split(".").pop() === leafLower || nameLower.endsWith(`.${targetLower}`);
135
+ return matchesPath(normalizedFieldPath, fieldLeafName, r.targetField);
74
136
  });
75
137
  if (matching) {
76
138
  setActiveUiRule(matching);
@@ -81,7 +143,7 @@ function ScopedQuotaSelectInput({
81
143
  return () => {
82
144
  isMounted = false;
83
145
  };
84
- }, [fieldLeafName, name]);
146
+ }, [fieldLeafName, normalizedFieldPath]);
85
147
  const optionsConfig = attribute?.options || {};
86
148
  const items = React.useMemo(() => {
87
149
  const rawItems = optionsConfig.items && optionsConfig.items.length > 0 ? optionsConfig.items : [];
@@ -115,7 +177,13 @@ function ScopedQuotaSelectInput({
115
177
  }, [optionsConfig.items, activeUiRule]);
116
178
  const disabledBehavior = activeUiRule?.exceededAction || optionsConfig.disabledBehavior || "disable";
117
179
  const otherUsage = React.useMemo(() => {
118
- return computeCrossComponentUsage(formValues, fieldLeafName, name, Boolean(activeUiRule?.includeRichTextHtml));
180
+ return computeCrossComponentUsage(
181
+ formValues,
182
+ activeUiRule?.targetField || fieldLeafName,
183
+ fieldLeafName,
184
+ name,
185
+ Boolean(activeUiRule?.includeRichTextHtml)
186
+ );
119
187
  }, [formValues, fieldLeafName, name, activeUiRule]);
120
188
  const processedOptions = React.useMemo(() => {
121
189
  return items.map((item) => {
@@ -207,4 +275,4 @@ function ScopedQuotaSelectInput({
207
275
  export {
208
276
  ScopedQuotaSelectInput as default
209
277
  };
210
- //# sourceMappingURL=ScopedQuotaSelectInput-Cy3eEj9-.mjs.map
278
+ //# sourceMappingURL=ScopedQuotaSelectInput-YKdQgrPt.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScopedQuotaSelectInput-YKdQgrPt.mjs","sources":["../../admin/src/components/ScopedQuotaSelectInput.tsx"],"sourcesContent":["import * as React from 'react';\nimport {\n Field,\n SingleSelect,\n SingleSelectOption,\n Box,\n Typography,\n} from '@strapi/design-system';\nimport { useForm } from '@strapi/admin/strapi-admin';\nimport { useFetchClient } from '@strapi/strapi/admin';\n\nexport interface QuotaItem {\n value: string;\n label: string;\n maxAllowed?: number | null; // e.g. 1 for H1, 3 for H2, null for unlimited\n}\n\nexport interface ScopedQuotaSelectProps {\n name: string;\n value?: string | null;\n onChange: (e: { target: { name: string; value: string } }) => void;\n attribute?: {\n type?: string;\n customField?: string;\n options?: {\n scope?: 'entry' | 'zone';\n disabledBehavior?: 'disable' | 'hide';\n items?: QuotaItem[];\n };\n };\n disabled?: boolean;\n required?: boolean;\n placeholder?: string;\n label?: React.ReactNode;\n hint?: React.ReactNode;\n}\n\n// Default fallback options if none configured on the attribute\nconst DEFAULT_HEADING_ITEMS: QuotaItem[] = [\n { value: 'h1', label: 'H1 - Main Page Title', maxAllowed: 1 },\n { value: 'h2', label: 'H2 - Primary Section Heading', maxAllowed: 3 },\n { value: 'h3', label: 'H3 - Sub-section Heading', maxAllowed: null },\n { value: 'h4', label: 'H4 - Component Title', maxAllowed: null },\n { value: 'h5', label: 'H5 - Card / Item Heading', maxAllowed: null },\n { value: 'h6', label: 'H6 - Minor Heading', maxAllowed: null },\n];\n\nfunction cleanKey(str: string): string {\n return (str || '').replace(/[-_]/g, '').toLowerCase().trim();\n}\n\nfunction matchesPath(normalizedPath: string, leafKey: string, targetField: string): boolean {\n if (!targetField) return false;\n const pClean = cleanKey(normalizedPath);\n const kClean = cleanKey(leafKey);\n const tClean = cleanKey(targetField);\n\n if (kClean === tClean || pClean === tClean) return true;\n\n const targetSegments = targetField\n .replace(/^api::/i, '')\n .split('.')\n .map(cleanKey)\n .filter(Boolean);\n\n const pathSegments = normalizedPath\n .split('.')\n .map(cleanKey)\n .filter(Boolean);\n\n if (targetSegments.length === 0 || pathSegments.length === 0) return false;\n\n if (targetSegments.length === 1) {\n const single = targetSegments[0];\n return kClean === single || pathSegments[pathSegments.length - 1] === single;\n }\n\n let isSuffix = true;\n for (let i = 0; i < targetSegments.length; i++) {\n const tSeg = targetSegments[targetSegments.length - 1 - i];\n const pSeg = pathSegments[pathSegments.length - 1 - i];\n if (tSeg !== '*' && tSeg !== pSeg) {\n isSuffix = false;\n break;\n }\n }\n if (isSuffix) return true;\n\n for (let startIdx = 1; startIdx < targetSegments.length; startIdx++) {\n const subTarget = targetSegments.slice(startIdx);\n if (subTarget.length > pathSegments.length) continue;\n let subSuffix = true;\n for (let i = 0; i < subTarget.length; i++) {\n const tSeg = subTarget[subTarget.length - 1 - i];\n const pSeg = pathSegments[pathSegments.length - 1 - i];\n if (tSeg !== '*' && tSeg !== pSeg) {\n subSuffix = false;\n break;\n }\n }\n if (subSuffix) return true;\n }\n\n let pIdx = 0;\n let matchesInOrder = true;\n let tStart = 0;\n if (targetSegments.length > 2 && targetSegments[0] !== pathSegments[0] && targetSegments[0] !== '*') {\n tStart = 1;\n }\n for (let tIdx = tStart; tIdx < targetSegments.length; tIdx++) {\n const tSeg = targetSegments[tIdx];\n if (tSeg === '*') continue;\n let found = false;\n while (pIdx < pathSegments.length) {\n if (pathSegments[pIdx] === tSeg) {\n found = true;\n pIdx++;\n break;\n }\n pIdx++;\n }\n if (!found) {\n matchesInOrder = false;\n break;\n }\n }\n\n return matchesInOrder;\n}\n\n/**\n * Recursively traverses the entire form data tree to count occurrences of target field values,\n * excluding the current input's exact field path.\n */\nfunction computeCrossComponentUsage(\n data: any,\n targetFieldRule: string,\n fieldLeafName: string,\n excludePath: string,\n includeRichTextHtml: boolean = false\n): Record<string, number> {\n const counts: Record<string, number> = {};\n\n function traverse(current: any, currentPath: string) {\n if (!current || typeof current !== 'object') return;\n\n for (const key of Object.keys(current)) {\n const val = current[key];\n const fullPath = currentPath ? `${currentPath}.${key}` : key;\n const normalizedPath = fullPath.replace(/\\.\\d+/g, '');\n\n const isTargetMatch = matchesPath(normalizedPath, key, targetFieldRule || fieldLeafName);\n\n if (isTargetMatch && fullPath !== excludePath && typeof val === 'string' && val.trim()) {\n const valNorm = val.toLowerCase().trim();\n counts[valNorm] = (counts[valNorm] || 0) + 1;\n }\n\n // If rule checks rich text HTML, scan strings with HTML or Markdown headings\n if (includeRichTextHtml && typeof val === 'string' && (val.includes('<h') || val.includes('#'))) {\n for (let level = 1; level <= 6; level++) {\n const tag = `h${level}`;\n const htmlMatches = val.match(new RegExp(`<${tag}\\\\b[^>]*>`, 'gi'));\n if (htmlMatches) {\n counts[tag] = (counts[tag] || 0) + htmlMatches.length;\n }\n const mdMatches = val.match(new RegExp(`(?:^|\\\\n)${'#'.repeat(level)}\\\\s+[^\\\\n]+`, 'g'));\n if (mdMatches) {\n counts[tag] = (counts[tag] || 0) + mdMatches.length;\n }\n }\n }\n\n if (typeof val === 'object' && val !== null) {\n traverse(val, fullPath);\n }\n }\n }\n\n traverse(data, '');\n return counts;\n}\n\nexport default function ScopedQuotaSelectInput({\n name,\n value,\n onChange,\n attribute,\n disabled = false,\n required = false,\n placeholder,\n label,\n hint,\n}: ScopedQuotaSelectProps) {\n const { get } = useFetchClient();\n\n // Subscribe to live Formik/Form values in Strapi v5 Admin Edit View\n const formValues = useForm('ScopedQuotaSelectInput', (state) => state.values) || {};\n\n // Extract the leaf property name (e.g., 'titleStyle' from 'blocks.0.titleStyle')\n const fieldLeafName = name.split('.').pop() || name;\n const normalizedFieldPath = name.replace(/\\.\\d+/g, '');\n\n // Active UI-configured rule state\n const [activeUiRule, setActiveUiRule] = React.useState<any>(null);\n\n React.useEffect(() => {\n let isMounted = true;\n get('/bulk-updater/validation-rules')\n .then((res: any) => {\n if (isMounted && res.data && res.data.success && Array.isArray(res.data.rules)) {\n const matching = res.data.rules.find((r: any) => {\n if (!r.enabled || !r.targetField) return false;\n return matchesPath(normalizedFieldPath, fieldLeafName, r.targetField);\n });\n\n if (matching) {\n setActiveUiRule(matching);\n }\n }\n })\n .catch(() => {\n // Fall back gracefully to schema options on network/auth failure\n });\n return () => {\n isMounted = false;\n };\n }, [fieldLeafName, normalizedFieldPath]);\n\n const optionsConfig = attribute?.options || {};\n\n // Dynamically merge items: include items from attribute options, UI rules, or default headings\n const items: QuotaItem[] = React.useMemo(() => {\n const rawItems: QuotaItem[] = optionsConfig.items && optionsConfig.items.length > 0\n ? optionsConfig.items\n : [];\n\n const map = new Map<string, QuotaItem>();\n rawItems.forEach((it) => {\n map.set((it.value || '').toLowerCase().trim(), { ...it });\n });\n\n if (activeUiRule?.quotas) {\n for (const [k, max] of Object.entries(activeUiRule.quotas)) {\n const lowerK = k.toLowerCase().trim();\n if (map.has(lowerK)) {\n const existing = map.get(lowerK)!;\n map.set(lowerK, {\n ...existing,\n maxAllowed: typeof max === 'number' ? max : existing.maxAllowed,\n });\n } else {\n // If option not in schema items, add it preserving the user-defined casing\n map.set(lowerK, {\n value: k, // preserve original casing from rule, e.g. \"H1\"\n label: k.toUpperCase(),\n maxAllowed: typeof max === 'number' ? max : null,\n });\n }\n }\n }\n\n if (map.size === 0) {\n DEFAULT_HEADING_ITEMS.forEach((it) => map.set(it.value.toLowerCase().trim(), { ...it }));\n }\n\n return Array.from(map.values());\n }, [optionsConfig.items, activeUiRule]);\n\n const disabledBehavior = activeUiRule?.exceededAction || optionsConfig.disabledBehavior || 'disable';\n\n // Compute other components' usage across the entire entry (and rich text HTML if rule enabled)\n const otherUsage = React.useMemo(() => {\n return computeCrossComponentUsage(\n formValues,\n activeUiRule?.targetField || fieldLeafName,\n fieldLeafName,\n name,\n Boolean(activeUiRule?.includeRichTextHtml)\n );\n }, [formValues, fieldLeafName, name, activeUiRule]);\n\n // Compute processed options: evaluate quotas and apply disable or hide rules\n const processedOptions = React.useMemo(() => {\n return items\n .map((item) => {\n const itemValLower = (item.value || '').toLowerCase().trim();\n const usedCount = otherUsage[itemValLower] || 0;\n const max = item.maxAllowed;\n\n // Quota is exhausted if other components have already reached maxAllowed\n const isQuotaReached = typeof max === 'number' && max > 0 && usedCount >= max;\n\n // Check if current field already holds this value (case-insensitively)\n const isCurrentValue = (value || '').toLowerCase().trim() === itemValLower;\n const isDisabled = isQuotaReached && !isCurrentValue;\n\n let quotaNotice = '';\n if (typeof max === 'number' && max > 0) {\n quotaNotice = `(${usedCount}/${max} used)`;\n }\n\n return {\n ...item,\n isDisabled,\n quotaNotice,\n isQuotaReached,\n };\n })\n .filter((item) => {\n if (disabledBehavior === 'hide' && item.isDisabled) {\n return false;\n }\n return true;\n });\n }, [items, otherUsage, value, disabledBehavior]);\n\n // Identify options whose quota has been exhausted by other components\n const exhaustedItems = React.useMemo(() => {\n return items.filter((i) => {\n const iValLower = (i.value || '').toLowerCase().trim();\n const isCurrentValue = (value || '').toLowerCase().trim() === iValLower;\n return (\n typeof i.maxAllowed === 'number' &&\n i.maxAllowed > 0 &&\n (otherUsage[iValLower] || 0) >= i.maxAllowed &&\n !isCurrentValue\n );\n });\n }, [items, otherUsage, value]);\n\n const handleChange = (selectedVal: string) => {\n if (onChange) {\n onChange({ target: { name, value: selectedVal } });\n }\n };\n\n const displayLabel = label || activeUiRule?.name || (fieldLeafName.charAt(0).toUpperCase() + fieldLeafName.slice(1));\n const displayPlaceholder = placeholder || `Select ${displayLabel}...`;\n const matchedItemValue = items.find((it) => (it.value || '').toLowerCase().trim() === (value || '').toLowerCase().trim())?.value || (value || '');\n\n return (\n <Field.Root name={name} required={required}>\n {displayLabel && <Field.Label>{displayLabel}</Field.Label>}\n <SingleSelect\n placeholder={displayPlaceholder}\n value={matchedItemValue}\n onChange={handleChange}\n disabled={disabled}\n >\n {processedOptions.map((opt) => (\n <SingleSelectOption\n key={opt.value}\n value={opt.value}\n disabled={opt.isDisabled}\n >\n {opt.label}{' '}\n {opt.quotaNotice && (\n <span style={{ opacity: 0.65, fontSize: '0.85em', marginLeft: 6 }}>\n [{opt.quotaNotice}]\n </span>\n )}\n </SingleSelectOption>\n ))}\n </SingleSelect>\n {hint && <Field.Hint>{hint}</Field.Hint>}\n\n {exhaustedItems.length > 0 && (\n <Box paddingTop={1}>\n {exhaustedItems.map((item) => (\n <Typography\n key={item.value}\n variant=\"pi\"\n textColor=\"neutral600\"\n style={{ display: 'block', marginTop: 2 }}\n >\n ℹ️ <strong>{item.label}</strong> quota reached (Max {item.maxAllowed} per entry).\n </Typography>\n ))}\n </Box>\n )}\n </Field.Root>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import { Box, Flex, Typography, Badge } from "@strapi/design-system";
3
3
  import { Check } from "@strapi/icons";
4
- import { V as ValidationRulesTab } from "./ValidationRulesTab-Dqkson67.mjs";
4
+ import { V as ValidationRulesTab } from "./ValidationRulesTab-VeRr-vxa.mjs";
5
5
  const ALL_FEATURES = [
6
6
  "Cross-collection batch attribute & relationship updates",
7
7
  "Interactive schema compatibility & target selection",
@@ -40,4 +40,4 @@ const SettingsPage = () => {
40
40
  export {
41
41
  SettingsPage as default
42
42
  };
43
- //# sourceMappingURL=SettingsPage-Bdse5MP4.mjs.map
43
+ //# sourceMappingURL=SettingsPage-BDObdCIY.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"SettingsPage-Bdse5MP4.mjs","sources":["../../admin/src/pages/SettingsPage.tsx"],"sourcesContent":["import React from 'react';\nimport {\n Box,\n Flex,\n Typography,\n Badge,\n} from '@strapi/design-system';\nimport { Check } from '@strapi/icons';\nimport ValidationRulesTab from './ValidationRulesTab';\n\nconst ALL_FEATURES = [\n 'Cross-collection batch attribute & relationship updates',\n 'Interactive schema compatibility & target selection',\n 'Full regex find & replace with live diff previews',\n 'Dynamic cross-component quota validation (UI-configurable)',\n 'Enterprise Maker-Checker 4-Eyes approval workflow',\n 'Server-enforced off-peak execution maintenance window',\n '3-Tier comprehensive audit logging with one-click reversible rollback',\n 'Zero-infra memory caching & pod-level ETag optimization',\n];\n\nconst SettingsPage: React.FC = () => {\n return (\n <Box padding={8} background=\"neutral100\">\n <Flex direction=\"column\" alignItems=\"flex-start\" gap={2} marginBottom={6}>\n <Flex justifyContent=\"space-between\" width=\"100%\">\n <Box>\n <Typography variant=\"alpha\" tag=\"h1\" fontWeight=\"bold\">\n Bulk Attribute Updater Settings\n </Typography>\n <Typography variant=\"omega\" textColor=\"neutral600\">\n Configure cross-component validation rules, manage quotas, and view active plugin capabilities.\n </Typography>\n </Box>\n </Flex>\n </Flex>\n\n {/* Plugin Status Card */}\n <Box padding={6} background=\"neutral0\" shadow=\"filterShadow\" hasRadius marginBottom={6}>\n <Flex justifyContent=\"space-between\" alignItems=\"center\" marginBottom={4}>\n <Box>\n <Typography variant=\"delta\" tag=\"h2\" fontWeight=\"semiBold\">\n Plugin Status\n </Typography>\n <Typography variant=\"pi\" textColor=\"neutral600\">\n All features are fully unlocked, active, and free under the MIT open-source license.\n </Typography>\n </Box>\n <Badge backgroundColor=\"success100\" textColor=\"success700\" size=\"M\">\n COMMUNITY & ENTERPRISE UNLOCKED\n </Badge>\n </Flex>\n\n {/* Features Checklist */}\n <Box marginTop={4}>\n <Typography variant=\"pi\" fontWeight=\"bold\" textColor=\"neutral700\" marginBottom={3}>\n Active Capabilities:\n </Typography>\n <Flex direction=\"column\" gap={2} alignItems=\"flex-start\" marginTop={2}>\n {ALL_FEATURES.map((feat, i) => (\n <Flex key={i} gap={2} alignItems=\"center\">\n <Check width=\"16px\" height=\"16px\" color=\"#328048\" />\n <Typography variant=\"omega\">{feat}</Typography>\n </Flex>\n ))}\n </Flex>\n </Box>\n </Box>\n\n {/* Validation Rules Section */}\n <Box marginTop={6}>\n <ValidationRulesTab />\n </Box>\n </Box>\n );\n};\n\nexport default SettingsPage;\n"],"names":[],"mappings":";;;;AAUA,MAAM,eAAe;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,eAAyB,MAAM;AACnC,SACE,qBAAC,KAAA,EAAI,SAAS,GAAG,YAAW,cAC1B,UAAA;AAAA,IAAA,oBAAC,QAAK,WAAU,UAAS,YAAW,cAAa,KAAK,GAAG,cAAc,GACrE,UAAA,oBAAC,QAAK,gBAAe,iBAAgB,OAAM,QACzC,+BAAC,KAAA,EACC,UAAA;AAAA,MAAA,oBAAC,cAAW,SAAQ,SAAQ,KAAI,MAAK,YAAW,QAAO,UAAA,kCAAA,CAEvD;AAAA,0BACC,YAAA,EAAW,SAAQ,SAAQ,WAAU,cAAa,UAAA,kGAAA,CAEnD;AAAA,IAAA,EAAA,CACF,GACF,GACF;AAAA,IAGA,qBAAC,KAAA,EAAI,SAAS,GAAG,YAAW,YAAW,QAAO,gBAAe,WAAS,MAAC,cAAc,GACnF,UAAA;AAAA,MAAA,qBAAC,QAAK,gBAAe,iBAAgB,YAAW,UAAS,cAAc,GACrE,UAAA;AAAA,QAAA,qBAAC,KAAA,EACC,UAAA;AAAA,UAAA,oBAAC,cAAW,SAAQ,SAAQ,KAAI,MAAK,YAAW,YAAW,UAAA,gBAAA,CAE3D;AAAA,8BACC,YAAA,EAAW,SAAQ,MAAK,WAAU,cAAa,UAAA,uFAAA,CAEhD;AAAA,QAAA,GACF;AAAA,QACA,oBAAC,SAAM,iBAAgB,cAAa,WAAU,cAAa,MAAK,KAAI,UAAA,kCAAA,CAEpE;AAAA,MAAA,GACF;AAAA,MAGA,qBAAC,KAAA,EAAI,WAAW,GACd,UAAA;AAAA,QAAA,oBAAC,YAAA,EAAW,SAAQ,MAAK,YAAW,QAAO,WAAU,cAAa,cAAc,GAAG,UAAA,uBAAA,CAEnF;AAAA,QACA,oBAAC,QAAK,WAAU,UAAS,KAAK,GAAG,YAAW,cAAa,WAAW,GACjE,uBAAa,IAAI,CAAC,MAAM,MACvB,qBAAC,QAAa,KAAK,GAAG,YAAW,UAC/B,UAAA;AAAA,UAAA,oBAAC,SAAM,OAAM,QAAO,QAAO,QAAO,OAAM,WAAU;AAAA,UAClD,oBAAC,YAAA,EAAW,SAAQ,SAAS,UAAA,KAAA,CAAK;AAAA,QAAA,EAAA,GAFzB,CAGX,CACD,EAAA,CACH;AAAA,MAAA,EAAA,CACF;AAAA,IAAA,GACF;AAAA,wBAGC,KAAA,EAAI,WAAW,GACd,UAAA,oBAAC,sBAAmB,EAAA,CACtB;AAAA,EAAA,GACF;AAEJ;"}
1
+ {"version":3,"file":"SettingsPage-BDObdCIY.mjs","sources":["../../admin/src/pages/SettingsPage.tsx"],"sourcesContent":["import React from 'react';\nimport {\n Box,\n Flex,\n Typography,\n Badge,\n} from '@strapi/design-system';\nimport { Check } from '@strapi/icons';\nimport ValidationRulesTab from './ValidationRulesTab';\n\nconst ALL_FEATURES = [\n 'Cross-collection batch attribute & relationship updates',\n 'Interactive schema compatibility & target selection',\n 'Full regex find & replace with live diff previews',\n 'Dynamic cross-component quota validation (UI-configurable)',\n 'Enterprise Maker-Checker 4-Eyes approval workflow',\n 'Server-enforced off-peak execution maintenance window',\n '3-Tier comprehensive audit logging with one-click reversible rollback',\n 'Zero-infra memory caching & pod-level ETag optimization',\n];\n\nconst SettingsPage: React.FC = () => {\n return (\n <Box padding={8} background=\"neutral100\">\n <Flex direction=\"column\" alignItems=\"flex-start\" gap={2} marginBottom={6}>\n <Flex justifyContent=\"space-between\" width=\"100%\">\n <Box>\n <Typography variant=\"alpha\" tag=\"h1\" fontWeight=\"bold\">\n Bulk Attribute Updater Settings\n </Typography>\n <Typography variant=\"omega\" textColor=\"neutral600\">\n Configure cross-component validation rules, manage quotas, and view active plugin capabilities.\n </Typography>\n </Box>\n </Flex>\n </Flex>\n\n {/* Plugin Status Card */}\n <Box padding={6} background=\"neutral0\" shadow=\"filterShadow\" hasRadius marginBottom={6}>\n <Flex justifyContent=\"space-between\" alignItems=\"center\" marginBottom={4}>\n <Box>\n <Typography variant=\"delta\" tag=\"h2\" fontWeight=\"semiBold\">\n Plugin Status\n </Typography>\n <Typography variant=\"pi\" textColor=\"neutral600\">\n All features are fully unlocked, active, and free under the MIT open-source license.\n </Typography>\n </Box>\n <Badge backgroundColor=\"success100\" textColor=\"success700\" size=\"M\">\n COMMUNITY & ENTERPRISE UNLOCKED\n </Badge>\n </Flex>\n\n {/* Features Checklist */}\n <Box marginTop={4}>\n <Typography variant=\"pi\" fontWeight=\"bold\" textColor=\"neutral700\" marginBottom={3}>\n Active Capabilities:\n </Typography>\n <Flex direction=\"column\" gap={2} alignItems=\"flex-start\" marginTop={2}>\n {ALL_FEATURES.map((feat, i) => (\n <Flex key={i} gap={2} alignItems=\"center\">\n <Check width=\"16px\" height=\"16px\" color=\"#328048\" />\n <Typography variant=\"omega\">{feat}</Typography>\n </Flex>\n ))}\n </Flex>\n </Box>\n </Box>\n\n {/* Validation Rules Section */}\n <Box marginTop={6}>\n <ValidationRulesTab />\n </Box>\n </Box>\n );\n};\n\nexport default SettingsPage;\n"],"names":[],"mappings":";;;;AAUA,MAAM,eAAe;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,eAAyB,MAAM;AACnC,SACE,qBAAC,KAAA,EAAI,SAAS,GAAG,YAAW,cAC1B,UAAA;AAAA,IAAA,oBAAC,QAAK,WAAU,UAAS,YAAW,cAAa,KAAK,GAAG,cAAc,GACrE,UAAA,oBAAC,QAAK,gBAAe,iBAAgB,OAAM,QACzC,+BAAC,KAAA,EACC,UAAA;AAAA,MAAA,oBAAC,cAAW,SAAQ,SAAQ,KAAI,MAAK,YAAW,QAAO,UAAA,kCAAA,CAEvD;AAAA,0BACC,YAAA,EAAW,SAAQ,SAAQ,WAAU,cAAa,UAAA,kGAAA,CAEnD;AAAA,IAAA,EAAA,CACF,GACF,GACF;AAAA,IAGA,qBAAC,KAAA,EAAI,SAAS,GAAG,YAAW,YAAW,QAAO,gBAAe,WAAS,MAAC,cAAc,GACnF,UAAA;AAAA,MAAA,qBAAC,QAAK,gBAAe,iBAAgB,YAAW,UAAS,cAAc,GACrE,UAAA;AAAA,QAAA,qBAAC,KAAA,EACC,UAAA;AAAA,UAAA,oBAAC,cAAW,SAAQ,SAAQ,KAAI,MAAK,YAAW,YAAW,UAAA,gBAAA,CAE3D;AAAA,8BACC,YAAA,EAAW,SAAQ,MAAK,WAAU,cAAa,UAAA,uFAAA,CAEhD;AAAA,QAAA,GACF;AAAA,QACA,oBAAC,SAAM,iBAAgB,cAAa,WAAU,cAAa,MAAK,KAAI,UAAA,kCAAA,CAEpE;AAAA,MAAA,GACF;AAAA,MAGA,qBAAC,KAAA,EAAI,WAAW,GACd,UAAA;AAAA,QAAA,oBAAC,YAAA,EAAW,SAAQ,MAAK,YAAW,QAAO,WAAU,cAAa,cAAc,GAAG,UAAA,uBAAA,CAEnF;AAAA,QACA,oBAAC,QAAK,WAAU,UAAS,KAAK,GAAG,YAAW,cAAa,WAAW,GACjE,uBAAa,IAAI,CAAC,MAAM,MACvB,qBAAC,QAAa,KAAK,GAAG,YAAW,UAC/B,UAAA;AAAA,UAAA,oBAAC,SAAM,OAAM,QAAO,QAAO,QAAO,OAAM,WAAU;AAAA,UAClD,oBAAC,YAAA,EAAW,SAAQ,SAAS,UAAA,KAAA,CAAK;AAAA,QAAA,EAAA,GAFzB,CAGX,CACD,EAAA,CACH;AAAA,MAAA,EAAA,CACF;AAAA,IAAA,GACF;AAAA,wBAGC,KAAA,EAAI,WAAW,GACd,UAAA,oBAAC,sBAAmB,EAAA,CACtB;AAAA,EAAA,GACF;AAEJ;"}
@@ -3,7 +3,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
4
  const designSystem = require("@strapi/design-system");
5
5
  const icons = require("@strapi/icons");
6
- const ValidationRulesTab = require("./ValidationRulesTab-BqGz1VLo.js");
6
+ const ValidationRulesTab = require("./ValidationRulesTab-DuvL-K4F.js");
7
7
  const ALL_FEATURES = [
8
8
  "Cross-collection batch attribute & relationship updates",
9
9
  "Interactive schema compatibility & target selection",
@@ -40,4 +40,4 @@ const SettingsPage = () => {
40
40
  ] });
41
41
  };
42
42
  exports.default = SettingsPage;
43
- //# sourceMappingURL=SettingsPage-B6Ia7SVS.js.map
43
+ //# sourceMappingURL=SettingsPage-M-HQ8Jtr.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SettingsPage-B6Ia7SVS.js","sources":["../../admin/src/pages/SettingsPage.tsx"],"sourcesContent":["import React from 'react';\nimport {\n Box,\n Flex,\n Typography,\n Badge,\n} from '@strapi/design-system';\nimport { Check } from '@strapi/icons';\nimport ValidationRulesTab from './ValidationRulesTab';\n\nconst ALL_FEATURES = [\n 'Cross-collection batch attribute & relationship updates',\n 'Interactive schema compatibility & target selection',\n 'Full regex find & replace with live diff previews',\n 'Dynamic cross-component quota validation (UI-configurable)',\n 'Enterprise Maker-Checker 4-Eyes approval workflow',\n 'Server-enforced off-peak execution maintenance window',\n '3-Tier comprehensive audit logging with one-click reversible rollback',\n 'Zero-infra memory caching & pod-level ETag optimization',\n];\n\nconst SettingsPage: React.FC = () => {\n return (\n <Box padding={8} background=\"neutral100\">\n <Flex direction=\"column\" alignItems=\"flex-start\" gap={2} marginBottom={6}>\n <Flex justifyContent=\"space-between\" width=\"100%\">\n <Box>\n <Typography variant=\"alpha\" tag=\"h1\" fontWeight=\"bold\">\n Bulk Attribute Updater Settings\n </Typography>\n <Typography variant=\"omega\" textColor=\"neutral600\">\n Configure cross-component validation rules, manage quotas, and view active plugin capabilities.\n </Typography>\n </Box>\n </Flex>\n </Flex>\n\n {/* Plugin Status Card */}\n <Box padding={6} background=\"neutral0\" shadow=\"filterShadow\" hasRadius marginBottom={6}>\n <Flex justifyContent=\"space-between\" alignItems=\"center\" marginBottom={4}>\n <Box>\n <Typography variant=\"delta\" tag=\"h2\" fontWeight=\"semiBold\">\n Plugin Status\n </Typography>\n <Typography variant=\"pi\" textColor=\"neutral600\">\n All features are fully unlocked, active, and free under the MIT open-source license.\n </Typography>\n </Box>\n <Badge backgroundColor=\"success100\" textColor=\"success700\" size=\"M\">\n COMMUNITY & ENTERPRISE UNLOCKED\n </Badge>\n </Flex>\n\n {/* Features Checklist */}\n <Box marginTop={4}>\n <Typography variant=\"pi\" fontWeight=\"bold\" textColor=\"neutral700\" marginBottom={3}>\n Active Capabilities:\n </Typography>\n <Flex direction=\"column\" gap={2} alignItems=\"flex-start\" marginTop={2}>\n {ALL_FEATURES.map((feat, i) => (\n <Flex key={i} gap={2} alignItems=\"center\">\n <Check width=\"16px\" height=\"16px\" color=\"#328048\" />\n <Typography variant=\"omega\">{feat}</Typography>\n </Flex>\n ))}\n </Flex>\n </Box>\n </Box>\n\n {/* Validation Rules Section */}\n <Box marginTop={6}>\n <ValidationRulesTab />\n </Box>\n </Box>\n );\n};\n\nexport default SettingsPage;\n"],"names":["jsxs","Box","jsx","Flex","Typography","Badge","Check","ValidationRulesTab"],"mappings":";;;;;;AAUA,MAAM,eAAe;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,eAAyB,MAAM;AACnC,SACEA,2BAAAA,KAACC,aAAAA,KAAA,EAAI,SAAS,GAAG,YAAW,cAC1B,UAAA;AAAA,IAAAC,2BAAAA,IAACC,aAAAA,QAAK,WAAU,UAAS,YAAW,cAAa,KAAK,GAAG,cAAc,GACrE,UAAAD,2BAAAA,IAACC,qBAAK,gBAAe,iBAAgB,OAAM,QACzC,0CAACF,kBAAA,EACC,UAAA;AAAA,MAAAC,2BAAAA,IAACE,aAAAA,cAAW,SAAQ,SAAQ,KAAI,MAAK,YAAW,QAAO,UAAA,kCAAA,CAEvD;AAAA,qCACCA,aAAAA,YAAA,EAAW,SAAQ,SAAQ,WAAU,cAAa,UAAA,kGAAA,CAEnD;AAAA,IAAA,EAAA,CACF,GACF,GACF;AAAA,IAGAJ,2BAAAA,KAACC,aAAAA,KAAA,EAAI,SAAS,GAAG,YAAW,YAAW,QAAO,gBAAe,WAAS,MAAC,cAAc,GACnF,UAAA;AAAA,MAAAD,gCAACG,aAAAA,QAAK,gBAAe,iBAAgB,YAAW,UAAS,cAAc,GACrE,UAAA;AAAA,QAAAH,gCAACC,aAAAA,KAAA,EACC,UAAA;AAAA,UAAAC,2BAAAA,IAACE,aAAAA,cAAW,SAAQ,SAAQ,KAAI,MAAK,YAAW,YAAW,UAAA,gBAAA,CAE3D;AAAA,yCACCA,aAAAA,YAAA,EAAW,SAAQ,MAAK,WAAU,cAAa,UAAA,uFAAA,CAEhD;AAAA,QAAA,GACF;AAAA,QACAF,2BAAAA,IAACG,aAAAA,SAAM,iBAAgB,cAAa,WAAU,cAAa,MAAK,KAAI,UAAA,kCAAA,CAEpE;AAAA,MAAA,GACF;AAAA,MAGAL,2BAAAA,KAACC,aAAAA,KAAA,EAAI,WAAW,GACd,UAAA;AAAA,QAAAC,2BAAAA,IAACE,aAAAA,YAAA,EAAW,SAAQ,MAAK,YAAW,QAAO,WAAU,cAAa,cAAc,GAAG,UAAA,uBAAA,CAEnF;AAAA,QACAF,2BAAAA,IAACC,aAAAA,QAAK,WAAU,UAAS,KAAK,GAAG,YAAW,cAAa,WAAW,GACjE,uBAAa,IAAI,CAAC,MAAM,MACvBH,gCAACG,aAAAA,QAAa,KAAK,GAAG,YAAW,UAC/B,UAAA;AAAA,UAAAD,+BAACI,MAAAA,SAAM,OAAM,QAAO,QAAO,QAAO,OAAM,WAAU;AAAA,UAClDJ,2BAAAA,IAACE,aAAAA,YAAA,EAAW,SAAQ,SAAS,UAAA,KAAA,CAAK;AAAA,QAAA,EAAA,GAFzB,CAGX,CACD,EAAA,CACH;AAAA,MAAA,EAAA,CACF;AAAA,IAAA,GACF;AAAA,mCAGCH,aAAAA,KAAA,EAAI,WAAW,GACd,UAAAC,2BAAAA,IAACK,yCAAmB,EAAA,CACtB;AAAA,EAAA,GACF;AAEJ;;"}
1
+ {"version":3,"file":"SettingsPage-M-HQ8Jtr.js","sources":["../../admin/src/pages/SettingsPage.tsx"],"sourcesContent":["import React from 'react';\nimport {\n Box,\n Flex,\n Typography,\n Badge,\n} from '@strapi/design-system';\nimport { Check } from '@strapi/icons';\nimport ValidationRulesTab from './ValidationRulesTab';\n\nconst ALL_FEATURES = [\n 'Cross-collection batch attribute & relationship updates',\n 'Interactive schema compatibility & target selection',\n 'Full regex find & replace with live diff previews',\n 'Dynamic cross-component quota validation (UI-configurable)',\n 'Enterprise Maker-Checker 4-Eyes approval workflow',\n 'Server-enforced off-peak execution maintenance window',\n '3-Tier comprehensive audit logging with one-click reversible rollback',\n 'Zero-infra memory caching & pod-level ETag optimization',\n];\n\nconst SettingsPage: React.FC = () => {\n return (\n <Box padding={8} background=\"neutral100\">\n <Flex direction=\"column\" alignItems=\"flex-start\" gap={2} marginBottom={6}>\n <Flex justifyContent=\"space-between\" width=\"100%\">\n <Box>\n <Typography variant=\"alpha\" tag=\"h1\" fontWeight=\"bold\">\n Bulk Attribute Updater Settings\n </Typography>\n <Typography variant=\"omega\" textColor=\"neutral600\">\n Configure cross-component validation rules, manage quotas, and view active plugin capabilities.\n </Typography>\n </Box>\n </Flex>\n </Flex>\n\n {/* Plugin Status Card */}\n <Box padding={6} background=\"neutral0\" shadow=\"filterShadow\" hasRadius marginBottom={6}>\n <Flex justifyContent=\"space-between\" alignItems=\"center\" marginBottom={4}>\n <Box>\n <Typography variant=\"delta\" tag=\"h2\" fontWeight=\"semiBold\">\n Plugin Status\n </Typography>\n <Typography variant=\"pi\" textColor=\"neutral600\">\n All features are fully unlocked, active, and free under the MIT open-source license.\n </Typography>\n </Box>\n <Badge backgroundColor=\"success100\" textColor=\"success700\" size=\"M\">\n COMMUNITY & ENTERPRISE UNLOCKED\n </Badge>\n </Flex>\n\n {/* Features Checklist */}\n <Box marginTop={4}>\n <Typography variant=\"pi\" fontWeight=\"bold\" textColor=\"neutral700\" marginBottom={3}>\n Active Capabilities:\n </Typography>\n <Flex direction=\"column\" gap={2} alignItems=\"flex-start\" marginTop={2}>\n {ALL_FEATURES.map((feat, i) => (\n <Flex key={i} gap={2} alignItems=\"center\">\n <Check width=\"16px\" height=\"16px\" color=\"#328048\" />\n <Typography variant=\"omega\">{feat}</Typography>\n </Flex>\n ))}\n </Flex>\n </Box>\n </Box>\n\n {/* Validation Rules Section */}\n <Box marginTop={6}>\n <ValidationRulesTab />\n </Box>\n </Box>\n );\n};\n\nexport default SettingsPage;\n"],"names":["jsxs","Box","jsx","Flex","Typography","Badge","Check","ValidationRulesTab"],"mappings":";;;;;;AAUA,MAAM,eAAe;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,eAAyB,MAAM;AACnC,SACEA,2BAAAA,KAACC,aAAAA,KAAA,EAAI,SAAS,GAAG,YAAW,cAC1B,UAAA;AAAA,IAAAC,2BAAAA,IAACC,aAAAA,QAAK,WAAU,UAAS,YAAW,cAAa,KAAK,GAAG,cAAc,GACrE,UAAAD,2BAAAA,IAACC,qBAAK,gBAAe,iBAAgB,OAAM,QACzC,0CAACF,kBAAA,EACC,UAAA;AAAA,MAAAC,2BAAAA,IAACE,aAAAA,cAAW,SAAQ,SAAQ,KAAI,MAAK,YAAW,QAAO,UAAA,kCAAA,CAEvD;AAAA,qCACCA,aAAAA,YAAA,EAAW,SAAQ,SAAQ,WAAU,cAAa,UAAA,kGAAA,CAEnD;AAAA,IAAA,EAAA,CACF,GACF,GACF;AAAA,IAGAJ,2BAAAA,KAACC,aAAAA,KAAA,EAAI,SAAS,GAAG,YAAW,YAAW,QAAO,gBAAe,WAAS,MAAC,cAAc,GACnF,UAAA;AAAA,MAAAD,gCAACG,aAAAA,QAAK,gBAAe,iBAAgB,YAAW,UAAS,cAAc,GACrE,UAAA;AAAA,QAAAH,gCAACC,aAAAA,KAAA,EACC,UAAA;AAAA,UAAAC,2BAAAA,IAACE,aAAAA,cAAW,SAAQ,SAAQ,KAAI,MAAK,YAAW,YAAW,UAAA,gBAAA,CAE3D;AAAA,yCACCA,aAAAA,YAAA,EAAW,SAAQ,MAAK,WAAU,cAAa,UAAA,uFAAA,CAEhD;AAAA,QAAA,GACF;AAAA,QACAF,2BAAAA,IAACG,aAAAA,SAAM,iBAAgB,cAAa,WAAU,cAAa,MAAK,KAAI,UAAA,kCAAA,CAEpE;AAAA,MAAA,GACF;AAAA,MAGAL,2BAAAA,KAACC,aAAAA,KAAA,EAAI,WAAW,GACd,UAAA;AAAA,QAAAC,2BAAAA,IAACE,aAAAA,YAAA,EAAW,SAAQ,MAAK,YAAW,QAAO,WAAU,cAAa,cAAc,GAAG,UAAA,uBAAA,CAEnF;AAAA,QACAF,2BAAAA,IAACC,aAAAA,QAAK,WAAU,UAAS,KAAK,GAAG,YAAW,cAAa,WAAW,GACjE,uBAAa,IAAI,CAAC,MAAM,MACvBH,gCAACG,aAAAA,QAAa,KAAK,GAAG,YAAW,UAC/B,UAAA;AAAA,UAAAD,+BAACI,MAAAA,SAAM,OAAM,QAAO,QAAO,QAAO,OAAM,WAAU;AAAA,UAClDJ,2BAAAA,IAACE,aAAAA,YAAA,EAAW,SAAQ,SAAS,UAAA,KAAA,CAAK;AAAA,QAAA,EAAA,GAFzB,CAGX,CACD,EAAA,CACH;AAAA,MAAA,EAAA,CACF;AAAA,IAAA,GACF;AAAA,mCAGCH,aAAAA,KAAA,EAAI,WAAW,GACd,UAAAC,2BAAAA,IAACK,yCAAmB,EAAA,CACtB;AAAA,EAAA,GACF;AAEJ;;"}
@@ -29,13 +29,19 @@ function ValidationRulesTab() {
29
29
  const fetchCollections = async () => {
30
30
  try {
31
31
  const res = await get("/bulk-updater/models");
32
- if (res.data?.targetUids && res.data?.metaByUid) {
33
- const list = res.data.targetUids.map((uid) => ({
32
+ let list = [];
33
+ if (Array.isArray(res.data?.collections)) {
34
+ list = res.data.collections.map((c) => ({
35
+ uid: c.uid,
36
+ displayName: c.displayName || c.singularName || c.uid
37
+ }));
38
+ } else if (res.data?.targetUids && res.data?.metaByUid) {
39
+ list = res.data.targetUids.map((uid) => ({
34
40
  uid,
35
41
  displayName: res.data.metaByUid[uid]?.displayName || uid
36
42
  }));
37
- setAvailableCollections(list);
38
43
  }
44
+ setAvailableCollections(list);
39
45
  } catch {
40
46
  }
41
47
  };
@@ -77,6 +83,7 @@ function ValidationRulesTab() {
77
83
  fetchCollections();
78
84
  }, []);
79
85
  const openCreateModal = () => {
86
+ fetchCollections();
80
87
  setEditingRule(null);
81
88
  setFormName("");
82
89
  setFormDesc("");
@@ -91,6 +98,7 @@ function ValidationRulesTab() {
91
98
  setModalOpen(true);
92
99
  };
93
100
  const openEditModal = (rule) => {
101
+ fetchCollections();
94
102
  setEditingRule(rule);
95
103
  setFormName(rule.name);
96
104
  setFormDesc(rule.description || "");
@@ -217,6 +225,11 @@ function ValidationRulesTab() {
217
225
  }
218
226
  };
219
227
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
228
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
229
+ [data-radix-popper-content-wrapper] {
230
+ z-index: 10005 !important;
231
+ }
232
+ ` }),
220
233
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 6, background: "neutral0", shadow: "filterShadow", hasRadius: true, marginBottom: 6, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: [
221
234
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
222
235
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", fontWeight: "bold", children: "Scoped & Cross-Component Validation Rules" }),
@@ -346,7 +359,7 @@ function ValidationRulesTab() {
346
359
  position: "fixed",
347
360
  inset: 0,
348
361
  backgroundColor: "rgba(33, 33, 52, 0.65)",
349
- zIndex: 9999,
362
+ zIndex: 1e3,
350
363
  display: "flex",
351
364
  alignItems: "center",
352
365
  justifyContent: "center"
@@ -395,10 +408,10 @@ function ValidationRulesTab() {
395
408
  designSystem.TextInput,
396
409
  {
397
410
  label: "Target Field / Component Property Path",
398
- placeholder: "e.g. headingLevel, titleStyle, or st-featured.titleHeadingStyle.titleStyle",
411
+ placeholder: "e.g. titleStyle, titleHeadingStyle.titleStyle, or content.titleHeadingStyle.titleStyle",
399
412
  value: formField,
400
413
  onChange: (e) => setFormField(e.target.value),
401
- hint: "Attribute name or dot-notated hierarchy (e.g. 'titleStyle' or 'st-featured.titleHeadingStyle.titleStyle').",
414
+ hint: "Matches leaf key ('titleStyle'), component path ('titleHeadingStyle.titleStyle'), dynamic zone path ('content.titleHeadingStyle.titleStyle'), or collection-prefixed path ('st-featured.titleHeadingStyle.titleStyle').",
402
415
  required: true
403
416
  }
404
417
  ),
@@ -524,7 +537,7 @@ function ValidationRulesTab() {
524
537
  position: "fixed",
525
538
  inset: 0,
526
539
  backgroundColor: "rgba(33, 33, 52, 0.65)",
527
- zIndex: 9999,
540
+ zIndex: 1e3,
528
541
  display: "flex",
529
542
  alignItems: "center",
530
543
  justifyContent: "center"
@@ -662,4 +675,4 @@ function ValidationRulesTab() {
662
675
  ] });
663
676
  }
664
677
  exports.ValidationRulesTab = ValidationRulesTab;
665
- //# sourceMappingURL=ValidationRulesTab-BqGz1VLo.js.map
678
+ //# sourceMappingURL=ValidationRulesTab-DuvL-K4F.js.map