@delmaredigital/payload-puck 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.
Files changed (128) hide show
  1. package/LICENSE +73 -0
  2. package/README.md +1580 -0
  3. package/dist/AccordionClient.d.mts +24 -0
  4. package/dist/AccordionClient.d.ts +24 -0
  5. package/dist/AccordionClient.js +786 -0
  6. package/dist/AccordionClient.js.map +1 -0
  7. package/dist/AccordionClient.mjs +784 -0
  8. package/dist/AccordionClient.mjs.map +1 -0
  9. package/dist/AnimatedWrapper.d.mts +30 -0
  10. package/dist/AnimatedWrapper.d.ts +30 -0
  11. package/dist/AnimatedWrapper.js +379 -0
  12. package/dist/AnimatedWrapper.js.map +1 -0
  13. package/dist/AnimatedWrapper.mjs +377 -0
  14. package/dist/AnimatedWrapper.mjs.map +1 -0
  15. package/dist/admin/client.d.mts +108 -0
  16. package/dist/admin/client.d.ts +108 -0
  17. package/dist/admin/client.js +177 -0
  18. package/dist/admin/client.js.map +1 -0
  19. package/dist/admin/client.mjs +173 -0
  20. package/dist/admin/client.mjs.map +1 -0
  21. package/dist/admin/index.d.mts +157 -0
  22. package/dist/admin/index.d.ts +157 -0
  23. package/dist/admin/index.js +31 -0
  24. package/dist/admin/index.js.map +1 -0
  25. package/dist/admin/index.mjs +29 -0
  26. package/dist/admin/index.mjs.map +1 -0
  27. package/dist/api/index.d.mts +460 -0
  28. package/dist/api/index.d.ts +460 -0
  29. package/dist/api/index.js +588 -0
  30. package/dist/api/index.js.map +1 -0
  31. package/dist/api/index.mjs +578 -0
  32. package/dist/api/index.mjs.map +1 -0
  33. package/dist/components/index.css +339 -0
  34. package/dist/components/index.css.map +1 -0
  35. package/dist/components/index.d.mts +222 -0
  36. package/dist/components/index.d.ts +222 -0
  37. package/dist/components/index.js +9177 -0
  38. package/dist/components/index.js.map +1 -0
  39. package/dist/components/index.mjs +9130 -0
  40. package/dist/components/index.mjs.map +1 -0
  41. package/dist/config/config.editor.css +339 -0
  42. package/dist/config/config.editor.css.map +1 -0
  43. package/dist/config/config.editor.d.mts +153 -0
  44. package/dist/config/config.editor.d.ts +153 -0
  45. package/dist/config/config.editor.js +9400 -0
  46. package/dist/config/config.editor.js.map +1 -0
  47. package/dist/config/config.editor.mjs +9368 -0
  48. package/dist/config/config.editor.mjs.map +1 -0
  49. package/dist/config/index.d.mts +68 -0
  50. package/dist/config/index.d.ts +68 -0
  51. package/dist/config/index.js +2017 -0
  52. package/dist/config/index.js.map +1 -0
  53. package/dist/config/index.mjs +1991 -0
  54. package/dist/config/index.mjs.map +1 -0
  55. package/dist/editor/index.d.mts +784 -0
  56. package/dist/editor/index.d.ts +784 -0
  57. package/dist/editor/index.js +4517 -0
  58. package/dist/editor/index.js.map +1 -0
  59. package/dist/editor/index.mjs +4483 -0
  60. package/dist/editor/index.mjs.map +1 -0
  61. package/dist/fields/index.css +339 -0
  62. package/dist/fields/index.css.map +1 -0
  63. package/dist/fields/index.d.mts +600 -0
  64. package/dist/fields/index.d.ts +600 -0
  65. package/dist/fields/index.js +7739 -0
  66. package/dist/fields/index.js.map +1 -0
  67. package/dist/fields/index.mjs +7590 -0
  68. package/dist/fields/index.mjs.map +1 -0
  69. package/dist/index-CQu6SzDg.d.mts +327 -0
  70. package/dist/index-CoUQnyC3.d.ts +327 -0
  71. package/dist/index.d.mts +6 -0
  72. package/dist/index.d.ts +6 -0
  73. package/dist/index.js +569 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/index.mjs +555 -0
  76. package/dist/index.mjs.map +1 -0
  77. package/dist/layouts/index.d.mts +96 -0
  78. package/dist/layouts/index.d.ts +96 -0
  79. package/dist/layouts/index.js +394 -0
  80. package/dist/layouts/index.js.map +1 -0
  81. package/dist/layouts/index.mjs +378 -0
  82. package/dist/layouts/index.mjs.map +1 -0
  83. package/dist/plugin/index.d.mts +289 -0
  84. package/dist/plugin/index.d.ts +289 -0
  85. package/dist/plugin/index.js +569 -0
  86. package/dist/plugin/index.js.map +1 -0
  87. package/dist/plugin/index.mjs +555 -0
  88. package/dist/plugin/index.mjs.map +1 -0
  89. package/dist/render/index.d.mts +109 -0
  90. package/dist/render/index.d.ts +109 -0
  91. package/dist/render/index.js +2146 -0
  92. package/dist/render/index.js.map +1 -0
  93. package/dist/render/index.mjs +2123 -0
  94. package/dist/render/index.mjs.map +1 -0
  95. package/dist/shared-DMAF1AcH.d.mts +545 -0
  96. package/dist/shared-DMAF1AcH.d.ts +545 -0
  97. package/dist/theme/index.d.mts +155 -0
  98. package/dist/theme/index.d.ts +155 -0
  99. package/dist/theme/index.js +201 -0
  100. package/dist/theme/index.js.map +1 -0
  101. package/dist/theme/index.mjs +186 -0
  102. package/dist/theme/index.mjs.map +1 -0
  103. package/dist/types-D7D3rZ1J.d.mts +116 -0
  104. package/dist/types-D7D3rZ1J.d.ts +116 -0
  105. package/dist/types-_6MvjyKv.d.mts +104 -0
  106. package/dist/types-_6MvjyKv.d.ts +104 -0
  107. package/dist/utils/index.d.mts +267 -0
  108. package/dist/utils/index.d.ts +267 -0
  109. package/dist/utils/index.js +426 -0
  110. package/dist/utils/index.js.map +1 -0
  111. package/dist/utils/index.mjs +412 -0
  112. package/dist/utils/index.mjs.map +1 -0
  113. package/dist/utils-DaRs9t0J.d.mts +85 -0
  114. package/dist/utils-gAvt0Vhw.d.ts +85 -0
  115. package/examples/README.md +240 -0
  116. package/examples/api/puck/pages/[id]/route.ts +64 -0
  117. package/examples/api/puck/pages/[id]/versions/route.ts +47 -0
  118. package/examples/api/puck/pages/route.ts +45 -0
  119. package/examples/app/(frontend)/page.tsx +94 -0
  120. package/examples/app/[...slug]/page.tsx +101 -0
  121. package/examples/app/pages/[id]/edit/page.tsx +148 -0
  122. package/examples/components/CustomBanner.tsx +368 -0
  123. package/examples/config/custom-config.ts +223 -0
  124. package/examples/config/payload.config.example.ts +64 -0
  125. package/examples/lib/puck-layouts.ts +258 -0
  126. package/examples/lib/puck-theme.ts +94 -0
  127. package/examples/styles/puck-theme.css +171 -0
  128. package/package.json +157 -0
