@cardinali-studio/adminkit 2.2.0 → 2.2.2
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/api.d.ts +2 -0
- package/dist/app/lib/home.js +24 -2
- package/dist/app/lib/settings.js +26 -3
- package/dist/app/site.d.ts +13 -0
- package/dist/persistence.d.ts +2 -0
- package/dist/persistence.js +2 -0
- package/package.json +1 -1
package/dist/api.d.ts
CHANGED
|
@@ -69,6 +69,8 @@ export type ContentDocument = {
|
|
|
69
69
|
kind: string;
|
|
70
70
|
locale: string;
|
|
71
71
|
draftRevisionId: string | null;
|
|
72
|
+
draftPayload: string | null;
|
|
73
|
+
draftTombstone: boolean | null;
|
|
72
74
|
publishedRevisionId: string | null;
|
|
73
75
|
publishedPayload: string | null;
|
|
74
76
|
publishedTombstone: boolean | null;
|
package/dist/app/lib/home.js
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { useCallback, useEffect, useState } from "react";
|
|
12
12
|
import { ApiError } from "../../index.js";
|
|
13
|
+
import { api } from "../../api.js";
|
|
14
|
+
import { contentPersistenceMode, site } from "../site.js";
|
|
13
15
|
async function call(url, init) {
|
|
14
16
|
const res = await fetch(url, { cache: "no-store", ...init });
|
|
15
17
|
const body = await res.json().catch(() => null);
|
|
@@ -24,9 +26,20 @@ export function useHome(active) {
|
|
|
24
26
|
const [dirty, setDirty] = useState(false);
|
|
25
27
|
const [saving, setSaving] = useState(false);
|
|
26
28
|
const [notice, setNotice] = useState(null);
|
|
29
|
+
const [revision, setRevision] = useState(null);
|
|
27
30
|
const load = useCallback(async () => {
|
|
28
31
|
try {
|
|
29
|
-
|
|
32
|
+
const mapped = site().homeDocument;
|
|
33
|
+
if (contentPersistenceMode() === "d1" && mapped) {
|
|
34
|
+
const { document } = await api.contentDocument(mapped.documentId);
|
|
35
|
+
const payload = document.draftPayload ?? document.publishedPayload;
|
|
36
|
+
if (!payload || (document.draftPayload !== null ? document.draftTombstone : document.publishedTombstone))
|
|
37
|
+
throw new Error("content_unavailable");
|
|
38
|
+
setData(JSON.parse(payload));
|
|
39
|
+
setRevision({ base: document.draftRevisionId ?? document.publishedRevisionId, expected: document.draftRevisionId });
|
|
40
|
+
}
|
|
41
|
+
else
|
|
42
|
+
setData((await call("/admin/api/singleton?name=home")).data);
|
|
30
43
|
setError(null);
|
|
31
44
|
setDirty(false);
|
|
32
45
|
}
|
|
@@ -44,6 +57,15 @@ export function useHome(active) {
|
|
|
44
57
|
setSaving(true);
|
|
45
58
|
setNotice(null);
|
|
46
59
|
try {
|
|
60
|
+
const mapped = site().homeDocument;
|
|
61
|
+
if (contentPersistenceMode() === "d1" && mapped && revision) {
|
|
62
|
+
const revisionId = crypto.randomUUID();
|
|
63
|
+
await api.saveContent({ revisionId, documentId: mapped.documentId, baseRevisionId: revision.base, expectedDraftRevisionId: revision.expected, kind: mapped.kind, locale: mapped.locale, schemaVersion: mapped.schemaVersion, payload: data, tombstone: false });
|
|
64
|
+
setRevision({ base: revisionId, expected: revisionId });
|
|
65
|
+
setNotice("Bozza D1 salvata");
|
|
66
|
+
setDirty(false);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
47
69
|
await call("/admin/api/singleton", {
|
|
48
70
|
method: "POST",
|
|
49
71
|
headers: { "Content-Type": "application/json" },
|
|
@@ -61,7 +83,7 @@ export function useHome(active) {
|
|
|
61
83
|
finally {
|
|
62
84
|
setSaving(false);
|
|
63
85
|
}
|
|
64
|
-
}, [data]);
|
|
86
|
+
}, [data, revision]);
|
|
65
87
|
const change = useCallback((next) => { setData(next); setDirty(true); }, []);
|
|
66
88
|
return { data, error, dirty, saving, notice, load, change, save, setNotice };
|
|
67
89
|
}
|
package/dist/app/lib/settings.js
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { useCallback, useEffect, useState } from "react";
|
|
8
8
|
import { ApiError } from "../../index.js";
|
|
9
|
+
import { api } from "../../api.js";
|
|
10
|
+
import { contentPersistenceMode, site } from "../site.js";
|
|
9
11
|
const empty = {
|
|
10
12
|
siteTitle: "",
|
|
11
13
|
shortDescription: "",
|
|
@@ -34,10 +36,22 @@ export function useSiteSettings(active) {
|
|
|
34
36
|
const [dirty, setDirty] = useState(false);
|
|
35
37
|
const [saving, setSaving] = useState(false);
|
|
36
38
|
const [notice, setNotice] = useState(null);
|
|
39
|
+
const [revision, setRevision] = useState(null);
|
|
37
40
|
const load = useCallback(async () => {
|
|
38
41
|
try {
|
|
39
|
-
const
|
|
40
|
-
|
|
42
|
+
const mapped = site().siteSettingsDocument;
|
|
43
|
+
if (contentPersistenceMode() === "d1" && mapped) {
|
|
44
|
+
const { document } = await api.contentDocument(mapped.documentId);
|
|
45
|
+
const payload = document.draftPayload ?? document.publishedPayload;
|
|
46
|
+
if (!payload || (document.draftPayload !== null ? document.draftTombstone : document.publishedTombstone))
|
|
47
|
+
throw new Error("content_unavailable");
|
|
48
|
+
setData({ ...empty, ...JSON.parse(payload), password: "" });
|
|
49
|
+
setRevision({ base: document.draftRevisionId ?? document.publishedRevisionId, expected: document.draftRevisionId });
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
const result = await call("/admin/api/settings");
|
|
53
|
+
setData({ ...empty, ...result.settings, password: "" });
|
|
54
|
+
}
|
|
41
55
|
setError(null);
|
|
42
56
|
setDirty(false);
|
|
43
57
|
}
|
|
@@ -55,6 +69,15 @@ export function useSiteSettings(active) {
|
|
|
55
69
|
setSaving(true);
|
|
56
70
|
setNotice(null);
|
|
57
71
|
try {
|
|
72
|
+
const mapped = site().siteSettingsDocument;
|
|
73
|
+
if (contentPersistenceMode() === "d1" && mapped && revision) {
|
|
74
|
+
const revisionId = crypto.randomUUID();
|
|
75
|
+
await api.saveContent({ revisionId, documentId: mapped.documentId, baseRevisionId: revision.base, expectedDraftRevisionId: revision.expected, kind: mapped.kind, locale: mapped.locale, schemaVersion: mapped.schemaVersion, payload: data, tombstone: false });
|
|
76
|
+
setRevision({ base: revisionId, expected: revisionId });
|
|
77
|
+
setDirty(false);
|
|
78
|
+
setNotice("Bozza D1 salvata");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
58
81
|
const result = await call("/admin/api/settings", {
|
|
59
82
|
method: "POST",
|
|
60
83
|
headers: { "Content-Type": "application/json" },
|
|
@@ -73,7 +96,7 @@ export function useSiteSettings(active) {
|
|
|
73
96
|
finally {
|
|
74
97
|
setSaving(false);
|
|
75
98
|
}
|
|
76
|
-
}, [data]);
|
|
99
|
+
}, [data, revision]);
|
|
77
100
|
const change = useCallback((next) => {
|
|
78
101
|
setData(next);
|
|
79
102
|
setDirty(true);
|
package/dist/app/site.d.ts
CHANGED
|
@@ -49,6 +49,19 @@ export interface SiteAdminConfig {
|
|
|
49
49
|
modules?: Record<string, boolean>;
|
|
50
50
|
/** Git remains the compatibility default. D1 is an explicit, later site cutover. */
|
|
51
51
|
contentPersistence?: "github" | "d1";
|
|
52
|
+
/** Lossless D1 identity for the singleton home; required before its editor may leave Git. */
|
|
53
|
+
homeDocument?: {
|
|
54
|
+
documentId: string;
|
|
55
|
+
kind: string;
|
|
56
|
+
locale: string;
|
|
57
|
+
schemaVersion: number;
|
|
58
|
+
};
|
|
59
|
+
siteSettingsDocument?: {
|
|
60
|
+
documentId: string;
|
|
61
|
+
kind: string;
|
|
62
|
+
locale: string;
|
|
63
|
+
schemaVersion: number;
|
|
64
|
+
};
|
|
52
65
|
}
|
|
53
66
|
/** Chiamata da `mountAdmin` prima del primo render. */
|
|
54
67
|
export declare function setSiteConfig(config: SiteAdminConfig): void;
|
package/dist/persistence.d.ts
CHANGED
|
@@ -86,6 +86,8 @@ export type EffectiveDocument = {
|
|
|
86
86
|
locale: string;
|
|
87
87
|
draftRevisionId: string | null;
|
|
88
88
|
publishedRevisionId: string | null;
|
|
89
|
+
draftPayload: string | null;
|
|
90
|
+
draftTombstone: boolean | null;
|
|
89
91
|
publishedPayload: string | null;
|
|
90
92
|
publishedTombstone: boolean | null;
|
|
91
93
|
};
|
package/dist/persistence.js
CHANGED
|
@@ -113,8 +113,10 @@ export class D1ContentRepository {
|
|
|
113
113
|
}
|
|
114
114
|
async getEffectiveDocument(documentId) {
|
|
115
115
|
return this.db.prepare(`SELECT d.document_id AS documentId, d.kind, d.locale, d.draft_revision_id AS draftRevisionId,
|
|
116
|
+
draft.payload_json AS draftPayload, draft.is_tombstone AS draftTombstone,
|
|
116
117
|
published.revision_id AS publishedRevisionId, published.payload_json AS publishedPayload, published.is_tombstone AS publishedTombstone
|
|
117
118
|
FROM content_documents d
|
|
119
|
+
LEFT JOIN content_revisions draft ON draft.document_id = d.document_id AND draft.revision_id = d.draft_revision_id
|
|
118
120
|
LEFT JOIN content_site_state state ON state.singleton = 1
|
|
119
121
|
LEFT JOIN content_release_entries entry ON entry.release_id = state.current_release_id AND entry.document_id = d.document_id
|
|
120
122
|
LEFT JOIN content_revisions published ON published.document_id = entry.document_id AND published.revision_id = entry.revision_id
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cardinali-studio/adminkit",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Motore riusabile di un pannello di amministrazione per siti Hugo su Cloudflare Pages: codec del front matter guidato dallo schema, form React, libreria media su R2. NIENTE di specifico di un sito qui dentro.",
|
|
6
6
|
"license": "UNLICENSED",
|