@kyro-cms/admin 0.12.5 → 0.12.7
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/index.cjs +32 -110
- package/dist/index.css +1 -1
- package/dist/index.d.cts +1 -3
- package/dist/index.d.ts +1 -3
- package/dist/index.js +32 -110
- package/package.json +7 -2
- package/src/components/AutoForm.tsx +35 -45
- package/src/components/DashboardMetrics.tsx +519 -290
- package/src/components/FieldRenderer.tsx +59 -47
- package/src/components/ListView.tsx +10 -5
- package/src/components/PluginsManager.tsx +37 -20
- package/src/components/Sidebar.astro +7 -14
- package/src/components/blocks/AccordionBlock.tsx +8 -4
- package/src/components/blocks/ArrayBlock.tsx +4 -3
- package/src/components/blocks/BlockEditModal.tsx +4 -3
- package/src/components/blocks/CardBlock.tsx +10 -2
- package/src/components/blocks/ChildBlocksTree.tsx +19 -18
- package/src/components/blocks/CodeBlock.tsx +7 -2
- package/src/components/blocks/FileBlock.tsx +6 -2
- package/src/components/blocks/GenericBlock.tsx +1 -1
- package/src/components/blocks/HeadingBlock.tsx +6 -2
- package/src/components/blocks/HeadingSubheadingBlock.tsx +7 -2
- package/src/components/blocks/HeroBlock.tsx +12 -3
- package/src/components/blocks/ImageBlock.tsx +8 -2
- package/src/components/blocks/ListBlock.tsx +6 -2
- package/src/components/blocks/ParagraphBlock.tsx +2 -2
- package/src/components/blocks/RelationshipBlock.tsx +10 -2
- package/src/components/blocks/VideoBlock.tsx +7 -2
- package/src/components/fields/AccordionField.tsx +1 -1
- package/src/components/fields/ArrayLayout.tsx +55 -58
- package/src/components/fields/BlocksField.tsx +1 -1
- package/src/components/fields/ChildrenField.tsx +3 -2
- package/src/components/fields/CodeField.tsx +3 -2
- package/src/components/fields/ColumnsField.tsx +3 -2
- package/src/components/fields/DateField.tsx +2 -2
- package/src/components/fields/FieldLayout.tsx +3 -3
- package/src/components/fields/GroupLayout.tsx +2 -2
- package/src/components/fields/MarkdownField.tsx +2 -2
- package/src/components/fields/NumberField.tsx +4 -4
- package/src/components/fields/RelationshipField.tsx +4 -1
- package/src/components/fields/RichTextField.tsx +200 -5
- package/src/components/fields/extensions/blockComponents.tsx +1 -1
- package/src/components/fields/extensions/blocksStore.ts +15 -12
- package/src/components/ui/Pagination.tsx +1 -1
- package/src/fields/index.ts +1 -1
- package/src/hooks/useAutoFormState.ts +7 -6
- package/src/index.ts +0 -1
- package/src/integration.ts +64 -26
- package/src/lib/api.ts +1 -0
- package/src/lib/config.ts +6 -19
- package/src/lib/core-types.ts +78 -0
- package/src/plugins/seo-admin.tsx +155 -0
- package/src/styles/main.css +2 -0
- package/src/vite-env.d.ts +10 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kyro-cms/admin",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.7",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=22"
|
|
6
6
|
},
|
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
"import": "./src/plugins/index.ts",
|
|
31
31
|
"default": "./src/plugins/index.ts"
|
|
32
32
|
},
|
|
33
|
+
"./plugins/seo-admin": {
|
|
34
|
+
"import": "./src/plugins/seo-admin.tsx",
|
|
35
|
+
"default": "./src/plugins/seo-admin.tsx"
|
|
36
|
+
},
|
|
33
37
|
"./blocks": {
|
|
34
38
|
"import": "./src/blocks/index.ts",
|
|
35
39
|
"default": "./src/blocks/index.ts"
|
|
@@ -104,6 +108,7 @@
|
|
|
104
108
|
"i18next-browser-languagedetector": "^8.2.1",
|
|
105
109
|
"idb-keyval": "^6.2.2",
|
|
106
110
|
"lucide-react": "^0.475.0",
|
|
111
|
+
"marked": "^18.0.6",
|
|
107
112
|
"react": "^19.0.0",
|
|
108
113
|
"react-dom": "^19.0.0",
|
|
109
114
|
"react-i18next": "^17.0.8",
|
|
@@ -126,7 +131,7 @@
|
|
|
126
131
|
"vitest": "^4.1.4"
|
|
127
132
|
},
|
|
128
133
|
"peerDependencies": {
|
|
129
|
-
"@kyro-cms/core": "^0.
|
|
134
|
+
"@kyro-cms/core": "^0.12.6",
|
|
130
135
|
"react": "^19.0.0",
|
|
131
136
|
"react-dom": "^19.0.0"
|
|
132
137
|
},
|
|
@@ -1,41 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Check, AlertTriangle } from "./ui/icons";
|
|
2
2
|
import { useState, useRef, useEffect } from "react";
|
|
3
3
|
import type {
|
|
4
4
|
CollectionConfig,
|
|
5
5
|
GlobalConfig,
|
|
6
6
|
Field,
|
|
7
|
-
Block,
|
|
8
7
|
} from "@kyro-cms/core/client";
|
|
8
|
+
import type { DeclarativeCondition } from "../lib/core-types";
|
|
9
9
|
|
|
10
10
|
type View = "edit" | "version" | "api";
|
|
11
|
-
import { UploadField } from "./fields/UploadField";
|
|
12
|
-
import { CodeField } from "./fields";
|
|
13
|
-
import NumberField from "./fields/NumberField";
|
|
14
|
-
import CheckboxField from "./fields/CheckboxField";
|
|
15
|
-
import SelectField from "./fields/SelectField";
|
|
16
|
-
import DateField from "./fields/DateField";
|
|
17
|
-
import { MarkdownField } from "./fields/MarkdownField";
|
|
18
|
-
import TextField from "./fields/TextField";
|
|
19
11
|
import { globals, collections } from "../lib/config";
|
|
20
|
-
import { slugifyText } from "../lib/slugify";
|
|
21
12
|
import { resolveUrl, apiGet, apiDelete, fetchWithAuth } from "../lib/api";
|
|
22
13
|
import { Shimmer } from "./ui/Shimmer";
|
|
23
14
|
import { normalizeUploadFields } from "../lib/normalize-upload-fields";
|
|
24
15
|
import { useAutoFormStore } from "../lib/autoform-store";
|
|
25
16
|
import { useAutoFormState } from "../hooks/useAutoFormState";
|
|
26
17
|
import { useUIStore, toast } from "../lib/stores";
|
|
27
|
-
import { EmptyState } from "./ui/EmptyState";
|
|
28
18
|
|
|
29
|
-
import { adminPath as ADMIN_BASE
|
|
19
|
+
import { adminPath as ADMIN_BASE } from "../lib/paths";
|
|
30
20
|
|
|
31
|
-
import { BlocksField } from "./fields/BlocksField";
|
|
32
|
-
import { ConfirmModal, Modal as UIModal } from "./ui/Modal";
|
|
33
|
-
import { ListField } from "./fields/ListField";
|
|
34
21
|
import { RelationshipBlockField } from "./fields/RelationshipBlockField";
|
|
35
22
|
import { FieldRenderer } from "./FieldRenderer";
|
|
36
|
-
import { Dropdown, DropdownItem, DropdownSeparator } from "./ui/Dropdown";
|
|
37
|
-
import { SplitButton } from "./ui/SplitButton";
|
|
38
|
-
import type { SplitButtonStatus } from "./ui/SplitButton";
|
|
39
23
|
import { TabsLayout } from "./fields/TabsLayout";
|
|
40
24
|
import { GroupLayout } from "./fields/GroupLayout";
|
|
41
25
|
import { ArrayLayout } from "./fields/ArrayLayout";
|
|
@@ -62,28 +46,33 @@ interface AutoFormProps {
|
|
|
62
46
|
documentStatus?: string;
|
|
63
47
|
}
|
|
64
48
|
|
|
65
|
-
function getNestedValue(obj:
|
|
49
|
+
function getNestedValue(obj: unknown, path: string): unknown {
|
|
66
50
|
if (!obj || typeof path !== "string") return undefined;
|
|
67
|
-
return path.split('.').reduce((acc, part) =>
|
|
51
|
+
return path.split('.').reduce<unknown>((acc, part) => {
|
|
52
|
+
if (acc && typeof acc === "object" && part in acc) {
|
|
53
|
+
return (acc as Record<string, unknown>)[part];
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
}, obj);
|
|
68
57
|
}
|
|
69
58
|
|
|
70
|
-
function evaluateDeclarativeCondition(cond:
|
|
59
|
+
function evaluateDeclarativeCondition(cond: DeclarativeCondition | undefined, currentData: Record<string, unknown>, formData: Record<string, unknown>): boolean {
|
|
71
60
|
if (!cond) return true;
|
|
72
|
-
|
|
73
|
-
if (Array.isArray(cond.and)) {
|
|
74
|
-
return cond.and.every((c:
|
|
61
|
+
|
|
62
|
+
if ("and" in cond && Array.isArray(cond.and)) {
|
|
63
|
+
return cond.and.every((c: DeclarativeCondition) => evaluateDeclarativeCondition(c, currentData, formData));
|
|
75
64
|
}
|
|
76
|
-
if (Array.isArray(cond.or)) {
|
|
77
|
-
return cond.or.some((c:
|
|
65
|
+
if ("or" in cond && Array.isArray(cond.or)) {
|
|
66
|
+
return cond.or.some((c: DeclarativeCondition) => evaluateDeclarativeCondition(c, currentData, formData));
|
|
78
67
|
}
|
|
79
|
-
|
|
80
|
-
if (cond.field) {
|
|
68
|
+
|
|
69
|
+
if ("field" in cond && cond.field) {
|
|
81
70
|
const targetField = cond.field;
|
|
82
71
|
let val = getNestedValue(currentData, targetField);
|
|
83
72
|
if (val === undefined) {
|
|
84
73
|
val = getNestedValue(formData, targetField);
|
|
85
74
|
}
|
|
86
|
-
|
|
75
|
+
|
|
87
76
|
if ("equals" in cond) {
|
|
88
77
|
return val === cond.equals;
|
|
89
78
|
}
|
|
@@ -91,16 +80,16 @@ function evaluateDeclarativeCondition(cond: any, currentData: any, formData: any
|
|
|
91
80
|
return val !== cond.notEquals;
|
|
92
81
|
}
|
|
93
82
|
if ("in" in cond && Array.isArray(cond.in)) {
|
|
94
|
-
return cond.in.includes(val);
|
|
83
|
+
return cond.in.includes(val as string | number | boolean);
|
|
95
84
|
}
|
|
96
|
-
if ("greaterThan" in cond) {
|
|
85
|
+
if ("greaterThan" in cond && cond.greaterThan !== undefined) {
|
|
97
86
|
return typeof val === "number" && val > cond.greaterThan;
|
|
98
87
|
}
|
|
99
|
-
|
|
88
|
+
|
|
100
89
|
// If field exists but no operator is provided, just check truthiness
|
|
101
90
|
return Boolean(val);
|
|
102
91
|
}
|
|
103
|
-
|
|
92
|
+
|
|
104
93
|
return true;
|
|
105
94
|
}
|
|
106
95
|
|
|
@@ -127,14 +116,15 @@ export function AutoForm({
|
|
|
127
116
|
: collectionSlug
|
|
128
117
|
? collections[collectionSlug]
|
|
129
118
|
: null);
|
|
130
|
-
|
|
131
|
-
const [liveConfig, setLiveConfig] = useState<
|
|
132
|
-
|
|
119
|
+
|
|
120
|
+
const [liveConfig, setLiveConfig] = useState<CollectionConfig | GlobalConfig | null>(activeConfig);
|
|
121
|
+
|
|
133
122
|
useEffect(() => {
|
|
134
|
-
if (globalSlug
|
|
135
|
-
apiGet("/api/kyro/schema").then((schema:
|
|
136
|
-
|
|
137
|
-
|
|
123
|
+
if (globalSlug && !activeConfig) {
|
|
124
|
+
apiGet("/api/kyro/schema").then((schema: unknown) => {
|
|
125
|
+
const schemaTyped = schema as { globals?: Record<string, GlobalConfig>; collections?: Record<string, CollectionConfig> };
|
|
126
|
+
if (schemaTyped?.globals?.[globalSlug]) {
|
|
127
|
+
setLiveConfig(schemaTyped.globals[globalSlug]);
|
|
138
128
|
}
|
|
139
129
|
}).catch(err => console.error("[AutoForm] Failed to fetch dynamic schema", err));
|
|
140
130
|
} else {
|
|
@@ -646,7 +636,7 @@ export function AutoForm({
|
|
|
646
636
|
useAutoFormStore.getState().loadDocument(savedData, savedData);
|
|
647
637
|
setLocalSaveStatus("saved");
|
|
648
638
|
onActionSuccess?.("Published successfully");
|
|
649
|
-
|
|
639
|
+
|
|
650
640
|
setTimeout(() => {
|
|
651
641
|
setLocalSaveStatus("idle");
|
|
652
642
|
}, 2000);
|
|
@@ -729,7 +719,7 @@ export function AutoForm({
|
|
|
729
719
|
const renderField = (
|
|
730
720
|
field: Field,
|
|
731
721
|
parentData?: Record<string, unknown>,
|
|
732
|
-
onParentChange?: (val: unknown) => void,
|
|
722
|
+
onParentChange?: (val: Record<string, unknown>) => void,
|
|
733
723
|
): React.ReactNode => {
|
|
734
724
|
const currentData = parentData !== undefined ? parentData : formData;
|
|
735
725
|
const isHidden = resolveAdminFlag((field.hidden !== undefined ? field.hidden : field.admin?.hidden) as any, currentData);
|
|
@@ -756,7 +746,7 @@ export function AutoForm({
|
|
|
756
746
|
}
|
|
757
747
|
} else if (typeof field.admin.condition === "object") {
|
|
758
748
|
try {
|
|
759
|
-
const shouldShow = evaluateDeclarativeCondition(field.admin.condition, currentData, formData);
|
|
749
|
+
const shouldShow = evaluateDeclarativeCondition(field.admin.condition as DeclarativeCondition, currentData, formData);
|
|
760
750
|
if (!shouldShow) {
|
|
761
751
|
return null;
|
|
762
752
|
}
|
|
@@ -1018,7 +1008,7 @@ export function AutoForm({
|
|
|
1018
1008
|
}
|
|
1019
1009
|
};
|
|
1020
1010
|
|
|
1021
|
-
if (clientLoading) {
|
|
1011
|
+
if (clientLoading || !config) {
|
|
1022
1012
|
return (
|
|
1023
1013
|
<div className="space-y-6 p-4">
|
|
1024
1014
|
<div className="space-y-2">
|