create-vexcms 0.0.8 → 0.0.10
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/package.json +1 -1
- package/templates/base-nextjs/convex/vex/globals.ts +82 -0
- package/templates/base-nextjs/convex/vex/versions.ts +66 -6
- package/templates/base-nextjs/package.json +7 -7
- package/templates/base-nextjs/src/app/admin/layout.tsx +29 -11
- package/templates/base-nextjs/src/db/constants/auth.ts +1 -3
- package/templates/base-nextjs/src/vexcms/access.ts +29 -13
- package/templates/base-nextjs/src/vexcms/collections/users.ts +2 -2
- package/templates/base-nextjs/vex.config.ts +2 -0
- package/templates/marketing-site/convex/footers.ts +14 -0
- package/templates/marketing-site/convex/headers.ts +14 -0
- package/templates/marketing-site/convex/siteSettings.ts +14 -0
- package/templates/marketing-site/convex/theme.ts +75 -0
- package/templates/marketing-site/public/favicons/favicon.ico +0 -0
- package/templates/marketing-site/src/app/(frontend)/PageContent.tsx +74 -0
- package/templates/marketing-site/src/app/(frontend)/[slug]/page.tsx +19 -51
- package/templates/marketing-site/src/app/(frontend)/layout.tsx +37 -0
- package/templates/marketing-site/src/app/(frontend)/page.tsx +24 -0
- package/templates/marketing-site/src/app/(frontend)/preview/[slug]/PreviewPageContent.tsx +55 -0
- package/templates/marketing-site/src/app/(frontend)/preview/[slug]/page.tsx +19 -63
- package/templates/marketing-site/src/app/(frontend)/preview/layout.tsx +18 -0
- package/templates/marketing-site/src/components/SiteFooter.tsx +28 -0
- package/templates/marketing-site/src/components/SiteHeader.tsx +28 -0
- package/templates/marketing-site/src/components/ThemeInjector.tsx +128 -0
- package/templates/marketing-site/src/components/ThemeStyle.tsx +104 -0
- package/templates/marketing-site/src/components/admin/IconPickerField.tsx +134 -0
- package/templates/marketing-site/src/components/admin/ThemeImport.tsx +174 -0
- package/templates/marketing-site/src/components/admin/ThemeImportField.tsx +33 -0
- package/templates/marketing-site/src/components/motion-primitives/animated-group.tsx +138 -0
- package/templates/marketing-site/src/components/motion-primitives/text-effect.tsx +292 -0
- package/templates/marketing-site/src/components/ui/accordion.tsx +74 -0
- package/templates/marketing-site/src/lib/colorConvert.ts +171 -0
- package/templates/marketing-site/src/lib/metadata.ts +104 -0
- package/templates/marketing-site/src/vexcms/access.ts +28 -0
- package/templates/marketing-site/src/vexcms/blocks/CTA/config.ts +36 -0
- package/templates/marketing-site/src/vexcms/blocks/CTA/index.tsx +46 -0
- package/templates/marketing-site/src/vexcms/blocks/FAQ/config.ts +70 -0
- package/templates/marketing-site/src/vexcms/blocks/FAQ/index.tsx +71 -0
- package/templates/marketing-site/src/vexcms/blocks/Features/config.ts +61 -0
- package/templates/marketing-site/src/vexcms/blocks/Features/index.tsx +73 -0
- package/templates/marketing-site/src/vexcms/blocks/Footer/config.ts +65 -0
- package/templates/marketing-site/src/vexcms/blocks/Footer/index.tsx +71 -0
- package/templates/marketing-site/src/vexcms/blocks/Header/config.ts +65 -0
- package/templates/marketing-site/src/vexcms/blocks/Header/index.tsx +174 -0
- package/templates/marketing-site/src/vexcms/blocks/Hero/config.ts +52 -0
- package/templates/marketing-site/src/vexcms/blocks/Hero/index.tsx +152 -0
- package/templates/marketing-site/src/vexcms/blocks/HowItWorks/config.ts +68 -0
- package/templates/marketing-site/src/vexcms/blocks/HowItWorks/index.tsx +74 -0
- package/templates/marketing-site/src/vexcms/blocks/Roadmap/config.ts +154 -0
- package/templates/marketing-site/src/vexcms/blocks/Roadmap/index.tsx +122 -0
- package/templates/marketing-site/src/vexcms/blocks/config.ts +40 -0
- package/templates/marketing-site/src/vexcms/blocks/constants.ts +9 -0
- package/templates/marketing-site/src/vexcms/blocks/index.ts +39 -0
- package/templates/marketing-site/src/vexcms/collections/footers.ts +11 -8
- package/templates/marketing-site/src/vexcms/collections/headers.ts +10 -14
- package/templates/marketing-site/src/vexcms/collections/index.ts +0 -1
- package/templates/marketing-site/src/vexcms/collections/pages.ts +30 -4
- package/templates/marketing-site/src/vexcms/collections/themes.ts +107 -14
- package/templates/marketing-site/src/vexcms/globals/index.ts +1 -0
- package/templates/marketing-site/src/vexcms/globals/siteSettings.ts +57 -0
- package/templates/marketing-site/vex.config.ts +5 -5
- package/templates/base-nextjs/src/auth/permissions.ts +0 -102
- package/templates/marketing-site/src/vexcms/collections/site_settings.ts +0 -31
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import type { BlockComponentProps } from "@vexcms/ui"
|
|
4
|
+
|
|
5
|
+
import { icons } from "lucide-react"
|
|
6
|
+
|
|
7
|
+
import { cn } from "~/lib/utils"
|
|
8
|
+
|
|
9
|
+
export { howItWorksBlock } from "./config"
|
|
10
|
+
|
|
11
|
+
function LucideIcon({ name }: { name: string }) {
|
|
12
|
+
const Icon = icons[name as keyof typeof icons]
|
|
13
|
+
if (!Icon) return null
|
|
14
|
+
return <Icon className="size-5" />
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default function HowItWorksBlock({
|
|
18
|
+
block,
|
|
19
|
+
blockStyles,
|
|
20
|
+
}: BlockComponentProps) {
|
|
21
|
+
const { heading, subheading, steps } = block as unknown as {
|
|
22
|
+
heading: string
|
|
23
|
+
subheading?: string
|
|
24
|
+
steps?: Array<{ icon?: string; title: string; description: string }>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<section className={cn("py-16 md:py-32", blockStyles)}>
|
|
29
|
+
<div className="mx-auto max-w-3xl px-6">
|
|
30
|
+
<div className="text-center">
|
|
31
|
+
<h2 className="text-4xl font-semibold text-balance lg:text-5xl">
|
|
32
|
+
{heading}
|
|
33
|
+
</h2>
|
|
34
|
+
{subheading && (
|
|
35
|
+
<p className="text-muted-foreground mt-4 text-balance">
|
|
36
|
+
{subheading}
|
|
37
|
+
</p>
|
|
38
|
+
)}
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<div className="mt-16">
|
|
42
|
+
{(steps ?? []).map((step, index) => {
|
|
43
|
+
const isLast = index === (steps?.length ?? 1) - 1
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div key={index} className="flex flex-row gap-6">
|
|
47
|
+
<div className="flex flex-col items-center">
|
|
48
|
+
<div className="bg-primary text-primary-foreground flex size-10 shrink-0 items-center justify-center rounded-full">
|
|
49
|
+
{step.icon ? (
|
|
50
|
+
<LucideIcon name={step.icon} />
|
|
51
|
+
) : (
|
|
52
|
+
<span className="text-sm font-semibold">
|
|
53
|
+
{index + 1}
|
|
54
|
+
</span>
|
|
55
|
+
)}
|
|
56
|
+
</div>
|
|
57
|
+
{!isLast && (
|
|
58
|
+
<div className="bg-border w-px flex-1" />
|
|
59
|
+
)}
|
|
60
|
+
</div>
|
|
61
|
+
<div className={cn("flex-1", !isLast && "pb-10")}>
|
|
62
|
+
<h3 className="text-lg font-semibold">{step.title}</h3>
|
|
63
|
+
<p className="text-muted-foreground mt-1">
|
|
64
|
+
{step.description}
|
|
65
|
+
</p>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
)
|
|
69
|
+
})}
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</section>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { array, defineBlock, object, select, text } from "@vexcms/core"
|
|
2
|
+
|
|
3
|
+
import { BLOCK_SLUG_ROADMAP } from "../constants"
|
|
4
|
+
|
|
5
|
+
export const roadmapBlock = defineBlock({
|
|
6
|
+
slug: BLOCK_SLUG_ROADMAP,
|
|
7
|
+
label: "Roadmap",
|
|
8
|
+
fields: {
|
|
9
|
+
heading: text({
|
|
10
|
+
label: "Heading",
|
|
11
|
+
required: true,
|
|
12
|
+
defaultValue: "Roadmap",
|
|
13
|
+
}),
|
|
14
|
+
subheading: text({
|
|
15
|
+
label: "Subheading",
|
|
16
|
+
defaultValue:
|
|
17
|
+
"What we've shipped and what's coming next. Vex CMS is actively developed — here's where we're headed.",
|
|
18
|
+
}),
|
|
19
|
+
items: array({
|
|
20
|
+
label: "Roadmap Items",
|
|
21
|
+
required: true,
|
|
22
|
+
items: object({
|
|
23
|
+
fields: {
|
|
24
|
+
feature: text({ label: "Feature Name", required: true }),
|
|
25
|
+
description: text({ label: "Description" }),
|
|
26
|
+
status: select({
|
|
27
|
+
label: "Status",
|
|
28
|
+
required: true,
|
|
29
|
+
options: [
|
|
30
|
+
{ label: "Shipped", value: "shipped" },
|
|
31
|
+
{ label: "Coming Soon", value: "coming-soon" },
|
|
32
|
+
{ label: "Planned", value: "planned" },
|
|
33
|
+
],
|
|
34
|
+
defaultValue: "shipped",
|
|
35
|
+
}),
|
|
36
|
+
},
|
|
37
|
+
}),
|
|
38
|
+
defaultValue: [
|
|
39
|
+
{
|
|
40
|
+
feature: "16 Field Types",
|
|
41
|
+
description:
|
|
42
|
+
"Text, number, select, date, relationship, upload, richtext, blocks, color, tabs, and more.",
|
|
43
|
+
status: "shipped",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
feature: "Admin Panel",
|
|
47
|
+
description:
|
|
48
|
+
"Full-featured admin UI with list views, edit forms, media library, and draft/publish workflow.",
|
|
49
|
+
status: "shipped",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
feature: "Real-Time Queries",
|
|
53
|
+
description:
|
|
54
|
+
"Every query is live via Convex. Content updates appear instantly across all connected clients.",
|
|
55
|
+
status: "shipped",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
feature: "Live Preview",
|
|
59
|
+
description:
|
|
60
|
+
"Side-by-side iframe preview with responsive breakpoints and real-time updates as you edit.",
|
|
61
|
+
status: "shipped",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
feature: "Block System",
|
|
65
|
+
description:
|
|
66
|
+
"Compose pages from reusable content blocks with drag-and-drop reordering and inline editing.",
|
|
67
|
+
status: "shipped",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
feature: "Authentication & RBAC",
|
|
71
|
+
description:
|
|
72
|
+
"Better Auth integration with role-based access control at the document and field level.",
|
|
73
|
+
status: "shipped",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
feature: "Rich Text Editor",
|
|
77
|
+
description:
|
|
78
|
+
"Plate.js-powered editor with media uploads, links, tables, and custom elements.",
|
|
79
|
+
status: "shipped",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
feature: "CLI & Scaffolding",
|
|
83
|
+
description:
|
|
84
|
+
"vex dev with watch/generate, and create-vexcms for instant project setup.",
|
|
85
|
+
status: "shipped",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
feature: "Theme System",
|
|
89
|
+
description:
|
|
90
|
+
"Database-driven themes with light/dark mode, CSS variables, and OKLCH color support.",
|
|
91
|
+
status: "shipped",
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
feature: "Block Styles",
|
|
95
|
+
description:
|
|
96
|
+
"Per-block responsive styling with Tailwind presets — margin, padding, typography, layout, and more.",
|
|
97
|
+
status: "shipped",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
feature: "Content Scheduling",
|
|
101
|
+
description:
|
|
102
|
+
"Set a publishAt timestamp and content goes live automatically via Convex scheduled functions.",
|
|
103
|
+
status: "coming-soon",
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
feature: "Team Management",
|
|
107
|
+
description:
|
|
108
|
+
"Invite users, assign roles, and manage pending invitations from the admin panel.",
|
|
109
|
+
status: "coming-soon",
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
feature: "API Keys",
|
|
113
|
+
description:
|
|
114
|
+
"Read-only API tokens for external integrations with configurable rate limiting.",
|
|
115
|
+
status: "coming-soon",
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
feature: "Audit Log",
|
|
119
|
+
description:
|
|
120
|
+
"Track who changed what and when across all collections and documents.",
|
|
121
|
+
status: "coming-soon",
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
feature: "Environments",
|
|
125
|
+
description:
|
|
126
|
+
"Project-level content branching with staging and production environments and atomic promotion.",
|
|
127
|
+
status: "planned",
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
feature: "Localization",
|
|
131
|
+
description:
|
|
132
|
+
"i18n field variants with per-locale versioning and content translation workflows.",
|
|
133
|
+
status: "planned",
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
feature: "Approval Workflows",
|
|
137
|
+
description:
|
|
138
|
+
"Review and sign-off steps before content goes live. Configurable multi-step approval chains.",
|
|
139
|
+
status: "planned",
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
feature: "Plugin System",
|
|
143
|
+
description:
|
|
144
|
+
"Extend VEX with community plugins for custom fields, integrations, and admin panel features.",
|
|
145
|
+
status: "planned",
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
}),
|
|
149
|
+
},
|
|
150
|
+
admin: {
|
|
151
|
+
icon: "map",
|
|
152
|
+
blockStyles: ["container"],
|
|
153
|
+
},
|
|
154
|
+
})
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import type { BlockComponentProps } from "@vexcms/ui"
|
|
4
|
+
|
|
5
|
+
import { Check, Clock, Compass } from "lucide-react"
|
|
6
|
+
|
|
7
|
+
import { cn } from "~/lib/utils"
|
|
8
|
+
|
|
9
|
+
export { roadmapBlock } from "./config"
|
|
10
|
+
|
|
11
|
+
const statusConfig = {
|
|
12
|
+
shipped: {
|
|
13
|
+
label: "Shipped",
|
|
14
|
+
icon: Check,
|
|
15
|
+
badgeClass:
|
|
16
|
+
"bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-500/20",
|
|
17
|
+
},
|
|
18
|
+
"coming-soon": {
|
|
19
|
+
label: "Coming Soon",
|
|
20
|
+
icon: Clock,
|
|
21
|
+
badgeClass:
|
|
22
|
+
"bg-amber-500/10 text-amber-600 dark:text-amber-400 border-amber-500/20",
|
|
23
|
+
},
|
|
24
|
+
planned: {
|
|
25
|
+
label: "Planned",
|
|
26
|
+
icon: Compass,
|
|
27
|
+
badgeClass:
|
|
28
|
+
"bg-zinc-500/10 text-zinc-600 dark:text-zinc-400 border-zinc-500/20",
|
|
29
|
+
},
|
|
30
|
+
} as const
|
|
31
|
+
|
|
32
|
+
type RoadmapItem = {
|
|
33
|
+
feature: string
|
|
34
|
+
description?: string
|
|
35
|
+
status: "shipped" | "coming-soon" | "planned"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type StatusKey = keyof typeof statusConfig
|
|
39
|
+
|
|
40
|
+
const statusOrder: StatusKey[] = ["shipped", "coming-soon", "planned"]
|
|
41
|
+
|
|
42
|
+
export default function RoadmapBlock({
|
|
43
|
+
block,
|
|
44
|
+
blockStyles,
|
|
45
|
+
}: BlockComponentProps) {
|
|
46
|
+
const { heading, subheading, items } = block as unknown as {
|
|
47
|
+
heading: string
|
|
48
|
+
subheading?: string
|
|
49
|
+
items?: RoadmapItem[]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const grouped = statusOrder
|
|
53
|
+
.map((status) => ({
|
|
54
|
+
status,
|
|
55
|
+
config: statusConfig[status],
|
|
56
|
+
items: (items ?? []).filter(
|
|
57
|
+
(item) => item.status === status && item.status in statusConfig
|
|
58
|
+
),
|
|
59
|
+
}))
|
|
60
|
+
.filter((group) => group.items.length > 0)
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<section className={cn("py-16 md:py-32", blockStyles)}>
|
|
64
|
+
<div className="mx-auto max-w-5xl px-6">
|
|
65
|
+
<div className="text-center">
|
|
66
|
+
<h2 className="text-4xl font-semibold text-balance lg:text-5xl">
|
|
67
|
+
{heading}
|
|
68
|
+
</h2>
|
|
69
|
+
{subheading && (
|
|
70
|
+
<p className="text-muted-foreground mt-4 text-balance">
|
|
71
|
+
{subheading}
|
|
72
|
+
</p>
|
|
73
|
+
)}
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
{grouped.map((group, groupIndex) => {
|
|
77
|
+
const StatusIcon = group.config.icon
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<div key={group.status} className={groupIndex === 0 ? "mt-16" : "mt-12"}>
|
|
81
|
+
<div className="flex items-center gap-2">
|
|
82
|
+
<StatusIcon className="size-5" />
|
|
83
|
+
<h3 className="text-lg font-semibold">{group.config.label}</h3>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div className="mt-6 grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
87
|
+
{group.items.map((item, itemIndex) => {
|
|
88
|
+
const BadgeIcon = group.config.icon
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<div
|
|
92
|
+
key={itemIndex}
|
|
93
|
+
className="bg-card rounded-xl border p-5"
|
|
94
|
+
>
|
|
95
|
+
<div className="flex items-center justify-between gap-4">
|
|
96
|
+
<span className="font-medium">{item.feature}</span>
|
|
97
|
+
<span
|
|
98
|
+
className={cn(
|
|
99
|
+
"inline-flex shrink-0 items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-xs font-medium",
|
|
100
|
+
group.config.badgeClass
|
|
101
|
+
)}
|
|
102
|
+
>
|
|
103
|
+
<BadgeIcon className="size-3" />
|
|
104
|
+
{group.config.label}
|
|
105
|
+
</span>
|
|
106
|
+
</div>
|
|
107
|
+
{item.description && (
|
|
108
|
+
<p className="text-muted-foreground mt-2 text-sm">
|
|
109
|
+
{item.description}
|
|
110
|
+
</p>
|
|
111
|
+
)}
|
|
112
|
+
</div>
|
|
113
|
+
)
|
|
114
|
+
})}
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
)
|
|
118
|
+
})}
|
|
119
|
+
</div>
|
|
120
|
+
</section>
|
|
121
|
+
)
|
|
122
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Block configs only — no React/motion dependencies
|
|
2
|
+
// Safe to import from collections and server-side code
|
|
3
|
+
import { heroBlock } from "./Hero/config"
|
|
4
|
+
import { featuresBlock } from "./Features/config"
|
|
5
|
+
import { ctaBlock } from "./CTA/config"
|
|
6
|
+
import { faqBlock } from "./FAQ/config"
|
|
7
|
+
import { headerBlock } from "./Header/config"
|
|
8
|
+
import { footerBlock } from "./Footer/config"
|
|
9
|
+
import { howItWorksBlock } from "./HowItWorks/config"
|
|
10
|
+
import { roadmapBlock } from "./Roadmap/config"
|
|
11
|
+
|
|
12
|
+
/** Page content block definitions */
|
|
13
|
+
export const pageBlocks = [
|
|
14
|
+
heroBlock,
|
|
15
|
+
featuresBlock,
|
|
16
|
+
howItWorksBlock,
|
|
17
|
+
roadmapBlock,
|
|
18
|
+
ctaBlock,
|
|
19
|
+
faqBlock,
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
/** Header block definitions */
|
|
23
|
+
export const headerBlocks = [headerBlock]
|
|
24
|
+
|
|
25
|
+
/** Footer block definitions */
|
|
26
|
+
export const footerBlocks = [footerBlock]
|
|
27
|
+
|
|
28
|
+
/** All block definitions combined */
|
|
29
|
+
export const allBlocks = [...pageBlocks, ...headerBlocks, ...footerBlocks]
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
heroBlock,
|
|
33
|
+
featuresBlock,
|
|
34
|
+
ctaBlock,
|
|
35
|
+
faqBlock,
|
|
36
|
+
headerBlock,
|
|
37
|
+
footerBlock,
|
|
38
|
+
howItWorksBlock,
|
|
39
|
+
roadmapBlock,
|
|
40
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Block slugs
|
|
2
|
+
export const BLOCK_SLUG_HERO = "hero" as const
|
|
3
|
+
export const BLOCK_SLUG_FEATURES = "features" as const
|
|
4
|
+
export const BLOCK_SLUG_CTA = "cta" as const
|
|
5
|
+
export const BLOCK_SLUG_FAQ = "faq" as const
|
|
6
|
+
export const BLOCK_SLUG_HEADER = "header" as const
|
|
7
|
+
export const BLOCK_SLUG_FOOTER = "footer" as const
|
|
8
|
+
export const BLOCK_SLUG_HOW_IT_WORKS = "how_it_works" as const
|
|
9
|
+
export const BLOCK_SLUG_ROADMAP = "roadmap" as const
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { BlockComponentProps } from "@vexcms/ui"
|
|
2
|
+
import type { ComponentType } from "react"
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
BLOCK_SLUG_CTA,
|
|
6
|
+
BLOCK_SLUG_FAQ,
|
|
7
|
+
BLOCK_SLUG_FEATURES,
|
|
8
|
+
BLOCK_SLUG_FOOTER,
|
|
9
|
+
BLOCK_SLUG_HEADER,
|
|
10
|
+
BLOCK_SLUG_HERO,
|
|
11
|
+
BLOCK_SLUG_HOW_IT_WORKS,
|
|
12
|
+
BLOCK_SLUG_ROADMAP,
|
|
13
|
+
} from "./constants"
|
|
14
|
+
import CTABlock from "./CTA"
|
|
15
|
+
import FAQBlock from "./FAQ"
|
|
16
|
+
import FeaturesBlock from "./Features"
|
|
17
|
+
import FooterBlock from "./Footer"
|
|
18
|
+
import HeaderBlock from "./Header"
|
|
19
|
+
import HeroBlock from "./Hero"
|
|
20
|
+
import HowItWorksBlock from "./HowItWorks"
|
|
21
|
+
import RoadmapBlock from "./Roadmap"
|
|
22
|
+
|
|
23
|
+
/** Block component map for use with RenderBlocks */
|
|
24
|
+
export const blockComponents: Record<
|
|
25
|
+
string,
|
|
26
|
+
ComponentType<BlockComponentProps>
|
|
27
|
+
> = {
|
|
28
|
+
[BLOCK_SLUG_CTA]: CTABlock,
|
|
29
|
+
[BLOCK_SLUG_FAQ]: FAQBlock,
|
|
30
|
+
[BLOCK_SLUG_FEATURES]: FeaturesBlock,
|
|
31
|
+
[BLOCK_SLUG_FOOTER]: FooterBlock,
|
|
32
|
+
[BLOCK_SLUG_HEADER]: HeaderBlock,
|
|
33
|
+
[BLOCK_SLUG_HERO]: HeroBlock,
|
|
34
|
+
[BLOCK_SLUG_HOW_IT_WORKS]: HowItWorksBlock,
|
|
35
|
+
[BLOCK_SLUG_ROADMAP]: RoadmapBlock,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Re-export configs for convenience in client code
|
|
39
|
+
export { allBlocks, footerBlocks, headerBlocks, pageBlocks } from "./config"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { blocks, defineCollection, text } from "@vexcms/core"
|
|
2
2
|
|
|
3
|
-
import { TABLE_SLUG_FOOTERS
|
|
3
|
+
import { TABLE_SLUG_FOOTERS } from "~/db/constants"
|
|
4
|
+
import { footerBlocks } from "~/vexcms/blocks/config"
|
|
4
5
|
|
|
5
6
|
export const footers = defineCollection({
|
|
6
7
|
slug: TABLE_SLUG_FOOTERS,
|
|
@@ -13,12 +14,14 @@ export const footers = defineCollection({
|
|
|
13
14
|
label: "Name",
|
|
14
15
|
required: true,
|
|
15
16
|
}),
|
|
16
|
-
content:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
content: blocks({
|
|
18
|
+
blocks: footerBlocks,
|
|
19
|
+
label: "Footer",
|
|
20
|
+
max: 1,
|
|
21
|
+
labels: {
|
|
22
|
+
singular: "Footer",
|
|
23
|
+
plural: "Footers",
|
|
24
|
+
},
|
|
22
25
|
}),
|
|
23
26
|
},
|
|
24
27
|
labels: {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { blocks, defineCollection, text } from "@vexcms/core"
|
|
2
2
|
|
|
3
|
-
import { TABLE_SLUG_HEADERS
|
|
3
|
+
import { TABLE_SLUG_HEADERS } from "~/db/constants"
|
|
4
|
+
import { headerBlocks } from "~/vexcms/blocks/config"
|
|
4
5
|
|
|
5
6
|
export const headers = defineCollection({
|
|
6
7
|
slug: TABLE_SLUG_HEADERS,
|
|
@@ -13,19 +14,14 @@ export const headers = defineCollection({
|
|
|
13
14
|
label: "Name",
|
|
14
15
|
required: true,
|
|
15
16
|
}),
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
content: blocks({
|
|
18
|
+
blocks: headerBlocks,
|
|
19
|
+
label: "Header",
|
|
20
|
+
max: 1,
|
|
21
|
+
labels: {
|
|
22
|
+
singular: "Header",
|
|
23
|
+
plural: "Headers",
|
|
22
24
|
},
|
|
23
|
-
label: "Logo URL",
|
|
24
|
-
to: TABLE_SLUG_MEDIA,
|
|
25
|
-
}),
|
|
26
|
-
sticky: checkbox({
|
|
27
|
-
defaultValue: false,
|
|
28
|
-
label: "Sticky Header",
|
|
29
25
|
}),
|
|
30
26
|
},
|
|
31
27
|
labels: {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { defineCollection,
|
|
1
|
+
import { blocks, defineCollection, imageUrl, text } from "@vexcms/core"
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { TABLE_SLUG_PAGES } from "~/db/constants"
|
|
4
|
+
import { pageBlocks } from "~/vexcms/blocks/config"
|
|
4
5
|
|
|
5
6
|
export const pages = defineCollection({
|
|
6
7
|
slug: TABLE_SLUG_PAGES,
|
|
@@ -25,9 +26,34 @@ export const pages = defineCollection({
|
|
|
25
26
|
label: "Slug",
|
|
26
27
|
required: true,
|
|
27
28
|
}),
|
|
28
|
-
content:
|
|
29
|
+
content: blocks({
|
|
30
|
+
blocks: pageBlocks,
|
|
29
31
|
label: "Content",
|
|
30
|
-
|
|
32
|
+
labels: {
|
|
33
|
+
singular: "Block",
|
|
34
|
+
plural: "Blocks",
|
|
35
|
+
},
|
|
36
|
+
}),
|
|
37
|
+
metaTitle: text({
|
|
38
|
+
label: "Meta Title",
|
|
39
|
+
admin: {
|
|
40
|
+
description: "Custom <title> tag. Falls back to page title if empty.",
|
|
41
|
+
position: "sidebar",
|
|
42
|
+
},
|
|
43
|
+
}),
|
|
44
|
+
metaDescription: text({
|
|
45
|
+
label: "Meta Description",
|
|
46
|
+
admin: {
|
|
47
|
+
description: "Custom meta description for search results.",
|
|
48
|
+
position: "sidebar",
|
|
49
|
+
},
|
|
50
|
+
}),
|
|
51
|
+
ogImage: imageUrl({
|
|
52
|
+
label: "OG Image",
|
|
53
|
+
admin: {
|
|
54
|
+
description: "Custom Open Graph image URL for social sharing.",
|
|
55
|
+
position: "sidebar",
|
|
56
|
+
},
|
|
31
57
|
}),
|
|
32
58
|
},
|
|
33
59
|
labels: {
|