@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.
Files changed (54) hide show
  1. package/dist/index.cjs +32 -110
  2. package/dist/index.css +1 -1
  3. package/dist/index.d.cts +1 -3
  4. package/dist/index.d.ts +1 -3
  5. package/dist/index.js +32 -110
  6. package/package.json +7 -2
  7. package/src/components/AutoForm.tsx +35 -45
  8. package/src/components/DashboardMetrics.tsx +519 -290
  9. package/src/components/FieldRenderer.tsx +59 -47
  10. package/src/components/ListView.tsx +10 -5
  11. package/src/components/PluginsManager.tsx +37 -20
  12. package/src/components/Sidebar.astro +7 -14
  13. package/src/components/blocks/AccordionBlock.tsx +8 -4
  14. package/src/components/blocks/ArrayBlock.tsx +4 -3
  15. package/src/components/blocks/BlockEditModal.tsx +4 -3
  16. package/src/components/blocks/CardBlock.tsx +10 -2
  17. package/src/components/blocks/ChildBlocksTree.tsx +19 -18
  18. package/src/components/blocks/CodeBlock.tsx +7 -2
  19. package/src/components/blocks/FileBlock.tsx +6 -2
  20. package/src/components/blocks/GenericBlock.tsx +1 -1
  21. package/src/components/blocks/HeadingBlock.tsx +6 -2
  22. package/src/components/blocks/HeadingSubheadingBlock.tsx +7 -2
  23. package/src/components/blocks/HeroBlock.tsx +12 -3
  24. package/src/components/blocks/ImageBlock.tsx +8 -2
  25. package/src/components/blocks/ListBlock.tsx +6 -2
  26. package/src/components/blocks/ParagraphBlock.tsx +2 -2
  27. package/src/components/blocks/RelationshipBlock.tsx +10 -2
  28. package/src/components/blocks/VideoBlock.tsx +7 -2
  29. package/src/components/fields/AccordionField.tsx +1 -1
  30. package/src/components/fields/ArrayLayout.tsx +55 -58
  31. package/src/components/fields/BlocksField.tsx +1 -1
  32. package/src/components/fields/ChildrenField.tsx +3 -2
  33. package/src/components/fields/CodeField.tsx +3 -2
  34. package/src/components/fields/ColumnsField.tsx +3 -2
  35. package/src/components/fields/DateField.tsx +2 -2
  36. package/src/components/fields/FieldLayout.tsx +3 -3
  37. package/src/components/fields/GroupLayout.tsx +2 -2
  38. package/src/components/fields/MarkdownField.tsx +2 -2
  39. package/src/components/fields/NumberField.tsx +4 -4
  40. package/src/components/fields/RelationshipField.tsx +4 -1
  41. package/src/components/fields/RichTextField.tsx +200 -5
  42. package/src/components/fields/extensions/blockComponents.tsx +1 -1
  43. package/src/components/fields/extensions/blocksStore.ts +15 -12
  44. package/src/components/ui/Pagination.tsx +1 -1
  45. package/src/fields/index.ts +1 -1
  46. package/src/hooks/useAutoFormState.ts +7 -6
  47. package/src/index.ts +0 -1
  48. package/src/integration.ts +64 -26
  49. package/src/lib/api.ts +1 -0
  50. package/src/lib/config.ts +6 -19
  51. package/src/lib/core-types.ts +78 -0
  52. package/src/plugins/seo-admin.tsx +155 -0
  53. package/src/styles/main.css +2 -0
  54. 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.5",
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.11.1",
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 { ChevronRight, Check, ExternalLink, X, AlertTriangle } from "./ui/icons";
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, apiPath as API_BASE } from "../lib/paths";
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: any, path: string): any {
49
+ function getNestedValue(obj: unknown, path: string): unknown {
66
50
  if (!obj || typeof path !== "string") return undefined;
67
- return path.split('.').reduce((acc, part) => (acc && acc[part] !== undefined ? acc[part] : undefined), obj);
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: any, currentData: any, formData: any): boolean {
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: any) => evaluateDeclarativeCondition(c, currentData, formData));
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: any) => evaluateDeclarativeCondition(c, currentData, formData));
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<any>(activeConfig);
132
-
119
+
120
+ const [liveConfig, setLiveConfig] = useState<CollectionConfig | GlobalConfig | null>(activeConfig);
121
+
133
122
  useEffect(() => {
134
- if (globalSlug === "storage-settings") {
135
- apiGet("/api/kyro/schema").then((schema: any) => {
136
- if (schema?.globals?.["storage-settings"]) {
137
- setLiveConfig(schema.globals["storage-settings"]);
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">