@kyro-cms/admin 0.10.3 → 0.10.5
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 +26 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/AutoForm.tsx +42 -1
- package/src/pages/[collection]/[id].astro +2 -34
- package/src/pages/settings/[slug].astro +1 -25
package/package.json
CHANGED
|
@@ -18,7 +18,8 @@ import { MarkdownField } from "./fields/MarkdownField";
|
|
|
18
18
|
import TextField from "./fields/TextField";
|
|
19
19
|
import { globals, collections } from "../lib/config";
|
|
20
20
|
import { slugifyText } from "../lib/slugify";
|
|
21
|
-
import { resolveUrl, apiDelete, fetchWithAuth } from "../lib/api";
|
|
21
|
+
import { resolveUrl, apiGet, apiDelete, fetchWithAuth } from "../lib/api";
|
|
22
|
+
import { Shimmer } from "./ui/Shimmer";
|
|
22
23
|
import { normalizeUploadFields } from "../lib/normalize-upload-fields";
|
|
23
24
|
import { useAutoFormStore } from "../lib/autoform-store";
|
|
24
25
|
import { useAutoFormState } from "../hooks/useAutoFormState";
|
|
@@ -47,6 +48,7 @@ interface AutoFormProps {
|
|
|
47
48
|
disabled?: boolean;
|
|
48
49
|
collectionSlug?: string;
|
|
49
50
|
globalSlug?: string;
|
|
51
|
+
documentId?: string;
|
|
50
52
|
documentName?: string;
|
|
51
53
|
layout?: "split" | "single";
|
|
52
54
|
onActionSuccess?: (message: string) => void;
|
|
@@ -63,6 +65,7 @@ export function AutoForm({
|
|
|
63
65
|
disabled: propDisabled,
|
|
64
66
|
collectionSlug,
|
|
65
67
|
globalSlug,
|
|
68
|
+
documentId,
|
|
66
69
|
documentName,
|
|
67
70
|
layout = "split",
|
|
68
71
|
onActionSuccess,
|
|
@@ -143,6 +146,30 @@ export function AutoForm({
|
|
|
143
146
|
const [localSaveStatus, setLocalSaveStatus] = useState<"idle" | "saving" | "saved" | "error">("idle");
|
|
144
147
|
const [now, setNow] = useState(Date.now());
|
|
145
148
|
const disabled = propDisabled;
|
|
149
|
+
const [clientLoading, setClientLoading] = useState(false);
|
|
150
|
+
|
|
151
|
+
// Client-side fetch when SSR didn't provide data
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
const shouldFetchCollection = collectionSlug && documentId && documentId !== "new";
|
|
154
|
+
const shouldFetchGlobal = globalSlug;
|
|
155
|
+
if (!shouldFetchCollection && !shouldFetchGlobal) return;
|
|
156
|
+
if (initialData && Object.keys(initialData).length > 0) return;
|
|
157
|
+
|
|
158
|
+
setClientLoading(true);
|
|
159
|
+
const url = globalSlug
|
|
160
|
+
? `/api/globals/${globalSlug}`
|
|
161
|
+
: `/api/${collectionSlug}/${documentId}`;
|
|
162
|
+
|
|
163
|
+
apiGet<{ data?: Record<string, unknown> }>(url, { autoToast: false })
|
|
164
|
+
.then((result) => {
|
|
165
|
+
const docData = result.data || {};
|
|
166
|
+
setFormData(docData);
|
|
167
|
+
setClientLoading(false);
|
|
168
|
+
})
|
|
169
|
+
.catch(() => {
|
|
170
|
+
setClientLoading(false);
|
|
171
|
+
});
|
|
172
|
+
}, [collectionSlug, documentId, globalSlug, initialData]);
|
|
146
173
|
|
|
147
174
|
// Tick every 10s so the "saved X ago" label stays fresh
|
|
148
175
|
useEffect(() => {
|
|
@@ -1807,6 +1834,20 @@ export function AutoForm({
|
|
|
1807
1834
|
</div>
|
|
1808
1835
|
);
|
|
1809
1836
|
|
|
1837
|
+
if (clientLoading) {
|
|
1838
|
+
return (
|
|
1839
|
+
<div className="space-y-6 p-4">
|
|
1840
|
+
<div className="space-y-2">
|
|
1841
|
+
<Shimmer variant="text" className="w-1/3" />
|
|
1842
|
+
<Shimmer variant="text" className="w-2/3" />
|
|
1843
|
+
</div>
|
|
1844
|
+
<div className="space-y-4">
|
|
1845
|
+
<Shimmer variant="rect" count={4} />
|
|
1846
|
+
</div>
|
|
1847
|
+
</div>
|
|
1848
|
+
);
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1810
1851
|
return (
|
|
1811
1852
|
<div className="flex flex-col h-full">
|
|
1812
1853
|
{layout !== "single" && renderHeader()}
|
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
import AdminLayout from "../../layouts/AdminLayout.astro";
|
|
3
3
|
import { collections } from "../../lib/config";
|
|
4
4
|
import { AutoForm } from "../../components/AutoForm";
|
|
5
|
+
import { adminPath } from "../../lib/paths";
|
|
5
6
|
|
|
6
7
|
const { collection, id } = Astro.params;
|
|
7
8
|
|
|
8
|
-
import { adminPath, apiPath } from "../../lib/paths";
|
|
9
|
-
|
|
10
9
|
// Validate collection exists
|
|
11
10
|
if (!collection || !collections[collection]) {
|
|
12
11
|
return Astro.redirect(adminPath);
|
|
@@ -14,39 +13,10 @@ if (!collection || !collections[collection]) {
|
|
|
14
13
|
|
|
15
14
|
const config = collections[collection];
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
let doc: any = null;
|
|
19
|
-
let fetchError: string | null = null;
|
|
20
|
-
if (id && id !== "new") {
|
|
21
|
-
try {
|
|
22
|
-
const response = await fetch(
|
|
23
|
-
`${Astro.url.origin}${apiPath}/${collection}/${id}`,
|
|
24
|
-
{
|
|
25
|
-
headers: Astro.request.headers,
|
|
26
|
-
credentials: "include",
|
|
27
|
-
},
|
|
28
|
-
);
|
|
29
|
-
if (response.ok) {
|
|
30
|
-
const result = await response.json();
|
|
31
|
-
doc = result.data || null;
|
|
32
|
-
} else {
|
|
33
|
-
const errorData = await response.json().catch(() => ({}));
|
|
34
|
-
fetchError = errorData.error || `Failed to load document (${response.status})`;
|
|
35
|
-
}
|
|
36
|
-
} catch (error) {
|
|
37
|
-
fetchError = "Failed to fetch document. Check server connection.";
|
|
38
|
-
console.error("Failed to fetch document:", error);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const isNew = !doc;
|
|
43
|
-
const docStatus = doc?.status || "draft";
|
|
16
|
+
const isNew = !id || id === "new";
|
|
44
17
|
const title = isNew
|
|
45
18
|
? `Create ${config.singularLabel || config.label || collection}`
|
|
46
19
|
: `Edit ${config.singularLabel || config.label || collection}`;
|
|
47
|
-
const description =
|
|
48
|
-
config.admin?.description ||
|
|
49
|
-
`Manage ${(config.label || collection || "").toLowerCase()} documents`;
|
|
50
20
|
---
|
|
51
21
|
|
|
52
22
|
<AdminLayout title={title}>
|
|
@@ -55,10 +25,8 @@ const description =
|
|
|
55
25
|
<AutoForm
|
|
56
26
|
client:load
|
|
57
27
|
config={config}
|
|
58
|
-
data={doc || {}}
|
|
59
28
|
collectionSlug={collection}
|
|
60
29
|
documentId={id || undefined}
|
|
61
|
-
documentName={(typeof doc?.title === "object" ? "" : doc?.title) || (typeof doc?.name === "object" ? "" : doc?.name) || doc?.slug || "new-document"}
|
|
62
30
|
/>
|
|
63
31
|
<input type="hidden" id="form-data" name="form-data" value="{}" />
|
|
64
32
|
</form>
|
|
@@ -3,7 +3,7 @@ import AdminLayout from "../../layouts/AdminLayout.astro";
|
|
|
3
3
|
import { globals } from "../../lib/config";
|
|
4
4
|
import { SettingsPage } from "../../components/SettingsPage";
|
|
5
5
|
|
|
6
|
-
import { adminPath
|
|
6
|
+
import { adminPath } from "../../lib/paths";
|
|
7
7
|
|
|
8
8
|
const { slug } = Astro.params;
|
|
9
9
|
|
|
@@ -15,29 +15,6 @@ if (!slug || !globals[slug]) {
|
|
|
15
15
|
const config = globals[slug];
|
|
16
16
|
const hasVersioning = !!config.versions;
|
|
17
17
|
|
|
18
|
-
// Fetch current settings data from API (authenticated)
|
|
19
|
-
let data: any = {};
|
|
20
|
-
try {
|
|
21
|
-
const fetchUrl = new URL(`${apiPath}/globals/${slug}`, Astro.url.origin);
|
|
22
|
-
if (hasVersioning) {
|
|
23
|
-
fetchUrl.searchParams.set("draft", "true");
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const response = await fetch(
|
|
27
|
-
fetchUrl.toString(),
|
|
28
|
-
{
|
|
29
|
-
headers: Astro.request.headers,
|
|
30
|
-
credentials: "include",
|
|
31
|
-
},
|
|
32
|
-
);
|
|
33
|
-
if (response.ok) {
|
|
34
|
-
const result = await response.json();
|
|
35
|
-
data = result.data || {};
|
|
36
|
-
}
|
|
37
|
-
} catch (error) {
|
|
38
|
-
console.error("Failed to fetch settings:", error);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
18
|
const activeSlug = slug;
|
|
42
19
|
const allGlobals = Object.values(globals);
|
|
43
20
|
|
|
@@ -116,7 +93,6 @@ const description =
|
|
|
116
93
|
client:load
|
|
117
94
|
config={config}
|
|
118
95
|
globalSlug={slug}
|
|
119
|
-
data={data}
|
|
120
96
|
layout={hasVersioning ? "split" : "single"}
|
|
121
97
|
/>
|
|
122
98
|
<input type="hidden" id="form-data" name="form-data" value="{}" />
|