@@ -0,0 +1,85 @@
1
+ import { L as LayoutDefinition, a as LayoutConfig, b as LayoutOption } from './types-D7D3rZ1J.js';
2
+
3
+ /**
4
+ * Default Layout Definitions
5
+ *
6
+ * These provide sensible defaults for common page layout patterns.
7
+ * Users can override or extend these in their own configuration.
8
+ */
9
+
10
+ /**
11
+ * Default layout - standard content width with padding
12
+ */
13
+ declare const defaultLayout: LayoutDefinition;
14
+ /**
15
+ * Landing layout - optimized for marketing/landing pages
16
+ */
17
+ declare const landingLayout: LayoutDefinition;
18
+ /**
19
+ * Full width layout - edge-to-edge content
20
+ */
21
+ declare const fullWidthLayout: LayoutDefinition;
22
+ /**
23
+ * Narrow layout - ideal for blog posts and articles
24
+ */
25
+ declare const narrowLayout: LayoutDefinition;
26
+ /**
27
+ * Wide layout - extra wide content area
28
+ */
29
+ declare const wideLayout: LayoutDefinition;
30
+ /**
31
+ * Default layouts included with the plugin
32
+ */
33
+ declare const DEFAULT_LAYOUTS: LayoutDefinition[];
34
+ /**
35
+ * Extended layouts for users who want more options
36
+ */
37
+ declare const EXTENDED_LAYOUTS: LayoutDefinition[];
38
+ /**
39
+ * Default layout configuration
40
+ */
41
+ declare const DEFAULT_LAYOUT_CONFIG: LayoutConfig;
42
+
43
+ /**
44
+ * Layout Utilities
45
+ *
46
+ * Functions for working with layout configurations.
47
+ */
48
+
49
+ /**
50
+ * Resolves a layout config, merging with defaults if needed
51
+ */
52
+ declare function resolveLayoutConfig(config?: Partial<LayoutConfig>): LayoutConfig;
53
+ /**
54
+ * Gets a layout definition by value
55
+ */
56
+ declare function getLayout(layouts: LayoutDefinition[], value: string, fallback?: string): LayoutDefinition | undefined;
57
+ /**
58
+ * Converts layout definitions to Puck select options
59
+ */
60
+ declare function layoutsToOptions(layouts: LayoutDefinition[]): LayoutOption[];
61
+ /**
62
+ * Converts layout definitions to Payload select options
63
+ */
64
+ declare function layoutsToPayloadOptions(layouts: LayoutDefinition[]): Array<{
65
+ label: string;
66
+ value: string;
67
+ }>;
68
+ /**
69
+ * Creates a custom layout definition
70
+ */
71
+ declare function createLayout(config: Omit<LayoutDefinition, 'value' | 'label'> & {
72
+ value: string;
73
+ label: string;
74
+ }): LayoutDefinition;
75
+ /**
76
+ * Merges layout configurations
77
+ */
78
+ declare function mergeLayouts(base: LayoutDefinition[], custom: LayoutDefinition[], options?: {
79
+ /** Replace base layouts instead of merging */
80
+ replace?: boolean;
81
+ /** Exclude these layout values from base */
82
+ exclude?: string[];
83
+ }): LayoutDefinition[];
84
+
85
+ export { DEFAULT_LAYOUTS as D, EXTENDED_LAYOUTS as E, DEFAULT_LAYOUT_CONFIG as a, layoutsToOptions as b, createLayout as c, defaultLayout as d, layoutsToPayloadOptions as e, fullWidthLayout as f, getLayout as g, landingLayout as l, mergeLayouts as m, narrowLayout as n, resolveLayoutConfig as r, wideLayout as w };
@@ -0,0 +1,240 @@
1
+ # Puck Plugin Examples
2
+
3
+ Copy these files to your project as a starting point. Each file includes comments explaining what to customize.
4
+
5
+ ## Directory Structure
6
+
7
+ ```
8
+ examples/
9
+ ├── api/
10
+ │ └── puck/
11
+ │ └── pages/
12
+ │ ├── route.ts # List & create pages
13
+ │ └── [id]/
14
+ │ ├── route.ts # Get, update, delete page
15
+ │ └── versions/
16
+ │ └── route.ts # Version history (optional)
17
+ ├── app/
18
+ │ ├── (frontend)/
19
+ │ │ └── page.tsx # Homepage route (root "/")
20
+ │ ├── pages/
21
+ │ │ └── [id]/
22
+ │ │ └── edit/
23
+ │ │ └── page.tsx # Visual editor page
24
+ │ └── [...slug]/
25
+ │ └── page.tsx # Dynamic page renderer
26
+ ├── config/
27
+ │ └── payload.config.example.ts # Payload plugin configuration
28
+ └── lib/
29
+ ├── puck-theme.ts # Theme configuration
30
+ └── puck-layouts.ts # Custom page layouts
31
+ ```
32
+
33
+ **Note:** The plugin automatically creates a `puck-templates` collection for the Template component. No additional API routes are needed - templates use Payload's built-in REST API at `/api/puck-templates`.
34
+
35
+ ## Quick Setup
36
+
37
+ ### 1. Add the Plugin to Payload Config
38
+
39
+ Reference the example configuration and merge with your existing `payload.config.ts`:
40
+
41
+ ```bash
42
+ # View the example config
43
+ cat node_modules/@delmaredigital/payload-puck/examples/config/payload.config.example.ts
44
+ ```
45
+
46
+ Add the plugin to your config:
47
+
48
+ ```typescript
49
+ import { createPuckPlugin } from '@delmaredigital/payload-puck/plugin'
50
+
51
+ export default buildConfig({
52
+ plugins: [
53
+ createPuckPlugin({
54
+ pagesCollection: 'pages',
55
+ }),
56
+ ],
57
+ // ... rest of your config
58
+ })
59
+ ```
60
+
61
+ ### 2. Copy API Routes
62
+
63
+ ```bash
64
+ # From your project root
65
+ cp -r node_modules/@delmaredigital/payload-puck/examples/api/puck src/app/api/
66
+ ```
67
+
68
+ ### 3. Copy Editor Page
69
+
70
+ ```bash
71
+ # Adjust the destination path as needed for your route structure
72
+ mkdir -p src/app/\(manage\)/pages/\[id\]/edit
73
+ cp node_modules/@delmaredigital/payload-puck/examples/app/pages/\[id\]/edit/page.tsx src/app/\(manage\)/pages/\[id\]/edit/
74
+ ```
75
+
76
+ ### 4. Copy Frontend Routes
77
+
78
+ ```bash
79
+ # Homepage route (handles root "/")
80
+ mkdir -p src/app/\(frontend\)
81
+ cp node_modules/@delmaredigital/payload-puck/examples/app/\(frontend\)/page.tsx src/app/\(frontend\)/
82
+
83
+ # Dynamic catch-all route (handles "/about", "/contact", etc.)
84
+ mkdir -p src/app/\(frontend\)/\[...slug\]
85
+ cp node_modules/@delmaredigital/payload-puck/examples/app/\[...slug\]/page.tsx src/app/\(frontend\)/\[...slug\]/
86
+ ```
87
+
88
+ ### 5. Copy Theme (Optional)
89
+
90
+ ```bash
91
+ mkdir -p src/lib
92
+ cp node_modules/@delmaredigital/payload-puck/examples/lib/puck-theme.ts src/lib/
93
+ ```
94
+
95
+ Then uncomment the theme imports in the editor and renderer pages.
96
+
97
+ ### 6. Copy Layouts (Optional)
98
+
99
+ ```bash
100
+ cp node_modules/@delmaredigital/payload-puck/examples/lib/puck-layouts.ts src/lib/
101
+ ```
102
+
103
+ Then update your plugin config and renderer to use custom layouts.
104
+
105
+ ## Customization
106
+
107
+ ### Authentication
108
+
109
+ Edit the `authenticate` function in each API route to match your auth setup:
110
+
111
+ ```typescript
112
+ authenticate: async (request) => {
113
+ // Your auth logic here
114
+ const session = await getSession(request)
115
+ if (!session?.user) return { authenticated: false }
116
+ return { authenticated: true, user: session.user }
117
+ },
118
+ ```
119
+
120
+ ### Permissions
121
+
122
+ Customize the `canView`, `canEdit`, `canPublish`, and `canDelete` hooks:
123
+
124
+ ```typescript
125
+ canEdit: async (user, pageId) => {
126
+ // Example: Only editors and admins can edit
127
+ return { allowed: ['editor', 'admin'].includes(user?.role) }
128
+ },
129
+ ```
130
+
131
+ ### Theme
132
+
133
+ Edit `lib/puck-theme.ts` to match your CSS variables:
134
+
135
+ ```typescript
136
+ buttonVariants: {
137
+ default: {
138
+ // Use your CSS variable classes
139
+ classes: 'bg-brand text-brand-foreground hover:bg-brand/90',
140
+ },
141
+ },
142
+ ```
143
+
144
+ ### Layouts
145
+
146
+ Edit `lib/puck-layouts.ts` to define custom page layouts with header/footer support:
147
+
148
+ ```typescript
149
+ import { createLayout, mergeLayouts, DEFAULT_LAYOUTS } from '@delmaredigital/payload-puck/layouts'
150
+ import { Header } from '@/components/header'
151
+ import { Footer } from '@/components/footer'
152
+
153
+ // Layout with sticky header
154
+ const defaultLayout = createLayout({
155
+ value: 'default',
156
+ label: 'Default',
157
+ description: 'Standard page with header and footer',
158
+ maxWidth: '1200px',
159
+ // Header/footer rendered in both editor preview and frontend
160
+ header: Header,
161
+ footer: Footer,
162
+ // Editor preview settings
163
+ editorBackground: '#ffffff',
164
+ editorDarkMode: false,
165
+ // IMPORTANT: Set this if your header is sticky/fixed
166
+ // This adds padding-top in both editor AND frontend so content doesn't render behind the header
167
+ stickyHeaderHeight: 80, // Height of your sticky header in pixels
168
+ })
169
+
170
+ // Landing layout without header/footer
171
+ const landingLayout = createLayout({
172
+ value: 'landing',
173
+ label: 'Landing',
174
+ description: 'Full-width layout without header/footer',
175
+ fullWidth: true,
176
+ // No header/footer - content controls the entire page
177
+ editorBackground: '#f8fafc',
178
+ })
179
+
180
+ // Dark theme example
181
+ const darkLayout = createLayout({
182
+ value: 'dark',
183
+ label: 'Dark',
184
+ description: 'Dark theme layout',
185
+ maxWidth: '1200px',
186
+ header: Header, // Could be a dark-themed header
187
+ footer: Footer,
188
+ editorBackground: '#111827',
189
+ editorDarkMode: true, // Sets dark mode class on iframe
190
+ stickyHeaderHeight: 80,
191
+ })
192
+
193
+ // Combine with defaults
194
+ export const customLayouts = mergeLayouts(
195
+ DEFAULT_LAYOUTS,
196
+ [defaultLayout, landingLayout, darkLayout],
197
+ { replace: true } // Replace defaults with our versions
198
+ )
199
+ ```
200
+
201
+ Use layouts in your editor page:
202
+
203
+ ```typescript
204
+ import { PuckEditor } from '@delmaredigital/payload-puck/editor'
205
+ import { customLayouts } from '@/lib/puck-layouts'
206
+
207
+ <PuckEditor
208
+ config={editorConfig}
209
+ pageId={page.id}
210
+ initialData={page.puckData}
211
+ layouts={customLayouts} // Editor reads header/footer from layouts
212
+ />
213
+ ```
214
+
215
+ And in your PageRenderer:
216
+
217
+ ```typescript
218
+ import { PageRenderer } from '@delmaredigital/payload-puck/render'
219
+ import { customLayouts } from '@/lib/puck-layouts'
220
+
221
+ <PageRenderer
222
+ data={page.puckData}
223
+ layouts={customLayouts} // Frontend renders header/footer from layouts
224
+ />
225
+ ```
226
+
227
+ #### Layout Definition Options
228
+
229
+ | Option | Type | Description |
230
+ |--------|------|-------------|
231
+ | `value` | `string` | Unique identifier |
232
+ | `label` | `string` | Display name in editor |
233
+ | `header` | `ComponentType` | Header component for preview & frontend |
234
+ | `footer` | `ComponentType` | Footer component for preview & frontend |
235
+ | `stickyHeaderHeight` | `number` | Height of sticky/fixed header (applies padding in editor & frontend) |
236
+ | `editorBackground` | `string` | Background color for editor preview |
237
+ | `editorDarkMode` | `boolean` | Use dark mode in editor preview |
238
+ | `maxWidth` | `string` | Container max-width (e.g., `'1200px'`) |
239
+ | `fullWidth` | `boolean` | If true, no container constraints |
240
+ | `classes` | `object` | CSS classes for wrapper/container/content |
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Puck Pages API - Get, Update, Delete
3
+ *
4
+ * Copy this file to: app/api/puck/pages/[id]/route.ts
5
+ *
6
+ * Provides:
7
+ * - GET: Get a single page by ID
8
+ * - PATCH: Update a page (supports draft/publish)
9
+ * - DELETE: Delete a page
10
+ */
11
+
12
+ import { createPuckApiRoutesWithId } from '@delmaredigital/payload-puck/api'
13
+ import config from '@payload-config'
14
+ import { getPayload } from 'payload'
15
+ import { headers } from 'next/headers'
16
+
17
+ export const { GET, PATCH, DELETE } = createPuckApiRoutesWithId({
18
+ collection: 'pages',
19
+ payloadConfig: config,
20
+ auth: {
21
+ // Customize authentication logic for your app
22
+ authenticate: async (request) => {
23
+ const payload = await getPayload({ config })
24
+ const { user } = await payload.auth({ headers: await headers() })
25
+
26
+ if (!user) {
27
+ return { authenticated: false }
28
+ }
29
+
30
+ return {
31
+ authenticated: true,
32
+ user: { id: user.id, role: (user as any).role },
33
+ }
34
+ },
35
+
36
+ // Optional: Customize who can view a page
37
+ canView: async (user, pageId) => {
38
+ return { allowed: true }
39
+ },
40
+
41
+ // Optional: Customize who can edit a page
42
+ canEdit: async (user, pageId) => {
43
+ return { allowed: !!user }
44
+ },
45
+
46
+ // Optional: Customize who can publish (defaults to canEdit)
47
+ canPublish: async (user, pageId) => {
48
+ return { allowed: !!user }
49
+ },
50
+
51
+ // Optional: Customize who can delete a page
52
+ canDelete: async (user, pageId) => {
53
+ // Example: Only admins can delete
54
+ return { allowed: user?.role === 'admin' }
55
+ },
56
+ },
57
+
58
+ // Optional: Map Puck root props to Payload fields
59
+ rootPropsMapping: [
60
+ { from: 'metaTitle', to: 'seo.metaTitle' },
61
+ { from: 'metaDescription', to: 'seo.metaDescription' },
62
+ { from: 'pageLayout', to: 'pageLayout' },
63
+ ],
64
+ })
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Puck Pages Versions API
3
+ *
4
+ * Copy this file to: app/api/puck/pages/[id]/versions/route.ts
5
+ *
6
+ * Provides:
7
+ * - GET: List page versions
8
+ * - POST: Restore a specific version
9
+ *
10
+ * The History button automatically appears in the editor when this route exists.
11
+ */
12
+
13
+ import { createPuckApiRoutesVersions } from '@delmaredigital/payload-puck/api'
14
+ import config from '@payload-config'
15
+ import { getPayload } from 'payload'
16
+ import { headers } from 'next/headers'
17
+
18
+ export const { GET, POST } = createPuckApiRoutesVersions({
19
+ collection: 'pages',
20
+ payloadConfig: config,
21
+ auth: {
22
+ // Customize authentication logic for your app
23
+ authenticate: async (request) => {
24
+ const payload = await getPayload({ config })
25
+ const { user } = await payload.auth({ headers: await headers() })
26
+
27
+ if (!user) {
28
+ return { authenticated: false }
29
+ }
30
+
31
+ return {
32
+ authenticated: true,
33
+ user: { id: user.id, role: (user as any).role },
34
+ }
35
+ },
36
+
37
+ // Optional: Customize who can view versions
38
+ canView: async (user, pageId) => {
39
+ return { allowed: !!user }
40
+ },
41
+
42
+ // Optional: Customize who can restore versions
43
+ canEdit: async (user, pageId) => {
44
+ return { allowed: !!user }
45
+ },
46
+ },
47
+ })
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Puck Pages API - List & Create
3
+ *
4
+ * Copy this file to: app/api/puck/pages/route.ts
5
+ *
6
+ * Provides:
7
+ * - GET: List all pages
8
+ * - POST: Create a new page
9
+ */
10
+
11
+ import { createPuckApiRoutes } from '@delmaredigital/payload-puck/api'
12
+ import config from '@payload-config'
13
+ import { getPayload } from 'payload'
14
+ import { headers } from 'next/headers'
15
+
16
+ export const { GET, POST } = createPuckApiRoutes({
17
+ collection: 'pages',
18
+ payloadConfig: config,
19
+ auth: {
20
+ // Customize authentication logic for your app
21
+ authenticate: async (request) => {
22
+ const payload = await getPayload({ config })
23
+ const { user } = await payload.auth({ headers: await headers() })
24
+
25
+ if (!user) {
26
+ return { authenticated: false }
27
+ }
28
+
29
+ return {
30
+ authenticated: true,
31
+ user: { id: user.id, role: (user as any).role },
32
+ }
33
+ },
34
+
35
+ // Optional: Customize who can list pages
36
+ canList: async (user) => {
37
+ return { allowed: true }
38
+ },
39
+
40
+ // Optional: Customize who can create pages
41
+ canCreate: async (user) => {
42
+ return { allowed: !!user }
43
+ },
44
+ },
45
+ })
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Homepage Route
3
+ *
4
+ * Copy this file to: app/(frontend)/page.tsx
5
+ *
6
+ * Handles the root URL ("/") separately from the catch-all [...slug] route.
7
+ * Looks for a page with slug "home" or isHomepage=true.
8
+ */
9
+
10
+ import { notFound } from 'next/navigation'
11
+ import { getPayload } from 'payload'
12
+ import config from '@payload-config'
13
+ import { PageRenderer } from '@delmaredigital/payload-puck/render'
14
+ import { baseConfig } from '@delmaredigital/payload-puck/config'
15
+ import { LayoutWrapper, DEFAULT_LAYOUTS } from '@delmaredigital/payload-puck/layouts'
16
+ // Import your custom layouts - create from examples/lib/puck-layouts.ts
17
+ // import { siteLayouts } from '@/lib/puck-layouts'
18
+ // Import your theme - create from examples/lib/puck-theme.ts
19
+ // import { puckTheme } from '@/lib/puck-theme'
20
+ import type { Data as PuckData } from '@measured/puck'
21
+ import type { Metadata } from 'next'
22
+
23
+ // Generate SEO metadata for homepage
24
+ export async function generateMetadata(): Promise<Metadata> {
25
+ const payload = await getPayload({ config })
26
+
27
+ // Find homepage by isHomepage flag or slug
28
+ const { docs } = await payload.find({
29
+ collection: 'pages',
30
+ where: {
31
+ or: [
32
+ { isHomepage: { equals: true } },
33
+ { slug: { equals: 'home' } },
34
+ ],
35
+ },
36
+ limit: 1,
37
+ })
38
+
39
+ const page = docs[0] as any
40
+ if (!page) return { title: 'Home' }
41
+
42
+ return {
43
+ title: page.meta?.title || page.title,
44
+ description: page.meta?.description,
45
+ }
46
+ }
47
+
48
+ export default async function HomePage() {
49
+ const payload = await getPayload({ config })
50
+
51
+ // Find homepage by isHomepage flag or slug
52
+ const { docs } = await payload.find({
53
+ collection: 'pages',
54
+ where: {
55
+ or: [
56
+ { isHomepage: { equals: true } },
57
+ { slug: { equals: 'home' } },
58
+ ],
59
+ },
60
+ limit: 1,
61
+ })
62
+
63
+ const page = docs[0] as any
64
+ if (!page) notFound()
65
+
66
+ // Handle pages without content
67
+ if (!page.puckData) {
68
+ return (
69
+ <div className="container mx-auto py-12 text-center">
70
+ <h1 className="text-2xl font-bold mb-4">{page.title}</h1>
71
+ <p className="text-muted-foreground">
72
+ This page has no content yet. Edit it in the admin panel.
73
+ </p>
74
+ </div>
75
+ )
76
+ }
77
+
78
+ // Find the layout definition based on page's pageLayout setting
79
+ // Use your custom siteLayouts instead of DEFAULT_LAYOUTS for header/footer
80
+ const layouts = DEFAULT_LAYOUTS // Replace with: siteLayouts
81
+ const pageLayout = page.puckData?.root?.props?.pageLayout || 'default'
82
+ const layout = layouts.find((l) => l.value === pageLayout)
83
+
84
+ return (
85
+ <LayoutWrapper layout={layout}>
86
+ <PageRenderer
87
+ data={page.puckData as PuckData}
88
+ config={baseConfig}
89
+ // Optional: Custom theme - uncomment after creating puck-theme.ts
90
+ // theme={puckTheme}
91
+ />
92
+ </LayoutWrapper>
93
+ )
94
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Dynamic Page Renderer
3
+ *
4
+ * Copy this file to: app/(frontend)/[...slug]/page.tsx
5
+ * (or your preferred route structure)
6
+ *
7
+ * Renders Puck pages from Payload CMS with:
8
+ * - SEO metadata generation
9
+ * - 404 handling for missing pages
10
+ * - Layout-based header/footer rendering
11
+ * - Optional theming support
12
+ */
13
+
14
+ import { notFound } from 'next/navigation'
15
+ import { getPayload } from 'payload'
16
+ import config from '@payload-config'
17
+ import { PageRenderer } from '@delmaredigital/payload-puck/render'
18
+ import { baseConfig } from '@delmaredigital/payload-puck/config'
19
+ import { LayoutWrapper, DEFAULT_LAYOUTS } from '@delmaredigital/payload-puck/layouts'
20
+ // Import your custom layouts - create from examples/lib/puck-layouts.ts
21
+ // import { siteLayouts } from '@/lib/puck-layouts'
22
+ // Import your theme - create from examples/lib/puck-theme.ts
23
+ // import { puckTheme } from '@/lib/puck-theme'
24
+ import type { Data as PuckData } from '@measured/puck'
25
+ import type { Metadata } from 'next'
26
+
27
+ interface PageParams {
28
+ slug: string[]
29
+ }
30
+
31
+ // Generate SEO metadata from page data
32
+ export async function generateMetadata({
33
+ params,
34
+ }: {
35
+ params: Promise<PageParams>
36
+ }): Promise<Metadata> {
37
+ const { slug } = await params
38
+ const payload = await getPayload({ config })
39
+
40
+ const { docs } = await payload.find({
41
+ collection: 'pages',
42
+ where: { slug: { equals: slug.join('/') } },
43
+ limit: 1,
44
+ })
45
+
46
+ const page = docs[0] as any
47
+ if (!page) return {}
48
+
49
+ return {
50
+ title: page.meta?.title || page.title,
51
+ description: page.meta?.description,
52
+ robots: page.meta?.noindex ? { index: false } : undefined,
53
+ }
54
+ }
55
+
56
+ export default async function Page({
57
+ params,
58
+ }: {
59
+ params: Promise<PageParams>
60
+ }) {
61
+ const { slug } = await params
62
+ const payload = await getPayload({ config })
63
+
64
+ const { docs } = await payload.find({
65
+ collection: 'pages',
66
+ where: { slug: { equals: slug.join('/') } },
67
+ limit: 1,
68
+ })
69
+
70
+ const page = docs[0] as any
71
+ if (!page) notFound()
72
+
73
+ // Handle pages without content
74
+ if (!page.puckData) {
75
+ return (
76
+ <div className="container mx-auto py-12 text-center">
77
+ <h1 className="text-2xl font-bold mb-4">{page.title}</h1>
78
+ <p className="text-muted-foreground">
79
+ This page has no content yet. Edit it in the admin panel.
80
+ </p>
81
+ </div>
82
+ )
83
+ }
84
+
85
+ // Find the layout definition based on page's pageLayout setting
86
+ // Use your custom siteLayouts instead of DEFAULT_LAYOUTS for header/footer
87
+ const layouts = DEFAULT_LAYOUTS // Replace with: siteLayouts
88
+ const pageLayout = page.puckData?.root?.props?.pageLayout || 'default'
89
+ const layout = layouts.find((l) => l.value === pageLayout)
90
+
91
+ return (
92
+ <LayoutWrapper layout={layout}>
93
+ <PageRenderer
94
+ data={page.puckData as PuckData}
95
+ config={baseConfig}
96
+ // Optional: Custom theme - uncomment after creating puck-theme.ts
97
+ // theme={puckTheme}
98
+ />
99
+ </LayoutWrapper>
100
+ )
101
+ }