strapi-plugin-bulk-updater 1.3.6 → 1.3.8
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-CXDs0Dws.js → BulkUpdater-BosdSbGF.js} +2 -2
- package/dist/_chunks/{BulkUpdater-CXDs0Dws.js.map → BulkUpdater-BosdSbGF.js.map} +1 -1
- package/dist/_chunks/{BulkUpdater-D812YfOE.mjs → BulkUpdater-DUrmamj-.mjs} +2 -2
- package/dist/_chunks/{BulkUpdater-D812YfOE.mjs.map → BulkUpdater-DUrmamj-.mjs.map} +1 -1
- package/dist/_chunks/{ScopedQuotaSelectInput-ChWrQIBV.mjs → ScopedQuotaSelectInput-Cy3eEj9-.mjs} +50 -23
- package/dist/_chunks/ScopedQuotaSelectInput-Cy3eEj9-.mjs.map +1 -0
- package/dist/_chunks/{ScopedQuotaSelectInput-s4oUMLgK.js → ScopedQuotaSelectInput-CyGlYa9x.js} +50 -23
- package/dist/_chunks/ScopedQuotaSelectInput-CyGlYa9x.js.map +1 -0
- package/dist/_chunks/{SettingsPage-BAvuOb7V.js → SettingsPage-B6Ia7SVS.js} +2 -2
- package/dist/_chunks/{SettingsPage-BAvuOb7V.js.map → SettingsPage-B6Ia7SVS.js.map} +1 -1
- package/dist/_chunks/{SettingsPage-BWo4l6Gd.mjs → SettingsPage-Bdse5MP4.mjs} +2 -2
- package/dist/_chunks/{SettingsPage-BWo4l6Gd.mjs.map → SettingsPage-Bdse5MP4.mjs.map} +1 -1
- package/dist/_chunks/{ValidationRulesTab-BLZWPGrZ.js → ValidationRulesTab-BqGz1VLo.js} +83 -16
- package/dist/_chunks/ValidationRulesTab-BqGz1VLo.js.map +1 -0
- package/dist/_chunks/{ValidationRulesTab-Dt53h-Ke.mjs → ValidationRulesTab-Dqkson67.mjs} +84 -17
- package/dist/_chunks/ValidationRulesTab-Dqkson67.mjs.map +1 -0
- package/dist/admin/index.js +3 -3
- package/dist/admin/index.mjs +3 -3
- package/dist/server/index.js +202 -66
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +202 -66
- package/dist/server/index.mjs.map +1 -1
- package/package.json +3 -3
- package/dist/_chunks/ScopedQuotaSelectInput-ChWrQIBV.mjs.map +0 -1
- package/dist/_chunks/ScopedQuotaSelectInput-s4oUMLgK.js.map +0 -1
- package/dist/_chunks/ValidationRulesTab-BLZWPGrZ.js.map +0 -1
- package/dist/_chunks/ValidationRulesTab-Dt53h-Ke.mjs.map +0 -1
package/dist/_chunks/{ScopedQuotaSelectInput-ChWrQIBV.mjs → ScopedQuotaSelectInput-Cy3eEj9-.mjs}
RENAMED
|
@@ -11,15 +11,32 @@ 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) {
|
|
14
|
+
function computeCrossComponentUsage(data, targetFieldLeafName, excludePath, includeRichTextHtml = false) {
|
|
15
15
|
const counts = {};
|
|
16
|
+
const targetLower = targetFieldLeafName.toLowerCase().trim();
|
|
16
17
|
function traverse(current, currentPath) {
|
|
17
18
|
if (!current || typeof current !== "object") return;
|
|
18
19
|
for (const key of Object.keys(current)) {
|
|
19
20
|
const val = current[key];
|
|
20
21
|
const fullPath = currentPath ? `${currentPath}.${key}` : key;
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
const keyLower = key.toLowerCase().trim();
|
|
23
|
+
const isTargetMatch = keyLower === targetLower || targetLower.endsWith(`.${keyLower}`) || targetLower.split(".").pop() === keyLower;
|
|
24
|
+
if (isTargetMatch && fullPath !== excludePath && typeof val === "string" && val.trim()) {
|
|
25
|
+
const valNorm = val.toLowerCase().trim();
|
|
26
|
+
counts[valNorm] = (counts[valNorm] || 0) + 1;
|
|
27
|
+
}
|
|
28
|
+
if (includeRichTextHtml && typeof val === "string" && (val.includes("<h") || val.includes("#"))) {
|
|
29
|
+
for (let level = 1; level <= 6; level++) {
|
|
30
|
+
const tag = `h${level}`;
|
|
31
|
+
const htmlMatches = val.match(new RegExp(`<${tag}\\b[^>]*>`, "gi"));
|
|
32
|
+
if (htmlMatches) {
|
|
33
|
+
counts[tag] = (counts[tag] || 0) + htmlMatches.length;
|
|
34
|
+
}
|
|
35
|
+
const mdMatches = val.match(new RegExp(`(?:^|\\n)${"#".repeat(level)}\\s+[^\\n]+`, "g"));
|
|
36
|
+
if (mdMatches) {
|
|
37
|
+
counts[tag] = (counts[tag] || 0) + mdMatches.length;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
23
40
|
}
|
|
24
41
|
if (typeof val === "object" && val !== null) {
|
|
25
42
|
traverse(val, fullPath);
|
|
@@ -48,9 +65,13 @@ function ScopedQuotaSelectInput({
|
|
|
48
65
|
let isMounted = true;
|
|
49
66
|
get("/bulk-updater/validation-rules").then((res) => {
|
|
50
67
|
if (isMounted && res.data && res.data.success && Array.isArray(res.data.rules)) {
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
)
|
|
68
|
+
const leafLower = fieldLeafName.toLowerCase().trim();
|
|
69
|
+
const nameLower = name.toLowerCase().trim();
|
|
70
|
+
const matching = res.data.rules.find((r) => {
|
|
71
|
+
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}`);
|
|
74
|
+
});
|
|
54
75
|
if (matching) {
|
|
55
76
|
setActiveUiRule(matching);
|
|
56
77
|
}
|
|
@@ -60,15 +81,17 @@ function ScopedQuotaSelectInput({
|
|
|
60
81
|
return () => {
|
|
61
82
|
isMounted = false;
|
|
62
83
|
};
|
|
63
|
-
}, [fieldLeafName]);
|
|
84
|
+
}, [fieldLeafName, name]);
|
|
64
85
|
const optionsConfig = attribute?.options || {};
|
|
65
86
|
const items = React.useMemo(() => {
|
|
66
|
-
const rawItems = optionsConfig.items && optionsConfig.items.length > 0 ? optionsConfig.items :
|
|
87
|
+
const rawItems = optionsConfig.items && optionsConfig.items.length > 0 ? optionsConfig.items : [];
|
|
67
88
|
const map = /* @__PURE__ */ new Map();
|
|
68
|
-
rawItems.forEach((it) =>
|
|
89
|
+
rawItems.forEach((it) => {
|
|
90
|
+
map.set((it.value || "").toLowerCase().trim(), { ...it });
|
|
91
|
+
});
|
|
69
92
|
if (activeUiRule?.quotas) {
|
|
70
93
|
for (const [k, max] of Object.entries(activeUiRule.quotas)) {
|
|
71
|
-
const lowerK = k.toLowerCase();
|
|
94
|
+
const lowerK = k.toLowerCase().trim();
|
|
72
95
|
if (map.has(lowerK)) {
|
|
73
96
|
const existing = map.get(lowerK);
|
|
74
97
|
map.set(lowerK, {
|
|
@@ -76,30 +99,31 @@ function ScopedQuotaSelectInput({
|
|
|
76
99
|
maxAllowed: typeof max === "number" ? max : existing.maxAllowed
|
|
77
100
|
});
|
|
78
101
|
} else {
|
|
79
|
-
const label2 = k.charAt(0).toUpperCase() + k.slice(1);
|
|
80
102
|
map.set(lowerK, {
|
|
81
|
-
value:
|
|
82
|
-
|
|
103
|
+
value: k,
|
|
104
|
+
// preserve original casing from rule, e.g. "H1"
|
|
105
|
+
label: k.toUpperCase(),
|
|
83
106
|
maxAllowed: typeof max === "number" ? max : null
|
|
84
107
|
});
|
|
85
108
|
}
|
|
86
109
|
}
|
|
87
110
|
}
|
|
88
111
|
if (map.size === 0) {
|
|
89
|
-
DEFAULT_HEADING_ITEMS.forEach((it) => map.set(it.value.toLowerCase(), { ...it }));
|
|
112
|
+
DEFAULT_HEADING_ITEMS.forEach((it) => map.set(it.value.toLowerCase().trim(), { ...it }));
|
|
90
113
|
}
|
|
91
114
|
return Array.from(map.values());
|
|
92
115
|
}, [optionsConfig.items, activeUiRule]);
|
|
93
116
|
const disabledBehavior = activeUiRule?.exceededAction || optionsConfig.disabledBehavior || "disable";
|
|
94
117
|
const otherUsage = React.useMemo(() => {
|
|
95
|
-
return computeCrossComponentUsage(formValues, fieldLeafName, name);
|
|
96
|
-
}, [formValues, fieldLeafName, name]);
|
|
118
|
+
return computeCrossComponentUsage(formValues, fieldLeafName, name, Boolean(activeUiRule?.includeRichTextHtml));
|
|
119
|
+
}, [formValues, fieldLeafName, name, activeUiRule]);
|
|
97
120
|
const processedOptions = React.useMemo(() => {
|
|
98
121
|
return items.map((item) => {
|
|
99
|
-
const
|
|
122
|
+
const itemValLower = (item.value || "").toLowerCase().trim();
|
|
123
|
+
const usedCount = otherUsage[itemValLower] || 0;
|
|
100
124
|
const max = item.maxAllowed;
|
|
101
125
|
const isQuotaReached = typeof max === "number" && max > 0 && usedCount >= max;
|
|
102
|
-
const isCurrentValue = value ===
|
|
126
|
+
const isCurrentValue = (value || "").toLowerCase().trim() === itemValLower;
|
|
103
127
|
const isDisabled = isQuotaReached && !isCurrentValue;
|
|
104
128
|
let quotaNotice = "";
|
|
105
129
|
if (typeof max === "number" && max > 0) {
|
|
@@ -119,9 +143,11 @@ function ScopedQuotaSelectInput({
|
|
|
119
143
|
});
|
|
120
144
|
}, [items, otherUsage, value, disabledBehavior]);
|
|
121
145
|
const exhaustedItems = React.useMemo(() => {
|
|
122
|
-
return items.filter(
|
|
123
|
-
|
|
124
|
-
|
|
146
|
+
return items.filter((i) => {
|
|
147
|
+
const iValLower = (i.value || "").toLowerCase().trim();
|
|
148
|
+
const isCurrentValue = (value || "").toLowerCase().trim() === iValLower;
|
|
149
|
+
return typeof i.maxAllowed === "number" && i.maxAllowed > 0 && (otherUsage[iValLower] || 0) >= i.maxAllowed && !isCurrentValue;
|
|
150
|
+
});
|
|
125
151
|
}, [items, otherUsage, value]);
|
|
126
152
|
const handleChange = (selectedVal) => {
|
|
127
153
|
if (onChange) {
|
|
@@ -130,13 +156,14 @@ function ScopedQuotaSelectInput({
|
|
|
130
156
|
};
|
|
131
157
|
const displayLabel = label || activeUiRule?.name || fieldLeafName.charAt(0).toUpperCase() + fieldLeafName.slice(1);
|
|
132
158
|
const displayPlaceholder = placeholder || `Select ${displayLabel}...`;
|
|
159
|
+
const matchedItemValue = items.find((it) => (it.value || "").toLowerCase().trim() === (value || "").toLowerCase().trim())?.value || (value || "");
|
|
133
160
|
return /* @__PURE__ */ jsxs(Field.Root, { name, required, children: [
|
|
134
161
|
displayLabel && /* @__PURE__ */ jsx(Field.Label, { children: displayLabel }),
|
|
135
162
|
/* @__PURE__ */ jsx(
|
|
136
163
|
SingleSelect,
|
|
137
164
|
{
|
|
138
165
|
placeholder: displayPlaceholder,
|
|
139
|
-
value:
|
|
166
|
+
value: matchedItemValue,
|
|
140
167
|
onChange: handleChange,
|
|
141
168
|
disabled,
|
|
142
169
|
children: processedOptions.map((opt) => /* @__PURE__ */ jsxs(
|
|
@@ -180,4 +207,4 @@ function ScopedQuotaSelectInput({
|
|
|
180
207
|
export {
|
|
181
208
|
ScopedQuotaSelectInput as default
|
|
182
209
|
};
|
|
183
|
-
//# sourceMappingURL=ScopedQuotaSelectInput-
|
|
210
|
+
//# sourceMappingURL=ScopedQuotaSelectInput-Cy3eEj9-.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScopedQuotaSelectInput-Cy3eEj9-.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\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 targetFieldLeafName: string,\n excludePath: string,\n includeRichTextHtml: boolean = false\n): Record<string, number> {\n const counts: Record<string, number> = {};\n const targetLower = targetFieldLeafName.toLowerCase().trim();\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\n const keyLower = key.toLowerCase().trim();\n const isTargetMatch =\n keyLower === targetLower ||\n targetLower.endsWith(`.${keyLower}`) ||\n targetLower.split('.').pop() === keyLower;\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\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 leafLower = fieldLeafName.toLowerCase().trim();\n const nameLower = name.toLowerCase().trim();\n\n const matching = res.data.rules.find((r: any) => {\n if (!r.enabled || !r.targetField) return false;\n const targetLower = r.targetField.toLowerCase().trim();\n return (\n targetLower === leafLower ||\n targetLower === nameLower ||\n targetLower.endsWith(`.${leafLower}`) ||\n targetLower.split('.').pop() === leafLower ||\n nameLower.endsWith(`.${targetLower}`)\n );\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, name]);\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(formValues, fieldLeafName, name, Boolean(activeUiRule?.includeRichTextHtml));\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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"}
|
package/dist/_chunks/{ScopedQuotaSelectInput-s4oUMLgK.js → ScopedQuotaSelectInput-CyGlYa9x.js}
RENAMED
|
@@ -31,15 +31,32 @@ 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) {
|
|
34
|
+
function computeCrossComponentUsage(data, targetFieldLeafName, excludePath, includeRichTextHtml = false) {
|
|
35
35
|
const counts = {};
|
|
36
|
+
const targetLower = targetFieldLeafName.toLowerCase().trim();
|
|
36
37
|
function traverse(current, currentPath) {
|
|
37
38
|
if (!current || typeof current !== "object") return;
|
|
38
39
|
for (const key of Object.keys(current)) {
|
|
39
40
|
const val = current[key];
|
|
40
41
|
const fullPath = currentPath ? `${currentPath}.${key}` : key;
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
const keyLower = key.toLowerCase().trim();
|
|
43
|
+
const isTargetMatch = keyLower === targetLower || targetLower.endsWith(`.${keyLower}`) || targetLower.split(".").pop() === keyLower;
|
|
44
|
+
if (isTargetMatch && fullPath !== excludePath && typeof val === "string" && val.trim()) {
|
|
45
|
+
const valNorm = val.toLowerCase().trim();
|
|
46
|
+
counts[valNorm] = (counts[valNorm] || 0) + 1;
|
|
47
|
+
}
|
|
48
|
+
if (includeRichTextHtml && typeof val === "string" && (val.includes("<h") || val.includes("#"))) {
|
|
49
|
+
for (let level = 1; level <= 6; level++) {
|
|
50
|
+
const tag = `h${level}`;
|
|
51
|
+
const htmlMatches = val.match(new RegExp(`<${tag}\\b[^>]*>`, "gi"));
|
|
52
|
+
if (htmlMatches) {
|
|
53
|
+
counts[tag] = (counts[tag] || 0) + htmlMatches.length;
|
|
54
|
+
}
|
|
55
|
+
const mdMatches = val.match(new RegExp(`(?:^|\\n)${"#".repeat(level)}\\s+[^\\n]+`, "g"));
|
|
56
|
+
if (mdMatches) {
|
|
57
|
+
counts[tag] = (counts[tag] || 0) + mdMatches.length;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
43
60
|
}
|
|
44
61
|
if (typeof val === "object" && val !== null) {
|
|
45
62
|
traverse(val, fullPath);
|
|
@@ -68,9 +85,13 @@ function ScopedQuotaSelectInput({
|
|
|
68
85
|
let isMounted = true;
|
|
69
86
|
get("/bulk-updater/validation-rules").then((res) => {
|
|
70
87
|
if (isMounted && res.data && res.data.success && Array.isArray(res.data.rules)) {
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
)
|
|
88
|
+
const leafLower = fieldLeafName.toLowerCase().trim();
|
|
89
|
+
const nameLower = name.toLowerCase().trim();
|
|
90
|
+
const matching = res.data.rules.find((r) => {
|
|
91
|
+
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}`);
|
|
94
|
+
});
|
|
74
95
|
if (matching) {
|
|
75
96
|
setActiveUiRule(matching);
|
|
76
97
|
}
|
|
@@ -80,15 +101,17 @@ function ScopedQuotaSelectInput({
|
|
|
80
101
|
return () => {
|
|
81
102
|
isMounted = false;
|
|
82
103
|
};
|
|
83
|
-
}, [fieldLeafName]);
|
|
104
|
+
}, [fieldLeafName, name]);
|
|
84
105
|
const optionsConfig = attribute?.options || {};
|
|
85
106
|
const items = React__namespace.useMemo(() => {
|
|
86
|
-
const rawItems = optionsConfig.items && optionsConfig.items.length > 0 ? optionsConfig.items :
|
|
107
|
+
const rawItems = optionsConfig.items && optionsConfig.items.length > 0 ? optionsConfig.items : [];
|
|
87
108
|
const map = /* @__PURE__ */ new Map();
|
|
88
|
-
rawItems.forEach((it) =>
|
|
109
|
+
rawItems.forEach((it) => {
|
|
110
|
+
map.set((it.value || "").toLowerCase().trim(), { ...it });
|
|
111
|
+
});
|
|
89
112
|
if (activeUiRule?.quotas) {
|
|
90
113
|
for (const [k, max] of Object.entries(activeUiRule.quotas)) {
|
|
91
|
-
const lowerK = k.toLowerCase();
|
|
114
|
+
const lowerK = k.toLowerCase().trim();
|
|
92
115
|
if (map.has(lowerK)) {
|
|
93
116
|
const existing = map.get(lowerK);
|
|
94
117
|
map.set(lowerK, {
|
|
@@ -96,30 +119,31 @@ function ScopedQuotaSelectInput({
|
|
|
96
119
|
maxAllowed: typeof max === "number" ? max : existing.maxAllowed
|
|
97
120
|
});
|
|
98
121
|
} else {
|
|
99
|
-
const label2 = k.charAt(0).toUpperCase() + k.slice(1);
|
|
100
122
|
map.set(lowerK, {
|
|
101
|
-
value:
|
|
102
|
-
|
|
123
|
+
value: k,
|
|
124
|
+
// preserve original casing from rule, e.g. "H1"
|
|
125
|
+
label: k.toUpperCase(),
|
|
103
126
|
maxAllowed: typeof max === "number" ? max : null
|
|
104
127
|
});
|
|
105
128
|
}
|
|
106
129
|
}
|
|
107
130
|
}
|
|
108
131
|
if (map.size === 0) {
|
|
109
|
-
DEFAULT_HEADING_ITEMS.forEach((it) => map.set(it.value.toLowerCase(), { ...it }));
|
|
132
|
+
DEFAULT_HEADING_ITEMS.forEach((it) => map.set(it.value.toLowerCase().trim(), { ...it }));
|
|
110
133
|
}
|
|
111
134
|
return Array.from(map.values());
|
|
112
135
|
}, [optionsConfig.items, activeUiRule]);
|
|
113
136
|
const disabledBehavior = activeUiRule?.exceededAction || optionsConfig.disabledBehavior || "disable";
|
|
114
137
|
const otherUsage = React__namespace.useMemo(() => {
|
|
115
|
-
return computeCrossComponentUsage(formValues, fieldLeafName, name);
|
|
116
|
-
}, [formValues, fieldLeafName, name]);
|
|
138
|
+
return computeCrossComponentUsage(formValues, fieldLeafName, name, Boolean(activeUiRule?.includeRichTextHtml));
|
|
139
|
+
}, [formValues, fieldLeafName, name, activeUiRule]);
|
|
117
140
|
const processedOptions = React__namespace.useMemo(() => {
|
|
118
141
|
return items.map((item) => {
|
|
119
|
-
const
|
|
142
|
+
const itemValLower = (item.value || "").toLowerCase().trim();
|
|
143
|
+
const usedCount = otherUsage[itemValLower] || 0;
|
|
120
144
|
const max = item.maxAllowed;
|
|
121
145
|
const isQuotaReached = typeof max === "number" && max > 0 && usedCount >= max;
|
|
122
|
-
const isCurrentValue = value ===
|
|
146
|
+
const isCurrentValue = (value || "").toLowerCase().trim() === itemValLower;
|
|
123
147
|
const isDisabled = isQuotaReached && !isCurrentValue;
|
|
124
148
|
let quotaNotice = "";
|
|
125
149
|
if (typeof max === "number" && max > 0) {
|
|
@@ -139,9 +163,11 @@ function ScopedQuotaSelectInput({
|
|
|
139
163
|
});
|
|
140
164
|
}, [items, otherUsage, value, disabledBehavior]);
|
|
141
165
|
const exhaustedItems = React__namespace.useMemo(() => {
|
|
142
|
-
return items.filter(
|
|
143
|
-
|
|
144
|
-
|
|
166
|
+
return items.filter((i) => {
|
|
167
|
+
const iValLower = (i.value || "").toLowerCase().trim();
|
|
168
|
+
const isCurrentValue = (value || "").toLowerCase().trim() === iValLower;
|
|
169
|
+
return typeof i.maxAllowed === "number" && i.maxAllowed > 0 && (otherUsage[iValLower] || 0) >= i.maxAllowed && !isCurrentValue;
|
|
170
|
+
});
|
|
145
171
|
}, [items, otherUsage, value]);
|
|
146
172
|
const handleChange = (selectedVal) => {
|
|
147
173
|
if (onChange) {
|
|
@@ -150,13 +176,14 @@ function ScopedQuotaSelectInput({
|
|
|
150
176
|
};
|
|
151
177
|
const displayLabel = label || activeUiRule?.name || fieldLeafName.charAt(0).toUpperCase() + fieldLeafName.slice(1);
|
|
152
178
|
const displayPlaceholder = placeholder || `Select ${displayLabel}...`;
|
|
179
|
+
const matchedItemValue = items.find((it) => (it.value || "").toLowerCase().trim() === (value || "").toLowerCase().trim())?.value || (value || "");
|
|
153
180
|
return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { name, required, children: [
|
|
154
181
|
displayLabel && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: displayLabel }),
|
|
155
182
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
156
183
|
designSystem.SingleSelect,
|
|
157
184
|
{
|
|
158
185
|
placeholder: displayPlaceholder,
|
|
159
|
-
value:
|
|
186
|
+
value: matchedItemValue,
|
|
160
187
|
onChange: handleChange,
|
|
161
188
|
disabled,
|
|
162
189
|
children: processedOptions.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -198,4 +225,4 @@ function ScopedQuotaSelectInput({
|
|
|
198
225
|
] });
|
|
199
226
|
}
|
|
200
227
|
exports.default = ScopedQuotaSelectInput;
|
|
201
|
-
//# sourceMappingURL=ScopedQuotaSelectInput-
|
|
228
|
+
//# sourceMappingURL=ScopedQuotaSelectInput-CyGlYa9x.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScopedQuotaSelectInput-CyGlYa9x.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\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 targetFieldLeafName: string,\n excludePath: string,\n includeRichTextHtml: boolean = false\n): Record<string, number> {\n const counts: Record<string, number> = {};\n const targetLower = targetFieldLeafName.toLowerCase().trim();\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\n const keyLower = key.toLowerCase().trim();\n const isTargetMatch =\n keyLower === targetLower ||\n targetLower.endsWith(`.${keyLower}`) ||\n targetLower.split('.').pop() === keyLower;\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\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 leafLower = fieldLeafName.toLowerCase().trim();\n const nameLower = name.toLowerCase().trim();\n\n const matching = res.data.rules.find((r: any) => {\n if (!r.enabled || !r.targetField) return false;\n const targetLower = r.targetField.toLowerCase().trim();\n return (\n targetLower === leafLower ||\n targetLower === nameLower ||\n targetLower.endsWith(`.${leafLower}`) ||\n targetLower.split('.').pop() === leafLower ||\n nameLower.endsWith(`.${targetLower}`)\n );\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, name]);\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(formValues, fieldLeafName, name, Boolean(activeUiRule?.includeRichTextHtml));\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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"}
|
|
@@ -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-BqGz1VLo.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-B6Ia7SVS.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SettingsPage-
|
|
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,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-Dqkson67.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-Bdse5MP4.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SettingsPage-
|
|
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;"}
|