@kyro-cms/admin 0.12.6 → 0.12.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/index.cjs +32 -110
- package/dist/index.css +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +32 -110
- package/package.json +11 -5
- package/src/components/AuthBridge.tsx +2 -2
- package/src/components/AutoForm.tsx +49 -57
- package/src/components/BrandingHub.tsx +1 -1
- package/src/components/DashboardMetrics.tsx +519 -290
- package/src/components/DetailView.tsx +5 -3
- package/src/components/FieldRenderer.tsx +59 -47
- package/src/components/ListView.tsx +25 -10
- package/src/components/MediaGallery.tsx +1 -1
- package/src/components/PluginsManager.tsx +39 -21
- package/src/components/Sidebar.astro +7 -14
- package/src/components/UserMenu.tsx +10 -9
- package/src/components/autoform/AutoFormApiView.tsx +1 -1
- package/src/components/autoform/AutoFormHeader.tsx +1 -1
- package/src/components/autoform/ErrorBoundary.tsx +1 -1
- 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/IconField.tsx +1 -1
- 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/Button.tsx +5 -2
- package/src/components/ui/Pagination.tsx +1 -1
- package/src/components/users/UserDetail.tsx +7 -7
- package/src/components/users/UserForm.tsx +3 -2
- package/src/components/users/UsersList.tsx +5 -4
- package/src/env.d.ts +4 -0
- package/src/fields/index.ts +1 -1
- package/src/hooks/useAutoFormState.ts +7 -6
- package/src/hooks/useQueue.ts +3 -2
- package/src/index.ts +0 -1
- package/src/integration.ts +50 -12
- package/src/kyro-cms.d.ts +5 -0
- package/src/lib/api.ts +1 -0
- package/src/lib/autoform-store.ts +4 -3
- package/src/lib/config.ts +6 -19
- package/src/lib/core-types.ts +78 -0
- package/src/lib/normalize-upload-fields.ts +17 -4
- package/src/pages/[collection]/index.astro +1 -1
- package/src/pages/users/index.astro +1 -1
- package/src/plugins/seo-admin.tsx +155 -0
- package/src/styles/main.css +2 -0
- package/src/vite-env.d.ts +10 -1
- package/src/components/fix_imports.cjs +0 -23
- package/src/components/fix_imports2.cjs +0 -19
- package/src/components/replace_svgs.cjs +0 -63
|
@@ -27,7 +27,7 @@ interface ArrayLayoutProps {
|
|
|
27
27
|
renderField: (
|
|
28
28
|
field: Field,
|
|
29
29
|
parentData: Record<string, unknown>,
|
|
30
|
-
onChange: (value: unknown) => void,
|
|
30
|
+
onChange: (value: Record<string, unknown>) => void,
|
|
31
31
|
) => React.ReactNode;
|
|
32
32
|
disabled?: boolean;
|
|
33
33
|
}
|
|
@@ -46,12 +46,16 @@ interface SortableArrayItemProps {
|
|
|
46
46
|
setOpenIndex: (index: number | null) => void;
|
|
47
47
|
item: Record<string, unknown>;
|
|
48
48
|
field: Field;
|
|
49
|
-
renderField:
|
|
50
|
-
|
|
49
|
+
renderField: (
|
|
50
|
+
field: Field,
|
|
51
|
+
parentData: Record<string, unknown>,
|
|
52
|
+
onChange: (value: Record<string, unknown>) => void,
|
|
53
|
+
) => React.ReactNode;
|
|
54
|
+
onChangeItem: (newItem: Record<string, unknown>) => void;
|
|
51
55
|
onRemove: () => void;
|
|
52
56
|
disabled?: boolean;
|
|
53
57
|
compact: boolean;
|
|
54
|
-
getItemLabel: (item:
|
|
58
|
+
getItemLabel: (item: Record<string, unknown>) => string;
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
function SortableArrayItem({
|
|
@@ -68,31 +72,28 @@ function SortableArrayItem({
|
|
|
68
72
|
compact,
|
|
69
73
|
getItemLabel,
|
|
70
74
|
}: SortableArrayItemProps) {
|
|
71
|
-
|
|
75
|
+
const { t } = useTranslation();
|
|
72
76
|
const {
|
|
73
77
|
attributes,
|
|
74
78
|
listeners,
|
|
75
79
|
setNodeRef,
|
|
76
80
|
transform,
|
|
77
81
|
transition,
|
|
78
|
-
isDragging,
|
|
79
82
|
} = useSortable({ id });
|
|
80
83
|
|
|
81
84
|
const style = {
|
|
82
85
|
transform: CSS.Transform.toString(transform),
|
|
83
86
|
transition,
|
|
84
|
-
zIndex: isDragging ? 10 : 1,
|
|
85
|
-
opacity: isDragging ? 0.8 : 1,
|
|
86
87
|
};
|
|
87
88
|
|
|
89
|
+
const fields = (field as Field & { fields?: Field[] }).fields || [];
|
|
90
|
+
|
|
88
91
|
if (compact) {
|
|
89
92
|
return (
|
|
90
93
|
<div
|
|
91
94
|
ref={setNodeRef}
|
|
92
95
|
style={style}
|
|
93
|
-
className=
|
|
94
|
-
isDragging ? "bg-[var(--kyro-surface-accent)]/50" : ""
|
|
95
|
-
}`}
|
|
96
|
+
className="flex items-start gap-2 px-3 py-1.5 border-b border-[var(--kyro-border)] last:border-b-0"
|
|
96
97
|
>
|
|
97
98
|
<div
|
|
98
99
|
{...attributes}
|
|
@@ -104,8 +105,8 @@ function SortableArrayItem({
|
|
|
104
105
|
<span className="text-[10px] font-bold text-[var(--kyro-text-muted)] pt-2.5 min-w-[18px] text-center">
|
|
105
106
|
{index + 1}
|
|
106
107
|
</span>
|
|
107
|
-
<div className={`flex-1 min-w-0 ${
|
|
108
|
-
{
|
|
108
|
+
<div className={`flex-1 min-w-0 ${fields.length >= 3 ? "flex flex-col gap-1.5" : "flex items-start gap-1.5"}`}>
|
|
109
|
+
{fields.map((f: Field) => (
|
|
109
110
|
<div key={f.name} className="flex-1 min-w-0">
|
|
110
111
|
{renderField(f, item, onChangeItem)}
|
|
111
112
|
</div>
|
|
@@ -130,50 +131,44 @@ function SortableArrayItem({
|
|
|
130
131
|
<div
|
|
131
132
|
ref={setNodeRef}
|
|
132
133
|
style={style}
|
|
133
|
-
className={`border border-[var(--kyro-border)] rounded-
|
|
134
|
-
isDragging ? "border-[var(--kyro-primary)] shadow-md" : ""
|
|
135
|
-
}`}
|
|
134
|
+
className={`border border-[var(--kyro-border)] rounded-[var(--kyro-radius-md)] bg-[var(--kyro-surface-accent)]/10 overflow-hidden`}
|
|
136
135
|
>
|
|
137
|
-
<div className="
|
|
138
|
-
<div
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
setOpenIndex(isOpen ? null : index);
|
|
154
|
-
}
|
|
155
|
-
}}
|
|
156
|
-
className="text-xs font-bold tracking-widest text-[var(--kyro-text-muted)] truncate cursor-pointer flex-1 py-1 text-left"
|
|
157
|
-
>
|
|
158
|
-
{getItemLabel(item) || `Item ${index + 1}`}
|
|
136
|
+
<div className="flex items-center gap-2 px-4 py-3 bg-[var(--kyro-surface-accent)]/20 border-b border-[var(--kyro-border)]">
|
|
137
|
+
<div
|
|
138
|
+
{...attributes}
|
|
139
|
+
{...listeners}
|
|
140
|
+
className="p-1 cursor-grab active:cursor-grabbing text-[var(--kyro-text-muted)] hover:bg-[var(--kyro-surface-accent)] rounded flex-shrink-0"
|
|
141
|
+
>
|
|
142
|
+
<GripVertical className="w-3.5 h-3.5" />
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<span className="text-xs font-bold text-[var(--kyro-text-muted)] min-w-[18px]">
|
|
146
|
+
{index + 1}
|
|
147
|
+
</span>
|
|
148
|
+
|
|
149
|
+
<div className="flex-1 min-w-0">
|
|
150
|
+
<span className="text-xs font-medium text-[var(--kyro-text-primary)] truncate block">
|
|
151
|
+
{getItemLabel(item) || `${field.label || field.name} Item`}
|
|
159
152
|
</span>
|
|
160
153
|
</div>
|
|
161
|
-
|
|
154
|
+
|
|
155
|
+
<div className="flex items-center gap-1 flex-shrink-0">
|
|
162
156
|
<button
|
|
163
157
|
type="button"
|
|
164
158
|
disabled={disabled}
|
|
165
|
-
onClick={
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}}
|
|
169
|
-
className="text-[11px] font-bold text-[var(--kyro-error)] opacity-0 group-hover:opacity-100 transition-opacity disabled:opacity-30 hover:bg-[var(--kyro-danger-bg)] rounded px-1.5 py-0.5"
|
|
159
|
+
onClick={onRemove}
|
|
160
|
+
className="text-[var(--kyro-text-muted)] hover:text-[var(--kyro-error)] transition-colors disabled:opacity-30 p-1 rounded hover:bg-[var(--kyro-surface-accent)]"
|
|
161
|
+
title={t("tooltips.remove", { defaultValue: "Remove" })}
|
|
170
162
|
>
|
|
171
|
-
|
|
163
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
164
|
+
<line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" />
|
|
165
|
+
</svg>
|
|
172
166
|
</button>
|
|
167
|
+
|
|
173
168
|
<button
|
|
174
169
|
type="button"
|
|
175
170
|
onClick={() => setOpenIndex(isOpen ? null : index)}
|
|
176
|
-
className="p-1 hover:bg-[var(--kyro-surface-accent)]
|
|
171
|
+
className="p-1 rounded hover:bg-[var(--kyro-surface-accent)] transition-colors"
|
|
177
172
|
>
|
|
178
173
|
{isOpen ? (
|
|
179
174
|
<ChevronUp className="w-4 h-4 text-[var(--kyro-text-muted)]" />
|
|
@@ -185,7 +180,7 @@ function SortableArrayItem({
|
|
|
185
180
|
</div>
|
|
186
181
|
{isOpen && (
|
|
187
182
|
<div className="p-4 bg-[var(--kyro-surface)] space-y-4">
|
|
188
|
-
{
|
|
183
|
+
{fields.map((f: Field) =>
|
|
189
184
|
renderField(f, item, onChangeItem),
|
|
190
185
|
)}
|
|
191
186
|
</div>
|
|
@@ -201,7 +196,7 @@ export function ArrayLayout({
|
|
|
201
196
|
renderField,
|
|
202
197
|
disabled,
|
|
203
198
|
}: ArrayLayoutProps) {
|
|
204
|
-
const items = Array.isArray(value) ? value : [];
|
|
199
|
+
const items = (Array.isArray(value) ? value : []) as Record<string, unknown>[];
|
|
205
200
|
const fields = (field as Field & { fields?: { name?: string; type?: string; relationTo?: string; label?: string }[] }).fields || [];
|
|
206
201
|
const firstField = fields[0];
|
|
207
202
|
const labelField = firstField?.name || "user";
|
|
@@ -212,14 +207,14 @@ export function ArrayLayout({
|
|
|
212
207
|
React.useEffect(() => {
|
|
213
208
|
let needsUpdate = false;
|
|
214
209
|
const updated = items
|
|
215
|
-
.filter((item
|
|
210
|
+
.filter((item) => {
|
|
216
211
|
if (typeof item !== "object" || item === null) {
|
|
217
212
|
needsUpdate = true;
|
|
218
213
|
return false;
|
|
219
214
|
}
|
|
220
215
|
return true;
|
|
221
216
|
})
|
|
222
|
-
.map((item
|
|
217
|
+
.map((item) => {
|
|
223
218
|
if (!item.id && !item._key) {
|
|
224
219
|
needsUpdate = true;
|
|
225
220
|
return {
|
|
@@ -239,13 +234,14 @@ export function ArrayLayout({
|
|
|
239
234
|
const val = item[key];
|
|
240
235
|
if (val && typeof val === "string") return val;
|
|
241
236
|
}
|
|
242
|
-
|
|
237
|
+
const fieldsTyped = fields as Field[];
|
|
238
|
+
for (const f of fieldsTyped) {
|
|
243
239
|
if (f.type === "text" || f.type === "textarea") {
|
|
244
240
|
const val = item[f.name!];
|
|
245
241
|
if (val && typeof val === "string") return val;
|
|
246
242
|
}
|
|
247
|
-
if (f.type === "group" && (f as
|
|
248
|
-
for (const inner of (f as
|
|
243
|
+
if (f.type === "group" && (f as Field & { fields?: Field[] }).fields) {
|
|
244
|
+
for (const inner of (f as Field & { fields?: Field[] }).fields || []) {
|
|
249
245
|
if (inner.type === "text" || inner.type === "textarea") {
|
|
250
246
|
const group = item[f.name!] as Record<string, unknown> | undefined;
|
|
251
247
|
const val = group?.[inner.name!];
|
|
@@ -272,8 +268,8 @@ export function ArrayLayout({
|
|
|
272
268
|
const { active, over } = event;
|
|
273
269
|
if (!over || active.id === over.id) return;
|
|
274
270
|
|
|
275
|
-
const oldIndex = items.findIndex((item
|
|
276
|
-
const newIndex = items.findIndex((item
|
|
271
|
+
const oldIndex = items.findIndex((item) => (item.id || item._key) === active.id);
|
|
272
|
+
const newIndex = items.findIndex((item) => (item.id || item._key) === over.id);
|
|
277
273
|
|
|
278
274
|
if (oldIndex !== -1 && newIndex !== -1) {
|
|
279
275
|
const newItems = [...items];
|
|
@@ -284,7 +280,7 @@ export function ArrayLayout({
|
|
|
284
280
|
};
|
|
285
281
|
|
|
286
282
|
const itemIds = React.useMemo(() => {
|
|
287
|
-
return items.map((item
|
|
283
|
+
return items.map((item) => (item?.id || item?._key || "") as string);
|
|
288
284
|
}, [items]);
|
|
289
285
|
|
|
290
286
|
if (isRelationship) {
|
|
@@ -296,11 +292,12 @@ export function ArrayLayout({
|
|
|
296
292
|
name: labelField,
|
|
297
293
|
relationTo: (field as Field & { fields?: { relationTo?: string }[] }).fields?.[0]?.relationTo || "",
|
|
298
294
|
hasMany: true,
|
|
299
|
-
|
|
295
|
+
// Omit the inner field's label to avoid duplicate labels in the UI
|
|
296
|
+
label: undefined,
|
|
300
297
|
}}
|
|
301
298
|
value={(items as Record<string, unknown>[]).map((i) => i[labelField]).filter(Boolean)}
|
|
302
299
|
onChange={(newValue) => {
|
|
303
|
-
const newItems = (newValue || []).map((id: string) => ({
|
|
300
|
+
const newItems = ((newValue as string[]) || []).map((id: string) => ({
|
|
304
301
|
[labelField]: id,
|
|
305
302
|
id: Math.random().toString(36).substr(2, 9),
|
|
306
303
|
}));
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { ChildBlocksTree } from "../blocks/ChildBlocksTree";
|
|
3
|
+
import type { BlockData } from "@kyro-cms/core/client";
|
|
3
4
|
|
|
4
5
|
interface ChildrenFieldProps {
|
|
5
6
|
blockId: string;
|
|
6
|
-
children:
|
|
7
|
-
onUpdateChildren: (newChildren:
|
|
7
|
+
children: BlockData[];
|
|
8
|
+
onUpdateChildren: (newChildren: BlockData[]) => void;
|
|
8
9
|
label?: string;
|
|
9
10
|
compact?: boolean;
|
|
10
11
|
}
|
|
@@ -3,6 +3,7 @@ import { githubLight } from "@uiw/codemirror-theme-github";
|
|
|
3
3
|
import { aura } from "@uiw/codemirror-theme-aura";
|
|
4
4
|
import type { CodeField as CodeFieldType } from "@kyro-cms/core/client";
|
|
5
5
|
import { useTheme } from "../ThemeProvider";
|
|
6
|
+
import type { Extension } from "@codemirror/state";
|
|
6
7
|
|
|
7
8
|
interface CodeFieldProps {
|
|
8
9
|
field: CodeFieldType;
|
|
@@ -74,7 +75,7 @@ export const CodeField: React.FC<CodeFieldProps> = ({
|
|
|
74
75
|
}) => {
|
|
75
76
|
const [isMounted, setIsMounted] = useState(false);
|
|
76
77
|
const [isDark, setIsDark] = useState(false);
|
|
77
|
-
const [extensions, setExtensions] = useState<
|
|
78
|
+
const [extensions, setExtensions] = useState<Extension[]>([]);
|
|
78
79
|
const [loading, setLoading] = useState(false);
|
|
79
80
|
const [copied, setCopied] = useState(false);
|
|
80
81
|
const [isFullScreen, setIsFullScreen] = useState(false);
|
|
@@ -104,7 +105,7 @@ export const CodeField: React.FC<CodeFieldProps> = ({
|
|
|
104
105
|
const loader =
|
|
105
106
|
languageExtensions[language] || languageExtensions.javascript;
|
|
106
107
|
const ext = await loader();
|
|
107
|
-
setExtensions([ext]);
|
|
108
|
+
setExtensions([ext as Extension]);
|
|
108
109
|
} catch (err) {
|
|
109
110
|
console.error("Failed to load language extension:", err);
|
|
110
111
|
setExtensions([]);
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { ChildBlocksTree } from "../blocks/ChildBlocksTree";
|
|
3
|
+
import type { BlockData } from "@kyro-cms/core/client";
|
|
3
4
|
|
|
4
5
|
interface ColumnData {
|
|
5
6
|
id: number;
|
|
6
|
-
children:
|
|
7
|
+
children: BlockData[];
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
interface ColumnsFieldProps {
|
|
10
11
|
columns?: number;
|
|
11
12
|
columnData?: ColumnData[];
|
|
12
13
|
onColumnsChange: (columns: number) => void;
|
|
13
|
-
onUpdateColumnChildren: (columnIndex: number, newChildren:
|
|
14
|
+
onUpdateColumnChildren: (columnIndex: number, newChildren: BlockData[]) => void;
|
|
14
15
|
compact?: boolean;
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -32,8 +32,8 @@ export default function DateField({
|
|
|
32
32
|
value={value == null ? "" : value}
|
|
33
33
|
onChange={(e) => onChange?.(e.target.value)}
|
|
34
34
|
disabled={disabled || isReadOnly}
|
|
35
|
-
min={field.minDate}
|
|
36
|
-
max={field.maxDate}
|
|
35
|
+
min={field.minDate as string | undefined}
|
|
36
|
+
max={field.maxDate as string | undefined}
|
|
37
37
|
required={field.required}
|
|
38
38
|
className={`kyro-form-input ${
|
|
39
39
|
disabled || isReadOnly ? "opacity-50 cursor-not-allowed" : ""
|
|
@@ -31,11 +31,11 @@ export default function FieldLayout({
|
|
|
31
31
|
{children}
|
|
32
32
|
</div>
|
|
33
33
|
|
|
34
|
-
{(field.admin?.description || error) && (
|
|
34
|
+
{((field.admin?.description as string | undefined) || error) && (
|
|
35
35
|
<div className="flex flex-col gap-1.5 px-1">
|
|
36
|
-
{field.admin?.description && !error && (
|
|
36
|
+
{((field.admin?.description as string | undefined) && !error) && (
|
|
37
37
|
<p className="text-[11px] leading-relaxed text-[var(--kyro-text-muted)] font-medium opacity-60 italic">
|
|
38
|
-
{field.admin
|
|
38
|
+
{field.admin?.description as string}
|
|
39
39
|
</p>
|
|
40
40
|
)}
|
|
41
41
|
{error && (
|
|
@@ -8,7 +8,7 @@ interface GroupLayoutProps {
|
|
|
8
8
|
renderField: (
|
|
9
9
|
field: Field,
|
|
10
10
|
parentData: Record<string, unknown>,
|
|
11
|
-
onChange: (value: unknown) => void,
|
|
11
|
+
onChange: (value: Record<string, unknown>) => void,
|
|
12
12
|
) => React.ReactNode;
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -29,7 +29,7 @@ export function GroupLayout({
|
|
|
29
29
|
|
|
30
30
|
</div>
|
|
31
31
|
<div className={field.admin?.inline ? "flex items-start gap-4" : "space-y-6"}>
|
|
32
|
-
{(field as Field & { fields?: Field[] }).fields
|
|
32
|
+
{(field as Field & { fields?: Field[] }).fields?.map((f: Field) =>
|
|
33
33
|
renderField(f, groupData, onChange),
|
|
34
34
|
)}
|
|
35
35
|
</div>
|
|
@@ -43,7 +43,7 @@ export default function IconField({
|
|
|
43
43
|
type="text"
|
|
44
44
|
value={normalizedValue}
|
|
45
45
|
onChange={(e) => onChange?.(e.target.value)}
|
|
46
|
-
placeholder={field.admin?.placeholder || "e.g., activity"}
|
|
46
|
+
placeholder={(field.admin?.placeholder as string) || "e.g., activity"}
|
|
47
47
|
disabled={disabled || isReadOnly}
|
|
48
48
|
required={field.required}
|
|
49
49
|
className={`kyro-form-input ${disabled || isReadOnly ? "opacity-70 bg-[var(--kyro-bg-secondary)] cursor-not-allowed" : ""}`}
|
|
@@ -237,8 +237,8 @@ export const MarkdownField: React.FC<MarkdownFieldProps> = ({
|
|
|
237
237
|
</div>
|
|
238
238
|
)}
|
|
239
239
|
|
|
240
|
-
{field.admin?.description && !error && (
|
|
241
|
-
<p className="kyro-form-help">{field.admin
|
|
240
|
+
{((field.admin?.description as string | undefined) && !error) && (
|
|
241
|
+
<p className="kyro-form-help">{field.admin?.description as string}</p>
|
|
242
242
|
)}
|
|
243
243
|
{error && <p className="kyro-form-error">{error}</p>}
|
|
244
244
|
</div>
|
|
@@ -31,11 +31,11 @@ export default function NumberField({
|
|
|
31
31
|
id={field.name}
|
|
32
32
|
value={value ?? ""}
|
|
33
33
|
onChange={(e) => onChange?.(parseFloat(e.target.value) || 0)}
|
|
34
|
-
placeholder={field.admin?.placeholder}
|
|
34
|
+
placeholder={field.admin?.placeholder as string | undefined}
|
|
35
35
|
disabled={disabled || isReadOnly}
|
|
36
|
-
min={field.min}
|
|
37
|
-
max={field.max}
|
|
38
|
-
step={field.step || (field.integer ? 1 : "any")}
|
|
36
|
+
min={field.min as number | undefined}
|
|
37
|
+
max={field.max as number | undefined}
|
|
38
|
+
step={(field.step as number | string | undefined) || (field.integer ? 1 : "any")}
|
|
39
39
|
required={field.required}
|
|
40
40
|
className={`kyro-form-input ${
|
|
41
41
|
disabled || isReadOnly ? "opacity-50 cursor-not-allowed" : ""
|
|
@@ -235,7 +235,10 @@ export function RelationshipField({
|
|
|
235
235
|
|
|
236
236
|
const getValueId = (val: unknown): string => {
|
|
237
237
|
if (typeof val === "object" && val !== null) {
|
|
238
|
-
|
|
238
|
+
const inner = (val as { value?: unknown }).value ?? (val as { id?: unknown }).id;
|
|
239
|
+
// Recursively unwrap in case of doubly-nested {relationTo, value: {relationTo, value: id}}
|
|
240
|
+
if (inner !== undefined && inner !== null) return getValueId(inner);
|
|
241
|
+
return "";
|
|
239
242
|
}
|
|
240
243
|
return String(val);
|
|
241
244
|
};
|