create-vexcms 0.0.3

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.
Files changed (116) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +2409 -0
  3. package/package.json +37 -0
  4. package/templates/base-nextjs/.prettierignore +26 -0
  5. package/templates/base-nextjs/.prettierrc +7 -0
  6. package/templates/base-nextjs/README.md +256 -0
  7. package/templates/base-nextjs/_gitignore +2 -0
  8. package/templates/base-nextjs/components.json +24 -0
  9. package/templates/base-nextjs/convex/_generated/api.d.ts +125 -0
  10. package/templates/base-nextjs/convex/_generated/api.js +23 -0
  11. package/templates/base-nextjs/convex/_generated/dataModel.d.ts +60 -0
  12. package/templates/base-nextjs/convex/_generated/server.d.ts +143 -0
  13. package/templates/base-nextjs/convex/_generated/server.js +93 -0
  14. package/templates/base-nextjs/convex/auth/adapter/index.ts +230 -0
  15. package/templates/base-nextjs/convex/auth/adapter/utils.ts +547 -0
  16. package/templates/base-nextjs/convex/auth/api.ts +8 -0
  17. package/templates/base-nextjs/convex/auth/config.ts +10 -0
  18. package/templates/base-nextjs/convex/auth/db.ts +303 -0
  19. package/templates/base-nextjs/convex/auth/index.ts +14 -0
  20. package/templates/base-nextjs/convex/auth/options.ts +63 -0
  21. package/templates/base-nextjs/convex/auth/plugins/index.ts +20 -0
  22. package/templates/base-nextjs/convex/auth/sessions.ts +60 -0
  23. package/templates/base-nextjs/convex/auth.config.ts +7 -0
  24. package/templates/base-nextjs/convex/convex.config.ts +5 -0
  25. package/templates/base-nextjs/convex/http.ts +28 -0
  26. package/templates/base-nextjs/convex/schema.ts +3 -0
  27. package/templates/base-nextjs/convex/vex/auth.ts +38 -0
  28. package/templates/base-nextjs/convex/vex/collections.ts +321 -0
  29. package/templates/base-nextjs/convex/vex/firstUser.ts +134 -0
  30. package/templates/base-nextjs/convex/vex/helpers.ts +33 -0
  31. package/templates/base-nextjs/convex/vex/impersonation.ts +51 -0
  32. package/templates/base-nextjs/convex/vex/media.ts +246 -0
  33. package/templates/base-nextjs/convex/vex/migrate.ts +74 -0
  34. package/templates/base-nextjs/convex/vex/model/collections.ts +196 -0
  35. package/templates/base-nextjs/convex/vex/model/media.ts +87 -0
  36. package/templates/base-nextjs/convex/vex/model/versions.ts +254 -0
  37. package/templates/base-nextjs/convex/vex/previewSnapshot.ts +53 -0
  38. package/templates/base-nextjs/convex/vex/versions.ts +588 -0
  39. package/templates/base-nextjs/eslint.config.mjs +164 -0
  40. package/templates/base-nextjs/next.config.ts +10 -0
  41. package/templates/base-nextjs/package.json +67 -0
  42. package/templates/base-nextjs/postcss.config.mjs +7 -0
  43. package/templates/base-nextjs/public/favicons/favicon.ico +0 -0
  44. package/templates/base-nextjs/public/file.svg +1 -0
  45. package/templates/base-nextjs/public/globe.svg +1 -0
  46. package/templates/base-nextjs/public/next.svg +1 -0
  47. package/templates/base-nextjs/public/vercel.svg +1 -0
  48. package/templates/base-nextjs/public/window.svg +1 -0
  49. package/templates/base-nextjs/src/app/(frontend)/@auth/(...)auth/[pathname]/page.tsx +9 -0
  50. package/templates/base-nextjs/src/app/(frontend)/@auth/(...)auth/[pathname]/view.tsx +23 -0
  51. package/templates/base-nextjs/src/app/(frontend)/@auth/default.tsx +3 -0
  52. package/templates/base-nextjs/src/app/(frontend)/@auth/page.tsx +3 -0
  53. package/templates/base-nextjs/src/app/(frontend)/auth/[pathname]/page.tsx +9 -0
  54. package/templates/base-nextjs/src/app/(frontend)/auth/[pathname]/view.tsx +11 -0
  55. package/templates/base-nextjs/src/app/(frontend)/layout.tsx +14 -0
  56. package/templates/base-nextjs/src/app/(frontend)/page.tsx +116 -0
  57. package/templates/base-nextjs/src/app/admin/AdminLayoutWrapper.tsx +33 -0
  58. package/templates/base-nextjs/src/app/admin/AdminPageWrapper.tsx +38 -0
  59. package/templates/base-nextjs/src/app/admin/RichTextFieldWithMedia.tsx +101 -0
  60. package/templates/base-nextjs/src/app/admin/[[...path]]/page.tsx +15 -0
  61. package/templates/base-nextjs/src/app/admin/layout.tsx +33 -0
  62. package/templates/base-nextjs/src/app/api/auth/[...all]/route.ts +6 -0
  63. package/templates/base-nextjs/src/app/favicon.ico +0 -0
  64. package/templates/base-nextjs/src/app/globals.css +130 -0
  65. package/templates/base-nextjs/src/app/layout.tsx +30 -0
  66. package/templates/base-nextjs/src/auth/client.tsx +41 -0
  67. package/templates/base-nextjs/src/auth/permissions.ts +102 -0
  68. package/templates/base-nextjs/src/auth/server.ts +16 -0
  69. package/templates/base-nextjs/src/auth/serverUtils.ts +64 -0
  70. package/templates/base-nextjs/src/auth/types.ts +3 -0
  71. package/templates/base-nextjs/src/components/component-example.tsx +474 -0
  72. package/templates/base-nextjs/src/components/example.tsx +52 -0
  73. package/templates/base-nextjs/src/components/providers/client.tsx +9 -0
  74. package/templates/base-nextjs/src/components/providers/convex.tsx +32 -0
  75. package/templates/base-nextjs/src/components/providers/server.tsx +15 -0
  76. package/templates/base-nextjs/src/components/providers/theme.tsx +11 -0
  77. package/templates/base-nextjs/src/components/ui/alert-dialog.tsx +162 -0
  78. package/templates/base-nextjs/src/components/ui/badge.tsx +52 -0
  79. package/templates/base-nextjs/src/components/ui/button.tsx +60 -0
  80. package/templates/base-nextjs/src/components/ui/card.tsx +92 -0
  81. package/templates/base-nextjs/src/components/ui/combobox.tsx +271 -0
  82. package/templates/base-nextjs/src/components/ui/dialog.tsx +135 -0
  83. package/templates/base-nextjs/src/components/ui/dropdown-menu.tsx +246 -0
  84. package/templates/base-nextjs/src/components/ui/field.tsx +224 -0
  85. package/templates/base-nextjs/src/components/ui/input-group.tsx +146 -0
  86. package/templates/base-nextjs/src/components/ui/input.tsx +20 -0
  87. package/templates/base-nextjs/src/components/ui/label.tsx +20 -0
  88. package/templates/base-nextjs/src/components/ui/select.tsx +189 -0
  89. package/templates/base-nextjs/src/components/ui/separator.tsx +21 -0
  90. package/templates/base-nextjs/src/components/ui/textarea.tsx +18 -0
  91. package/templates/base-nextjs/src/components/ui/theme-toggle.tsx +67 -0
  92. package/templates/base-nextjs/src/db/constants/auth.ts +8 -0
  93. package/templates/base-nextjs/src/db/constants/index.ts +44 -0
  94. package/templates/base-nextjs/src/db/types.ts +6 -0
  95. package/templates/base-nextjs/src/env.mjs +48 -0
  96. package/templates/base-nextjs/src/lib/utils.ts +6 -0
  97. package/templates/base-nextjs/src/proxy.ts +23 -0
  98. package/templates/base-nextjs/src/vexcms/access.ts +28 -0
  99. package/templates/base-nextjs/src/vexcms/auth.ts +4 -0
  100. package/templates/base-nextjs/src/vexcms/collections/index.ts +1 -0
  101. package/templates/base-nextjs/src/vexcms/collections/media.ts +15 -0
  102. package/templates/base-nextjs/src/vexcms/collections/users.ts +46 -0
  103. package/templates/base-nextjs/tsconfig.json +60 -0
  104. package/templates/base-nextjs/vex.config.ts +23 -0
  105. package/templates/marketing-site/convex/pages.ts +46 -0
  106. package/templates/marketing-site/src/app/(frontend)/[slug]/page.tsx +58 -0
  107. package/templates/marketing-site/src/app/(frontend)/preview/[slug]/page.tsx +69 -0
  108. package/templates/marketing-site/src/app/admin/AdminLayoutWrapper.tsx +71 -0
  109. package/templates/marketing-site/src/db/constants/index.ts +52 -0
  110. package/templates/marketing-site/src/vexcms/collections/footers.ts +28 -0
  111. package/templates/marketing-site/src/vexcms/collections/headers.ts +35 -0
  112. package/templates/marketing-site/src/vexcms/collections/index.ts +7 -0
  113. package/templates/marketing-site/src/vexcms/collections/pages.ts +40 -0
  114. package/templates/marketing-site/src/vexcms/collections/site_settings.ts +31 -0
  115. package/templates/marketing-site/src/vexcms/collections/themes.ts +37 -0
  116. package/templates/marketing-site/vex.config.ts +32 -0
