litecms 0.1.0
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/LICENSE +21 -0
- package/README.md +1387 -0
- package/dist/admin/CmsAdminLayout.d.ts +27 -0
- package/dist/admin/CmsAdminLayout.d.ts.map +1 -0
- package/dist/admin/CmsAdminPage.d.ts +31 -0
- package/dist/admin/CmsAdminPage.d.ts.map +1 -0
- package/dist/admin/config.d.ts +83 -0
- package/dist/admin/config.d.ts.map +1 -0
- package/dist/admin/config.js +53 -0
- package/dist/admin/exports.d.ts +7 -0
- package/dist/admin/exports.d.ts.map +1 -0
- package/dist/admin/exports.js +452 -0
- package/dist/admin/index.d.ts +147 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/components/CmsAutoForm.d.ts +73 -0
- package/dist/components/CmsAutoForm.d.ts.map +1 -0
- package/dist/components/CmsField.d.ts +50 -0
- package/dist/components/CmsField.d.ts.map +1 -0
- package/dist/components/CmsForm.d.ts +74 -0
- package/dist/components/CmsForm.d.ts.map +1 -0
- package/dist/components/CmsImageField.d.ts +33 -0
- package/dist/components/CmsImageField.d.ts.map +1 -0
- package/dist/components/CmsNavSection.d.ts +7 -0
- package/dist/components/CmsNavSection.d.ts.map +1 -0
- package/dist/components/CmsSimpleForm.d.ts +54 -0
- package/dist/components/CmsSimpleForm.d.ts.map +1 -0
- package/dist/components/index.d.ts +43 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +619 -0
- package/dist/domain/index.d.ts +1 -0
- package/dist/domain/index.d.ts.map +1 -0
- package/dist/index-8zcd33mx.js +39 -0
- package/dist/index-pmb5m3ek.js +4135 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/schema/index.d.ts +80 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +46 -0
- package/dist/server/index.d.ts +79 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +117 -0
- package/dist/shared/utils.d.ts +23 -0
- package/dist/shared/utils.d.ts.map +1 -0
- package/dist/storage/index.d.ts +86 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +86 -0
- package/dist/stores/index.d.ts +1 -0
- package/dist/stores/index.d.ts.map +1 -0
- package/package.json +90 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { NavPageInfo } from './index';
|
|
2
|
+
/**
|
|
3
|
+
* Serializable layout props
|
|
4
|
+
*/
|
|
5
|
+
export type CmsAdminLayoutProps = {
|
|
6
|
+
/** Admin panel title */
|
|
7
|
+
adminTitle?: string;
|
|
8
|
+
/** Site name */
|
|
9
|
+
siteName?: string;
|
|
10
|
+
/** Base path for admin routes */
|
|
11
|
+
basePath?: string;
|
|
12
|
+
/** Link to public site */
|
|
13
|
+
publicSiteUrl?: string;
|
|
14
|
+
/** Navigation pages (serializable) */
|
|
15
|
+
pages: NavPageInfo[];
|
|
16
|
+
/** Current page slug. Auto-detected if not provided. */
|
|
17
|
+
currentSlug?: string;
|
|
18
|
+
/** Logout handler */
|
|
19
|
+
onLogout?: () => void;
|
|
20
|
+
/** Child content */
|
|
21
|
+
children?: any;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* General admin layout with navigation and content.
|
|
25
|
+
*/
|
|
26
|
+
export declare function CmsAdminLayout({ adminTitle, siteName, basePath, publicSiteUrl, pages, currentSlug: currentSlugProp, onLogout, children, }: CmsAdminLayoutProps): import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
//# sourceMappingURL=CmsAdminLayout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CmsAdminLayout.d.ts","sourceRoot":"","sources":["../../src/admin/CmsAdminLayout.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAa,WAAW,EAAE,MAAM,SAAS,CAAC;AAMtD;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAC9B,wBAAwB;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0BAA0B;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sCAAsC;IACtC,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,oBAAoB;IAEpB,QAAQ,CAAC,EAAE,GAAG,CAAC;CAClB,CAAC;AAEF;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAC3B,UAAsB,EACtB,QAAQ,EACR,QAAmB,EACnB,aAAa,EACb,KAAK,EACL,WAAW,EAAE,eAAe,EAC5B,QAAQ,EACR,QAAQ,GACX,EAAE,mBAAmB,2CAyGrB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ActionState } from '../server';
|
|
2
|
+
import type { FieldInfo } from '../schema';
|
|
3
|
+
import { type CmsAutoFormStyles, type ImageUploadConfig } from '../components/CmsSimpleForm';
|
|
4
|
+
/**
|
|
5
|
+
* Serializable page props for client component
|
|
6
|
+
*/
|
|
7
|
+
export type CmsAdminPageProps = {
|
|
8
|
+
/** Page title */
|
|
9
|
+
title: string;
|
|
10
|
+
/** Page description - helps guide the user */
|
|
11
|
+
description?: string;
|
|
12
|
+
/** Pre-extracted editable fields (serializable) */
|
|
13
|
+
fields: FieldInfo[];
|
|
14
|
+
/** Default/current values (serializable) */
|
|
15
|
+
values: Record<string, unknown>;
|
|
16
|
+
/** Server action to save data */
|
|
17
|
+
action: (prevState: ActionState<any>, formData: FormData) => Promise<ActionState<any>>;
|
|
18
|
+
/** Custom form styles */
|
|
19
|
+
styles?: CmsAutoFormStyles;
|
|
20
|
+
/** Custom success message */
|
|
21
|
+
successMessage?: string;
|
|
22
|
+
/** Custom submit button text */
|
|
23
|
+
submitText?: string;
|
|
24
|
+
/** Configuration for image upload endpoints */
|
|
25
|
+
storage?: ImageUploadConfig;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Admin page with auto-generated form.
|
|
29
|
+
*/
|
|
30
|
+
export declare function CmsAdminPage({ title, description, fields, values, action, styles, successMessage, submitText, storage, }: CmsAdminPageProps): import("react/jsx-runtime").JSX.Element;
|
|
31
|
+
//# sourceMappingURL=CmsAdminPage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CmsAdminPage.d.ts","sourceRoot":"","sources":["../../src/admin/CmsAdminPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C,OAAO,EAEH,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACzB,MAAM,6BAA6B,CAAC;AAErC;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC5B,iBAAiB;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,iCAAiC;IAEjC,MAAM,EAAE,CACJ,SAAS,EAAE,WAAW,CAAC,GAAG,CAAC,EAC3B,QAAQ,EAAE,QAAQ,KACjB,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/B,yBAAyB;IACzB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,6BAA6B;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gCAAgC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC/B,CAAC;AAEF;;GAEG;AACH,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,WAAW,EACX,MAAM,EACN,MAAM,EACN,MAAM,EACN,MAAM,EACN,cAAc,EACd,UAAU,EACV,OAAO,GACV,EAAE,iBAAiB,2CAgCnB"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { type FieldInfo } from '../schema';
|
|
2
|
+
import type { ActionState } from '../server';
|
|
3
|
+
import type { CmsAdminLayoutProps } from './CmsAdminLayout';
|
|
4
|
+
import type { ImageUploadConfig } from '../index';
|
|
5
|
+
export { createCmsConfig, getCmsConfig, getPageBySlug, getNavPages, definePage, type CmsConfig, type CmsPageConfig, type TypedCmsPageConfig, } from './index';
|
|
6
|
+
export type { FieldInfo } from '../schema';
|
|
7
|
+
export type { NavPageInfo } from './index';
|
|
8
|
+
/**
|
|
9
|
+
* Serializable props for CmsAdminPage
|
|
10
|
+
*/
|
|
11
|
+
export type AdminPageProps = {
|
|
12
|
+
title: string;
|
|
13
|
+
/** Optional description to guide the user */
|
|
14
|
+
description?: string;
|
|
15
|
+
fields: FieldInfo[];
|
|
16
|
+
values: Record<string, unknown>;
|
|
17
|
+
action: (prevState: ActionState<any>, formData: FormData) => Promise<ActionState<any>>;
|
|
18
|
+
storage?: ImageUploadConfig;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Extract serializable props from a page config.
|
|
22
|
+
* Use this in server components to prepare data for CmsAdminPage.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* import { extractPageProps } from 'litecms/admin/config';
|
|
27
|
+
* import { CmsAdminPage } from 'litecms/admin';
|
|
28
|
+
* import { cmsConfig } from '@/cms/config';
|
|
29
|
+
*
|
|
30
|
+
* export default async function AdminPage({ params }) {
|
|
31
|
+
* const props = await extractPageProps(cmsConfig, params.slug);
|
|
32
|
+
* if (!props) return notFound();
|
|
33
|
+
* return <CmsAdminPage {...props} />;
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function extractPageProps(config: {
|
|
38
|
+
pages: Array<{
|
|
39
|
+
slug: string;
|
|
40
|
+
title: string;
|
|
41
|
+
description?: string;
|
|
42
|
+
definition: unknown;
|
|
43
|
+
action: unknown;
|
|
44
|
+
getData: () => Promise<unknown>;
|
|
45
|
+
}>;
|
|
46
|
+
storage?: ImageUploadConfig;
|
|
47
|
+
}, slug: string): Promise<AdminPageProps | null>;
|
|
48
|
+
/**
|
|
49
|
+
* Serializable props for CmsAdminLayout
|
|
50
|
+
*/
|
|
51
|
+
export type LayoutProps = Omit<CmsAdminLayoutProps, 'children' | 'currentSlug'>;
|
|
52
|
+
/**
|
|
53
|
+
* Extract serializable layout props from CMS config.
|
|
54
|
+
* Use this to pass only serializable data to CmsAdminLayout.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```tsx
|
|
58
|
+
* import { extractLayoutProps } from 'litecms/admin/config';
|
|
59
|
+
* import { CmsAdminLayout } from 'litecms/admin';
|
|
60
|
+
* import { cmsConfig } from '@/cms/config';
|
|
61
|
+
*
|
|
62
|
+
* export default function AdminLayout({ children }) {
|
|
63
|
+
* return (
|
|
64
|
+
* <CmsAdminLayout {...extractLayoutProps(cmsConfig)}>
|
|
65
|
+
* {children}
|
|
66
|
+
* </CmsAdminLayout>
|
|
67
|
+
* );
|
|
68
|
+
* }
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export declare function extractLayoutProps(config: {
|
|
72
|
+
adminTitle?: string;
|
|
73
|
+
siteName?: string;
|
|
74
|
+
basePath?: string;
|
|
75
|
+
publicSiteUrl?: string;
|
|
76
|
+
pages: Array<{
|
|
77
|
+
slug: string;
|
|
78
|
+
title: string;
|
|
79
|
+
group?: string;
|
|
80
|
+
icon?: string;
|
|
81
|
+
}>;
|
|
82
|
+
}): LayoutProps;
|
|
83
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/admin/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAElD,OAAO,EACH,eAAe,EACf,YAAY,EACZ,aAAa,EACb,WAAW,EACX,UAAU,EACV,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,kBAAkB,GAC1B,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEhC,MAAM,EAAE,CAAC,SAAS,EAAE,WAAW,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;IACvF,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC/B,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,gBAAgB,CAClC,MAAM,EAAE;IACJ,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IAC3I,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC/B,EACD,IAAI,EAAE,MAAM,GACb,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAuBhC;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,mBAAmB,EAAE,UAAU,GAAG,aAAa,CAAC,CAAC;AAEhF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kBAAkB,CAC9B,MAAM,EAAE;IACJ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChF,GACF,WAAW,CAab"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getEditableFields
|
|
3
|
+
} from "../schema/index.js";
|
|
4
|
+
import {
|
|
5
|
+
createCmsConfig,
|
|
6
|
+
definePage,
|
|
7
|
+
getCmsConfig,
|
|
8
|
+
getNavPages,
|
|
9
|
+
getPageBySlug
|
|
10
|
+
} from "../index-8zcd33mx.js";
|
|
11
|
+
|
|
12
|
+
// src/admin/config.ts
|
|
13
|
+
async function extractPageProps(config, slug) {
|
|
14
|
+
const page = config.pages.find((p) => p.slug === slug);
|
|
15
|
+
if (!page)
|
|
16
|
+
return null;
|
|
17
|
+
const fields = getEditableFields(page.definition);
|
|
18
|
+
const data = await page.getData();
|
|
19
|
+
const definition = page.definition;
|
|
20
|
+
const dataObj = data ?? {};
|
|
21
|
+
const values = { ...definition.defaults, ...dataObj };
|
|
22
|
+
return {
|
|
23
|
+
title: page.title,
|
|
24
|
+
description: page.description,
|
|
25
|
+
fields,
|
|
26
|
+
values,
|
|
27
|
+
action: page.action,
|
|
28
|
+
storage: config.storage
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function extractLayoutProps(config) {
|
|
32
|
+
return {
|
|
33
|
+
adminTitle: config.adminTitle,
|
|
34
|
+
siteName: config.siteName,
|
|
35
|
+
basePath: config.basePath,
|
|
36
|
+
publicSiteUrl: config.publicSiteUrl,
|
|
37
|
+
pages: config.pages.map((p) => ({
|
|
38
|
+
slug: p.slug,
|
|
39
|
+
title: p.title,
|
|
40
|
+
group: p.group,
|
|
41
|
+
icon: p.icon
|
|
42
|
+
}))
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export {
|
|
46
|
+
getPageBySlug,
|
|
47
|
+
getNavPages,
|
|
48
|
+
getCmsConfig,
|
|
49
|
+
extractPageProps,
|
|
50
|
+
extractLayoutProps,
|
|
51
|
+
definePage,
|
|
52
|
+
createCmsConfig
|
|
53
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { CmsAdminLayout, type CmsAdminLayoutProps, } from './CmsAdminLayout';
|
|
2
|
+
export { CmsAdminPage, type CmsAdminPageProps, } from './CmsAdminPage';
|
|
3
|
+
export { createCmsConfig, getCmsConfig, getPageBySlug, getNavPages, definePage, type CmsConfig, type CmsPageConfig, type TypedCmsPageConfig, } from './index';
|
|
4
|
+
export { NavPageInfo, PageGroup } from './index';
|
|
5
|
+
export { type StorageConfig, } from '../storage';
|
|
6
|
+
export { type ImageUploadConfig, } from '../components';
|
|
7
|
+
//# sourceMappingURL=exports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../../src/admin/exports.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,cAAc,EACd,KAAK,mBAAmB,GAC3B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACH,YAAY,EACZ,KAAK,iBAAiB,GACzB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACH,eAAe,EACf,YAAY,EACZ,aAAa,EACb,WAAW,EACX,UAAU,EACV,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,kBAAkB,GAC1B,MAAM,SAAS,CAAC;AAEjB,OAAO,EACH,WAAW,EACX,SAAS,EACZ,MAAM,SAAS,CAAC;AAEjB,OAAO,EACH,KAAK,aAAa,GACrB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACH,KAAK,iBAAiB,GACzB,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
isDevelopmentEnv
|
|
4
|
+
} from "../server/index.js";
|
|
5
|
+
import {
|
|
6
|
+
CmsCheckbox,
|
|
7
|
+
CmsField,
|
|
8
|
+
CmsImageField,
|
|
9
|
+
FormProvider,
|
|
10
|
+
flattenValue,
|
|
11
|
+
groupFields,
|
|
12
|
+
groupPages,
|
|
13
|
+
useForm
|
|
14
|
+
} from "../index-pmb5m3ek.js";
|
|
15
|
+
import {
|
|
16
|
+
createCmsConfig,
|
|
17
|
+
definePage,
|
|
18
|
+
getCmsConfig,
|
|
19
|
+
getNavPages,
|
|
20
|
+
getPageBySlug
|
|
21
|
+
} from "../index-8zcd33mx.js";
|
|
22
|
+
|
|
23
|
+
// src/admin/CmsAdminLayout.tsx
|
|
24
|
+
import { usePathname } from "next/navigation";
|
|
25
|
+
|
|
26
|
+
// src/components/CmsNavSection.tsx
|
|
27
|
+
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
28
|
+
function NavSection({
|
|
29
|
+
group,
|
|
30
|
+
basePath,
|
|
31
|
+
currentSlug
|
|
32
|
+
}) {
|
|
33
|
+
return /* @__PURE__ */ jsxDEV("div", {
|
|
34
|
+
children: [
|
|
35
|
+
/* @__PURE__ */ jsxDEV("div", {
|
|
36
|
+
className: "text-[10px] font-medium uppercase tracking-widest text-neutral-400 mb-4",
|
|
37
|
+
children: group.name ?? "Pages"
|
|
38
|
+
}, undefined, false, undefined, this),
|
|
39
|
+
/* @__PURE__ */ jsxDEV("div", {
|
|
40
|
+
className: "space-y-1",
|
|
41
|
+
children: group.pages.map((page) => /* @__PURE__ */ jsxDEV("a", {
|
|
42
|
+
href: `${basePath}/${page.slug}`,
|
|
43
|
+
className: `block py-1.5 text-sm transition-colors ${currentSlug === page.slug ? "text-neutral-900" : "text-neutral-400 hover:text-neutral-900"}`,
|
|
44
|
+
"aria-current": currentSlug === page.slug ? "page" : undefined,
|
|
45
|
+
children: page.title
|
|
46
|
+
}, page.slug, false, undefined, this))
|
|
47
|
+
}, undefined, false, undefined, this)
|
|
48
|
+
]
|
|
49
|
+
}, undefined, true, undefined, this);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/admin/CmsAdminLayout.tsx
|
|
53
|
+
import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
|
|
54
|
+
function CmsAdminLayout({
|
|
55
|
+
adminTitle = "Content",
|
|
56
|
+
siteName,
|
|
57
|
+
basePath = "/admin",
|
|
58
|
+
publicSiteUrl,
|
|
59
|
+
pages,
|
|
60
|
+
currentSlug: currentSlugProp,
|
|
61
|
+
onLogout,
|
|
62
|
+
children
|
|
63
|
+
}) {
|
|
64
|
+
const pathname = usePathname();
|
|
65
|
+
const detectedSlug = pathname?.replace(new RegExp(`^${basePath}/?`), "").split("/")[0];
|
|
66
|
+
const currentSlug = currentSlugProp ?? (detectedSlug || undefined);
|
|
67
|
+
const groupedPages = groupPages(pages);
|
|
68
|
+
return /* @__PURE__ */ jsxDEV2("div", {
|
|
69
|
+
className: "min-h-screen font-sans antialiased bg-neutral-50 text-neutral-900 selection:bg-neutral-200 selection:text-neutral-900 [--cms-accent:#171717] [--cms-accent-subtle:rgba(23,23,23,0.08)]",
|
|
70
|
+
children: [
|
|
71
|
+
/* @__PURE__ */ jsxDEV2("aside", {
|
|
72
|
+
className: "fixed inset-y-0 left-0 z-50 hidden w-56 flex-col border-r border-neutral-200 bg-white md:flex",
|
|
73
|
+
children: [
|
|
74
|
+
/* @__PURE__ */ jsxDEV2("div", {
|
|
75
|
+
className: "flex h-16 items-center px-6 border-b border-neutral-200",
|
|
76
|
+
children: /* @__PURE__ */ jsxDEV2("a", {
|
|
77
|
+
href: basePath,
|
|
78
|
+
className: "text-xs tracking-widest uppercase text-neutral-900 hover:text-neutral-600 transition-colors",
|
|
79
|
+
children: adminTitle
|
|
80
|
+
}, undefined, false, undefined, this)
|
|
81
|
+
}, undefined, false, undefined, this),
|
|
82
|
+
/* @__PURE__ */ jsxDEV2("nav", {
|
|
83
|
+
className: "flex-1 overflow-y-auto px-6 py-8 space-y-8",
|
|
84
|
+
children: groupedPages.map((group) => /* @__PURE__ */ jsxDEV2(NavSection, {
|
|
85
|
+
group,
|
|
86
|
+
basePath,
|
|
87
|
+
currentSlug
|
|
88
|
+
}, group.name ?? "__default", false, undefined, this))
|
|
89
|
+
}, undefined, false, undefined, this),
|
|
90
|
+
/* @__PURE__ */ jsxDEV2("div", {
|
|
91
|
+
className: "border-t border-neutral-200 px-6 py-4 space-y-3",
|
|
92
|
+
children: [
|
|
93
|
+
publicSiteUrl && /* @__PURE__ */ jsxDEV2("a", {
|
|
94
|
+
href: publicSiteUrl,
|
|
95
|
+
className: "flex items-center gap-2 text-xs tracking-wide text-neutral-400 hover:text-neutral-900 transition-colors",
|
|
96
|
+
target: "_blank",
|
|
97
|
+
rel: "noopener noreferrer",
|
|
98
|
+
children: [
|
|
99
|
+
/* @__PURE__ */ jsxDEV2("span", {
|
|
100
|
+
children: "View site"
|
|
101
|
+
}, undefined, false, undefined, this),
|
|
102
|
+
/* @__PURE__ */ jsxDEV2("svg", {
|
|
103
|
+
className: "h-3 w-3",
|
|
104
|
+
viewBox: "0 0 24 24",
|
|
105
|
+
fill: "none",
|
|
106
|
+
stroke: "currentColor",
|
|
107
|
+
strokeWidth: "1.5",
|
|
108
|
+
children: /* @__PURE__ */ jsxDEV2("path", {
|
|
109
|
+
strokeLinecap: "round",
|
|
110
|
+
strokeLinejoin: "round",
|
|
111
|
+
d: "M4.5 19.5l15-15m0 0H8.25m11.25 0v11.25"
|
|
112
|
+
}, undefined, false, undefined, this)
|
|
113
|
+
}, undefined, false, undefined, this)
|
|
114
|
+
]
|
|
115
|
+
}, undefined, true, undefined, this),
|
|
116
|
+
onLogout && /* @__PURE__ */ jsxDEV2("button", {
|
|
117
|
+
onClick: onLogout,
|
|
118
|
+
className: "text-xs tracking-wide text-neutral-400 hover:text-neutral-900 transition-colors cursor-pointer",
|
|
119
|
+
children: "Sign out"
|
|
120
|
+
}, undefined, false, undefined, this)
|
|
121
|
+
]
|
|
122
|
+
}, undefined, true, undefined, this)
|
|
123
|
+
]
|
|
124
|
+
}, undefined, true, undefined, this),
|
|
125
|
+
/* @__PURE__ */ jsxDEV2("header", {
|
|
126
|
+
className: "sticky top-0 z-40 flex h-14 items-center gap-4 px-4 bg-white border-b border-neutral-200 md:hidden",
|
|
127
|
+
children: /* @__PURE__ */ jsxDEV2("a", {
|
|
128
|
+
href: basePath,
|
|
129
|
+
className: "text-xs tracking-widest uppercase text-neutral-900",
|
|
130
|
+
children: adminTitle
|
|
131
|
+
}, undefined, false, undefined, this)
|
|
132
|
+
}, undefined, false, undefined, this),
|
|
133
|
+
/* @__PURE__ */ jsxDEV2("nav", {
|
|
134
|
+
className: "flex gap-4 overflow-x-auto px-4 py-3 bg-white border-b border-neutral-200 md:hidden",
|
|
135
|
+
children: pages.map((page) => /* @__PURE__ */ jsxDEV2("a", {
|
|
136
|
+
href: `${basePath}/${page.slug}`,
|
|
137
|
+
className: `shrink-0 text-sm transition-colors ${currentSlug === page.slug ? "text-neutral-900 border-b border-neutral-900 pb-1" : "text-neutral-400 hover:text-neutral-900"}`,
|
|
138
|
+
children: page.title
|
|
139
|
+
}, page.slug, false, undefined, this))
|
|
140
|
+
}, undefined, false, undefined, this),
|
|
141
|
+
/* @__PURE__ */ jsxDEV2("main", {
|
|
142
|
+
className: "md:ml-56 min-h-screen bg-white",
|
|
143
|
+
children: /* @__PURE__ */ jsxDEV2("div", {
|
|
144
|
+
className: "mx-auto",
|
|
145
|
+
children
|
|
146
|
+
}, undefined, false, undefined, this)
|
|
147
|
+
}, undefined, false, undefined, this)
|
|
148
|
+
]
|
|
149
|
+
}, undefined, true, undefined, this);
|
|
150
|
+
}
|
|
151
|
+
// src/components/CmsSimpleForm.tsx
|
|
152
|
+
import * as React from "react";
|
|
153
|
+
import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
|
|
154
|
+
function CmsSimpleForm({
|
|
155
|
+
fields,
|
|
156
|
+
action,
|
|
157
|
+
values = {},
|
|
158
|
+
styles = {},
|
|
159
|
+
submitText = "Save",
|
|
160
|
+
submitPendingText = "Saving...",
|
|
161
|
+
successMessage = "Changes saved",
|
|
162
|
+
storage
|
|
163
|
+
}) {
|
|
164
|
+
const [state, setState] = React.useState({
|
|
165
|
+
success: false
|
|
166
|
+
});
|
|
167
|
+
const [isPending, setIsPending] = React.useState(false);
|
|
168
|
+
const [showSuccess, setShowSuccess] = React.useState(false);
|
|
169
|
+
const form = useForm({
|
|
170
|
+
defaultValues: values
|
|
171
|
+
});
|
|
172
|
+
const hiddenFieldEntries = React.useMemo(() => {
|
|
173
|
+
const editableFieldNames = new Set(fields.map((f) => f.name));
|
|
174
|
+
const entries = [];
|
|
175
|
+
for (const [key, value] of Object.entries(values)) {
|
|
176
|
+
if (!editableFieldNames.has(key)) {
|
|
177
|
+
flattenValue(key, value, entries);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return entries;
|
|
181
|
+
}, [fields, values]);
|
|
182
|
+
React.useEffect(() => {
|
|
183
|
+
if (state.success) {
|
|
184
|
+
setShowSuccess(true);
|
|
185
|
+
const timer = setTimeout(() => setShowSuccess(false), 4000);
|
|
186
|
+
return () => clearTimeout(timer);
|
|
187
|
+
} else if (state.errors?.fieldErrors) {
|
|
188
|
+
setShowSuccess(false);
|
|
189
|
+
for (const [field, messages] of Object.entries(state.errors.fieldErrors)) {
|
|
190
|
+
if (messages && messages[0]) {
|
|
191
|
+
form.setError(field, {
|
|
192
|
+
type: "server",
|
|
193
|
+
message: messages[0]
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}, [state, form]);
|
|
199
|
+
const handleSubmit = async (event) => {
|
|
200
|
+
event.preventDefault();
|
|
201
|
+
setIsPending(true);
|
|
202
|
+
setShowSuccess(false);
|
|
203
|
+
try {
|
|
204
|
+
const formData = new FormData(event.currentTarget);
|
|
205
|
+
if (typeof window !== "undefined" && isDevelopmentEnv) {
|
|
206
|
+
console.log("[litecms] Submitting form data:", Object.fromEntries(formData.entries()));
|
|
207
|
+
}
|
|
208
|
+
const result = await action(state, formData);
|
|
209
|
+
if (typeof window !== "undefined" && isDevelopmentEnv) {
|
|
210
|
+
console.log("[litecms] Action result:", result);
|
|
211
|
+
}
|
|
212
|
+
if (!result) {
|
|
213
|
+
console.error("[litecms] Action returned null/undefined");
|
|
214
|
+
setState({
|
|
215
|
+
success: false,
|
|
216
|
+
errors: {
|
|
217
|
+
formError: "Server action returned no response."
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
setState(result);
|
|
223
|
+
} catch (error) {
|
|
224
|
+
console.error("[litecms] Form submission error:", error);
|
|
225
|
+
setState({
|
|
226
|
+
success: false,
|
|
227
|
+
errors: {
|
|
228
|
+
formError: error instanceof Error ? `Error: ${error.message}` : "Something went wrong."
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
} finally {
|
|
232
|
+
setIsPending(false);
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
const groupedFields = groupFields(fields);
|
|
236
|
+
return /* @__PURE__ */ jsxDEV3(FormProvider, {
|
|
237
|
+
...form,
|
|
238
|
+
children: /* @__PURE__ */ jsxDEV3("form", {
|
|
239
|
+
onSubmit: handleSubmit,
|
|
240
|
+
children: [
|
|
241
|
+
hiddenFieldEntries.map(([name, value]) => /* @__PURE__ */ jsxDEV3("input", {
|
|
242
|
+
type: "hidden",
|
|
243
|
+
name,
|
|
244
|
+
value
|
|
245
|
+
}, name, false, undefined, this)),
|
|
246
|
+
/* @__PURE__ */ jsxDEV3("div", {
|
|
247
|
+
className: styles.wrapper ?? "",
|
|
248
|
+
children: [
|
|
249
|
+
/* @__PURE__ */ jsxDEV3("div", {
|
|
250
|
+
className: "space-y-16",
|
|
251
|
+
children: groupedFields.map((group, groupIndex) => /* @__PURE__ */ jsxDEV3(FieldGroup, {
|
|
252
|
+
group,
|
|
253
|
+
styles,
|
|
254
|
+
storage
|
|
255
|
+
}, group.name ?? `__ungrouped-${groupIndex}`, false, undefined, this))
|
|
256
|
+
}, undefined, false, undefined, this),
|
|
257
|
+
state.errors?.formError && /* @__PURE__ */ jsxDEV3("div", {
|
|
258
|
+
className: "mt-12 py-4 px-5 border border-red-200 bg-red-50 text-red-900 text-sm",
|
|
259
|
+
children: state.errors.formError
|
|
260
|
+
}, undefined, false, undefined, this),
|
|
261
|
+
showSuccess && /* @__PURE__ */ jsxDEV3("div", {
|
|
262
|
+
className: "mt-12 py-4 px-5 border border-green-200 bg-green-50 text-green-900 text-sm",
|
|
263
|
+
children: successMessage
|
|
264
|
+
}, undefined, false, undefined, this),
|
|
265
|
+
/* @__PURE__ */ jsxDEV3("div", {
|
|
266
|
+
className: "mt-16 pt-8 border-t border-neutral-200 flex items-center justify-between",
|
|
267
|
+
children: [
|
|
268
|
+
/* @__PURE__ */ jsxDEV3("button", {
|
|
269
|
+
type: "button",
|
|
270
|
+
className: "text-sm text-neutral-400 hover:text-neutral-900 transition-colors cursor-pointer",
|
|
271
|
+
onClick: () => form.reset(),
|
|
272
|
+
disabled: isPending,
|
|
273
|
+
children: "Reset"
|
|
274
|
+
}, undefined, false, undefined, this),
|
|
275
|
+
/* @__PURE__ */ jsxDEV3("button", {
|
|
276
|
+
type: "submit",
|
|
277
|
+
disabled: isPending,
|
|
278
|
+
className: styles.submitButton ?? "inline-flex items-center gap-2 text-sm tracking-wide text-neutral-900 border-b border-neutral-900 pb-1 hover:pb-1.5 transition-all disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer",
|
|
279
|
+
children: [
|
|
280
|
+
isPending && /* @__PURE__ */ jsxDEV3("svg", {
|
|
281
|
+
className: "h-3.5 w-3.5 animate-spin",
|
|
282
|
+
viewBox: "0 0 24 24",
|
|
283
|
+
fill: "none",
|
|
284
|
+
stroke: "currentColor",
|
|
285
|
+
strokeWidth: "2",
|
|
286
|
+
children: /* @__PURE__ */ jsxDEV3("path", {
|
|
287
|
+
d: "M21 12a9 9 0 1 1-6.219-8.56"
|
|
288
|
+
}, undefined, false, undefined, this)
|
|
289
|
+
}, undefined, false, undefined, this),
|
|
290
|
+
isPending ? submitPendingText : submitText,
|
|
291
|
+
!isPending && /* @__PURE__ */ jsxDEV3("span", {
|
|
292
|
+
children: "→"
|
|
293
|
+
}, undefined, false, undefined, this)
|
|
294
|
+
]
|
|
295
|
+
}, undefined, true, undefined, this)
|
|
296
|
+
]
|
|
297
|
+
}, undefined, true, undefined, this)
|
|
298
|
+
]
|
|
299
|
+
}, undefined, true, undefined, this)
|
|
300
|
+
]
|
|
301
|
+
}, undefined, true, undefined, this)
|
|
302
|
+
}, undefined, false, undefined, this);
|
|
303
|
+
}
|
|
304
|
+
function FieldGroup({
|
|
305
|
+
group,
|
|
306
|
+
styles,
|
|
307
|
+
storage
|
|
308
|
+
}) {
|
|
309
|
+
if (group.name) {
|
|
310
|
+
return /* @__PURE__ */ jsxDEV3("fieldset", {
|
|
311
|
+
className: "space-y-8",
|
|
312
|
+
children: [
|
|
313
|
+
/* @__PURE__ */ jsxDEV3("legend", {
|
|
314
|
+
className: "text-xs font-medium uppercase tracking-widest text-neutral-400 pb-4 border-b border-neutral-200 w-full",
|
|
315
|
+
children: group.name
|
|
316
|
+
}, undefined, false, undefined, this),
|
|
317
|
+
/* @__PURE__ */ jsxDEV3("div", {
|
|
318
|
+
className: "grid grid-cols-1 md:grid-cols-2 gap-8",
|
|
319
|
+
children: group.fields.map((field) => /* @__PURE__ */ jsxDEV3(AutoField, {
|
|
320
|
+
field,
|
|
321
|
+
styles,
|
|
322
|
+
storage
|
|
323
|
+
}, field.name, false, undefined, this))
|
|
324
|
+
}, undefined, false, undefined, this)
|
|
325
|
+
]
|
|
326
|
+
}, undefined, true, undefined, this);
|
|
327
|
+
}
|
|
328
|
+
return /* @__PURE__ */ jsxDEV3("div", {
|
|
329
|
+
className: "grid grid-cols-1 md:grid-cols-2 gap-8",
|
|
330
|
+
children: group.fields.map((field) => /* @__PURE__ */ jsxDEV3(AutoField, {
|
|
331
|
+
field,
|
|
332
|
+
styles,
|
|
333
|
+
storage
|
|
334
|
+
}, field.name, false, undefined, this))
|
|
335
|
+
}, undefined, false, undefined, this);
|
|
336
|
+
}
|
|
337
|
+
function AutoField({
|
|
338
|
+
field,
|
|
339
|
+
styles,
|
|
340
|
+
storage
|
|
341
|
+
}) {
|
|
342
|
+
const { name, meta, required } = field;
|
|
343
|
+
const isFullWidth = meta.type === "textarea" || meta.type === "image";
|
|
344
|
+
const colSpan = isFullWidth ? "col-span-1 md:col-span-2" : "col-span-1";
|
|
345
|
+
if (meta.type === "checkbox") {
|
|
346
|
+
return /* @__PURE__ */ jsxDEV3("div", {
|
|
347
|
+
className: colSpan,
|
|
348
|
+
children: /* @__PURE__ */ jsxDEV3(CmsCheckbox, {
|
|
349
|
+
name,
|
|
350
|
+
label: meta.label,
|
|
351
|
+
helpText: meta.helpText,
|
|
352
|
+
className: styles.field,
|
|
353
|
+
labelClassName: styles.label,
|
|
354
|
+
inputClassName: styles.input,
|
|
355
|
+
errorClassName: styles.error,
|
|
356
|
+
helpClassName: styles.help
|
|
357
|
+
}, undefined, false, undefined, this)
|
|
358
|
+
}, undefined, false, undefined, this);
|
|
359
|
+
}
|
|
360
|
+
if (meta.type === "image") {
|
|
361
|
+
return /* @__PURE__ */ jsxDEV3("div", {
|
|
362
|
+
className: colSpan,
|
|
363
|
+
children: /* @__PURE__ */ jsxDEV3(CmsImageField, {
|
|
364
|
+
name,
|
|
365
|
+
label: meta.label,
|
|
366
|
+
helpText: meta.helpText,
|
|
367
|
+
required,
|
|
368
|
+
accept: meta.accept,
|
|
369
|
+
className: styles.field,
|
|
370
|
+
labelClassName: styles.label,
|
|
371
|
+
errorClassName: styles.error,
|
|
372
|
+
helpClassName: styles.help,
|
|
373
|
+
storage
|
|
374
|
+
}, undefined, false, undefined, this)
|
|
375
|
+
}, undefined, false, undefined, this);
|
|
376
|
+
}
|
|
377
|
+
return /* @__PURE__ */ jsxDEV3("div", {
|
|
378
|
+
className: colSpan,
|
|
379
|
+
children: /* @__PURE__ */ jsxDEV3(CmsField, {
|
|
380
|
+
name,
|
|
381
|
+
label: meta.label,
|
|
382
|
+
type: meta.type ?? "text",
|
|
383
|
+
placeholder: meta.placeholder,
|
|
384
|
+
helpText: meta.helpText,
|
|
385
|
+
options: meta.options,
|
|
386
|
+
rows: meta.rows,
|
|
387
|
+
required,
|
|
388
|
+
className: styles.field,
|
|
389
|
+
labelClassName: styles.label,
|
|
390
|
+
inputClassName: styles.input,
|
|
391
|
+
errorClassName: styles.error,
|
|
392
|
+
helpClassName: styles.help
|
|
393
|
+
}, undefined, false, undefined, this)
|
|
394
|
+
}, undefined, false, undefined, this);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// src/admin/CmsAdminPage.tsx
|
|
398
|
+
import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
|
|
399
|
+
function CmsAdminPage({
|
|
400
|
+
title,
|
|
401
|
+
description,
|
|
402
|
+
fields,
|
|
403
|
+
values,
|
|
404
|
+
action,
|
|
405
|
+
styles,
|
|
406
|
+
successMessage,
|
|
407
|
+
submitText,
|
|
408
|
+
storage
|
|
409
|
+
}) {
|
|
410
|
+
return /* @__PURE__ */ jsxDEV4("div", {
|
|
411
|
+
className: "space-y-12",
|
|
412
|
+
children: [
|
|
413
|
+
/* @__PURE__ */ jsxDEV4("header", {
|
|
414
|
+
className: "border-b border-neutral-200 h-16 px-8 flex items-center justify-between",
|
|
415
|
+
children: [
|
|
416
|
+
/* @__PURE__ */ jsxDEV4("h1", {
|
|
417
|
+
className: "text-2xl font-light tracking-tight text-neutral-900",
|
|
418
|
+
children: title
|
|
419
|
+
}, undefined, false, undefined, this),
|
|
420
|
+
description && /* @__PURE__ */ jsxDEV4("p", {
|
|
421
|
+
className: "mt-3 text-sm text-neutral-500 max-w-xl leading-relaxed",
|
|
422
|
+
children: description
|
|
423
|
+
}, undefined, false, undefined, this)
|
|
424
|
+
]
|
|
425
|
+
}, undefined, true, undefined, this),
|
|
426
|
+
/* @__PURE__ */ jsxDEV4("div", {
|
|
427
|
+
className: "px-8 pb-12",
|
|
428
|
+
children: /* @__PURE__ */ jsxDEV4(CmsSimpleForm, {
|
|
429
|
+
fields,
|
|
430
|
+
action,
|
|
431
|
+
values,
|
|
432
|
+
styles,
|
|
433
|
+
successMessage: successMessage ?? "Changes saved successfully.",
|
|
434
|
+
submitText: submitText ?? "Save",
|
|
435
|
+
submitPendingText: "Saving...",
|
|
436
|
+
storage
|
|
437
|
+
}, undefined, false, undefined, this)
|
|
438
|
+
}, undefined, false, undefined, this)
|
|
439
|
+
]
|
|
440
|
+
}, undefined, true, undefined, this);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// src/admin/exports.ts
|
|
444
|
+
export {
|
|
445
|
+
getPageBySlug,
|
|
446
|
+
getNavPages,
|
|
447
|
+
getCmsConfig,
|
|
448
|
+
definePage,
|
|
449
|
+
createCmsConfig,
|
|
450
|
+
CmsAdminPage,
|
|
451
|
+
CmsAdminLayout
|
|
452
|
+
};
|