@fnd-platform/cms 1.0.0-alpha.1
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 +283 -0
- package/lib/cms-project.d.ts +127 -0
- package/lib/cms-project.d.ts.map +1 -0
- package/lib/cms-project.js +343 -0
- package/lib/cms-project.js.map +1 -0
- package/lib/index.d.ts +11 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +20 -0
- package/lib/index.js.map +1 -0
- package/lib/options.d.ts +59 -0
- package/lib/options.d.ts.map +1 -0
- package/lib/options.js +3 -0
- package/lib/options.js.map +1 -0
- package/lib/templates/admin-breadcrumbs.d.ts +13 -0
- package/lib/templates/admin-breadcrumbs.d.ts.map +1 -0
- package/lib/templates/admin-breadcrumbs.js +80 -0
- package/lib/templates/admin-breadcrumbs.js.map +1 -0
- package/lib/templates/admin-content-route.d.ts +18 -0
- package/lib/templates/admin-content-route.d.ts.map +1 -0
- package/lib/templates/admin-content-route.js +100 -0
- package/lib/templates/admin-content-route.js.map +1 -0
- package/lib/templates/admin-content-type-route.d.ts +9 -0
- package/lib/templates/admin-content-type-route.d.ts.map +1 -0
- package/lib/templates/admin-content-type-route.js +96 -0
- package/lib/templates/admin-content-type-route.js.map +1 -0
- package/lib/templates/admin-header.d.ts +13 -0
- package/lib/templates/admin-header.d.ts.map +1 -0
- package/lib/templates/admin-header.js +123 -0
- package/lib/templates/admin-header.js.map +1 -0
- package/lib/templates/admin-index.d.ts +9 -0
- package/lib/templates/admin-index.d.ts.map +1 -0
- package/lib/templates/admin-index.js +60 -0
- package/lib/templates/admin-index.js.map +1 -0
- package/lib/templates/admin-layout.d.ts +10 -0
- package/lib/templates/admin-layout.d.ts.map +1 -0
- package/lib/templates/admin-layout.js +46 -0
- package/lib/templates/admin-layout.js.map +1 -0
- package/lib/templates/admin-sidebar.d.ts +13 -0
- package/lib/templates/admin-sidebar.d.ts.map +1 -0
- package/lib/templates/admin-sidebar.js +149 -0
- package/lib/templates/admin-sidebar.js.map +1 -0
- package/lib/templates/content-editor.d.ts +10 -0
- package/lib/templates/content-editor.d.ts.map +1 -0
- package/lib/templates/content-editor.js +354 -0
- package/lib/templates/content-editor.js.map +1 -0
- package/lib/templates/content-schema.d.ts +10 -0
- package/lib/templates/content-schema.d.ts.map +1 -0
- package/lib/templates/content-schema.js +274 -0
- package/lib/templates/content-schema.js.map +1 -0
- package/lib/templates/content-table.d.ts +13 -0
- package/lib/templates/content-table.d.ts.map +1 -0
- package/lib/templates/content-table.js +177 -0
- package/lib/templates/content-table.js.map +1 -0
- package/lib/templates/content-types-examples.d.ts +19 -0
- package/lib/templates/content-types-examples.d.ts.map +1 -0
- package/lib/templates/content-types-examples.js +275 -0
- package/lib/templates/content-types-examples.js.map +1 -0
- package/lib/templates/content-types-registry.d.ts +10 -0
- package/lib/templates/content-types-registry.d.ts.map +1 -0
- package/lib/templates/content-types-registry.js +87 -0
- package/lib/templates/content-types-registry.js.map +1 -0
- package/lib/templates/content-types.d.ts +10 -0
- package/lib/templates/content-types.d.ts.map +1 -0
- package/lib/templates/content-types.js +384 -0
- package/lib/templates/content-types.js.map +1 -0
- package/lib/templates/dashboard-stats.d.ts +13 -0
- package/lib/templates/dashboard-stats.d.ts.map +1 -0
- package/lib/templates/dashboard-stats.js +117 -0
- package/lib/templates/dashboard-stats.js.map +1 -0
- package/lib/templates/editor/index.d.ts +6 -0
- package/lib/templates/editor/index.d.ts.map +1 -0
- package/lib/templates/editor/index.js +21 -0
- package/lib/templates/editor/index.js.map +1 -0
- package/lib/templates/editor/rich-text-editor.d.ts +7 -0
- package/lib/templates/editor/rich-text-editor.d.ts.map +1 -0
- package/lib/templates/editor/rich-text-editor.js +115 -0
- package/lib/templates/editor/rich-text-editor.js.map +1 -0
- package/lib/templates/editor/toolbar.d.ts +7 -0
- package/lib/templates/editor/toolbar.d.ts.map +1 -0
- package/lib/templates/editor/toolbar.js +272 -0
- package/lib/templates/editor/toolbar.js.map +1 -0
- package/lib/templates/form-fields/boolean-field.d.ts +7 -0
- package/lib/templates/form-fields/boolean-field.d.ts.map +1 -0
- package/lib/templates/form-fields/boolean-field.js +76 -0
- package/lib/templates/form-fields/boolean-field.js.map +1 -0
- package/lib/templates/form-fields/date-field.d.ts +7 -0
- package/lib/templates/form-fields/date-field.d.ts.map +1 -0
- package/lib/templates/form-fields/date-field.js +61 -0
- package/lib/templates/form-fields/date-field.js.map +1 -0
- package/lib/templates/form-fields/datetime-field.d.ts +7 -0
- package/lib/templates/form-fields/datetime-field.d.ts.map +1 -0
- package/lib/templates/form-fields/datetime-field.js +87 -0
- package/lib/templates/form-fields/datetime-field.js.map +1 -0
- package/lib/templates/form-fields/index.d.ts +23 -0
- package/lib/templates/form-fields/index.d.ts.map +1 -0
- package/lib/templates/form-fields/index.js +275 -0
- package/lib/templates/form-fields/index.js.map +1 -0
- package/lib/templates/form-fields/media-field.d.ts +10 -0
- package/lib/templates/form-fields/media-field.d.ts.map +1 -0
- package/lib/templates/form-fields/media-field.js +225 -0
- package/lib/templates/form-fields/media-field.js.map +1 -0
- package/lib/templates/form-fields/multiselect-field.d.ts +7 -0
- package/lib/templates/form-fields/multiselect-field.d.ts.map +1 -0
- package/lib/templates/form-fields/multiselect-field.js +121 -0
- package/lib/templates/form-fields/multiselect-field.js.map +1 -0
- package/lib/templates/form-fields/number-field.d.ts +7 -0
- package/lib/templates/form-fields/number-field.d.ts.map +1 -0
- package/lib/templates/form-fields/number-field.js +87 -0
- package/lib/templates/form-fields/number-field.js.map +1 -0
- package/lib/templates/form-fields/reference-field.d.ts +9 -0
- package/lib/templates/form-fields/reference-field.d.ts.map +1 -0
- package/lib/templates/form-fields/reference-field.js +145 -0
- package/lib/templates/form-fields/reference-field.js.map +1 -0
- package/lib/templates/form-fields/richtext-field.d.ts +9 -0
- package/lib/templates/form-fields/richtext-field.d.ts.map +1 -0
- package/lib/templates/form-fields/richtext-field.js +60 -0
- package/lib/templates/form-fields/richtext-field.js.map +1 -0
- package/lib/templates/form-fields/select-field.d.ts +7 -0
- package/lib/templates/form-fields/select-field.d.ts.map +1 -0
- package/lib/templates/form-fields/select-field.js +70 -0
- package/lib/templates/form-fields/select-field.js.map +1 -0
- package/lib/templates/form-fields/slug-field.d.ts +7 -0
- package/lib/templates/form-fields/slug-field.d.ts.map +1 -0
- package/lib/templates/form-fields/slug-field.js +143 -0
- package/lib/templates/form-fields/slug-field.js.map +1 -0
- package/lib/templates/form-fields/tags-field.d.ts +7 -0
- package/lib/templates/form-fields/tags-field.d.ts.map +1 -0
- package/lib/templates/form-fields/tags-field.js +172 -0
- package/lib/templates/form-fields/tags-field.js.map +1 -0
- package/lib/templates/form-fields/text-field.d.ts +7 -0
- package/lib/templates/form-fields/text-field.d.ts.map +1 -0
- package/lib/templates/form-fields/text-field.js +63 -0
- package/lib/templates/form-fields/text-field.js.map +1 -0
- package/lib/templates/form-fields/textarea-field.d.ts +7 -0
- package/lib/templates/form-fields/textarea-field.d.ts.map +1 -0
- package/lib/templates/form-fields/textarea-field.js +64 -0
- package/lib/templates/form-fields/textarea-field.js.map +1 -0
- package/lib/templates/index.d.ts +34 -0
- package/lib/templates/index.d.ts.map +1 -0
- package/lib/templates/index.js +92 -0
- package/lib/templates/index.js.map +1 -0
- package/lib/templates/media/index.d.ts +12 -0
- package/lib/templates/media/index.d.ts.map +1 -0
- package/lib/templates/media/index.js +50 -0
- package/lib/templates/media/index.js.map +1 -0
- package/lib/templates/media/media-api.d.ts +13 -0
- package/lib/templates/media/media-api.d.ts.map +1 -0
- package/lib/templates/media/media-api.js +274 -0
- package/lib/templates/media/media-api.js.map +1 -0
- package/lib/templates/media/media-grid.d.ts +14 -0
- package/lib/templates/media/media-grid.d.ts.map +1 -0
- package/lib/templates/media/media-grid.js +314 -0
- package/lib/templates/media/media-grid.js.map +1 -0
- package/lib/templates/media/media-library-route.d.ts +13 -0
- package/lib/templates/media/media-library-route.d.ts.map +1 -0
- package/lib/templates/media/media-library-route.js +105 -0
- package/lib/templates/media/media-library-route.js.map +1 -0
- package/lib/templates/media/media-picker.d.ts +13 -0
- package/lib/templates/media/media-picker.d.ts.map +1 -0
- package/lib/templates/media/media-picker.js +152 -0
- package/lib/templates/media/media-picker.js.map +1 -0
- package/lib/templates/media/media-uploader.d.ts +14 -0
- package/lib/templates/media/media-uploader.d.ts.map +1 -0
- package/lib/templates/media/media-uploader.js +318 -0
- package/lib/templates/media/media-uploader.js.map +1 -0
- package/lib/templates/recent-content.d.ts +13 -0
- package/lib/templates/recent-content.d.ts.map +1 -0
- package/lib/templates/recent-content.js +138 -0
- package/lib/templates/recent-content.js.map +1 -0
- package/lib/templates/slug-utils.d.ts +10 -0
- package/lib/templates/slug-utils.d.ts.map +1 -0
- package/lib/templates/slug-utils.js +194 -0
- package/lib/templates/slug-utils.js.map +1 -0
- package/lib/templates/ui-avatar.d.ts +8 -0
- package/lib/templates/ui-avatar.d.ts.map +1 -0
- package/lib/templates/ui-avatar.js +60 -0
- package/lib/templates/ui-avatar.js.map +1 -0
- package/lib/templates/ui-badge.d.ts +8 -0
- package/lib/templates/ui-badge.d.ts.map +1 -0
- package/lib/templates/ui-badge.js +52 -0
- package/lib/templates/ui-badge.js.map +1 -0
- package/lib/templates/ui-dialog.d.ts +10 -0
- package/lib/templates/ui-dialog.d.ts.map +1 -0
- package/lib/templates/ui-dialog.js +134 -0
- package/lib/templates/ui-dialog.js.map +1 -0
- package/lib/templates/ui-dropdown-menu.d.ts +8 -0
- package/lib/templates/ui-dropdown-menu.d.ts.map +1 -0
- package/lib/templates/ui-dropdown-menu.js +210 -0
- package/lib/templates/ui-dropdown-menu.js.map +1 -0
- package/lib/templates/ui-popover.d.ts +8 -0
- package/lib/templates/ui-popover.d.ts.map +1 -0
- package/lib/templates/ui-popover.js +43 -0
- package/lib/templates/ui-popover.js.map +1 -0
- package/lib/templates/ui-progress.d.ts +10 -0
- package/lib/templates/ui-progress.d.ts.map +1 -0
- package/lib/templates/ui-progress.js +40 -0
- package/lib/templates/ui-progress.js.map +1 -0
- package/lib/templates/ui-table.d.ts +8 -0
- package/lib/templates/ui-table.d.ts.map +1 -0
- package/lib/templates/ui-table.js +129 -0
- package/lib/templates/ui-table.js.map +1 -0
- package/lib/templates/ui-tabs.d.ts +10 -0
- package/lib/templates/ui-tabs.d.ts.map +1 -0
- package/lib/templates/ui-tabs.js +67 -0
- package/lib/templates/ui-tabs.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
3
|
+
exports.getContentTableTemplate = getContentTableTemplate;
|
|
4
|
+
/**
|
|
5
|
+
* Generates the content table component template.
|
|
6
|
+
*
|
|
7
|
+
* This component displays:
|
|
8
|
+
* - Table of content items for a specific type
|
|
9
|
+
* - Status badges
|
|
10
|
+
* - Edit/Delete actions
|
|
11
|
+
* - Empty state
|
|
12
|
+
*
|
|
13
|
+
* @returns Template string for app/components/admin/content-table.tsx
|
|
14
|
+
*/
|
|
15
|
+
function getContentTableTemplate() {
|
|
16
|
+
return `import { Link } from '@remix-run/react';
|
|
17
|
+
import { Pencil, Trash2, MoreHorizontal, Plus } from 'lucide-react';
|
|
18
|
+
import { Button } from '~/components/ui/button';
|
|
19
|
+
import { Badge } from '~/components/ui/badge';
|
|
20
|
+
import {
|
|
21
|
+
Table,
|
|
22
|
+
TableBody,
|
|
23
|
+
TableCell,
|
|
24
|
+
TableHead,
|
|
25
|
+
TableHeader,
|
|
26
|
+
TableRow,
|
|
27
|
+
} from '~/components/ui/table';
|
|
28
|
+
import {
|
|
29
|
+
DropdownMenu,
|
|
30
|
+
DropdownMenuContent,
|
|
31
|
+
DropdownMenuItem,
|
|
32
|
+
DropdownMenuTrigger,
|
|
33
|
+
} from '~/components/ui/dropdown-menu';
|
|
34
|
+
|
|
35
|
+
export interface ContentItem {
|
|
36
|
+
id: string;
|
|
37
|
+
title: string;
|
|
38
|
+
status: 'draft' | 'published' | 'scheduled' | 'archived';
|
|
39
|
+
createdAt: string;
|
|
40
|
+
updatedAt: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ContentType {
|
|
44
|
+
name: string;
|
|
45
|
+
label: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface ContentTableProps {
|
|
49
|
+
contentType: ContentType;
|
|
50
|
+
items: ContentItem[];
|
|
51
|
+
canEdit?: boolean;
|
|
52
|
+
canDelete?: boolean;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function getStatusBadgeVariant(
|
|
56
|
+
status: string
|
|
57
|
+
): 'default' | 'secondary' | 'success' | 'warning' | 'outline' {
|
|
58
|
+
switch (status) {
|
|
59
|
+
case 'published':
|
|
60
|
+
return 'success';
|
|
61
|
+
case 'draft':
|
|
62
|
+
return 'secondary';
|
|
63
|
+
case 'scheduled':
|
|
64
|
+
return 'warning';
|
|
65
|
+
case 'archived':
|
|
66
|
+
return 'outline';
|
|
67
|
+
default:
|
|
68
|
+
return 'default';
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function formatDate(dateString: string): string {
|
|
73
|
+
return new Date(dateString).toLocaleDateString(undefined, {
|
|
74
|
+
year: 'numeric',
|
|
75
|
+
month: 'short',
|
|
76
|
+
day: 'numeric',
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function ContentTable({
|
|
81
|
+
contentType,
|
|
82
|
+
items,
|
|
83
|
+
canEdit = true,
|
|
84
|
+
canDelete = true,
|
|
85
|
+
}: ContentTableProps) {
|
|
86
|
+
if (items.length === 0) {
|
|
87
|
+
return (
|
|
88
|
+
<div className="flex flex-col items-center justify-center rounded-lg border border-dashed p-12 text-center">
|
|
89
|
+
<p className="text-sm text-muted-foreground">
|
|
90
|
+
No {contentType.label.toLowerCase()} found.
|
|
91
|
+
</p>
|
|
92
|
+
<Button asChild className="mt-4">
|
|
93
|
+
<Link to={\`/admin/content/\${contentType.name}/new\`}>
|
|
94
|
+
<Plus className="mr-2 h-4 w-4" />
|
|
95
|
+
Create {contentType.label}
|
|
96
|
+
</Link>
|
|
97
|
+
</Button>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<div className="rounded-md border">
|
|
104
|
+
<Table>
|
|
105
|
+
<TableHeader>
|
|
106
|
+
<TableRow>
|
|
107
|
+
<TableHead className="w-[40%]">Title</TableHead>
|
|
108
|
+
<TableHead>Status</TableHead>
|
|
109
|
+
<TableHead>Created</TableHead>
|
|
110
|
+
<TableHead>Updated</TableHead>
|
|
111
|
+
<TableHead className="w-[70px]">Actions</TableHead>
|
|
112
|
+
</TableRow>
|
|
113
|
+
</TableHeader>
|
|
114
|
+
<TableBody>
|
|
115
|
+
{items.map((item) => (
|
|
116
|
+
<TableRow key={item.id}>
|
|
117
|
+
<TableCell className="font-medium">
|
|
118
|
+
<Link
|
|
119
|
+
to={\`/admin/content/\${contentType.name}/\${item.id}\`}
|
|
120
|
+
className="hover:underline"
|
|
121
|
+
>
|
|
122
|
+
{item.title}
|
|
123
|
+
</Link>
|
|
124
|
+
</TableCell>
|
|
125
|
+
<TableCell>
|
|
126
|
+
<Badge
|
|
127
|
+
variant={getStatusBadgeVariant(item.status)}
|
|
128
|
+
className="capitalize"
|
|
129
|
+
>
|
|
130
|
+
{item.status}
|
|
131
|
+
</Badge>
|
|
132
|
+
</TableCell>
|
|
133
|
+
<TableCell className="text-muted-foreground">
|
|
134
|
+
{formatDate(item.createdAt)}
|
|
135
|
+
</TableCell>
|
|
136
|
+
<TableCell className="text-muted-foreground">
|
|
137
|
+
{formatDate(item.updatedAt)}
|
|
138
|
+
</TableCell>
|
|
139
|
+
<TableCell>
|
|
140
|
+
<DropdownMenu>
|
|
141
|
+
<DropdownMenuTrigger asChild>
|
|
142
|
+
<Button variant="ghost" size="icon">
|
|
143
|
+
<MoreHorizontal className="h-4 w-4" />
|
|
144
|
+
<span className="sr-only">Open menu</span>
|
|
145
|
+
</Button>
|
|
146
|
+
</DropdownMenuTrigger>
|
|
147
|
+
<DropdownMenuContent align="end">
|
|
148
|
+
{canEdit && (
|
|
149
|
+
<DropdownMenuItem asChild>
|
|
150
|
+
<Link
|
|
151
|
+
to={\`/admin/content/\${contentType.name}/\${item.id}\`}
|
|
152
|
+
className="flex items-center"
|
|
153
|
+
>
|
|
154
|
+
<Pencil className="mr-2 h-4 w-4" />
|
|
155
|
+
Edit
|
|
156
|
+
</Link>
|
|
157
|
+
</DropdownMenuItem>
|
|
158
|
+
)}
|
|
159
|
+
{canDelete && (
|
|
160
|
+
<DropdownMenuItem className="text-destructive focus:text-destructive">
|
|
161
|
+
<Trash2 className="mr-2 h-4 w-4" />
|
|
162
|
+
Delete
|
|
163
|
+
</DropdownMenuItem>
|
|
164
|
+
)}
|
|
165
|
+
</DropdownMenuContent>
|
|
166
|
+
</DropdownMenu>
|
|
167
|
+
</TableCell>
|
|
168
|
+
</TableRow>
|
|
169
|
+
))}
|
|
170
|
+
</TableBody>
|
|
171
|
+
</Table>
|
|
172
|
+
</div>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
`;
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=content-table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-table.js","sourceRoot":"","sources":["../../src/templates/content-table.ts"],"names":[],"mappings":";;AAWA,0DAiKC;AA5KD;;;;;;;;;;GAUG;AACH,SAAgB,uBAAuB;IACrC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+JR,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates example content type definition templates.
|
|
3
|
+
*
|
|
4
|
+
* These templates provide example content types (blog-post, page)
|
|
5
|
+
* that demonstrate how to use the content type system.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Generates the blog post content type template.
|
|
9
|
+
*
|
|
10
|
+
* @returns Template string for app/content-types/blog-post.ts
|
|
11
|
+
*/
|
|
12
|
+
export declare function getBlogPostContentTypeTemplate(): string;
|
|
13
|
+
/**
|
|
14
|
+
* Generates the page content type template.
|
|
15
|
+
*
|
|
16
|
+
* @returns Template string for app/content-types/page.ts
|
|
17
|
+
*/
|
|
18
|
+
export declare function getPageContentTypeTemplate(): string;
|
|
19
|
+
//# sourceMappingURL=content-types-examples.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-types-examples.d.ts","sourceRoot":"","sources":["../../src/templates/content-types-examples.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;GAIG;AACH,wBAAgB,8BAA8B,IAAI,MAAM,CAmIvD;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,CAyHnD"}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* Generates example content type definition templates.
|
|
4
|
+
*
|
|
5
|
+
* These templates provide example content types (blog-post, page)
|
|
6
|
+
* that demonstrate how to use the content type system.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
9
|
+
exports.getBlogPostContentTypeTemplate = getBlogPostContentTypeTemplate;
|
|
10
|
+
exports.getPageContentTypeTemplate = getPageContentTypeTemplate;
|
|
11
|
+
/**
|
|
12
|
+
* Generates the blog post content type template.
|
|
13
|
+
*
|
|
14
|
+
* @returns Template string for app/content-types/blog-post.ts
|
|
15
|
+
*/
|
|
16
|
+
function getBlogPostContentTypeTemplate() {
|
|
17
|
+
return `/**
|
|
18
|
+
* Blog Post Content Type
|
|
19
|
+
*
|
|
20
|
+
* Defines the structure for blog posts in the CMS.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { defineContentType } from '~/lib/content-types';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Blog post content type definition.
|
|
27
|
+
*
|
|
28
|
+
* Fields:
|
|
29
|
+
* - title: Post title (required)
|
|
30
|
+
* - slug: URL-friendly identifier (auto-generated from title)
|
|
31
|
+
* - excerpt: Short summary for listings
|
|
32
|
+
* - content: Main post content (rich text)
|
|
33
|
+
* - featuredImage: Hero image for the post
|
|
34
|
+
* - category: Post category
|
|
35
|
+
* - tags: Post tags for organization
|
|
36
|
+
* - author: Post author reference
|
|
37
|
+
* - publishedAt: Publication date
|
|
38
|
+
* - featured: Whether to feature on homepage
|
|
39
|
+
*/
|
|
40
|
+
export const blogPost = defineContentType({
|
|
41
|
+
name: 'blog-post',
|
|
42
|
+
label: 'Blog Posts',
|
|
43
|
+
singularLabel: 'Blog Post',
|
|
44
|
+
description: 'Blog articles and news posts',
|
|
45
|
+
icon: 'FileText',
|
|
46
|
+
titleField: 'title',
|
|
47
|
+
subtitleField: 'excerpt',
|
|
48
|
+
fields: [
|
|
49
|
+
{
|
|
50
|
+
type: 'text',
|
|
51
|
+
name: 'title',
|
|
52
|
+
label: 'Title',
|
|
53
|
+
description: 'The title of your blog post',
|
|
54
|
+
required: true,
|
|
55
|
+
placeholder: 'Enter post title...',
|
|
56
|
+
minLength: 3,
|
|
57
|
+
maxLength: 200,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
type: 'slug',
|
|
61
|
+
name: 'slug',
|
|
62
|
+
label: 'URL Slug',
|
|
63
|
+
description: 'URL-friendly identifier (auto-generated from title)',
|
|
64
|
+
required: true,
|
|
65
|
+
sourceField: 'title',
|
|
66
|
+
prefix: '/blog/',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
type: 'textarea',
|
|
70
|
+
name: 'excerpt',
|
|
71
|
+
label: 'Excerpt',
|
|
72
|
+
description: 'A short summary shown in post listings',
|
|
73
|
+
placeholder: 'Write a brief summary...',
|
|
74
|
+
rows: 3,
|
|
75
|
+
maxLength: 300,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
type: 'richtext',
|
|
79
|
+
name: 'content',
|
|
80
|
+
label: 'Content',
|
|
81
|
+
description: 'The main content of your blog post',
|
|
82
|
+
required: true,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
type: 'media',
|
|
86
|
+
name: 'featuredImage',
|
|
87
|
+
label: 'Featured Image',
|
|
88
|
+
description: 'Hero image displayed at the top of the post',
|
|
89
|
+
accept: ['image/*'],
|
|
90
|
+
maxSize: 5 * 1024 * 1024, // 5MB
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
type: 'select',
|
|
94
|
+
name: 'category',
|
|
95
|
+
label: 'Category',
|
|
96
|
+
description: 'Select a category for this post',
|
|
97
|
+
placeholder: 'Choose category...',
|
|
98
|
+
options: [
|
|
99
|
+
{ value: 'news', label: 'News' },
|
|
100
|
+
{ value: 'tutorials', label: 'Tutorials' },
|
|
101
|
+
{ value: 'announcements', label: 'Announcements' },
|
|
102
|
+
{ value: 'case-studies', label: 'Case Studies' },
|
|
103
|
+
{ value: 'other', label: 'Other' },
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
type: 'tags',
|
|
108
|
+
name: 'tags',
|
|
109
|
+
label: 'Tags',
|
|
110
|
+
description: 'Add tags to help organize your content',
|
|
111
|
+
placeholder: 'Add a tag...',
|
|
112
|
+
suggestions: ['javascript', 'typescript', 'react', 'aws', 'serverless', 'tutorial'],
|
|
113
|
+
max: 10,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
type: 'datetime',
|
|
117
|
+
name: 'publishedAt',
|
|
118
|
+
label: 'Publish Date',
|
|
119
|
+
description: 'When should this post be published?',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
type: 'boolean',
|
|
123
|
+
name: 'featured',
|
|
124
|
+
label: 'Featured Post',
|
|
125
|
+
description: 'Feature this post on the homepage',
|
|
126
|
+
defaultValue: false,
|
|
127
|
+
},
|
|
128
|
+
] as const,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
export type BlogPostData = {
|
|
132
|
+
id: string;
|
|
133
|
+
status: string;
|
|
134
|
+
title: string;
|
|
135
|
+
slug: string;
|
|
136
|
+
excerpt?: string;
|
|
137
|
+
content: string;
|
|
138
|
+
featuredImage?: string;
|
|
139
|
+
category?: string;
|
|
140
|
+
tags?: string[];
|
|
141
|
+
publishedAt?: string;
|
|
142
|
+
featured: boolean;
|
|
143
|
+
createdAt: string;
|
|
144
|
+
updatedAt: string;
|
|
145
|
+
};
|
|
146
|
+
`;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Generates the page content type template.
|
|
150
|
+
*
|
|
151
|
+
* @returns Template string for app/content-types/page.ts
|
|
152
|
+
*/
|
|
153
|
+
function getPageContentTypeTemplate() {
|
|
154
|
+
return `/**
|
|
155
|
+
* Page Content Type
|
|
156
|
+
*
|
|
157
|
+
* Defines the structure for static pages in the CMS.
|
|
158
|
+
*/
|
|
159
|
+
|
|
160
|
+
import { defineContentType } from '~/lib/content-types';
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Page content type definition.
|
|
164
|
+
*
|
|
165
|
+
* Fields:
|
|
166
|
+
* - title: Page title (required)
|
|
167
|
+
* - slug: URL path (auto-generated from title)
|
|
168
|
+
* - description: SEO description
|
|
169
|
+
* - content: Main page content (rich text)
|
|
170
|
+
* - featuredImage: OG image for social sharing
|
|
171
|
+
* - template: Page template/layout
|
|
172
|
+
* - showInNav: Whether to show in navigation
|
|
173
|
+
* - navOrder: Order in navigation
|
|
174
|
+
*/
|
|
175
|
+
export const page = defineContentType({
|
|
176
|
+
name: 'page',
|
|
177
|
+
label: 'Pages',
|
|
178
|
+
singularLabel: 'Page',
|
|
179
|
+
description: 'Static pages like About, Contact, etc.',
|
|
180
|
+
icon: 'File',
|
|
181
|
+
titleField: 'title',
|
|
182
|
+
fields: [
|
|
183
|
+
{
|
|
184
|
+
type: 'text',
|
|
185
|
+
name: 'title',
|
|
186
|
+
label: 'Title',
|
|
187
|
+
description: 'The page title (shown in browser tab and header)',
|
|
188
|
+
required: true,
|
|
189
|
+
placeholder: 'Enter page title...',
|
|
190
|
+
minLength: 2,
|
|
191
|
+
maxLength: 100,
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
type: 'slug',
|
|
195
|
+
name: 'slug',
|
|
196
|
+
label: 'URL Path',
|
|
197
|
+
description: 'The URL path for this page (e.g., "about" for /about)',
|
|
198
|
+
required: true,
|
|
199
|
+
sourceField: 'title',
|
|
200
|
+
prefix: '/',
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
type: 'textarea',
|
|
204
|
+
name: 'description',
|
|
205
|
+
label: 'Meta Description',
|
|
206
|
+
description: 'SEO description shown in search results',
|
|
207
|
+
placeholder: 'Write a description for search engines...',
|
|
208
|
+
rows: 2,
|
|
209
|
+
maxLength: 160,
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
type: 'richtext',
|
|
213
|
+
name: 'content',
|
|
214
|
+
label: 'Content',
|
|
215
|
+
description: 'The main content of the page',
|
|
216
|
+
required: true,
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
type: 'media',
|
|
220
|
+
name: 'featuredImage',
|
|
221
|
+
label: 'Social Image',
|
|
222
|
+
description: 'Image shown when page is shared on social media',
|
|
223
|
+
accept: ['image/*'],
|
|
224
|
+
maxSize: 2 * 1024 * 1024, // 2MB
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
type: 'select',
|
|
228
|
+
name: 'template',
|
|
229
|
+
label: 'Page Template',
|
|
230
|
+
description: 'Select a layout template for this page',
|
|
231
|
+
placeholder: 'Choose template...',
|
|
232
|
+
options: [
|
|
233
|
+
{ value: 'default', label: 'Default' },
|
|
234
|
+
{ value: 'full-width', label: 'Full Width' },
|
|
235
|
+
{ value: 'sidebar', label: 'With Sidebar' },
|
|
236
|
+
{ value: 'landing', label: 'Landing Page' },
|
|
237
|
+
],
|
|
238
|
+
defaultValue: 'default',
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
type: 'boolean',
|
|
242
|
+
name: 'showInNav',
|
|
243
|
+
label: 'Show in Navigation',
|
|
244
|
+
description: 'Include this page in the main navigation menu',
|
|
245
|
+
defaultValue: false,
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
type: 'number',
|
|
249
|
+
name: 'navOrder',
|
|
250
|
+
label: 'Navigation Order',
|
|
251
|
+
description: 'Order in the navigation menu (lower = earlier)',
|
|
252
|
+
min: 0,
|
|
253
|
+
max: 100,
|
|
254
|
+
defaultValue: 50,
|
|
255
|
+
},
|
|
256
|
+
] as const,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
export type PageData = {
|
|
260
|
+
id: string;
|
|
261
|
+
status: string;
|
|
262
|
+
title: string;
|
|
263
|
+
slug: string;
|
|
264
|
+
description?: string;
|
|
265
|
+
content: string;
|
|
266
|
+
featuredImage?: string;
|
|
267
|
+
template?: string;
|
|
268
|
+
showInNav: boolean;
|
|
269
|
+
navOrder: number;
|
|
270
|
+
createdAt: string;
|
|
271
|
+
updatedAt: string;
|
|
272
|
+
};
|
|
273
|
+
`;
|
|
274
|
+
}
|
|
275
|
+
//# sourceMappingURL=content-types-examples.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-types-examples.js","sourceRoot":"","sources":["../../src/templates/content-types-examples.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAOH,wEAmIC;AAOD,gEAyHC;AAxQD;;;;GAIG;AACH,SAAgB,8BAA8B;IAC5C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiIR,CAAC;AACF,CAAC;AAED;;;;GAIG;AACH,SAAgB,0BAA0B;IACxC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuHR,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates the content types registry template.
|
|
3
|
+
*
|
|
4
|
+
* This template provides a registry for managing content types
|
|
5
|
+
* and provides functions to access them.
|
|
6
|
+
*
|
|
7
|
+
* @returns Template string for app/content-types/index.ts
|
|
8
|
+
*/
|
|
9
|
+
export declare function getContentTypesRegistryTemplate(): string;
|
|
10
|
+
//# sourceMappingURL=content-types-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-types-registry.d.ts","sourceRoot":"","sources":["../../src/templates/content-types-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,+BAA+B,IAAI,MAAM,CA0ExD"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
3
|
+
exports.getContentTypesRegistryTemplate = getContentTypesRegistryTemplate;
|
|
4
|
+
/**
|
|
5
|
+
* Generates the content types registry template.
|
|
6
|
+
*
|
|
7
|
+
* This template provides a registry for managing content types
|
|
8
|
+
* and provides functions to access them.
|
|
9
|
+
*
|
|
10
|
+
* @returns Template string for app/content-types/index.ts
|
|
11
|
+
*/
|
|
12
|
+
function getContentTypesRegistryTemplate() {
|
|
13
|
+
return `/**
|
|
14
|
+
* Content Types Registry
|
|
15
|
+
*
|
|
16
|
+
* This module provides a central registry for all content types
|
|
17
|
+
* and utilities for accessing them.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type { ContentType } from '~/lib/content-types';
|
|
21
|
+
import { blogPost } from './blog-post';
|
|
22
|
+
import { page } from './page';
|
|
23
|
+
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// Registry
|
|
26
|
+
// ============================================================================
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Registry of all content types.
|
|
30
|
+
*/
|
|
31
|
+
const contentTypeRegistry = new Map<string, ContentType>();
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Register a content type in the registry.
|
|
35
|
+
*/
|
|
36
|
+
export function registerContentType(contentType: ContentType): void {
|
|
37
|
+
if (contentTypeRegistry.has(contentType.name)) {
|
|
38
|
+
console.warn(\`Content type "\${contentType.name}" is already registered. Overwriting.\`);
|
|
39
|
+
}
|
|
40
|
+
contentTypeRegistry.set(contentType.name, contentType);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get a content type by name.
|
|
45
|
+
*/
|
|
46
|
+
export function getContentType(name: string): ContentType | undefined {
|
|
47
|
+
return contentTypeRegistry.get(name);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get all registered content types.
|
|
52
|
+
*/
|
|
53
|
+
export function getAllContentTypes(): ContentType[] {
|
|
54
|
+
return Array.from(contentTypeRegistry.values());
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Check if a content type exists.
|
|
59
|
+
*/
|
|
60
|
+
export function hasContentType(name: string): boolean {
|
|
61
|
+
return contentTypeRegistry.has(name);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get content type names.
|
|
66
|
+
*/
|
|
67
|
+
export function getContentTypeNames(): string[] {
|
|
68
|
+
return Array.from(contentTypeRegistry.keys());
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ============================================================================
|
|
72
|
+
// Register Default Content Types
|
|
73
|
+
// ============================================================================
|
|
74
|
+
|
|
75
|
+
// Register the built-in content types
|
|
76
|
+
registerContentType(blogPost);
|
|
77
|
+
registerContentType(page);
|
|
78
|
+
|
|
79
|
+
// ============================================================================
|
|
80
|
+
// Exports
|
|
81
|
+
// ============================================================================
|
|
82
|
+
|
|
83
|
+
export { blogPost } from './blog-post';
|
|
84
|
+
export { page } from './page';
|
|
85
|
+
`;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=content-types-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-types-registry.js","sourceRoot":"","sources":["../../src/templates/content-types-registry.ts"],"names":[],"mappings":";;AAQA,0EA0EC;AAlFD;;;;;;;GAOG;AACH,SAAgB,+BAA+B;IAC7C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwER,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates the content types definition template.
|
|
3
|
+
*
|
|
4
|
+
* This template provides the `defineContentType()` helper function and
|
|
5
|
+
* TypeScript interfaces for defining custom content structures.
|
|
6
|
+
*
|
|
7
|
+
* @returns Template string for app/lib/content-types.ts
|
|
8
|
+
*/
|
|
9
|
+
export declare function getContentTypesTemplate(): string;
|
|
10
|
+
//# sourceMappingURL=content-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-types.d.ts","sourceRoot":"","sources":["../../src/templates/content-types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAmXhD"}
|