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.
- package/dist/_chunks/{BulkUpdater-BosdSbGF.js → BulkUpdater-DRHQBZZV.js} +20 -4
- package/dist/_chunks/BulkUpdater-DRHQBZZV.js.map +1 -0
- package/dist/_chunks/{BulkUpdater-DUrmamj-.mjs → BulkUpdater-J21ZgJWx.mjs} +20 -4
- package/dist/_chunks/BulkUpdater-J21ZgJWx.mjs.map +1 -0
- package/dist/_chunks/{ScopedQuotaSelectInput-CyGlYa9x.js → ScopedQuotaSelectInput-BsBpmwun.js} +79 -11
- package/dist/_chunks/ScopedQuotaSelectInput-BsBpmwun.js.map +1 -0
- package/dist/_chunks/{ScopedQuotaSelectInput-Cy3eEj9-.mjs → ScopedQuotaSelectInput-YKdQgrPt.mjs} +79 -11
- package/dist/_chunks/ScopedQuotaSelectInput-YKdQgrPt.mjs.map +1 -0
- package/dist/_chunks/{SettingsPage-Bdse5MP4.mjs → SettingsPage-BDObdCIY.mjs} +2 -2
- package/dist/_chunks/{SettingsPage-Bdse5MP4.mjs.map → SettingsPage-BDObdCIY.mjs.map} +1 -1
- package/dist/_chunks/{SettingsPage-B6Ia7SVS.js → SettingsPage-M-HQ8Jtr.js} +2 -2
- package/dist/_chunks/{SettingsPage-B6Ia7SVS.js.map → SettingsPage-M-HQ8Jtr.js.map} +1 -1
- package/dist/_chunks/{ValidationRulesTab-BqGz1VLo.js → ValidationRulesTab-DuvL-K4F.js} +21 -8
- package/dist/_chunks/ValidationRulesTab-DuvL-K4F.js.map +1 -0
- package/dist/_chunks/{ValidationRulesTab-Dqkson67.mjs → ValidationRulesTab-VeRr-vxa.mjs} +21 -8
- package/dist/_chunks/ValidationRulesTab-VeRr-vxa.mjs.map +1 -0
- package/dist/admin/index.js +3 -3
- package/dist/admin/index.mjs +3 -3
- package/dist/server/index.js +223 -42
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +223 -42
- package/dist/server/index.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/_chunks/BulkUpdater-BosdSbGF.js.map +0 -1
- package/dist/_chunks/BulkUpdater-DUrmamj-.mjs.map +0 -1
- package/dist/_chunks/ScopedQuotaSelectInput-Cy3eEj9-.mjs.map +0 -1
- package/dist/_chunks/ScopedQuotaSelectInput-CyGlYa9x.js.map +0 -1
- package/dist/_chunks/ValidationRulesTab-BqGz1VLo.js.map +0 -1
- package/dist/_chunks/ValidationRulesTab-Dqkson67.mjs.map +0 -1
package/dist/_chunks/{ScopedQuotaSelectInput-CyGlYa9x.js → ScopedQuotaSelectInput-BsBpmwun.js}
RENAMED
|
@@ -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
|
|
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
|
|
43
|
-
const isTargetMatch =
|
|
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
|
-
|
|
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,
|
|
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(
|
|
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-
|
|
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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"}
|
package/dist/_chunks/{ScopedQuotaSelectInput-Cy3eEj9-.mjs → ScopedQuotaSelectInput-YKdQgrPt.mjs}
RENAMED
|
@@ -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
|
|
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
|
|
23
|
-
const isTargetMatch =
|
|
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
|
-
|
|
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,
|
|
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(
|
|
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-
|
|
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-
|
|
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-
|
|
43
|
+
//# sourceMappingURL=SettingsPage-BDObdCIY.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SettingsPage-
|
|
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-
|
|
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-
|
|
43
|
+
//# sourceMappingURL=SettingsPage-M-HQ8Jtr.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SettingsPage-
|
|
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
|
-
|
|
33
|
-
|
|
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:
|
|
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.
|
|
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: "
|
|
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:
|
|
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-
|
|
678
|
+
//# sourceMappingURL=ValidationRulesTab-DuvL-K4F.js.map
|