@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.
- package/LICENSE +73 -0
- package/README.md +1580 -0
- package/dist/AccordionClient.d.mts +24 -0
- package/dist/AccordionClient.d.ts +24 -0
- package/dist/AccordionClient.js +786 -0
- package/dist/AccordionClient.js.map +1 -0
- package/dist/AccordionClient.mjs +784 -0
- package/dist/AccordionClient.mjs.map +1 -0
- package/dist/AnimatedWrapper.d.mts +30 -0
- package/dist/AnimatedWrapper.d.ts +30 -0
- package/dist/AnimatedWrapper.js +379 -0
- package/dist/AnimatedWrapper.js.map +1 -0
- package/dist/AnimatedWrapper.mjs +377 -0
- package/dist/AnimatedWrapper.mjs.map +1 -0
- package/dist/admin/client.d.mts +108 -0
- package/dist/admin/client.d.ts +108 -0
- package/dist/admin/client.js +177 -0
- package/dist/admin/client.js.map +1 -0
- package/dist/admin/client.mjs +173 -0
- package/dist/admin/client.mjs.map +1 -0
- package/dist/admin/index.d.mts +157 -0
- package/dist/admin/index.d.ts +157 -0
- package/dist/admin/index.js +31 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/index.mjs +29 -0
- package/dist/admin/index.mjs.map +1 -0
- package/dist/api/index.d.mts +460 -0
- package/dist/api/index.d.ts +460 -0
- package/dist/api/index.js +588 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/index.mjs +578 -0
- package/dist/api/index.mjs.map +1 -0
- package/dist/components/index.css +339 -0
- package/dist/components/index.css.map +1 -0
- package/dist/components/index.d.mts +222 -0
- package/dist/components/index.d.ts +222 -0
- package/dist/components/index.js +9177 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/index.mjs +9130 -0
- package/dist/components/index.mjs.map +1 -0
- package/dist/config/config.editor.css +339 -0
- package/dist/config/config.editor.css.map +1 -0
- package/dist/config/config.editor.d.mts +153 -0
- package/dist/config/config.editor.d.ts +153 -0
- package/dist/config/config.editor.js +9400 -0
- package/dist/config/config.editor.js.map +1 -0
- package/dist/config/config.editor.mjs +9368 -0
- package/dist/config/config.editor.mjs.map +1 -0
- package/dist/config/index.d.mts +68 -0
- package/dist/config/index.d.ts +68 -0
- package/dist/config/index.js +2017 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/index.mjs +1991 -0
- package/dist/config/index.mjs.map +1 -0
- package/dist/editor/index.d.mts +784 -0
- package/dist/editor/index.d.ts +784 -0
- package/dist/editor/index.js +4517 -0
- package/dist/editor/index.js.map +1 -0
- package/dist/editor/index.mjs +4483 -0
- package/dist/editor/index.mjs.map +1 -0
- package/dist/fields/index.css +339 -0
- package/dist/fields/index.css.map +1 -0
- package/dist/fields/index.d.mts +600 -0
- package/dist/fields/index.d.ts +600 -0
- package/dist/fields/index.js +7739 -0
- package/dist/fields/index.js.map +1 -0
- package/dist/fields/index.mjs +7590 -0
- package/dist/fields/index.mjs.map +1 -0
- package/dist/index-CQu6SzDg.d.mts +327 -0
- package/dist/index-CoUQnyC3.d.ts +327 -0
- package/dist/index.d.mts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +569 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +555 -0
- package/dist/index.mjs.map +1 -0
- package/dist/layouts/index.d.mts +96 -0
- package/dist/layouts/index.d.ts +96 -0
- package/dist/layouts/index.js +394 -0
- package/dist/layouts/index.js.map +1 -0
- package/dist/layouts/index.mjs +378 -0
- package/dist/layouts/index.mjs.map +1 -0
- package/dist/plugin/index.d.mts +289 -0
- package/dist/plugin/index.d.ts +289 -0
- package/dist/plugin/index.js +569 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/plugin/index.mjs +555 -0
- package/dist/plugin/index.mjs.map +1 -0
- package/dist/render/index.d.mts +109 -0
- package/dist/render/index.d.ts +109 -0
- package/dist/render/index.js +2146 -0
- package/dist/render/index.js.map +1 -0
- package/dist/render/index.mjs +2123 -0
- package/dist/render/index.mjs.map +1 -0
- package/dist/shared-DMAF1AcH.d.mts +545 -0
- package/dist/shared-DMAF1AcH.d.ts +545 -0
- package/dist/theme/index.d.mts +155 -0
- package/dist/theme/index.d.ts +155 -0
- package/dist/theme/index.js +201 -0
- package/dist/theme/index.js.map +1 -0
- package/dist/theme/index.mjs +186 -0
- package/dist/theme/index.mjs.map +1 -0
- package/dist/types-D7D3rZ1J.d.mts +116 -0
- package/dist/types-D7D3rZ1J.d.ts +116 -0
- package/dist/types-_6MvjyKv.d.mts +104 -0
- package/dist/types-_6MvjyKv.d.ts +104 -0
- package/dist/utils/index.d.mts +267 -0
- package/dist/utils/index.d.ts +267 -0
- package/dist/utils/index.js +426 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/index.mjs +412 -0
- package/dist/utils/index.mjs.map +1 -0
- package/dist/utils-DaRs9t0J.d.mts +85 -0
- package/dist/utils-gAvt0Vhw.d.ts +85 -0
- package/examples/README.md +240 -0
- package/examples/api/puck/pages/[id]/route.ts +64 -0
- package/examples/api/puck/pages/[id]/versions/route.ts +47 -0
- package/examples/api/puck/pages/route.ts +45 -0
- package/examples/app/(frontend)/page.tsx +94 -0
- package/examples/app/[...slug]/page.tsx +101 -0
- package/examples/app/pages/[id]/edit/page.tsx +148 -0
- package/examples/components/CustomBanner.tsx +368 -0
- package/examples/config/custom-config.ts +223 -0
- package/examples/config/payload.config.example.ts +64 -0
- package/examples/lib/puck-layouts.ts +258 -0
- package/examples/lib/puck-theme.ts +94 -0
- package/examples/styles/puck-theme.css +171 -0
- 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
|
+
}
|