strapi-plugin-bulk-updater 1.3.3 → 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-C2lw4D0D.js → BulkUpdater-BosdSbGF.js} +182 -143
- package/dist/_chunks/BulkUpdater-BosdSbGF.js.map +1 -0
- package/dist/_chunks/{BulkUpdater-CgsqzdyX.mjs → BulkUpdater-DUrmamj-.mjs} +183 -144
- package/dist/_chunks/BulkUpdater-DUrmamj-.mjs.map +1 -0
- 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 +45 -3
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +44 -4
- package/dist/admin/index.mjs.map +1 -1
- package/dist/server/index.js +607 -128
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +607 -128
- package/dist/server/index.mjs.map +1 -1
- package/package.json +3 -3
- package/dist/_chunks/BulkUpdater-C2lw4D0D.js.map +0 -1
- package/dist/_chunks/BulkUpdater-CgsqzdyX.mjs.map +0 -1
- 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
|
@@ -11,10 +11,13 @@ function ValidationRulesTab() {
|
|
|
11
11
|
const [loading, setLoading] = React.useState(true);
|
|
12
12
|
const [modalOpen, setModalOpen] = React.useState(false);
|
|
13
13
|
const [editingRule, setEditingRule] = React.useState(null);
|
|
14
|
+
const [availableCollections, setAvailableCollections] = React.useState([]);
|
|
14
15
|
const [formName, setFormName] = React.useState("");
|
|
15
16
|
const [formDesc, setFormDesc] = React.useState("");
|
|
17
|
+
const [formCollectionUid, setFormCollectionUid] = React.useState("*");
|
|
16
18
|
const [formField, setFormField] = React.useState("");
|
|
17
19
|
const [formAction, setFormAction] = React.useState("disable");
|
|
20
|
+
const [formIncludeRichText, setFormIncludeRichText] = React.useState(true);
|
|
18
21
|
const [formQuotas, setFormQuotas] = React.useState([
|
|
19
22
|
{ key: "h1", maxAllowed: 1 },
|
|
20
23
|
{ key: "h2", maxAllowed: 3 }
|
|
@@ -23,6 +26,19 @@ function ValidationRulesTab() {
|
|
|
23
26
|
const [scanModalOpen, setScanModalOpen] = React.useState(false);
|
|
24
27
|
const [scanningRuleId, setScanningRuleId] = React.useState(null);
|
|
25
28
|
const [scanReport, setScanReport] = React.useState(null);
|
|
29
|
+
const fetchCollections = async () => {
|
|
30
|
+
try {
|
|
31
|
+
const res = await get("/bulk-updater/models");
|
|
32
|
+
if (res.data?.targetUids && res.data?.metaByUid) {
|
|
33
|
+
const list = res.data.targetUids.map((uid) => ({
|
|
34
|
+
uid,
|
|
35
|
+
displayName: res.data.metaByUid[uid]?.displayName || uid
|
|
36
|
+
}));
|
|
37
|
+
setAvailableCollections(list);
|
|
38
|
+
}
|
|
39
|
+
} catch {
|
|
40
|
+
}
|
|
41
|
+
};
|
|
26
42
|
const handleScanRule = async (rule) => {
|
|
27
43
|
try {
|
|
28
44
|
setScanningRuleId(rule.id);
|
|
@@ -58,13 +74,16 @@ function ValidationRulesTab() {
|
|
|
58
74
|
};
|
|
59
75
|
React.useEffect(() => {
|
|
60
76
|
fetchRules();
|
|
77
|
+
fetchCollections();
|
|
61
78
|
}, []);
|
|
62
79
|
const openCreateModal = () => {
|
|
63
80
|
setEditingRule(null);
|
|
64
81
|
setFormName("");
|
|
65
82
|
setFormDesc("");
|
|
83
|
+
setFormCollectionUid("*");
|
|
66
84
|
setFormField("headingLevel");
|
|
67
85
|
setFormAction("disable");
|
|
86
|
+
setFormIncludeRichText(true);
|
|
68
87
|
setFormQuotas([
|
|
69
88
|
{ key: "h1", maxAllowed: 1 },
|
|
70
89
|
{ key: "h2", maxAllowed: 3 }
|
|
@@ -75,8 +94,10 @@ function ValidationRulesTab() {
|
|
|
75
94
|
setEditingRule(rule);
|
|
76
95
|
setFormName(rule.name);
|
|
77
96
|
setFormDesc(rule.description || "");
|
|
97
|
+
setFormCollectionUid(rule.collectionUid || "*");
|
|
78
98
|
setFormField(rule.targetField);
|
|
79
99
|
setFormAction(rule.exceededAction || "disable");
|
|
100
|
+
setFormIncludeRichText(rule.includeRichTextHtml ?? true);
|
|
80
101
|
const quotaPairs = Object.entries(rule.quotas || {}).map(([k, v]) => ({
|
|
81
102
|
key: k,
|
|
82
103
|
maxAllowed: v
|
|
@@ -130,11 +151,13 @@ function ValidationRulesTab() {
|
|
|
130
151
|
id: editingRule ? editingRule.id : void 0,
|
|
131
152
|
name: formName.trim(),
|
|
132
153
|
description: formDesc.trim(),
|
|
154
|
+
collectionUid: formCollectionUid,
|
|
133
155
|
targetField: formField.trim(),
|
|
134
156
|
quotas: quotasRecord,
|
|
135
157
|
exceededAction: formAction,
|
|
136
158
|
enabled: editingRule ? editingRule.enabled : true,
|
|
137
|
-
scope: "entry"
|
|
159
|
+
scope: "entry",
|
|
160
|
+
includeRichTextHtml: formIncludeRichText
|
|
138
161
|
};
|
|
139
162
|
const res = await post("/bulk-updater/validation-rules", { rule: payload });
|
|
140
163
|
if (res.data && res.data.success) {
|
|
@@ -196,8 +219,8 @@ function ValidationRulesTab() {
|
|
|
196
219
|
return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
197
220
|
/* @__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: [
|
|
198
221
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
199
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", fontWeight: "bold", children: "Cross-Component Validation
|
|
200
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { marginTop: 4 }, children: "Define and
|
|
222
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", fontWeight: "bold", children: "Scoped & Cross-Component Validation Rules" }),
|
|
223
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { marginTop: 4 }, children: "Define collection-scoped rules and combined heading limits across structured components and free-form Rich Text / CKEditor HTML / Markdown content." })
|
|
201
224
|
] }),
|
|
202
225
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
|
|
203
226
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -227,32 +250,43 @@ function ValidationRulesTab() {
|
|
|
227
250
|
rules.length,
|
|
228
251
|
")"
|
|
229
252
|
] }),
|
|
230
|
-
loading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral500", children: "Loading validation rules..." }) : rules.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Alert, { variant: "info", title: "No Rules Defined", children: 'Click "Create New Rule" above to add your first cross-component quota rule.' }) : /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount:
|
|
253
|
+
loading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral500", children: "Loading validation rules..." }) : rules.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Alert, { variant: "info", title: "No Rules Defined", children: 'Click "Create New Rule" above to add your first cross-component quota rule.' }) : /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: 7, rowCount: rules.length + 1, children: [
|
|
231
254
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
|
|
232
255
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "RULE NAME & TARGET FIELD" }) }),
|
|
233
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "
|
|
256
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "COLLECTION SCOPE" }) }),
|
|
234
257
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "QUOTA CONSTRAINTS" }) }),
|
|
258
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "RICH TEXT / HTML" }) }),
|
|
235
259
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "EXCEEDED ACTION" }) }),
|
|
236
260
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "STATUS" }) }),
|
|
237
261
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { textAlign: "right" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "ACTIONS" }) })
|
|
238
262
|
] }) }),
|
|
239
263
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: rules.map((rule) => {
|
|
240
264
|
const quotaKeys = Object.entries(rule.quotas || {});
|
|
265
|
+
const isGlobal = !rule.collectionUid || rule.collectionUid === "*";
|
|
266
|
+
const collectionName = !isGlobal ? availableCollections.find((c) => c.uid === rule.collectionUid)?.displayName || rule.collectionUid : "All Collections (Global)";
|
|
241
267
|
return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
|
|
242
268
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Td, { style: { minWidth: 200 }, children: [
|
|
243
269
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", textColor: "neutral800", display: "block", children: rule.name }),
|
|
244
270
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: [
|
|
245
|
-
"Target
|
|
271
|
+
"Target: ",
|
|
246
272
|
/* @__PURE__ */ jsxRuntime.jsx("code", { children: rule.targetField })
|
|
247
273
|
] })
|
|
248
274
|
] }),
|
|
249
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: {
|
|
250
|
-
|
|
275
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { minWidth: 160 }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
276
|
+
designSystem.Badge,
|
|
277
|
+
{
|
|
278
|
+
backgroundColor: isGlobal ? "neutral150" : "primary100",
|
|
279
|
+
textColor: isGlobal ? "neutral700" : "primary700",
|
|
280
|
+
children: collectionName
|
|
281
|
+
}
|
|
282
|
+
) }),
|
|
283
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { minWidth: 180 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 1, wrap: "wrap", children: quotaKeys.map(([optVal, maxVal]) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "neutral100", textColor: "primary700", children: [
|
|
251
284
|
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: optVal.toUpperCase() }),
|
|
252
285
|
": max ",
|
|
253
286
|
maxVal
|
|
254
287
|
] }, optVal)) }) }),
|
|
255
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { minWidth:
|
|
288
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { minWidth: 140 }, children: rule.includeRichTextHtml ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: "HTML & Rich Text Active" }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "neutral150", textColor: "neutral600", children: "Structured Only" }) }),
|
|
289
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { minWidth: 110 }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
256
290
|
designSystem.Badge,
|
|
257
291
|
{
|
|
258
292
|
backgroundColor: rule.exceededAction === "hide" ? "secondary100" : "warning100",
|
|
@@ -324,32 +358,61 @@ function ValidationRulesTab() {
|
|
|
324
358
|
hasRadius: true,
|
|
325
359
|
shadow: "popupShadow",
|
|
326
360
|
padding: 6,
|
|
327
|
-
style: { width: "90%", maxWidth:
|
|
361
|
+
style: { width: "90%", maxWidth: 640, maxHeight: "90vh", overflowY: "auto" },
|
|
328
362
|
children: [
|
|
329
363
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", fontWeight: "bold", marginBottom: 1, children: editingRule ? "Edit Validation Rule" : "Create New Validation Rule" }),
|
|
330
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", marginBottom: 4, style: { display: "block" }, children: "Configure
|
|
364
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", marginBottom: 4, style: { display: "block" }, children: "Configure collection-scoped quotas. Rules apply consistently to target properties and optionally scan rich text HTML content." }),
|
|
331
365
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 4, alignItems: "stretch", children: [
|
|
332
366
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
333
367
|
designSystem.TextInput,
|
|
334
368
|
{
|
|
335
369
|
label: "Rule Name",
|
|
336
|
-
placeholder: "e.g. SEO Single H1 Rule",
|
|
370
|
+
placeholder: "e.g. SEO Single H1 Page Rule",
|
|
337
371
|
value: formName,
|
|
338
372
|
onChange: (e) => setFormName(e.target.value),
|
|
339
373
|
required: true
|
|
340
374
|
}
|
|
341
375
|
),
|
|
376
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
377
|
+
designSystem.SingleSelect,
|
|
378
|
+
{
|
|
379
|
+
label: "Target Collection Type",
|
|
380
|
+
value: formCollectionUid,
|
|
381
|
+
onChange: (val) => setFormCollectionUid(val),
|
|
382
|
+
hint: "Scope this rule to a specific collection type, or select All Collections.",
|
|
383
|
+
children: [
|
|
384
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "*", children: "* (All Collections / Global)" }),
|
|
385
|
+
availableCollections.map((c) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.SingleSelectOption, { value: c.uid, children: [
|
|
386
|
+
c.displayName,
|
|
387
|
+
" (",
|
|
388
|
+
c.uid,
|
|
389
|
+
")"
|
|
390
|
+
] }, c.uid))
|
|
391
|
+
]
|
|
392
|
+
}
|
|
393
|
+
),
|
|
342
394
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
343
395
|
designSystem.TextInput,
|
|
344
396
|
{
|
|
345
|
-
label: "Target Field
|
|
346
|
-
placeholder: "e.g. headingLevel, titleStyle, or
|
|
397
|
+
label: "Target Field / Component Property Path",
|
|
398
|
+
placeholder: "e.g. headingLevel, titleStyle, or st-featured.titleHeadingStyle.titleStyle",
|
|
347
399
|
value: formField,
|
|
348
400
|
onChange: (e) => setFormField(e.target.value),
|
|
349
|
-
hint: "Attribute name
|
|
401
|
+
hint: "Attribute name or dot-notated hierarchy (e.g. 'titleStyle' or 'st-featured.titleHeadingStyle.titleStyle').",
|
|
350
402
|
required: true
|
|
351
403
|
}
|
|
352
404
|
),
|
|
405
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 3, background: "neutral100", hasRadius: true, style: { border: "1px solid #e0e0e0" }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
406
|
+
designSystem.Checkbox,
|
|
407
|
+
{
|
|
408
|
+
checked: formIncludeRichText,
|
|
409
|
+
onCheckedChange: (checked) => setFormIncludeRichText(checked),
|
|
410
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginLeft: 2, children: [
|
|
411
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", children: "Scan & Combine Rich Text / CKEditor HTML Headings" }),
|
|
412
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", display: "block", children: "When enabled, any <h1>..<h6> tags, Markdown headings (#), or Strapi Blocks inside rich text fields are counted together with structured component choices towards the quota." })
|
|
413
|
+
] })
|
|
414
|
+
}
|
|
415
|
+
) }),
|
|
353
416
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
354
417
|
designSystem.Textarea,
|
|
355
418
|
{
|
|
@@ -517,6 +580,10 @@ function ValidationRulesTab() {
|
|
|
517
580
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral500", display: "block", children: [
|
|
518
581
|
"Collection: ",
|
|
519
582
|
v.collectionDisplayName
|
|
583
|
+
] }),
|
|
584
|
+
v.detail && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", display: "block", style: { fontSize: 11 }, children: [
|
|
585
|
+
"Breakdown: ",
|
|
586
|
+
v.detail
|
|
520
587
|
] })
|
|
521
588
|
] }),
|
|
522
589
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: v.violatingOption }) }),
|
|
@@ -595,4 +662,4 @@ function ValidationRulesTab() {
|
|
|
595
662
|
] });
|
|
596
663
|
}
|
|
597
664
|
exports.ValidationRulesTab = ValidationRulesTab;
|
|
598
|
-
//# sourceMappingURL=ValidationRulesTab-
|
|
665
|
+
//# sourceMappingURL=ValidationRulesTab-BqGz1VLo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ValidationRulesTab-BqGz1VLo.js","sources":["../../admin/src/pages/ValidationRulesTab.tsx"],"sourcesContent":["import React, { useState, useEffect } from 'react';\nimport { useFetchClient, useNotification } from '@strapi/strapi/admin';\nimport {\n Box,\n Flex,\n Typography,\n Button,\n TextInput,\n Textarea,\n SingleSelect,\n SingleSelectOption,\n Badge,\n Table,\n Thead,\n Tbody,\n Tr,\n Th,\n Td,\n Alert,\n Checkbox,\n} from '@strapi/design-system';\nimport { Plus, Trash, Check, ArrowClockwise, Search, Download } from '@strapi/icons';\n\nexport interface ValidationRule {\n id: string;\n name: string;\n description?: string;\n collectionUid?: string;\n targetField: string;\n quotas: Record<string, number>;\n exceededAction: 'disable' | 'hide';\n enabled: boolean;\n scope: 'entry' | 'document';\n includeRichTextHtml?: boolean;\n richTextFields?: string[];\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport default function ValidationRulesTab() {\n const { get, post, del } = useFetchClient();\n const { toggleNotification } = useNotification();\n\n const [rules, setRules] = useState<ValidationRule[]>([]);\n const [loading, setLoading] = useState<boolean>(true);\n const [modalOpen, setModalOpen] = useState<boolean>(false);\n const [editingRule, setEditingRule] = useState<ValidationRule | null>(null);\n\n // Available collections from backend models\n const [availableCollections, setAvailableCollections] = useState<Array<{ uid: string; displayName: string }>>([]);\n\n // Form state for creating/editing a rule\n const [formName, setFormName] = useState<string>('');\n const [formDesc, setFormDesc] = useState<string>('');\n const [formCollectionUid, setFormCollectionUid] = useState<string>('*');\n const [formField, setFormField] = useState<string>('');\n const [formAction, setFormAction] = useState<'disable' | 'hide'>('disable');\n const [formIncludeRichText, setFormIncludeRichText] = useState<boolean>(true);\n const [formQuotas, setFormQuotas] = useState<Array<{ key: string; maxAllowed: number }>>([\n { key: 'h1', maxAllowed: 1 },\n { key: 'h2', maxAllowed: 3 },\n ]);\n const [saving, setSaving] = useState<boolean>(false);\n\n // Scan modal state\n const [scanModalOpen, setScanModalOpen] = useState<boolean>(false);\n const [scanningRuleId, setScanningRuleId] = useState<string | null>(null);\n const [scanReport, setScanReport] = useState<any>(null);\n\n const fetchCollections = async () => {\n try {\n const res = await get('/bulk-updater/models');\n if (res.data?.targetUids && res.data?.metaByUid) {\n const list = res.data.targetUids.map((uid: string) => ({\n uid,\n displayName: res.data.metaByUid[uid]?.displayName || uid,\n }));\n setAvailableCollections(list);\n }\n } catch {\n // Fallback silently if models not loaded\n }\n };\n\n const handleScanRule = async (rule: ValidationRule) => {\n try {\n setScanningRuleId(rule.id);\n const res = await get(`/bulk-updater/validation-rules/${rule.id}/scan`);\n if (res.data && res.data.success) {\n setScanReport(res.data);\n setScanModalOpen(true);\n }\n } catch (err: any) {\n toggleNotification({\n type: 'danger',\n message: `Scan failed: ${err.message}`,\n });\n } finally {\n setScanningRuleId(null);\n }\n };\n\n const fetchRules = async () => {\n try {\n setLoading(true);\n const res = await get('/bulk-updater/validation-rules');\n if (res.data && res.data.success) {\n setRules(res.data.rules || []);\n }\n } catch (err: any) {\n toggleNotification({\n type: 'danger',\n message: `Failed to load rules: ${err.message}`,\n });\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n fetchRules();\n fetchCollections();\n }, []);\n\n const openCreateModal = () => {\n setEditingRule(null);\n setFormName('');\n setFormDesc('');\n setFormCollectionUid('*');\n setFormField('headingLevel');\n setFormAction('disable');\n setFormIncludeRichText(true);\n setFormQuotas([\n { key: 'h1', maxAllowed: 1 },\n { key: 'h2', maxAllowed: 3 },\n ]);\n setModalOpen(true);\n };\n\n const openEditModal = (rule: ValidationRule) => {\n setEditingRule(rule);\n setFormName(rule.name);\n setFormDesc(rule.description || '');\n setFormCollectionUid(rule.collectionUid || '*');\n setFormField(rule.targetField);\n setFormAction(rule.exceededAction || 'disable');\n setFormIncludeRichText(rule.includeRichTextHtml ?? true);\n const quotaPairs = Object.entries(rule.quotas || {}).map(([k, v]) => ({\n key: k,\n maxAllowed: v,\n }));\n setFormQuotas(quotaPairs.length > 0 ? quotaPairs : [{ key: 'h1', maxAllowed: 1 }]);\n setModalOpen(true);\n };\n\n const handleAddQuotaRow = () => {\n setFormQuotas((prev) => [...prev, { key: '', maxAllowed: 1 }]);\n };\n\n const handleRemoveQuotaRow = (idx: number) => {\n setFormQuotas((prev) => prev.filter((_, i) => i !== idx));\n };\n\n const handleQuotaChange = (idx: number, field: 'key' | 'maxAllowed', value: any) => {\n setFormQuotas((prev) =>\n prev.map((row, i) => {\n if (i === idx) {\n return {\n ...row,\n [field]: field === 'maxAllowed' ? Math.max(1, parseInt(value, 10) || 1) : String(value).trim(),\n };\n }\n return row;\n })\n );\n };\n\n const handleSave = async () => {\n if (!formName.trim() || !formField.trim()) {\n toggleNotification({\n type: 'warning',\n message: 'Rule Name and Target Field are required.',\n });\n return;\n }\n\n const quotasRecord: Record<string, number> = {};\n for (const q of formQuotas) {\n if (q.key.trim()) {\n quotasRecord[q.key.trim().toLowerCase()] = q.maxAllowed;\n }\n }\n\n if (Object.keys(quotasRecord).length === 0) {\n toggleNotification({\n type: 'warning',\n message: 'At least one quota option with a limit is required.',\n });\n return;\n }\n\n try {\n setSaving(true);\n const payload: Partial<ValidationRule> = {\n id: editingRule ? editingRule.id : undefined,\n name: formName.trim(),\n description: formDesc.trim(),\n collectionUid: formCollectionUid,\n targetField: formField.trim(),\n quotas: quotasRecord,\n exceededAction: formAction,\n enabled: editingRule ? editingRule.enabled : true,\n scope: 'entry',\n includeRichTextHtml: formIncludeRichText,\n };\n\n const res = await post('/bulk-updater/validation-rules', { rule: payload });\n if (res.data && res.data.success) {\n toggleNotification({\n type: 'success',\n message: `Rule \"${payload.name}\" saved successfully!`,\n });\n setModalOpen(false);\n fetchRules();\n }\n } catch (err: any) {\n toggleNotification({\n type: 'danger',\n message: `Failed to save rule: ${err.message}`,\n });\n } finally {\n setSaving(false);\n }\n };\n\n const handleToggle = async (rule: ValidationRule) => {\n try {\n const res = await post(`/bulk-updater/validation-rules/${rule.id}/toggle`, {\n enabled: !rule.enabled,\n });\n if (res.data && res.data.success) {\n setRules((prev) =>\n prev.map((r) => (r.id === rule.id ? { ...r, enabled: !rule.enabled } : r))\n );\n toggleNotification({\n type: 'info',\n message: `Rule \"${rule.name}\" is now ${!rule.enabled ? 'ACTIVE' : 'DISABLED'}.`,\n });\n }\n } catch (err: any) {\n toggleNotification({\n type: 'danger',\n message: `Failed to update rule status: ${err.message}`,\n });\n }\n };\n\n const handleDelete = async (ruleId: string, ruleName: string) => {\n if (!window.confirm(`Are you sure you want to delete the rule \"${ruleName}\"?`)) return;\n\n try {\n const res = await del(`/bulk-updater/validation-rules/${ruleId}`);\n if (res.data && res.data.success) {\n setRules((prev) => prev.filter((r) => r.id !== ruleId));\n toggleNotification({\n type: 'success',\n message: `Rule \"${ruleName}\" deleted.`,\n });\n }\n } catch (err: any) {\n toggleNotification({\n type: 'danger',\n message: `Failed to delete rule: ${err.message}`,\n });\n }\n };\n\n return (\n <Box>\n {/* Header Panel */}\n <Box padding={6} background=\"neutral0\" shadow=\"filterShadow\" hasRadius marginBottom={6}>\n <Flex justifyContent=\"space-between\" alignItems=\"center\" wrap=\"wrap\" gap={3}>\n <Box>\n <Typography variant=\"delta\" tag=\"h2\" fontWeight=\"bold\">\n Scoped & Cross-Component Validation Rules\n </Typography>\n <Typography variant=\"pi\" textColor=\"neutral600\" style={{ marginTop: 4 }}>\n Define collection-scoped rules and combined heading limits across structured components and free-form Rich Text / CKEditor HTML / Markdown content.\n </Typography>\n </Box>\n <Flex gap={2}>\n <Button\n variant=\"tertiary\"\n startIcon={<ArrowClockwise />}\n onClick={fetchRules}\n loading={loading}\n >\n Refresh\n </Button>\n <Button\n variant=\"default\"\n startIcon={<Plus />}\n onClick={openCreateModal}\n >\n Create New Rule\n </Button>\n </Flex>\n </Flex>\n </Box>\n\n {/* Rules Table */}\n <Box padding={6} background=\"neutral0\" shadow=\"filterShadow\" hasRadius>\n <Typography variant=\"sigma\" textColor=\"neutral600\" marginBottom={3} style={{ display: 'block' }}>\n CONFIGURED RULES ({rules.length})\n </Typography>\n\n {loading ? (\n <Typography variant=\"omega\" textColor=\"neutral500\">Loading validation rules...</Typography>\n ) : rules.length === 0 ? (\n <Alert variant=\"info\" title=\"No Rules Defined\">\n Click \"Create New Rule\" above to add your first cross-component quota rule.\n </Alert>\n ) : (\n <Table colCount={7} rowCount={rules.length + 1}>\n <Thead>\n <Tr>\n <Th><Typography variant=\"sigma\">RULE NAME & TARGET FIELD</Typography></Th>\n <Th><Typography variant=\"sigma\">COLLECTION SCOPE</Typography></Th>\n <Th><Typography variant=\"sigma\">QUOTA CONSTRAINTS</Typography></Th>\n <Th><Typography variant=\"sigma\">RICH TEXT / HTML</Typography></Th>\n <Th><Typography variant=\"sigma\">EXCEEDED ACTION</Typography></Th>\n <Th><Typography variant=\"sigma\">STATUS</Typography></Th>\n <Th style={{ textAlign: 'right' }}><Typography variant=\"sigma\">ACTIONS</Typography></Th>\n </Tr>\n </Thead>\n <Tbody>\n {rules.map((rule) => {\n const quotaKeys = Object.entries(rule.quotas || {});\n const isGlobal = !rule.collectionUid || rule.collectionUid === '*';\n const collectionName = !isGlobal\n ? (availableCollections.find((c) => c.uid === rule.collectionUid)?.displayName || rule.collectionUid)\n : 'All Collections (Global)';\n\n return (\n <Tr key={rule.id}>\n <Td style={{ minWidth: 200 }}>\n <Typography variant=\"omega\" fontWeight=\"bold\" textColor=\"neutral800\" display=\"block\">\n {rule.name}\n </Typography>\n <Typography variant=\"pi\" textColor=\"neutral500\">\n Target: <code>{rule.targetField}</code>\n </Typography>\n </Td>\n <Td style={{ minWidth: 160 }}>\n <Badge\n backgroundColor={isGlobal ? 'neutral150' : 'primary100'}\n textColor={isGlobal ? 'neutral700' : 'primary700'}\n >\n {collectionName}\n </Badge>\n </Td>\n <Td style={{ minWidth: 180 }}>\n <Flex gap={1} wrap=\"wrap\">\n {quotaKeys.map(([optVal, maxVal]) => (\n <Badge key={optVal} backgroundColor=\"neutral100\" textColor=\"primary700\">\n <strong>{optVal.toUpperCase()}</strong>: max {maxVal}\n </Badge>\n ))}\n </Flex>\n </Td>\n <Td style={{ minWidth: 140 }}>\n {rule.includeRichTextHtml ? (\n <Badge backgroundColor=\"success100\" textColor=\"success700\">\n HTML & Rich Text Active\n </Badge>\n ) : (\n <Badge backgroundColor=\"neutral150\" textColor=\"neutral600\">\n Structured Only\n </Badge>\n )}\n </Td>\n <Td style={{ minWidth: 110 }}>\n <Badge\n backgroundColor={rule.exceededAction === 'hide' ? 'secondary100' : 'warning100'}\n textColor={rule.exceededAction === 'hide' ? 'secondary700' : 'warning700'}\n >\n {rule.exceededAction.toUpperCase()}\n </Badge>\n </Td>\n <Td style={{ minWidth: 100 }}>\n <Button\n variant={rule.enabled ? 'success-light' : 'tertiary'}\n size=\"S\"\n onClick={() => handleToggle(rule)}\n >\n {rule.enabled ? '● ACTIVE' : '○ DISABLED'}\n </Button>\n </Td>\n <Td style={{ textAlign: 'right', minWidth: 260 }}>\n <Flex gap={2} justifyContent=\"flex-end\">\n <Button\n variant=\"secondary\"\n size=\"S\"\n startIcon={<Search />}\n onClick={() => handleScanRule(rule)}\n loading={scanningRuleId === rule.id}\n >\n Scan Violations\n </Button>\n <Button\n variant=\"tertiary\"\n size=\"S\"\n onClick={() => openEditModal(rule)}\n >\n Edit\n </Button>\n <Button\n variant=\"danger-light\"\n size=\"S\"\n startIcon={<Trash />}\n onClick={() => handleDelete(rule.id, rule.name)}\n >\n Delete\n </Button>\n </Flex>\n </Td>\n </Tr>\n );\n })}\n </Tbody>\n </Table>\n )}\n </Box>\n\n {/* Create / Edit Rule Modal */}\n {modalOpen && (\n <Box\n style={{\n position: 'fixed',\n inset: 0,\n backgroundColor: 'rgba(33, 33, 52, 0.65)',\n zIndex: 9999,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Box\n background=\"neutral0\"\n hasRadius\n shadow=\"popupShadow\"\n padding={6}\n style={{ width: '90%', maxWidth: 640, maxHeight: '90vh', overflowY: 'auto' }}\n >\n <Typography variant=\"delta\" tag=\"h2\" fontWeight=\"bold\" marginBottom={1}>\n {editingRule ? 'Edit Validation Rule' : 'Create New Validation Rule'}\n </Typography>\n <Typography variant=\"pi\" textColor=\"neutral600\" marginBottom={4} style={{ display: 'block' }}>\n Configure collection-scoped quotas. Rules apply consistently to target properties and optionally scan rich text HTML content.\n </Typography>\n\n <Flex direction=\"column\" gap={4} alignItems=\"stretch\">\n <TextInput\n label=\"Rule Name\"\n placeholder=\"e.g. SEO Single H1 Page Rule\"\n value={formName}\n onChange={(e: any) => setFormName(e.target.value)}\n required\n />\n\n <SingleSelect\n label=\"Target Collection Type\"\n value={formCollectionUid}\n onChange={(val: any) => setFormCollectionUid(val)}\n hint=\"Scope this rule to a specific collection type, or select All Collections.\"\n >\n <SingleSelectOption value=\"*\">* (All Collections / Global)</SingleSelectOption>\n {availableCollections.map((c) => (\n <SingleSelectOption key={c.uid} value={c.uid}>\n {c.displayName} ({c.uid})\n </SingleSelectOption>\n ))}\n </SingleSelect>\n\n <TextInput\n label=\"Target Field / Component Property Path\"\n placeholder=\"e.g. headingLevel, titleStyle, or st-featured.titleHeadingStyle.titleStyle\"\n value={formField}\n onChange={(e: any) => setFormField(e.target.value)}\n hint=\"Attribute name or dot-notated hierarchy (e.g. 'titleStyle' or 'st-featured.titleHeadingStyle.titleStyle').\"\n required\n />\n\n <Box padding={3} background=\"neutral100\" hasRadius style={{ border: '1px solid #e0e0e0' }}>\n <Checkbox\n checked={formIncludeRichText}\n onCheckedChange={(checked: boolean) => setFormIncludeRichText(checked)}\n >\n <Box marginLeft={2}>\n <Typography variant=\"omega\" fontWeight=\"bold\">\n Scan & Combine Rich Text / CKEditor HTML Headings\n </Typography>\n <Typography variant=\"pi\" textColor=\"neutral600\" display=\"block\">\n When enabled, any <h1>..<h6> tags, Markdown headings (#), or Strapi Blocks inside rich text fields are counted together with structured component choices towards the quota.\n </Typography>\n </Box>\n </Checkbox>\n </Box>\n\n <Textarea\n label=\"Rule Description\"\n placeholder=\"Briefly explain what this constraint accomplishes...\"\n value={formDesc}\n onChange={(e: any) => setFormDesc(e.target.value)}\n />\n\n <SingleSelect\n label=\"When Quota Is Exceeded in Another Component\"\n value={formAction}\n onChange={(val: any) => setFormAction(val as 'disable' | 'hide')}\n >\n <SingleSelectOption value=\"disable\">\n Disable Option with Usage Badge [(1/1 used)]\n </SingleSelectOption>\n <SingleSelectOption value=\"hide\">\n Hide Option completely from dropdown menu\n </SingleSelectOption>\n </SingleSelect>\n\n {/* Quotas Editor */}\n <Box\n padding={4}\n background=\"neutral100\"\n hasRadius\n style={{ border: '1px solid #e0e0e0' }}\n >\n <Flex justifyContent=\"space-between\" alignItems=\"center\" marginBottom={3}>\n <Box>\n <Typography variant=\"pi\" fontWeight=\"bold\" textColor=\"neutral800\">\n Option Quotas (Max Allowed per Entry)\n </Typography>\n <Typography variant=\"pi\" textColor=\"neutral600\" style={{ display: 'block' }}>\n Options left unlisted will have unlimited usage.\n </Typography>\n </Box>\n <Button\n variant=\"secondary\"\n size=\"S\"\n startIcon={<Plus />}\n onClick={handleAddQuotaRow}\n >\n Add Option\n </Button>\n </Flex>\n\n <Flex direction=\"column\" gap={2}>\n {formQuotas.map((q, idx) => (\n <Flex key={idx} gap={2} alignItems=\"center\">\n <Box style={{ flex: 2 }}>\n <TextInput\n placeholder=\"Option key (e.g. h1)\"\n value={q.key}\n onChange={(e: any) => handleQuotaChange(idx, 'key', e.target.value)}\n />\n </Box>\n <Box style={{ flex: 1 }}>\n <TextInput\n type=\"number\"\n placeholder=\"Max\"\n value={String(q.maxAllowed)}\n onChange={(e: any) => handleQuotaChange(idx, 'maxAllowed', e.target.value)}\n />\n </Box>\n <Button\n variant=\"ghost\"\n size=\"S\"\n onClick={() => handleRemoveQuotaRow(idx)}\n disabled={formQuotas.length <= 1}\n >\n ✕\n </Button>\n </Flex>\n ))}\n </Flex>\n </Box>\n </Flex>\n\n {/* Modal Actions */}\n <Flex justifyContent=\"flex-end\" gap={2} marginTop={6}>\n <Button\n variant=\"tertiary\"\n onClick={() => setModalOpen(false)}\n disabled={saving}\n >\n Cancel\n </Button>\n <Button\n variant=\"default\"\n startIcon={<Check />}\n onClick={handleSave}\n loading={saving}\n >\n Save Rule\n </Button>\n </Flex>\n </Box>\n </Box>\n )}\n\n {/* Scan Results Modal */}\n {scanModalOpen && scanReport && (\n <Box\n style={{\n position: 'fixed',\n inset: 0,\n backgroundColor: 'rgba(33, 33, 52, 0.65)',\n zIndex: 9999,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Box\n background=\"neutral0\"\n hasRadius\n shadow=\"popupShadow\"\n padding={6}\n style={{ width: '90%', maxWidth: 780, maxHeight: '85vh', overflowY: 'auto' }}\n >\n <Flex justifyContent=\"space-between\" alignItems=\"center\" marginBottom={4}>\n <Box>\n <Typography variant=\"beta\" tag=\"h2\" fontWeight=\"bold\">\n Rule Violation Audit: {scanReport.rule?.name}\n </Typography>\n <Typography variant=\"pi\" textColor=\"neutral600\">\n Target Field: <code>{scanReport.rule?.targetField}</code> • Total Entries Scanned: {scanReport.totalScanned}\n </Typography>\n </Box>\n <Badge\n backgroundColor={scanReport.totalViolations > 0 ? 'danger100' : 'success100'}\n textColor={scanReport.totalViolations > 0 ? 'danger700' : 'success700'}\n size=\"M\"\n >\n {scanReport.totalViolations > 0\n ? `${scanReport.totalViolations} VIOLATION(S) FOUND`\n : '100% COMPLIANT'}\n </Badge>\n </Flex>\n\n {scanReport.totalViolations === 0 ? (\n <Alert variant=\"success\" title=\"No Pre-Existing Violations Detected\" marginBottom={4}>\n All existing documents across your collections adhere strictly to the configured quota limits.\n </Alert>\n ) : (\n <Box>\n <Alert variant=\"danger\" title=\"Legacy Records Found Exceeding Quota\" marginBottom={4}>\n The following {scanReport.totalViolations} record(s) contain more instances of restricted options than currently permitted by this rule.\n </Alert>\n\n <Table colCount={4} rowCount={scanReport.violations.length + 1}>\n <Thead>\n <Tr>\n <Th><Typography variant=\"sigma\">CONTENT TYPE & ENTRY</Typography></Th>\n <Th><Typography variant=\"sigma\">RESTRICTED OPTION</Typography></Th>\n <Th><Typography variant=\"sigma\">COUNT VS LIMIT</Typography></Th>\n <Th style={{ textAlign: 'right' }}><Typography variant=\"sigma\">ACTION</Typography></Th>\n </Tr>\n </Thead>\n <Tbody>\n {scanReport.violations.map((v: any, idx: number) => (\n <Tr key={idx}>\n <Td>\n <Typography variant=\"omega\" fontWeight=\"bold\">\n {v.entryLabel}\n </Typography>\n <Typography variant=\"pi\" textColor=\"neutral500\" display=\"block\">\n Collection: {v.collectionDisplayName}\n </Typography>\n {v.detail && (\n <Typography variant=\"pi\" textColor=\"neutral600\" display=\"block\" style={{ fontSize: 11 }}>\n Breakdown: {v.detail}\n </Typography>\n )}\n </Td>\n <Td>\n <Badge backgroundColor=\"warning100\" textColor=\"warning700\">\n {v.violatingOption}\n </Badge>\n </Td>\n <Td>\n <Typography variant=\"omega\" textColor=\"danger600\" fontWeight=\"bold\">\n {v.foundCount} used\n </Typography>\n <Typography variant=\"pi\" textColor=\"neutral500\">\n {' '}(Allowed max: {v.maxAllowed})\n </Typography>\n </Td>\n <Td style={{ textAlign: 'right' }}>\n <Button\n variant=\"secondary\"\n size=\"S\"\n onClick={() => window.open(v.editUrl, '_blank')}\n >\n Open in Editor ↗\n </Button>\n </Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n </Box>\n )}\n\n <Flex justifyContent=\"space-between\" alignItems=\"center\" marginTop={6}>\n {scanReport.totalViolations > 0 ? (\n <Button\n variant=\"secondary\"\n startIcon={<Download />}\n onClick={async () => {\n try {\n const res = await get(`/bulk-updater/csv/export-violations/${scanReport.rule.id}`);\n const csvStr = res.data?.csvContent || (typeof res.data === 'string' ? res.data : '');\n if (!csvStr) {\n toggleNotification({\n type: 'warning',\n message: 'No violation data to export',\n });\n return;\n }\n const safeContent = csvStr.startsWith('\\uFEFF') ? csvStr : '\\uFEFF' + csvStr;\n const blob = new Blob([safeContent], { type: 'text/csv;charset=utf-8;' });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.setAttribute('download', `violations_${scanReport.rule.id}.csv`);\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n toggleNotification({\n type: 'success',\n message: 'Violations exported to CSV successfully',\n });\n } catch (e: any) {\n toggleNotification({\n type: 'danger',\n message: e.message || 'Failed exporting violations CSV',\n });\n }\n }}\n >\n Export Violations CSV\n </Button>\n ) : (\n <Box />\n )}\n <Button variant=\"default\" onClick={() => setScanModalOpen(false)}>\n Close Report\n </Button>\n </Flex>\n </Box>\n </Box>\n )}\n </Box>\n );\n}\n"],"names":["useFetchClient","useNotification","useState","useEffect","Box","jsx","jsxs","Flex","Typography","Button","ArrowClockwise","Plus","Alert","Table","Thead","Tr","Th","Tbody","Td","Badge","Search","Trash","TextInput","SingleSelect","SingleSelectOption","Checkbox","Textarea","Check","Download"],"mappings":";;;;;;AAuCA,SAAwB,qBAAqB;AAC3C,QAAM,EAAE,KAAK,MAAM,IAAA,IAAQA,MAAAA,eAAA;AAC3B,QAAM,EAAE,mBAAA,IAAuBC,sBAAA;AAE/B,QAAM,CAAC,OAAO,QAAQ,IAAIC,MAAAA,SAA2B,CAAA,CAAE;AACvD,QAAM,CAAC,SAAS,UAAU,IAAIA,MAAAA,SAAkB,IAAI;AACpD,QAAM,CAAC,WAAW,YAAY,IAAIA,MAAAA,SAAkB,KAAK;AACzD,QAAM,CAAC,aAAa,cAAc,IAAIA,MAAAA,SAAgC,IAAI;AAG1E,QAAM,CAAC,sBAAsB,uBAAuB,IAAIA,MAAAA,SAAsD,CAAA,CAAE;AAGhH,QAAM,CAAC,UAAU,WAAW,IAAIA,MAAAA,SAAiB,EAAE;AACnD,QAAM,CAAC,UAAU,WAAW,IAAIA,MAAAA,SAAiB,EAAE;AACnD,QAAM,CAAC,mBAAmB,oBAAoB,IAAIA,MAAAA,SAAiB,GAAG;AACtE,QAAM,CAAC,WAAW,YAAY,IAAIA,MAAAA,SAAiB,EAAE;AACrD,QAAM,CAAC,YAAY,aAAa,IAAIA,MAAAA,SAA6B,SAAS;AAC1E,QAAM,CAAC,qBAAqB,sBAAsB,IAAIA,MAAAA,SAAkB,IAAI;AAC5E,QAAM,CAAC,YAAY,aAAa,IAAIA,eAAqD;AAAA,IACvF,EAAE,KAAK,MAAM,YAAY,EAAA;AAAA,IACzB,EAAE,KAAK,MAAM,YAAY,EAAA;AAAA,EAAE,CAC5B;AACD,QAAM,CAAC,QAAQ,SAAS,IAAIA,MAAAA,SAAkB,KAAK;AAGnD,QAAM,CAAC,eAAe,gBAAgB,IAAIA,MAAAA,SAAkB,KAAK;AACjE,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,MAAAA,SAAwB,IAAI;AACxE,QAAM,CAAC,YAAY,aAAa,IAAIA,MAAAA,SAAc,IAAI;AAEtD,QAAM,mBAAmB,YAAY;AACnC,QAAI;AACF,YAAM,MAAM,MAAM,IAAI,sBAAsB;AAC5C,UAAI,IAAI,MAAM,cAAc,IAAI,MAAM,WAAW;AAC/C,cAAM,OAAO,IAAI,KAAK,WAAW,IAAI,CAAC,SAAiB;AAAA,UACrD;AAAA,UACA,aAAa,IAAI,KAAK,UAAU,GAAG,GAAG,eAAe;AAAA,QAAA,EACrD;AACF,gCAAwB,IAAI;AAAA,MAAA;AAAA,IAC9B,QACM;AAAA,IAAA;AAAA,EAER;AAGF,QAAM,iBAAiB,OAAO,SAAyB;AACrD,QAAI;AACF,wBAAkB,KAAK,EAAE;AACzB,YAAM,MAAM,MAAM,IAAI,kCAAkC,KAAK,EAAE,OAAO;AACtE,UAAI,IAAI,QAAQ,IAAI,KAAK,SAAS;AAChC,sBAAc,IAAI,IAAI;AACtB,yBAAiB,IAAI;AAAA,MAAA;AAAA,IACvB,SACO,KAAU;AACjB,yBAAmB;AAAA,QACjB,MAAM;AAAA,QACN,SAAS,gBAAgB,IAAI,OAAO;AAAA,MAAA,CACrC;AAAA,IAAA,UACH;AACE,wBAAkB,IAAI;AAAA,IAAA;AAAA,EACxB;AAGF,QAAM,aAAa,YAAY;AAC7B,QAAI;AACF,iBAAW,IAAI;AACf,YAAM,MAAM,MAAM,IAAI,gCAAgC;AACtD,UAAI,IAAI,QAAQ,IAAI,KAAK,SAAS;AAChC,iBAAS,IAAI,KAAK,SAAS,CAAA,CAAE;AAAA,MAAA;AAAA,IAC/B,SACO,KAAU;AACjB,yBAAmB;AAAA,QACjB,MAAM;AAAA,QACN,SAAS,yBAAyB,IAAI,OAAO;AAAA,MAAA,CAC9C;AAAA,IAAA,UACH;AACE,iBAAW,KAAK;AAAA,IAAA;AAAA,EAClB;AAGFC,QAAAA,UAAU,MAAM;AACd,eAAA;AACA,qBAAA;AAAA,EAAiB,GAChB,EAAE;AAEL,QAAM,kBAAkB,MAAM;AAC5B,mBAAe,IAAI;AACnB,gBAAY,EAAE;AACd,gBAAY,EAAE;AACd,yBAAqB,GAAG;AACxB,iBAAa,cAAc;AAC3B,kBAAc,SAAS;AACvB,2BAAuB,IAAI;AAC3B,kBAAc;AAAA,MACZ,EAAE,KAAK,MAAM,YAAY,EAAA;AAAA,MACzB,EAAE,KAAK,MAAM,YAAY,EAAA;AAAA,IAAE,CAC5B;AACD,iBAAa,IAAI;AAAA,EAAA;AAGnB,QAAM,gBAAgB,CAAC,SAAyB;AAC9C,mBAAe,IAAI;AACnB,gBAAY,KAAK,IAAI;AACrB,gBAAY,KAAK,eAAe,EAAE;AAClC,yBAAqB,KAAK,iBAAiB,GAAG;AAC9C,iBAAa,KAAK,WAAW;AAC7B,kBAAc,KAAK,kBAAkB,SAAS;AAC9C,2BAAuB,KAAK,uBAAuB,IAAI;AACvD,UAAM,aAAa,OAAO,QAAQ,KAAK,UAAU,EAAE,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO;AAAA,MACpE,KAAK;AAAA,MACL,YAAY;AAAA,IAAA,EACZ;AACF,kBAAc,WAAW,SAAS,IAAI,aAAa,CAAC,EAAE,KAAK,MAAM,YAAY,EAAA,CAAG,CAAC;AACjF,iBAAa,IAAI;AAAA,EAAA;AAGnB,QAAM,oBAAoB,MAAM;AAC9B,kBAAc,CAAC,SAAS,CAAC,GAAG,MAAM,EAAE,KAAK,IAAI,YAAY,EAAA,CAAG,CAAC;AAAA,EAAA;AAG/D,QAAM,uBAAuB,CAAC,QAAgB;AAC5C,kBAAc,CAAC,SAAS,KAAK,OAAO,CAAC,GAAG,MAAM,MAAM,GAAG,CAAC;AAAA,EAAA;AAG1D,QAAM,oBAAoB,CAAC,KAAa,OAA6B,UAAe;AAClF;AAAA,MAAc,CAAC,SACb,KAAK,IAAI,CAAC,KAAK,MAAM;AACnB,YAAI,MAAM,KAAK;AACb,iBAAO;AAAA,YACL,GAAG;AAAA,YACH,CAAC,KAAK,GAAG,UAAU,eAAe,KAAK,IAAI,GAAG,SAAS,OAAO,EAAE,KAAK,CAAC,IAAI,OAAO,KAAK,EAAE,KAAA;AAAA,UAAK;AAAA,QAC/F;AAEF,eAAO;AAAA,MAAA,CACR;AAAA,IAAA;AAAA,EACH;AAGF,QAAM,aAAa,YAAY;AAC7B,QAAI,CAAC,SAAS,KAAA,KAAU,CAAC,UAAU,QAAQ;AACzC,yBAAmB;AAAA,QACjB,MAAM;AAAA,QACN,SAAS;AAAA,MAAA,CACV;AACD;AAAA,IAAA;AAGF,UAAM,eAAuC,CAAA;AAC7C,eAAW,KAAK,YAAY;AAC1B,UAAI,EAAE,IAAI,QAAQ;AAChB,qBAAa,EAAE,IAAI,KAAA,EAAO,YAAA,CAAa,IAAI,EAAE;AAAA,MAAA;AAAA,IAC/C;AAGF,QAAI,OAAO,KAAK,YAAY,EAAE,WAAW,GAAG;AAC1C,yBAAmB;AAAA,QACjB,MAAM;AAAA,QACN,SAAS;AAAA,MAAA,CACV;AACD;AAAA,IAAA;AAGF,QAAI;AACF,gBAAU,IAAI;AACd,YAAM,UAAmC;AAAA,QACvC,IAAI,cAAc,YAAY,KAAK;AAAA,QACnC,MAAM,SAAS,KAAA;AAAA,QACf,aAAa,SAAS,KAAA;AAAA,QACtB,eAAe;AAAA,QACf,aAAa,UAAU,KAAA;AAAA,QACvB,QAAQ;AAAA,QACR,gBAAgB;AAAA,QAChB,SAAS,cAAc,YAAY,UAAU;AAAA,QAC7C,OAAO;AAAA,QACP,qBAAqB;AAAA,MAAA;AAGvB,YAAM,MAAM,MAAM,KAAK,kCAAkC,EAAE,MAAM,SAAS;AAC1E,UAAI,IAAI,QAAQ,IAAI,KAAK,SAAS;AAChC,2BAAmB;AAAA,UACjB,MAAM;AAAA,UACN,SAAS,SAAS,QAAQ,IAAI;AAAA,QAAA,CAC/B;AACD,qBAAa,KAAK;AAClB,mBAAA;AAAA,MAAW;AAAA,IACb,SACO,KAAU;AACjB,yBAAmB;AAAA,QACjB,MAAM;AAAA,QACN,SAAS,wBAAwB,IAAI,OAAO;AAAA,MAAA,CAC7C;AAAA,IAAA,UACH;AACE,gBAAU,KAAK;AAAA,IAAA;AAAA,EACjB;AAGF,QAAM,eAAe,OAAO,SAAyB;AACnD,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,kCAAkC,KAAK,EAAE,WAAW;AAAA,QACzE,SAAS,CAAC,KAAK;AAAA,MAAA,CAChB;AACD,UAAI,IAAI,QAAQ,IAAI,KAAK,SAAS;AAChC;AAAA,UAAS,CAAC,SACR,KAAK,IAAI,CAAC,MAAO,EAAE,OAAO,KAAK,KAAK,EAAE,GAAG,GAAG,SAAS,CAAC,KAAK,QAAA,IAAY,CAAE;AAAA,QAAA;AAE3E,2BAAmB;AAAA,UACjB,MAAM;AAAA,UACN,SAAS,SAAS,KAAK,IAAI,YAAY,CAAC,KAAK,UAAU,WAAW,UAAU;AAAA,QAAA,CAC7E;AAAA,MAAA;AAAA,IACH,SACO,KAAU;AACjB,yBAAmB;AAAA,QACjB,MAAM;AAAA,QACN,SAAS,iCAAiC,IAAI,OAAO;AAAA,MAAA,CACtD;AAAA,IAAA;AAAA,EACH;AAGF,QAAM,eAAe,OAAO,QAAgB,aAAqB;AAC/D,QAAI,CAAC,OAAO,QAAQ,6CAA6C,QAAQ,IAAI,EAAG;AAEhF,QAAI;AACF,YAAM,MAAM,MAAM,IAAI,kCAAkC,MAAM,EAAE;AAChE,UAAI,IAAI,QAAQ,IAAI,KAAK,SAAS;AAChC,iBAAS,CAAC,SAAS,KAAK,OAAO,CAAC,MAAM,EAAE,OAAO,MAAM,CAAC;AACtD,2BAAmB;AAAA,UACjB,MAAM;AAAA,UACN,SAAS,SAAS,QAAQ;AAAA,QAAA,CAC3B;AAAA,MAAA;AAAA,IACH,SACO,KAAU;AACjB,yBAAmB;AAAA,QACjB,MAAM;AAAA,QACN,SAAS,0BAA0B,IAAI,OAAO;AAAA,MAAA,CAC/C;AAAA,IAAA;AAAA,EACH;AAGF,yCACGC,kBAAA,EAEC,UAAA;AAAA,IAAAC,2BAAAA,IAACD,aAAAA,KAAA,EAAI,SAAS,GAAG,YAAW,YAAW,QAAO,gBAAe,WAAS,MAAC,cAAc,GACnF,UAAAE,gCAACC,aAAAA,MAAA,EAAK,gBAAe,iBAAgB,YAAW,UAAS,MAAK,QAAO,KAAK,GACxE,UAAA;AAAA,MAAAD,gCAACF,aAAAA,KAAA,EACC,UAAA;AAAA,QAAAC,2BAAAA,IAACG,aAAAA,cAAW,SAAQ,SAAQ,KAAI,MAAK,YAAW,QAAO,UAAA,4CAAA,CAEvD;AAAA,QACAH,2BAAAA,IAACG,aAAAA,YAAA,EAAW,SAAQ,MAAK,WAAU,cAAa,OAAO,EAAE,WAAW,KAAK,UAAA,sJAAA,CAEzE;AAAA,MAAA,GACF;AAAA,MACAF,2BAAAA,KAACC,aAAAA,MAAA,EAAK,KAAK,GACT,UAAA;AAAA,QAAAF,2BAAAA;AAAAA,UAACI,aAAAA;AAAAA,UAAA;AAAA,YACC,SAAQ;AAAA,YACR,0CAAYC,MAAAA,gBAAA,EAAe;AAAA,YAC3B,SAAS;AAAA,YACT;AAAA,YACD,UAAA;AAAA,UAAA;AAAA,QAAA;AAAA,QAGDL,2BAAAA;AAAAA,UAACI,aAAAA;AAAAA,UAAA;AAAA,YACC,SAAQ;AAAA,YACR,0CAAYE,MAAAA,MAAA,EAAK;AAAA,YACjB,SAAS;AAAA,YACV,UAAA;AAAA,UAAA;AAAA,QAAA;AAAA,MAED,EAAA,CACF;AAAA,IAAA,EAAA,CACF,EAAA,CACF;AAAA,IAGAL,2BAAAA,KAACF,aAAAA,OAAI,SAAS,GAAG,YAAW,YAAW,QAAO,gBAAe,WAAS,MACpE,UAAA;AAAA,MAAAE,2BAAAA,KAACE,aAAAA,YAAA,EAAW,SAAQ,SAAQ,WAAU,cAAa,cAAc,GAAG,OAAO,EAAE,SAAS,QAAA,GAAW,UAAA;AAAA,QAAA;AAAA,QAC5E,MAAM;AAAA,QAAO;AAAA,MAAA,GAClC;AAAA,MAEC,UACCH,2BAAAA,IAACG,aAAAA,YAAA,EAAW,SAAQ,SAAQ,WAAU,cAAa,UAAA,8BAAA,CAA2B,IAC5E,MAAM,WAAW,IACnBH,2BAAAA,IAACO,oBAAA,EAAM,SAAQ,QAAO,OAAM,oBAAmB,UAAA,8EAAA,CAE/C,IAEAN,gCAACO,aAAAA,OAAA,EAAM,UAAU,GAAG,UAAU,MAAM,SAAS,GAC3C,UAAA;AAAA,QAAAR,2BAAAA,IAACS,aAAAA,OAAA,EACC,0CAACC,aAAAA,IAAA,EACC,UAAA;AAAA,UAAAV,2BAAAA,IAACW,aAAAA,MAAG,UAAAX,2BAAAA,IAACG,aAAAA,YAAA,EAAW,SAAQ,SAAQ,sCAAwB,EAAA,CAAa;AAAA,yCACpEQ,aAAAA,IAAA,EAAG,UAAAX,2BAAAA,IAACG,aAAAA,cAAW,SAAQ,SAAQ,8BAAgB,EAAA,CAAa;AAAA,yCAC5DQ,aAAAA,IAAA,EAAG,UAAAX,2BAAAA,IAACG,aAAAA,cAAW,SAAQ,SAAQ,+BAAiB,EAAA,CAAa;AAAA,yCAC7DQ,aAAAA,IAAA,EAAG,UAAAX,2BAAAA,IAACG,aAAAA,cAAW,SAAQ,SAAQ,8BAAgB,EAAA,CAAa;AAAA,yCAC5DQ,aAAAA,IAAA,EAAG,UAAAX,2BAAAA,IAACG,aAAAA,cAAW,SAAQ,SAAQ,6BAAe,EAAA,CAAa;AAAA,yCAC3DQ,aAAAA,IAAA,EAAG,UAAAX,2BAAAA,IAACG,aAAAA,cAAW,SAAQ,SAAQ,oBAAM,EAAA,CAAa;AAAA,UACnDH,2BAAAA,IAACW,aAAAA,IAAA,EAAG,OAAO,EAAE,WAAW,QAAA,GAAW,UAAAX,2BAAAA,IAACG,aAAAA,YAAA,EAAW,SAAQ,SAAQ,UAAA,UAAA,CAAO,EAAA,CAAa;AAAA,QAAA,EAAA,CACrF,EAAA,CACF;AAAA,QACAH,2BAAAA,IAACY,aAAAA,OAAA,EACE,UAAA,MAAM,IAAI,CAAC,SAAS;AACnB,gBAAM,YAAY,OAAO,QAAQ,KAAK,UAAU,CAAA,CAAE;AAClD,gBAAM,WAAW,CAAC,KAAK,iBAAiB,KAAK,kBAAkB;AAC/D,gBAAM,iBAAiB,CAAC,WACnB,qBAAqB,KAAK,CAAC,MAAM,EAAE,QAAQ,KAAK,aAAa,GAAG,eAAe,KAAK,gBACrF;AAEJ,iDACGF,iBAAA,EACC,UAAA;AAAA,YAAAT,2BAAAA,KAACY,aAAAA,IAAA,EAAG,OAAO,EAAE,UAAU,OACrB,UAAA;AAAA,cAAAb,2BAAAA,IAACG,aAAAA,YAAA,EAAW,SAAQ,SAAQ,YAAW,QAAO,WAAU,cAAa,SAAQ,SAC1E,UAAA,KAAK,KAAA,CACR;AAAA,cACAF,2BAAAA,KAACE,aAAAA,YAAA,EAAW,SAAQ,MAAK,WAAU,cAAa,UAAA;AAAA,gBAAA;AAAA,gBACtCH,2BAAAA,IAAC,QAAA,EAAM,UAAA,KAAK,YAAA,CAAY;AAAA,cAAA,EAAA,CAClC;AAAA,YAAA,GACF;AAAA,2CACCa,aAAAA,IAAA,EAAG,OAAO,EAAE,UAAU,OACrB,UAAAb,2BAAAA;AAAAA,cAACc,aAAAA;AAAAA,cAAA;AAAA,gBACC,iBAAiB,WAAW,eAAe;AAAA,gBAC3C,WAAW,WAAW,eAAe;AAAA,gBAEpC,UAAA;AAAA,cAAA;AAAA,YAAA,GAEL;AAAA,YACAd,2BAAAA,IAACa,aAAAA,IAAA,EAAG,OAAO,EAAE,UAAU,OACrB,UAAAb,2BAAAA,IAACE,aAAAA,MAAA,EAAK,KAAK,GAAG,MAAK,QAChB,UAAA,UAAU,IAAI,CAAC,CAAC,QAAQ,MAAM,MAC7BD,2BAAAA,KAACa,aAAAA,OAAA,EAAmB,iBAAgB,cAAa,WAAU,cACzD,UAAA;AAAA,cAAAd,2BAAAA,IAAC,UAAA,EAAQ,UAAA,OAAO,YAAA,EAAY,CAAE;AAAA,cAAS;AAAA,cAAO;AAAA,YAAA,EAAA,GADpC,MAEZ,CACD,EAAA,CACH,GACF;AAAA,YACAA,2BAAAA,IAACa,aAAAA,IAAA,EAAG,OAAO,EAAE,UAAU,OACpB,UAAA,KAAK,sBACJb,2BAAAA,IAACc,aAAAA,OAAA,EAAM,iBAAgB,cAAa,WAAU,cAAa,UAAA,0BAAA,CAE3D,IAEAd,2BAAAA,IAACc,aAAAA,OAAA,EAAM,iBAAgB,cAAa,WAAU,cAAa,UAAA,kBAAA,CAE3D,EAAA,CAEJ;AAAA,2CACCD,aAAAA,IAAA,EAAG,OAAO,EAAE,UAAU,OACrB,UAAAb,2BAAAA;AAAAA,cAACc,aAAAA;AAAAA,cAAA;AAAA,gBACC,iBAAiB,KAAK,mBAAmB,SAAS,iBAAiB;AAAA,gBACnE,WAAW,KAAK,mBAAmB,SAAS,iBAAiB;AAAA,gBAE5D,UAAA,KAAK,eAAe,YAAA;AAAA,cAAY;AAAA,YAAA,GAErC;AAAA,2CACCD,aAAAA,IAAA,EAAG,OAAO,EAAE,UAAU,OACrB,UAAAb,2BAAAA;AAAAA,cAACI,aAAAA;AAAAA,cAAA;AAAA,gBACC,SAAS,KAAK,UAAU,kBAAkB;AAAA,gBAC1C,MAAK;AAAA,gBACL,SAAS,MAAM,aAAa,IAAI;AAAA,gBAE/B,UAAA,KAAK,UAAU,aAAa;AAAA,cAAA;AAAA,YAAA,GAEjC;AAAA,YACAJ,2BAAAA,IAACa,aAAAA,IAAA,EAAG,OAAO,EAAE,WAAW,SAAS,UAAU,IAAA,GACzC,UAAAZ,2BAAAA,KAACC,aAAAA,MAAA,EAAK,KAAK,GAAG,gBAAe,YAC3B,UAAA;AAAA,cAAAF,2BAAAA;AAAAA,gBAACI,aAAAA;AAAAA,gBAAA;AAAA,kBACC,SAAQ;AAAA,kBACR,MAAK;AAAA,kBACL,0CAAYW,MAAAA,QAAA,EAAO;AAAA,kBACnB,SAAS,MAAM,eAAe,IAAI;AAAA,kBAClC,SAAS,mBAAmB,KAAK;AAAA,kBAClC,UAAA;AAAA,gBAAA;AAAA,cAAA;AAAA,cAGDf,2BAAAA;AAAAA,gBAACI,aAAAA;AAAAA,gBAAA;AAAA,kBACC,SAAQ;AAAA,kBACR,MAAK;AAAA,kBACL,SAAS,MAAM,cAAc,IAAI;AAAA,kBAClC,UAAA;AAAA,gBAAA;AAAA,cAAA;AAAA,cAGDJ,2BAAAA;AAAAA,gBAACI,aAAAA;AAAAA,gBAAA;AAAA,kBACC,SAAQ;AAAA,kBACR,MAAK;AAAA,kBACL,0CAAYY,MAAAA,OAAA,EAAM;AAAA,kBAClB,SAAS,MAAM,aAAa,KAAK,IAAI,KAAK,IAAI;AAAA,kBAC/C,UAAA;AAAA,gBAAA;AAAA,cAAA;AAAA,YAED,EAAA,CACF,EAAA,CACF;AAAA,UAAA,EAAA,GAjFO,KAAK,EAkFd;AAAA,QAAA,CAEH,EAAA,CACH;AAAA,MAAA,EAAA,CACF;AAAA,IAAA,GAEJ;AAAA,IAGC,aACChB,2BAAAA;AAAAA,MAACD,aAAAA;AAAAA,MAAA;AAAA,QACC,OAAO;AAAA,UACL,UAAU;AAAA,UACV,OAAO;AAAA,UACP,iBAAiB;AAAA,UACjB,QAAQ;AAAA,UACR,SAAS;AAAA,UACT,YAAY;AAAA,UACZ,gBAAgB;AAAA,QAAA;AAAA,QAGlB,UAAAE,2BAAAA;AAAAA,UAACF,aAAAA;AAAAA,UAAA;AAAA,YACC,YAAW;AAAA,YACX,WAAS;AAAA,YACT,QAAO;AAAA,YACP,SAAS;AAAA,YACT,OAAO,EAAE,OAAO,OAAO,UAAU,KAAK,WAAW,QAAQ,WAAW,OAAA;AAAA,YAEpE,UAAA;AAAA,cAAAC,2BAAAA,IAACG,aAAAA,YAAA,EAAW,SAAQ,SAAQ,KAAI,MAAK,YAAW,QAAO,cAAc,GAClE,UAAA,cAAc,yBAAyB,8BAC1C;AAAA,cACAH,2BAAAA,IAACG,aAAAA,YAAA,EAAW,SAAQ,MAAK,WAAU,cAAa,cAAc,GAAG,OAAO,EAAE,SAAS,QAAA,GAAW,UAAA,iIAE9F;AAAA,8CAECD,aAAAA,MAAA,EAAK,WAAU,UAAS,KAAK,GAAG,YAAW,WAC1C,UAAA;AAAA,gBAAAF,2BAAAA;AAAAA,kBAACiB,aAAAA;AAAAA,kBAAA;AAAA,oBACC,OAAM;AAAA,oBACN,aAAY;AAAA,oBACZ,OAAO;AAAA,oBACP,UAAU,CAAC,MAAW,YAAY,EAAE,OAAO,KAAK;AAAA,oBAChD,UAAQ;AAAA,kBAAA;AAAA,gBAAA;AAAA,gBAGVhB,2BAAAA;AAAAA,kBAACiB,aAAAA;AAAAA,kBAAA;AAAA,oBACC,OAAM;AAAA,oBACN,OAAO;AAAA,oBACP,UAAU,CAAC,QAAa,qBAAqB,GAAG;AAAA,oBAChD,MAAK;AAAA,oBAEL,UAAA;AAAA,sBAAAlB,2BAAAA,IAACmB,aAAAA,oBAAA,EAAmB,OAAM,KAAI,UAAA,gCAA4B;AAAA,sBACzD,qBAAqB,IAAI,CAAC,sCACxBA,iCAAA,EAA+B,OAAO,EAAE,KACtC,UAAA;AAAA,wBAAA,EAAE;AAAA,wBAAY;AAAA,wBAAG,EAAE;AAAA,wBAAI;AAAA,sBAAA,EAAA,GADD,EAAE,GAE3B,CACD;AAAA,oBAAA;AAAA,kBAAA;AAAA,gBAAA;AAAA,gBAGHnB,2BAAAA;AAAAA,kBAACiB,aAAAA;AAAAA,kBAAA;AAAA,oBACC,OAAM;AAAA,oBACN,aAAY;AAAA,oBACZ,OAAO;AAAA,oBACP,UAAU,CAAC,MAAW,aAAa,EAAE,OAAO,KAAK;AAAA,oBACjD,MAAK;AAAA,oBACL,UAAQ;AAAA,kBAAA;AAAA,gBAAA;AAAA,gBAGVjB,2BAAAA,IAACD,aAAAA,KAAA,EAAI,SAAS,GAAG,YAAW,cAAa,WAAS,MAAC,OAAO,EAAE,QAAQ,oBAAA,GAClE,UAAAC,2BAAAA;AAAAA,kBAACoB,aAAAA;AAAAA,kBAAA;AAAA,oBACC,SAAS;AAAA,oBACT,iBAAiB,CAAC,YAAqB,uBAAuB,OAAO;AAAA,oBAErE,UAAAnB,2BAAAA,KAACF,aAAAA,KAAA,EAAI,YAAY,GACf,UAAA;AAAA,sBAAAC,+BAACG,aAAAA,YAAA,EAAW,SAAQ,SAAQ,YAAW,QAAO,UAAA,qDAE9C;AAAA,sBACAH,2BAAAA,IAACG,aAAAA,cAAW,SAAQ,MAAK,WAAU,cAAa,SAAQ,SAAQ,UAAA,+KAAA,CAEhE;AAAA,oBAAA,EAAA,CACF;AAAA,kBAAA;AAAA,gBAAA,GAEJ;AAAA,gBAEAH,2BAAAA;AAAAA,kBAACqB,aAAAA;AAAAA,kBAAA;AAAA,oBACC,OAAM;AAAA,oBACN,aAAY;AAAA,oBACZ,OAAO;AAAA,oBACP,UAAU,CAAC,MAAW,YAAY,EAAE,OAAO,KAAK;AAAA,kBAAA;AAAA,gBAAA;AAAA,gBAGlDpB,2BAAAA;AAAAA,kBAACiB,aAAAA;AAAAA,kBAAA;AAAA,oBACC,OAAM;AAAA,oBACN,OAAO;AAAA,oBACP,UAAU,CAAC,QAAa,cAAc,GAAyB;AAAA,oBAE/D,UAAA;AAAA,sBAAAlB,2BAAAA,IAACmB,aAAAA,oBAAA,EAAmB,OAAM,WAAU,UAAA,gDAEpC;AAAA,sBACAnB,2BAAAA,IAACmB,aAAAA,oBAAA,EAAmB,OAAM,QAAO,UAAA,4CAAA,CAEjC;AAAA,oBAAA;AAAA,kBAAA;AAAA,gBAAA;AAAA,gBAIFlB,2BAAAA;AAAAA,kBAACF,aAAAA;AAAAA,kBAAA;AAAA,oBACC,SAAS;AAAA,oBACT,YAAW;AAAA,oBACX,WAAS;AAAA,oBACT,OAAO,EAAE,QAAQ,oBAAA;AAAA,oBAEjB,UAAA;AAAA,sBAAAE,gCAACC,aAAAA,QAAK,gBAAe,iBAAgB,YAAW,UAAS,cAAc,GACrE,UAAA;AAAA,wBAAAD,gCAACF,aAAAA,KAAA,EACC,UAAA;AAAA,0BAAAC,2BAAAA,IAACG,aAAAA,cAAW,SAAQ,MAAK,YAAW,QAAO,WAAU,cAAa,UAAA,wCAAA,CAElE;AAAA,0BACAH,2BAAAA,IAACG,aAAAA,YAAA,EAAW,SAAQ,MAAK,WAAU,cAAa,OAAO,EAAE,SAAS,WAAW,UAAA,mDAAA,CAE7E;AAAA,wBAAA,GACF;AAAA,wBACAH,2BAAAA;AAAAA,0BAACI,aAAAA;AAAAA,0BAAA;AAAA,4BACC,SAAQ;AAAA,4BACR,MAAK;AAAA,4BACL,0CAAYE,MAAAA,MAAA,EAAK;AAAA,4BACjB,SAAS;AAAA,4BACV,UAAA;AAAA,0BAAA;AAAA,wBAAA;AAAA,sBAED,GACF;AAAA,qDAECJ,aAAAA,MAAA,EAAK,WAAU,UAAS,KAAK,GAC3B,UAAA,WAAW,IAAI,CAAC,GAAG,QAClBD,2BAAAA,KAACC,mBAAA,EAAe,KAAK,GAAG,YAAW,UACjC,UAAA;AAAA,wBAAAF,2BAAAA,IAACD,aAAAA,KAAA,EAAI,OAAO,EAAE,MAAM,KAClB,UAAAC,2BAAAA;AAAAA,0BAACiB,aAAAA;AAAAA,0BAAA;AAAA,4BACC,aAAY;AAAA,4BACZ,OAAO,EAAE;AAAA,4BACT,UAAU,CAAC,MAAW,kBAAkB,KAAK,OAAO,EAAE,OAAO,KAAK;AAAA,0BAAA;AAAA,wBAAA,GAEtE;AAAA,uDACClB,aAAAA,KAAA,EAAI,OAAO,EAAE,MAAM,KAClB,UAAAC,2BAAAA;AAAAA,0BAACiB,aAAAA;AAAAA,0BAAA;AAAA,4BACC,MAAK;AAAA,4BACL,aAAY;AAAA,4BACZ,OAAO,OAAO,EAAE,UAAU;AAAA,4BAC1B,UAAU,CAAC,MAAW,kBAAkB,KAAK,cAAc,EAAE,OAAO,KAAK;AAAA,0BAAA;AAAA,wBAAA,GAE7E;AAAA,wBACAjB,2BAAAA;AAAAA,0BAACI,aAAAA;AAAAA,0BAAA;AAAA,4BACC,SAAQ;AAAA,4BACR,MAAK;AAAA,4BACL,SAAS,MAAM,qBAAqB,GAAG;AAAA,4BACvC,UAAU,WAAW,UAAU;AAAA,4BAChC,UAAA;AAAA,0BAAA;AAAA,wBAAA;AAAA,sBAED,EAAA,GAvBS,GAwBX,CACD,EAAA,CACH;AAAA,oBAAA;AAAA,kBAAA;AAAA,gBAAA;AAAA,cACF,GACF;AAAA,8CAGCF,aAAAA,MAAA,EAAK,gBAAe,YAAW,KAAK,GAAG,WAAW,GACjD,UAAA;AAAA,gBAAAF,2BAAAA;AAAAA,kBAACI,aAAAA;AAAAA,kBAAA;AAAA,oBACC,SAAQ;AAAA,oBACR,SAAS,MAAM,aAAa,KAAK;AAAA,oBACjC,UAAU;AAAA,oBACX,UAAA;AAAA,kBAAA;AAAA,gBAAA;AAAA,gBAGDJ,2BAAAA;AAAAA,kBAACI,aAAAA;AAAAA,kBAAA;AAAA,oBACC,SAAQ;AAAA,oBACR,0CAAYkB,MAAAA,OAAA,EAAM;AAAA,oBAClB,SAAS;AAAA,oBACT,SAAS;AAAA,oBACV,UAAA;AAAA,kBAAA;AAAA,gBAAA;AAAA,cAED,EAAA,CACF;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA;AAAA,MACF;AAAA,IAAA;AAAA,IAKH,iBAAiB,cAChBtB,2BAAAA;AAAAA,MAACD,aAAAA;AAAAA,MAAA;AAAA,QACC,OAAO;AAAA,UACL,UAAU;AAAA,UACV,OAAO;AAAA,UACP,iBAAiB;AAAA,UACjB,QAAQ;AAAA,UACR,SAAS;AAAA,UACT,YAAY;AAAA,UACZ,gBAAgB;AAAA,QAAA;AAAA,QAGlB,UAAAE,2BAAAA;AAAAA,UAACF,aAAAA;AAAAA,UAAA;AAAA,YACC,YAAW;AAAA,YACX,WAAS;AAAA,YACT,QAAO;AAAA,YACP,SAAS;AAAA,YACT,OAAO,EAAE,OAAO,OAAO,UAAU,KAAK,WAAW,QAAQ,WAAW,OAAA;AAAA,YAEpE,UAAA;AAAA,cAAAE,gCAACC,aAAAA,QAAK,gBAAe,iBAAgB,YAAW,UAAS,cAAc,GACrE,UAAA;AAAA,gBAAAD,gCAACF,aAAAA,KAAA,EACC,UAAA;AAAA,kBAAAE,gCAACE,aAAAA,cAAW,SAAQ,QAAO,KAAI,MAAK,YAAW,QAAO,UAAA;AAAA,oBAAA;AAAA,oBAC7B,WAAW,MAAM;AAAA,kBAAA,GAC1C;AAAA,kBACAF,2BAAAA,KAACE,aAAAA,YAAA,EAAW,SAAQ,MAAK,WAAU,cAAa,UAAA;AAAA,oBAAA;AAAA,oBAChCH,2BAAAA,IAAC,QAAA,EAAM,UAAA,WAAW,MAAM,aAAY;AAAA,oBAAO;AAAA,oBAA2B,WAAW;AAAA,kBAAA,EAAA,CACjG;AAAA,gBAAA,GACF;AAAA,gBACAA,2BAAAA;AAAAA,kBAACc,aAAAA;AAAAA,kBAAA;AAAA,oBACC,iBAAiB,WAAW,kBAAkB,IAAI,cAAc;AAAA,oBAChE,WAAW,WAAW,kBAAkB,IAAI,cAAc;AAAA,oBAC1D,MAAK;AAAA,oBAEJ,qBAAW,kBAAkB,IAC1B,GAAG,WAAW,eAAe,wBAC7B;AAAA,kBAAA;AAAA,gBAAA;AAAA,cACN,GACF;AAAA,cAEC,WAAW,oBAAoB,IAC9Bd,2BAAAA,IAACO,aAAAA,SAAM,SAAQ,WAAU,OAAM,uCAAsC,cAAc,GAAG,UAAA,iGAAA,CAEtF,oCAECR,aAAAA,KAAA,EACC,UAAA;AAAA,gBAAAE,gCAACM,aAAAA,SAAM,SAAQ,UAAS,OAAM,wCAAuC,cAAc,GAAG,UAAA;AAAA,kBAAA;AAAA,kBACrE,WAAW;AAAA,kBAAgB;AAAA,gBAAA,GAC5C;AAAA,gBAEAN,2BAAAA,KAACO,aAAAA,SAAM,UAAU,GAAG,UAAU,WAAW,WAAW,SAAS,GAC3D,UAAA;AAAA,kBAAAR,2BAAAA,IAACS,aAAAA,OAAA,EACC,0CAACC,aAAAA,IAAA,EACC,UAAA;AAAA,oBAAAV,2BAAAA,IAACW,aAAAA,MAAG,UAAAX,2BAAAA,IAACG,aAAAA,YAAA,EAAW,SAAQ,SAAQ,kCAAoB,EAAA,CAAa;AAAA,mDAChEQ,aAAAA,IAAA,EAAG,UAAAX,2BAAAA,IAACG,aAAAA,cAAW,SAAQ,SAAQ,+BAAiB,EAAA,CAAa;AAAA,mDAC7DQ,aAAAA,IAAA,EAAG,UAAAX,2BAAAA,IAACG,aAAAA,cAAW,SAAQ,SAAQ,4BAAc,EAAA,CAAa;AAAA,oBAC3DH,2BAAAA,IAACW,aAAAA,IAAA,EAAG,OAAO,EAAE,WAAW,QAAA,GAAW,UAAAX,2BAAAA,IAACG,aAAAA,YAAA,EAAW,SAAQ,SAAQ,UAAA,SAAA,CAAM,EAAA,CAAa;AAAA,kBAAA,EAAA,CACpF,EAAA,CACF;AAAA,kBACAH,2BAAAA,IAACY,aAAAA,SACE,UAAA,WAAW,WAAW,IAAI,CAAC,GAAQ,QAClCX,2BAAAA,KAACS,aAAAA,IAAA,EACC,UAAA;AAAA,oBAAAT,gCAACY,aAAAA,IAAA,EACC,UAAA;AAAA,sBAAAb,+BAACG,aAAAA,cAAW,SAAQ,SAAQ,YAAW,QACpC,YAAE,YACL;AAAA,sDACCA,aAAAA,YAAA,EAAW,SAAQ,MAAK,WAAU,cAAa,SAAQ,SAAQ,UAAA;AAAA,wBAAA;AAAA,wBACjD,EAAE;AAAA,sBAAA,GACjB;AAAA,sBACC,EAAE,UACDF,2BAAAA,KAACE,aAAAA,YAAA,EAAW,SAAQ,MAAK,WAAU,cAAa,SAAQ,SAAQ,OAAO,EAAE,UAAU,MAAM,UAAA;AAAA,wBAAA;AAAA,wBAC3E,EAAE;AAAA,sBAAA,EAAA,CAChB;AAAA,oBAAA,GAEJ;AAAA,oBACAH,2BAAAA,IAACa,aAAAA,IAAA,EACC,UAAAb,2BAAAA,IAACc,aAAAA,OAAA,EAAM,iBAAgB,cAAa,WAAU,cAC3C,UAAA,EAAE,gBAAA,CACL,EAAA,CACF;AAAA,oDACCD,aAAAA,IAAA,EACC,UAAA;AAAA,sBAAAZ,gCAACE,aAAAA,cAAW,SAAQ,SAAQ,WAAU,aAAY,YAAW,QAC1D,UAAA;AAAA,wBAAA,EAAE;AAAA,wBAAW;AAAA,sBAAA,GAChB;AAAA,sBACAF,2BAAAA,KAACE,aAAAA,YAAA,EAAW,SAAQ,MAAK,WAAU,cAChC,UAAA;AAAA,wBAAA;AAAA,wBAAI;AAAA,wBAAe,EAAE;AAAA,wBAAW;AAAA,sBAAA,EAAA,CACnC;AAAA,oBAAA,GACF;AAAA,mDACCU,aAAAA,IAAA,EAAG,OAAO,EAAE,WAAW,WACtB,UAAAb,2BAAAA;AAAAA,sBAACI,aAAAA;AAAAA,sBAAA;AAAA,wBACC,SAAQ;AAAA,wBACR,MAAK;AAAA,wBACL,SAAS,MAAM,OAAO,KAAK,EAAE,SAAS,QAAQ;AAAA,wBAC/C,UAAA;AAAA,sBAAA;AAAA,oBAAA,EAED,CACF;AAAA,kBAAA,EAAA,GAnCO,GAoCT,CACD,EAAA,CACH;AAAA,gBAAA,EAAA,CACF;AAAA,cAAA,GACF;AAAA,8CAGDF,aAAAA,MAAA,EAAK,gBAAe,iBAAgB,YAAW,UAAS,WAAW,GACjE,UAAA;AAAA,gBAAA,WAAW,kBAAkB,IAC5BF,2BAAAA;AAAAA,kBAACI,aAAAA;AAAAA,kBAAA;AAAA,oBACC,SAAQ;AAAA,oBACR,0CAAYmB,MAAAA,UAAA,EAAS;AAAA,oBACrB,SAAS,YAAY;AACnB,0BAAI;AACF,8BAAM,MAAM,MAAM,IAAI,uCAAuC,WAAW,KAAK,EAAE,EAAE;AACjF,8BAAM,SAAS,IAAI,MAAM,eAAe,OAAO,IAAI,SAAS,WAAW,IAAI,OAAO;AAClF,4BAAI,CAAC,QAAQ;AACX,6CAAmB;AAAA,4BACjB,MAAM;AAAA,4BACN,SAAS;AAAA,0BAAA,CACV;AACD;AAAA,wBAAA;AAEF,8BAAM,cAAc,OAAO,WAAW,QAAQ,IAAI,SAAS,WAAW;AACtE,8BAAM,OAAO,IAAI,KAAK,CAAC,WAAW,GAAG,EAAE,MAAM,2BAA2B;AACxE,8BAAM,MAAM,IAAI,gBAAgB,IAAI;AACpC,8BAAM,OAAO,SAAS,cAAc,GAAG;AACvC,6BAAK,OAAO;AACZ,6BAAK,aAAa,YAAY,cAAc,WAAW,KAAK,EAAE,MAAM;AACpE,iCAAS,KAAK,YAAY,IAAI;AAC9B,6BAAK,MAAA;AACL,iCAAS,KAAK,YAAY,IAAI;AAC9B,4BAAI,gBAAgB,GAAG;AACvB,2CAAmB;AAAA,0BACjB,MAAM;AAAA,0BACN,SAAS;AAAA,wBAAA,CACV;AAAA,sBAAA,SACM,GAAQ;AACf,2CAAmB;AAAA,0BACjB,MAAM;AAAA,0BACN,SAAS,EAAE,WAAW;AAAA,wBAAA,CACvB;AAAA,sBAAA;AAAA,oBACH;AAAA,oBAEH,UAAA;AAAA,kBAAA;AAAA,gBAAA,mCAIAxB,aAAAA,KAAA,EAAI;AAAA,gBAEPC,2BAAAA,IAACI,aAAAA,UAAO,SAAQ,WAAU,SAAS,MAAM,iBAAiB,KAAK,GAAG,UAAA,eAAA,CAElE;AAAA,cAAA,EAAA,CACF;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA;AAAA,MACF;AAAA,IAAA;AAAA,EACF,GAEJ;AAEJ;;"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useEffect } from "react";
|
|
3
3
|
import { useFetchClient, useNotification } from "@strapi/strapi/admin";
|
|
4
|
-
import { Box, Flex, Typography, Button, Alert, Table, Thead, Tr, Th, Tbody, Td, Badge, TextInput,
|
|
4
|
+
import { Box, Flex, Typography, Button, Alert, Table, Thead, Tr, Th, Tbody, Td, Badge, TextInput, SingleSelect, SingleSelectOption, Checkbox, Textarea } from "@strapi/design-system";
|
|
5
5
|
import { ArrowClockwise, Plus, Search, Trash, Check, Download } from "@strapi/icons";
|
|
6
6
|
function ValidationRulesTab() {
|
|
7
7
|
const { get, post, del } = useFetchClient();
|
|
@@ -10,10 +10,13 @@ function ValidationRulesTab() {
|
|
|
10
10
|
const [loading, setLoading] = useState(true);
|
|
11
11
|
const [modalOpen, setModalOpen] = useState(false);
|
|
12
12
|
const [editingRule, setEditingRule] = useState(null);
|
|
13
|
+
const [availableCollections, setAvailableCollections] = useState([]);
|
|
13
14
|
const [formName, setFormName] = useState("");
|
|
14
15
|
const [formDesc, setFormDesc] = useState("");
|
|
16
|
+
const [formCollectionUid, setFormCollectionUid] = useState("*");
|
|
15
17
|
const [formField, setFormField] = useState("");
|
|
16
18
|
const [formAction, setFormAction] = useState("disable");
|
|
19
|
+
const [formIncludeRichText, setFormIncludeRichText] = useState(true);
|
|
17
20
|
const [formQuotas, setFormQuotas] = useState([
|
|
18
21
|
{ key: "h1", maxAllowed: 1 },
|
|
19
22
|
{ key: "h2", maxAllowed: 3 }
|
|
@@ -22,6 +25,19 @@ function ValidationRulesTab() {
|
|
|
22
25
|
const [scanModalOpen, setScanModalOpen] = useState(false);
|
|
23
26
|
const [scanningRuleId, setScanningRuleId] = useState(null);
|
|
24
27
|
const [scanReport, setScanReport] = useState(null);
|
|
28
|
+
const fetchCollections = async () => {
|
|
29
|
+
try {
|
|
30
|
+
const res = await get("/bulk-updater/models");
|
|
31
|
+
if (res.data?.targetUids && res.data?.metaByUid) {
|
|
32
|
+
const list = res.data.targetUids.map((uid) => ({
|
|
33
|
+
uid,
|
|
34
|
+
displayName: res.data.metaByUid[uid]?.displayName || uid
|
|
35
|
+
}));
|
|
36
|
+
setAvailableCollections(list);
|
|
37
|
+
}
|
|
38
|
+
} catch {
|
|
39
|
+
}
|
|
40
|
+
};
|
|
25
41
|
const handleScanRule = async (rule) => {
|
|
26
42
|
try {
|
|
27
43
|
setScanningRuleId(rule.id);
|
|
@@ -57,13 +73,16 @@ function ValidationRulesTab() {
|
|
|
57
73
|
};
|
|
58
74
|
useEffect(() => {
|
|
59
75
|
fetchRules();
|
|
76
|
+
fetchCollections();
|
|
60
77
|
}, []);
|
|
61
78
|
const openCreateModal = () => {
|
|
62
79
|
setEditingRule(null);
|
|
63
80
|
setFormName("");
|
|
64
81
|
setFormDesc("");
|
|
82
|
+
setFormCollectionUid("*");
|
|
65
83
|
setFormField("headingLevel");
|
|
66
84
|
setFormAction("disable");
|
|
85
|
+
setFormIncludeRichText(true);
|
|
67
86
|
setFormQuotas([
|
|
68
87
|
{ key: "h1", maxAllowed: 1 },
|
|
69
88
|
{ key: "h2", maxAllowed: 3 }
|
|
@@ -74,8 +93,10 @@ function ValidationRulesTab() {
|
|
|
74
93
|
setEditingRule(rule);
|
|
75
94
|
setFormName(rule.name);
|
|
76
95
|
setFormDesc(rule.description || "");
|
|
96
|
+
setFormCollectionUid(rule.collectionUid || "*");
|
|
77
97
|
setFormField(rule.targetField);
|
|
78
98
|
setFormAction(rule.exceededAction || "disable");
|
|
99
|
+
setFormIncludeRichText(rule.includeRichTextHtml ?? true);
|
|
79
100
|
const quotaPairs = Object.entries(rule.quotas || {}).map(([k, v]) => ({
|
|
80
101
|
key: k,
|
|
81
102
|
maxAllowed: v
|
|
@@ -129,11 +150,13 @@ function ValidationRulesTab() {
|
|
|
129
150
|
id: editingRule ? editingRule.id : void 0,
|
|
130
151
|
name: formName.trim(),
|
|
131
152
|
description: formDesc.trim(),
|
|
153
|
+
collectionUid: formCollectionUid,
|
|
132
154
|
targetField: formField.trim(),
|
|
133
155
|
quotas: quotasRecord,
|
|
134
156
|
exceededAction: formAction,
|
|
135
157
|
enabled: editingRule ? editingRule.enabled : true,
|
|
136
|
-
scope: "entry"
|
|
158
|
+
scope: "entry",
|
|
159
|
+
includeRichTextHtml: formIncludeRichText
|
|
137
160
|
};
|
|
138
161
|
const res = await post("/bulk-updater/validation-rules", { rule: payload });
|
|
139
162
|
if (res.data && res.data.success) {
|
|
@@ -195,8 +218,8 @@ function ValidationRulesTab() {
|
|
|
195
218
|
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
196
219
|
/* @__PURE__ */ jsx(Box, { padding: 6, background: "neutral0", shadow: "filterShadow", hasRadius: true, marginBottom: 6, children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", wrap: "wrap", gap: 3, children: [
|
|
197
220
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
198
|
-
/* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h2", fontWeight: "bold", children: "Cross-Component Validation
|
|
199
|
-
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { marginTop: 4 }, children: "Define and
|
|
221
|
+
/* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h2", fontWeight: "bold", children: "Scoped & Cross-Component Validation Rules" }),
|
|
222
|
+
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { marginTop: 4 }, children: "Define collection-scoped rules and combined heading limits across structured components and free-form Rich Text / CKEditor HTML / Markdown content." })
|
|
200
223
|
] }),
|
|
201
224
|
/* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
|
|
202
225
|
/* @__PURE__ */ jsx(
|
|
@@ -226,32 +249,43 @@ function ValidationRulesTab() {
|
|
|
226
249
|
rules.length,
|
|
227
250
|
")"
|
|
228
251
|
] }),
|
|
229
|
-
loading ? /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "neutral500", children: "Loading validation rules..." }) : rules.length === 0 ? /* @__PURE__ */ jsx(Alert, { variant: "info", title: "No Rules Defined", children: 'Click "Create New Rule" above to add your first cross-component quota rule.' }) : /* @__PURE__ */ jsxs(Table, { colCount:
|
|
252
|
+
loading ? /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "neutral500", children: "Loading validation rules..." }) : rules.length === 0 ? /* @__PURE__ */ jsx(Alert, { variant: "info", title: "No Rules Defined", children: 'Click "Create New Rule" above to add your first cross-component quota rule.' }) : /* @__PURE__ */ jsxs(Table, { colCount: 7, rowCount: rules.length + 1, children: [
|
|
230
253
|
/* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [
|
|
231
254
|
/* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "RULE NAME & TARGET FIELD" }) }),
|
|
232
|
-
/* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "
|
|
255
|
+
/* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "COLLECTION SCOPE" }) }),
|
|
233
256
|
/* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "QUOTA CONSTRAINTS" }) }),
|
|
257
|
+
/* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "RICH TEXT / HTML" }) }),
|
|
234
258
|
/* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "EXCEEDED ACTION" }) }),
|
|
235
259
|
/* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "STATUS" }) }),
|
|
236
260
|
/* @__PURE__ */ jsx(Th, { style: { textAlign: "right" }, children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "ACTIONS" }) })
|
|
237
261
|
] }) }),
|
|
238
262
|
/* @__PURE__ */ jsx(Tbody, { children: rules.map((rule) => {
|
|
239
263
|
const quotaKeys = Object.entries(rule.quotas || {});
|
|
264
|
+
const isGlobal = !rule.collectionUid || rule.collectionUid === "*";
|
|
265
|
+
const collectionName = !isGlobal ? availableCollections.find((c) => c.uid === rule.collectionUid)?.displayName || rule.collectionUid : "All Collections (Global)";
|
|
240
266
|
return /* @__PURE__ */ jsxs(Tr, { children: [
|
|
241
267
|
/* @__PURE__ */ jsxs(Td, { style: { minWidth: 200 }, children: [
|
|
242
268
|
/* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", textColor: "neutral800", display: "block", children: rule.name }),
|
|
243
269
|
/* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral500", children: [
|
|
244
|
-
"Target
|
|
270
|
+
"Target: ",
|
|
245
271
|
/* @__PURE__ */ jsx("code", { children: rule.targetField })
|
|
246
272
|
] })
|
|
247
273
|
] }),
|
|
248
|
-
/* @__PURE__ */ jsx(Td, { style: {
|
|
249
|
-
|
|
274
|
+
/* @__PURE__ */ jsx(Td, { style: { minWidth: 160 }, children: /* @__PURE__ */ jsx(
|
|
275
|
+
Badge,
|
|
276
|
+
{
|
|
277
|
+
backgroundColor: isGlobal ? "neutral150" : "primary100",
|
|
278
|
+
textColor: isGlobal ? "neutral700" : "primary700",
|
|
279
|
+
children: collectionName
|
|
280
|
+
}
|
|
281
|
+
) }),
|
|
282
|
+
/* @__PURE__ */ jsx(Td, { style: { minWidth: 180 }, children: /* @__PURE__ */ jsx(Flex, { gap: 1, wrap: "wrap", children: quotaKeys.map(([optVal, maxVal]) => /* @__PURE__ */ jsxs(Badge, { backgroundColor: "neutral100", textColor: "primary700", children: [
|
|
250
283
|
/* @__PURE__ */ jsx("strong", { children: optVal.toUpperCase() }),
|
|
251
284
|
": max ",
|
|
252
285
|
maxVal
|
|
253
286
|
] }, optVal)) }) }),
|
|
254
|
-
/* @__PURE__ */ jsx(Td, { style: { minWidth:
|
|
287
|
+
/* @__PURE__ */ jsx(Td, { style: { minWidth: 140 }, children: rule.includeRichTextHtml ? /* @__PURE__ */ jsx(Badge, { backgroundColor: "success100", textColor: "success700", children: "HTML & Rich Text Active" }) : /* @__PURE__ */ jsx(Badge, { backgroundColor: "neutral150", textColor: "neutral600", children: "Structured Only" }) }),
|
|
288
|
+
/* @__PURE__ */ jsx(Td, { style: { minWidth: 110 }, children: /* @__PURE__ */ jsx(
|
|
255
289
|
Badge,
|
|
256
290
|
{
|
|
257
291
|
backgroundColor: rule.exceededAction === "hide" ? "secondary100" : "warning100",
|
|
@@ -323,32 +357,61 @@ function ValidationRulesTab() {
|
|
|
323
357
|
hasRadius: true,
|
|
324
358
|
shadow: "popupShadow",
|
|
325
359
|
padding: 6,
|
|
326
|
-
style: { width: "90%", maxWidth:
|
|
360
|
+
style: { width: "90%", maxWidth: 640, maxHeight: "90vh", overflowY: "auto" },
|
|
327
361
|
children: [
|
|
328
362
|
/* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h2", fontWeight: "bold", marginBottom: 1, children: editingRule ? "Edit Validation Rule" : "Create New Validation Rule" }),
|
|
329
|
-
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", marginBottom: 4, style: { display: "block" }, children: "Configure
|
|
363
|
+
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", marginBottom: 4, style: { display: "block" }, children: "Configure collection-scoped quotas. Rules apply consistently to target properties and optionally scan rich text HTML content." }),
|
|
330
364
|
/* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 4, alignItems: "stretch", children: [
|
|
331
365
|
/* @__PURE__ */ jsx(
|
|
332
366
|
TextInput,
|
|
333
367
|
{
|
|
334
368
|
label: "Rule Name",
|
|
335
|
-
placeholder: "e.g. SEO Single H1 Rule",
|
|
369
|
+
placeholder: "e.g. SEO Single H1 Page Rule",
|
|
336
370
|
value: formName,
|
|
337
371
|
onChange: (e) => setFormName(e.target.value),
|
|
338
372
|
required: true
|
|
339
373
|
}
|
|
340
374
|
),
|
|
375
|
+
/* @__PURE__ */ jsxs(
|
|
376
|
+
SingleSelect,
|
|
377
|
+
{
|
|
378
|
+
label: "Target Collection Type",
|
|
379
|
+
value: formCollectionUid,
|
|
380
|
+
onChange: (val) => setFormCollectionUid(val),
|
|
381
|
+
hint: "Scope this rule to a specific collection type, or select All Collections.",
|
|
382
|
+
children: [
|
|
383
|
+
/* @__PURE__ */ jsx(SingleSelectOption, { value: "*", children: "* (All Collections / Global)" }),
|
|
384
|
+
availableCollections.map((c) => /* @__PURE__ */ jsxs(SingleSelectOption, { value: c.uid, children: [
|
|
385
|
+
c.displayName,
|
|
386
|
+
" (",
|
|
387
|
+
c.uid,
|
|
388
|
+
")"
|
|
389
|
+
] }, c.uid))
|
|
390
|
+
]
|
|
391
|
+
}
|
|
392
|
+
),
|
|
341
393
|
/* @__PURE__ */ jsx(
|
|
342
394
|
TextInput,
|
|
343
395
|
{
|
|
344
|
-
label: "Target Field
|
|
345
|
-
placeholder: "e.g. headingLevel, titleStyle, or
|
|
396
|
+
label: "Target Field / Component Property Path",
|
|
397
|
+
placeholder: "e.g. headingLevel, titleStyle, or st-featured.titleHeadingStyle.titleStyle",
|
|
346
398
|
value: formField,
|
|
347
399
|
onChange: (e) => setFormField(e.target.value),
|
|
348
|
-
hint: "Attribute name
|
|
400
|
+
hint: "Attribute name or dot-notated hierarchy (e.g. 'titleStyle' or 'st-featured.titleHeadingStyle.titleStyle').",
|
|
349
401
|
required: true
|
|
350
402
|
}
|
|
351
403
|
),
|
|
404
|
+
/* @__PURE__ */ jsx(Box, { padding: 3, background: "neutral100", hasRadius: true, style: { border: "1px solid #e0e0e0" }, children: /* @__PURE__ */ jsx(
|
|
405
|
+
Checkbox,
|
|
406
|
+
{
|
|
407
|
+
checked: formIncludeRichText,
|
|
408
|
+
onCheckedChange: (checked) => setFormIncludeRichText(checked),
|
|
409
|
+
children: /* @__PURE__ */ jsxs(Box, { marginLeft: 2, children: [
|
|
410
|
+
/* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", children: "Scan & Combine Rich Text / CKEditor HTML Headings" }),
|
|
411
|
+
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", display: "block", children: "When enabled, any <h1>..<h6> tags, Markdown headings (#), or Strapi Blocks inside rich text fields are counted together with structured component choices towards the quota." })
|
|
412
|
+
] })
|
|
413
|
+
}
|
|
414
|
+
) }),
|
|
352
415
|
/* @__PURE__ */ jsx(
|
|
353
416
|
Textarea,
|
|
354
417
|
{
|
|
@@ -516,6 +579,10 @@ function ValidationRulesTab() {
|
|
|
516
579
|
/* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral500", display: "block", children: [
|
|
517
580
|
"Collection: ",
|
|
518
581
|
v.collectionDisplayName
|
|
582
|
+
] }),
|
|
583
|
+
v.detail && /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", display: "block", style: { fontSize: 11 }, children: [
|
|
584
|
+
"Breakdown: ",
|
|
585
|
+
v.detail
|
|
519
586
|
] })
|
|
520
587
|
] }),
|
|
521
588
|
/* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Badge, { backgroundColor: "warning100", textColor: "warning700", children: v.violatingOption }) }),
|
|
@@ -596,4 +663,4 @@ function ValidationRulesTab() {
|
|
|
596
663
|
export {
|
|
597
664
|
ValidationRulesTab as V
|
|
598
665
|
};
|
|
599
|
-
//# sourceMappingURL=ValidationRulesTab-
|
|
666
|
+
//# sourceMappingURL=ValidationRulesTab-Dqkson67.mjs.map
|