@@ -0,0 +1,46 @@
1
+ import { checkbox, defineCollection, imageUrl, select, text } from "@vexcms/core"
2
+
3
+ import { TABLE_SLUG_USERS } from "~/db/constants"
4
+ import { auth } from "~/vexcms/auth"
5
+
6
+ export const users = defineCollection({
7
+ slug: TABLE_SLUG_USERS,
8
+ admin: {
9
+ defaultColumns: ["name", "email", "createdAt", "role"],
10
+ group: "Admin",
11
+ useAsTitle: "name",
12
+ },
13
+ auth,
14
+ fields: {
15
+ name: text({
16
+ label: "Name",
17
+ required: true,
18
+ }),
19
+ image: imageUrl({
20
+ label: "Image",
21
+ }),
22
+ role: select({
23
+ defaultValue: "user",
24
+ hasMany: true,
25
+ labels: {
26
+ plural: "Roles",
27
+ singular: "Role",
28
+ },
29
+ options: [
30
+ { badgeColor: "#ff0000", label: "Admin", value: "admin" },
31
+ { label: "Editor", value: "editor" },
32
+ { badgeColor: "#333333", label: "User", value: "user" },
33
+ ],
34
+ required: true,
35
+ }),
36
+ vex_onboarding_complete: checkbox({
37
+ admin: { hidden: true },
38
+ defaultValue: false,
39
+ label: "Onboarding Complete",
40
+ }),
41
+ },
42
+ labels: {
43
+ plural: "Users",
44
+ singular: "User",
45
+ },
46
+ })
@@ -0,0 +1,60 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2017",
4
+ "lib": [
5
+ "dom",
6
+ "dom.iterable",
7
+ "esnext"
8
+ ],
9
+ "allowJs": true,
10
+ "skipLibCheck": true,
11
+ "strict": true,
12
+ "noEmit": true,
13
+ "esModuleInterop": true,
14
+ "module": "esnext",
15
+ "moduleResolution": "bundler",
16
+ "resolveJsonModule": true,
17
+ "isolatedModules": true,
18
+ "jsx": "react-jsx",
19
+ "incremental": true,
20
+ "plugins": [
21
+ {
22
+ "name": "next"
23
+ }
24
+ ],
25
+ "paths": {
26
+ "~/*": [
27
+ "./src/*"
28
+ ],
29
+ "@convex/*": [
30
+ "./convex/*"
31
+ ],
32
+ "@vexcms/core": [
33
+ "../../packages/core/src/index.ts"
34
+ ],
35
+ "@vexcms/ui": [
36
+ "../../packages/ui/src/index.ts"
37
+ ],
38
+ "@vexcms/admin-next": [
39
+ "../../packages/admin-next/src/index.ts"
40
+ ],
41
+ "@vexcms/better-auth": [
42
+ "../../packages/better-auth/src/index.ts"
43
+ ],
44
+ "@vexcms/file-storage-convex": [
45
+ "../../packages/file-storage-convex/src/index.ts"
46
+ ]
47
+ }
48
+ },
49
+ "include": [
50
+ "next-env.d.ts",
51
+ "**/*.ts",
52
+ "**/*.tsx",
53
+ ".next/types/**/*.ts",
54
+ ".next/dev/types/**/*.ts",
55
+ "**/*.mts"
56
+ ],
57
+ "exclude": [
58
+ "node_modules"
59
+ ]
60
+ }
@@ -0,0 +1,23 @@
1
+ import { defineConfig } from "@vexcms/core"
2
+ import { convexFileStorage } from "@vexcms/file-storage-convex"
3
+
4
+ import { auth } from "~/vexcms/auth"
5
+
6
+ export default defineConfig({
7
+ admin: {
8
+ meta: {
9
+ titleSuffix: " | Admin",
10
+ },
11
+ sidebar: {
12
+ hideGlobals: true,
13
+ },
14
+ user: "user",
15
+ },
16
+ auth,
17
+ basePath: "/admin",
18
+ collections: [],
19
+ media: {
20
+ collections: [],
21
+ storageAdapter: convexFileStorage(),
22
+ },
23
+ })
@@ -0,0 +1,46 @@
1
+ import { getPreviewSnapshot } from "@vexcms/core"
2
+ import { v } from "convex/values"
3
+
4
+ import { TABLE_SLUG_PAGES } from "~/db/constants"
5
+
6
+ import { vexQuery } from "./vex/helpers"
7
+
8
+ /**
9
+ * Get a published page by slug.
10
+ * Returns null if the page doesn't exist or isn't published.
11
+ *
12
+ * Uses vexQuery which automatically handles _vexDrafts arg
13
+ * and provides ctx.drafts for draft-aware queries.
14
+ */
15
+ export const getBySlug = vexQuery({
16
+ args: { slug: v.string() },
17
+ handler: async (ctx, args) => {
18
+ const page = await ctx.db
19
+ .query(TABLE_SLUG_PAGES)
20
+ .withIndex("by_slug", (q) => q.eq("slug", args.slug))
21
+ .first()
22
+
23
+ if (!page) {
24
+ return null
25
+ }
26
+
27
+ // On the public route, only return published pages
28
+ if (ctx.drafts === false && page.vex_status !== "published") {
29
+ return null
30
+ }
31
+
32
+ // For preview/snapshot mode, merge the draft snapshot
33
+ if (ctx.drafts === "snapshot") {
34
+ const snapshot = await getPreviewSnapshot({
35
+ collection: TABLE_SLUG_PAGES,
36
+ ctx,
37
+ documentId: page._id,
38
+ })
39
+ if (snapshot) {
40
+ return { ...page, ...snapshot }
41
+ }
42
+ }
43
+
44
+ return page
45
+ },
46
+ })
@@ -0,0 +1,58 @@
1
+ "use client"
2
+
3
+ import type { RichTextDocument } from "@vexcms/core"
4
+
5
+ import { api } from "@convex/_generated/api"
6
+ import { RichText } from "@vexcms/richtext/render"
7
+ import { useQuery } from "convex/react"
8
+ import Link from "next/link"
9
+ import { useParams } from "next/navigation"
10
+
11
+ /**
12
+ * Public page route — renders published pages by slug.
13
+ * Passes _vexDrafts: false to only get published content.
14
+ */
15
+ export default function PublicPage() {
16
+ const { slug } = useParams<{ slug: string }>()
17
+
18
+ const page = useQuery(api.pages.getBySlug, {
19
+ slug,
20
+ _vexDrafts: false,
21
+ })
22
+
23
+ if (page === undefined) {
24
+ return (
25
+ <div className="mx-auto max-w-3xl px-4 py-12">
26
+ <p className="text-muted-foreground">Loading…</p>
27
+ </div>
28
+ )
29
+ }
30
+
31
+ if (page === null) {
32
+ return (
33
+ <div className="mx-auto max-w-3xl px-4 py-12">
34
+ <h1 className="text-2xl font-bold">Page not found</h1>
35
+ <p className="mt-2 text-muted-foreground">
36
+ The page &ldquo;{slug}&rdquo; doesn&apos;t exist or hasn&apos;t been published yet.
37
+ </p>
38
+ <Link className="mt-4 inline-block text-sm text-primary hover:underline" href="/">
39
+ ← Back to home
40
+ </Link>
41
+ </div>
42
+ )
43
+ }
44
+
45
+ return (
46
+ <div className="mx-auto max-w-3xl px-4 py-12">
47
+ <h1 className="text-3xl font-bold mb-6">{page.title ?? "Untitled"}</h1>
48
+
49
+ {page.content ? (
50
+ <div className="prose prose-lg max-w-none">
51
+ <RichText content={page.content as RichTextDocument} />
52
+ </div>
53
+ ) : (
54
+ <p className="text-muted-foreground">This page has no content yet.</p>
55
+ )}
56
+ </div>
57
+ )
58
+ }
@@ -0,0 +1,69 @@
1
+ "use client"
2
+
3
+ import type { RichTextDocument } from "@vexcms/core"
4
+
5
+ import { api } from "@convex/_generated/api"
6
+ import { RichText } from "@vexcms/richtext/render"
7
+ import { useVexPreview } from "@vexcms/ui"
8
+ import { useQuery } from "convex/react"
9
+ import { useParams } from "next/navigation"
10
+
11
+ /**
12
+ * Preview page route — renders draft pages for live preview.
13
+ * Used by the admin panel's live preview iframe.
14
+ *
15
+ * Uses the same getBySlug query but with _vexDrafts: "snapshot"
16
+ * so the draft snapshot is automatically merged onto the document.
17
+ */
18
+ export default function PreviewPage() {
19
+ const { slug } = useParams<{ slug: string }>()
20
+
21
+ const page = useQuery(api.pages.getBySlug, {
22
+ slug,
23
+ _vexDrafts: "snapshot",
24
+ })
25
+
26
+ // Notify admin panel's live preview when data changes
27
+ useVexPreview({ data: page })
28
+
29
+ if (page === undefined) {
30
+ return (
31
+ <div className="mx-auto max-w-3xl px-4 py-12">
32
+ <div className="flex items-center gap-2">
33
+ <div className="h-2 w-2 rounded-full bg-yellow-500 animate-pulse" />
34
+ <p className="text-muted-foreground">Loading preview…</p>
35
+ </div>
36
+ </div>
37
+ )
38
+ }
39
+
40
+ if (page === null) {
41
+ return (
42
+ <div className="mx-auto max-w-3xl px-4 py-12">
43
+ <h1 className="text-2xl font-bold">Preview not found</h1>
44
+ <p className="mt-2 text-muted-foreground">
45
+ No page with slug &ldquo;{slug}&rdquo; exists.
46
+ </p>
47
+ </div>
48
+ )
49
+ }
50
+
51
+ return (
52
+ <div className="mx-auto max-w-3xl px-4 py-12">
53
+ {/* Preview banner */}
54
+ <div className="mb-6 rounded-md border border-yellow-200 bg-yellow-50 px-4 py-2 text-sm text-yellow-800">
55
+ Preview Mode — This page may not be published yet.
56
+ </div>
57
+
58
+ <h1 className="text-3xl font-bold mb-6">{page.title ?? "Untitled"}</h1>
59
+
60
+ {page.content ? (
61
+ <div className="prose prose-lg max-w-none">
62
+ <RichText content={page.content as RichTextDocument} />
63
+ </div>
64
+ ) : (
65
+ <p className="text-muted-foreground">This page has no content yet.</p>
66
+ )}
67
+ </div>
68
+ )
69
+ }
@@ -0,0 +1,71 @@
1
+ "use client"
2
+
3
+ import { useCallback } from "react"
4
+ import { useMutation, useQuery } from "convex/react"
5
+ import { useRouter } from "next/navigation"
6
+ import { AdminLayout, OnboardingTour, useOnboardingTour } from "@vexcms/admin-next"
7
+ import type { PermissionUser } from "@vexcms/admin-next"
8
+ import type { ClientVexConfig } from "@vexcms/core"
9
+ import { api } from "@convex/_generated/api"
10
+ import { access } from "~/vexcms/access"
11
+
12
+ /**
13
+ * Client wrapper that provides the access config directly (not through RSC serialization).
14
+ * Includes the onboarding tour for the marketing site template.
15
+ */
16
+ export function AdminLayoutWrapper({
17
+ config,
18
+ user,
19
+ permissionUser,
20
+ children,
21
+ }: {
22
+ config: ClientVexConfig
23
+ user?: { name: string; email: string; avatar?: string }
24
+ permissionUser?: PermissionUser
25
+ children: React.ReactNode
26
+ }) {
27
+ const router = useRouter()
28
+ const onboardingStatus = useQuery(api.vex.firstUser.getOnboardingStatus)
29
+ const completeOnboarding = useMutation(api.vex.firstUser.completeOnboarding)
30
+ const resetOnboarding = useMutation(api.vex.firstUser.resetOnboarding)
31
+
32
+ const handleComplete = useCallback(() => {
33
+ completeOnboarding()
34
+ }, [completeOnboarding])
35
+
36
+ const handleReset = useCallback(() => {
37
+ resetOnboarding()
38
+ }, [resetOnboarding])
39
+
40
+ const navigate = useCallback((path: string) => {
41
+ router.push(path)
42
+ }, [router])
43
+
44
+ const { showTour, completeTour } = useOnboardingTour({
45
+ config,
46
+ isComplete: onboardingStatus?.complete ?? true,
47
+ onComplete: handleComplete,
48
+ onReset: handleReset,
49
+ })
50
+
51
+ // Use the first collection as the example for the tour
52
+ const exampleCollection = config.collections[0]?.slug
53
+
54
+ return (
55
+ <AdminLayout
56
+ config={config}
57
+ user={user}
58
+ permissionUser={permissionUser}
59
+ access={access}
60
+ >
61
+ {children}
62
+ <OnboardingTour
63
+ active={showTour}
64
+ onComplete={completeTour}
65
+ basePath={config.basePath}
66
+ exampleCollection={exampleCollection}
67
+ navigate={navigate}
68
+ />
69
+ </AdminLayout>
70
+ )
71
+ }
@@ -0,0 +1,52 @@
1
+ export * from "./auth"
2
+
3
+ // Better Auth
4
+ export const TABLE_SLUG_USERS = "user" as const
5
+ export const TABLE_SLUG_ACCOUNTS = "account" as const
6
+ export const TABLE_SLUG_SESSIONS = "session" as const
7
+ export const TABLE_SLUG_VERIFICATIONS = "verification" as const
8
+ export const TABLE_SLUG_JWKS = "jwks" as const
9
+
10
+ // Media
11
+ export const TABLE_SLUG_MEDIA = "media" as const
12
+
13
+ // Site Builder
14
+ export const TABLE_SLUG_PAGES = "pages" as const
15
+ export const TABLE_SLUG_HEADERS = "headers" as const
16
+ export const TABLE_SLUG_FOOTERS = "footers" as const
17
+ export const TABLE_SLUG_THEMES = "themes" as const
18
+ export const TABLE_SLUG_SITE_SETTINGS = "site_settings" as const
19
+
20
+ export const AUTH_PROVIDERS = {
21
+ apple: "apple",
22
+ atlassian: "atlassian",
23
+ cognito: "cognito",
24
+ discord: "discord",
25
+ dropbox: "dropbox",
26
+ facebook: "facebook",
27
+ figma: "figma",
28
+ github: "github",
29
+ gitlab: "gitlab",
30
+ google: "google",
31
+ huggingface: "huggingface",
32
+ kakao: "kakao",
33
+ kick: "kick",
34
+ line: "line",
35
+ linear: "linear",
36
+ linkedin: "linkedin",
37
+ microsoft: "microsoft",
38
+ naver: "naver",
39
+ notion: "notion",
40
+ paypal: "paypal",
41
+ reddit: "reddit",
42
+ roblox: "roblox",
43
+ salesforce: "salesforce",
44
+ slack: "slack",
45
+ spotify: "spotify",
46
+ tiktok: "tiktok",
47
+ twitch: "twitch",
48
+ twitter: "twitter",
49
+ vk: "vk",
50
+ zoom: "zoom",
51
+ } as const
52
+ export type AuthProvider = (typeof AUTH_PROVIDERS)[keyof typeof AUTH_PROVIDERS]
@@ -0,0 +1,28 @@
1
+ import { defineCollection, richtext, text } from "@vexcms/core"
2
+
3
+ import { TABLE_SLUG_FOOTERS, TABLE_SLUG_MEDIA } from "~/db/constants"
4
+
5
+ export const footers = defineCollection({
6
+ slug: TABLE_SLUG_FOOTERS,
7
+ admin: {
8
+ group: "Site Builder",
9
+ useAsTitle: "name",
10
+ },
11
+ fields: {
12
+ name: text({
13
+ label: "Name",
14
+ required: true,
15
+ }),
16
+ content: richtext({
17
+ label: "Content",
18
+ mediaCollection: TABLE_SLUG_MEDIA,
19
+ }),
20
+ copyright: text({
21
+ label: "Copyright Text",
22
+ }),
23
+ },
24
+ labels: {
25
+ plural: "Footers",
26
+ singular: "Footer",
27
+ },
28
+ })
@@ -0,0 +1,35 @@
1
+ import { checkbox, defineCollection, text, upload } from "@vexcms/core"
2
+
3
+ import { TABLE_SLUG_HEADERS, TABLE_SLUG_MEDIA } from "~/db/constants"
4
+
5
+ export const headers = defineCollection({
6
+ slug: TABLE_SLUG_HEADERS,
7
+ admin: {
8
+ group: "Site Builder",
9
+ useAsTitle: "name",
10
+ },
11
+ fields: {
12
+ name: text({
13
+ label: "Name",
14
+ required: true,
15
+ }),
16
+ logoText: text({
17
+ label: "Logo Text",
18
+ }),
19
+ logoUrl: upload({
20
+ admin: {
21
+ description: "URL for the header logo image",
22
+ },
23
+ label: "Logo URL",
24
+ to: TABLE_SLUG_MEDIA,
25
+ }),
26
+ sticky: checkbox({
27
+ defaultValue: false,
28
+ label: "Sticky Header",
29
+ }),
30
+ },
31
+ labels: {
32
+ plural: "Headers",
33
+ singular: "Header",
34
+ },
35
+ })
@@ -0,0 +1,7 @@
1
+ export * from "./footers"
2
+ export * from "./headers"
3
+ export * from "./media"
4
+ export * from "./pages"
5
+ export * from "./site_settings"
6
+ export * from "./themes"
7
+ export * from "./users"
@@ -0,0 +1,40 @@
1
+ import { defineCollection, richtext, text } from "@vexcms/core"
2
+
3
+ import { TABLE_SLUG_MEDIA, TABLE_SLUG_PAGES } from "~/db/constants"
4
+
5
+ export const pages = defineCollection({
6
+ slug: TABLE_SLUG_PAGES,
7
+ admin: {
8
+ defaultColumns: ["title", "slug", "vex_status", "_id"],
9
+ group: "Site Builder",
10
+ livePreview: {
11
+ url: (doc) => `/preview/${doc.slug ?? doc._id}`,
12
+ },
13
+ useAsTitle: "title",
14
+ },
15
+ fields: {
16
+ title: text({
17
+ label: "Title",
18
+ required: true,
19
+ }),
20
+ slug: text({
21
+ admin: {
22
+ description: "URL-friendly page path",
23
+ },
24
+ index: "by_slug",
25
+ label: "Slug",
26
+ required: true,
27
+ }),
28
+ content: richtext({
29
+ label: "Content",
30
+ mediaCollection: TABLE_SLUG_MEDIA,
31
+ }),
32
+ },
33
+ labels: {
34
+ plural: "Pages",
35
+ singular: "Page",
36
+ },
37
+ versions: {
38
+ drafts: true,
39
+ },
40
+ })
@@ -0,0 +1,31 @@
1
+ import { defineCollection, text, upload } from "@vexcms/core"
2
+
3
+ import { TABLE_SLUG_MEDIA, TABLE_SLUG_SITE_SETTINGS } from "~/db/constants"
4
+
5
+ export const siteSettings = defineCollection({
6
+ slug: TABLE_SLUG_SITE_SETTINGS,
7
+ admin: {
8
+ group: "Site Builder",
9
+ useAsTitle: "name",
10
+ },
11
+ fields: {
12
+ name: text({
13
+ label: "Site Name",
14
+ required: true,
15
+ }),
16
+ description: text({
17
+ label: "Site Description",
18
+ }),
19
+ favicon: upload({
20
+ admin: {
21
+ description: "Site favicon image",
22
+ },
23
+ label: "Favicon",
24
+ to: TABLE_SLUG_MEDIA,
25
+ }),
26
+ },
27
+ labels: {
28
+ plural: "Site Settings",
29
+ singular: "Site Settings",
30
+ },
31
+ })
@@ -0,0 +1,37 @@
1
+ import { defineCollection, text } from "@vexcms/core"
2
+
3
+ import { TABLE_SLUG_THEMES } from "~/db/constants"
4
+
5
+ export const themes = defineCollection({
6
+ slug: TABLE_SLUG_THEMES,
7
+ admin: {
8
+ group: "Site Builder",
9
+ useAsTitle: "name",
10
+ },
11
+ fields: {
12
+ name: text({
13
+ label: "Name",
14
+ required: true,
15
+ }),
16
+ backgroundColor: text({
17
+ defaultValue: "#ffffff",
18
+ label: "Background Color",
19
+ }),
20
+ fontFamily: text({
21
+ defaultValue: "Inter, sans-serif",
22
+ label: "Font Family",
23
+ }),
24
+ primaryColor: text({
25
+ defaultValue: "#3b82f6",
26
+ label: "Primary Color",
27
+ }),
28
+ secondaryColor: text({
29
+ defaultValue: "#6b7280",
30
+ label: "Secondary Color",
31
+ }),
32
+ },
33
+ labels: {
34
+ plural: "Themes",
35
+ singular: "Theme",
36
+ },
37
+ })
@@ -0,0 +1,32 @@
1
+ import { defineConfig } from "@vexcms/core"
2
+ import { convexFileStorage } from "@vexcms/file-storage-convex"
3
+
4
+ import { auth } from "~/vexcms/auth"
5
+ import {
6
+ footers,
7
+ headers,
8
+ media,
9
+ pages,
10
+ siteSettings,
11
+ themes,
12
+ users,
13
+ } from "~/vexcms/collections"
14
+
15
+ export default defineConfig({
16
+ admin: {
17
+ meta: {
18
+ titleSuffix: " | My Site",
19
+ },
20
+ sidebar: {
21
+ hideGlobals: true,
22
+ },
23
+ user: "user",
24
+ },
25
+ auth,
26
+ basePath: "/admin",
27
+ collections: [pages, headers, footers, themes, siteSettings, users],
28
+ media: {
29
+ collections: [media],
30
+ storageAdapter: convexFileStorage(),
31
+ },
32
+ })