@agility/create-next-app 1.0.0-beta.2
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/.claude/settings.json +7 -0
- package/.claude/settings.local.json +24 -0
- package/FEATURE_ROADMAP.md +343 -0
- package/README.md +205 -0
- package/TESTING.md +131 -0
- package/bin/create-agility-app.js +48 -0
- package/dist/agility/api-keys/generateApiKeys.d.ts +9 -0
- package/dist/agility/api-keys/generateApiKeys.d.ts.map +1 -0
- package/dist/agility/api-keys/generateApiKeys.js +99 -0
- package/dist/agility/api-keys/generateApiKeys.js.map +1 -0
- package/dist/agility/api-keys/getApiKeys.d.ts +9 -0
- package/dist/agility/api-keys/getApiKeys.d.ts.map +1 -0
- package/dist/agility/api-keys/getApiKeys.js +14 -0
- package/dist/agility/api-keys/getApiKeys.js.map +1 -0
- package/dist/agility/index.d.ts +3 -0
- package/dist/agility/index.d.ts.map +1 -0
- package/dist/agility/index.js +8 -0
- package/dist/agility/index.js.map +1 -0
- package/dist/agility/instance/createNewInstance.d.ts +8 -0
- package/dist/agility/instance/createNewInstance.d.ts.map +1 -0
- package/dist/agility/instance/createNewInstance.js +65 -0
- package/dist/agility/instance/createNewInstance.js.map +1 -0
- package/dist/agility/instance/getAvailableInstances.d.ts +8 -0
- package/dist/agility/instance/getAvailableInstances.d.ts.map +1 -0
- package/dist/agility/instance/getAvailableInstances.js +43 -0
- package/dist/agility/instance/getAvailableInstances.js.map +1 -0
- package/dist/agility/instance/manageInstance.d.ts +9 -0
- package/dist/agility/instance/manageInstance.d.ts.map +1 -0
- package/dist/agility/instance/manageInstance.js +82 -0
- package/dist/agility/instance/manageInstance.js.map +1 -0
- package/dist/agility/utils/getMgmtAPIUrl.d.ts +20 -0
- package/dist/agility/utils/getMgmtAPIUrl.d.ts.map +1 -0
- package/dist/agility/utils/getMgmtAPIUrl.js +61 -0
- package/dist/agility/utils/getMgmtAPIUrl.js.map +1 -0
- package/dist/auth/api-key/authenticateWithApiKey.d.ts +6 -0
- package/dist/auth/api-key/authenticateWithApiKey.d.ts.map +1 -0
- package/dist/auth/api-key/authenticateWithApiKey.js +28 -0
- package/dist/auth/api-key/authenticateWithApiKey.js.map +1 -0
- package/dist/auth/index.d.ts +3 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +8 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/oauth/authenticate.d.ts +6 -0
- package/dist/auth/oauth/authenticate.d.ts.map +1 -0
- package/dist/auth/oauth/authenticate.js +162 -0
- package/dist/auth/oauth/authenticate.js.map +1 -0
- package/dist/auth/oauth/constants.d.ts +5 -0
- package/dist/auth/oauth/constants.d.ts.map +1 -0
- package/dist/auth/oauth/constants.js +9 -0
- package/dist/auth/oauth/constants.js.map +1 -0
- package/dist/auth/oauth/exchangeCodeForToken.d.ts +7 -0
- package/dist/auth/oauth/exchangeCodeForToken.d.ts.map +1 -0
- package/dist/auth/oauth/exchangeCodeForToken.js +39 -0
- package/dist/auth/oauth/exchangeCodeForToken.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +290 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/promptForMissingOptions.d.ts +8 -0
- package/dist/cli/promptForMissingOptions.d.ts.map +1 -0
- package/dist/cli/promptForMissingOptions.js +92 -0
- package/dist/cli/promptForMissingOptions.js.map +1 -0
- package/dist/config/env/createEnvFile.d.ts +6 -0
- package/dist/config/env/createEnvFile.d.ts.map +1 -0
- package/dist/config/env/createEnvFile.js +31 -0
- package/dist/config/env/createEnvFile.js.map +1 -0
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +6 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/mcp/createMcpConfig.d.ts +5 -0
- package/dist/config/mcp/createMcpConfig.d.ts.map +1 -0
- package/dist/config/mcp/createMcpConfig.js +32 -0
- package/dist/config/mcp/createMcpConfig.js.map +1 -0
- package/dist/config/packages/installAgilityPackages.d.ts +6 -0
- package/dist/config/packages/installAgilityPackages.d.ts.map +1 -0
- package/dist/config/packages/installAgilityPackages.js +61 -0
- package/dist/config/packages/installAgilityPackages.js.map +1 -0
- package/dist/config/setupProject.d.ts +8 -0
- package/dist/config/setupProject.d.ts.map +1 -0
- package/dist/config/setupProject.js +32 -0
- package/dist/config/setupProject.js.map +1 -0
- package/dist/create-next-app/createNextApp.d.ts +9 -0
- package/dist/create-next-app/createNextApp.d.ts.map +1 -0
- package/dist/create-next-app/createNextApp.js +83 -0
- package/dist/create-next-app/createNextApp.js.map +1 -0
- package/dist/create-next-app/index.d.ts +3 -0
- package/dist/create-next-app/index.d.ts.map +1 -0
- package/dist/create-next-app/index.js +8 -0
- package/dist/create-next-app/index.js.map +1 -0
- package/dist/scaffold/components/createPageComponents.d.ts +6 -0
- package/dist/scaffold/components/createPageComponents.d.ts.map +1 -0
- package/dist/scaffold/components/createPageComponents.js +62 -0
- package/dist/scaffold/components/createPageComponents.js.map +1 -0
- package/dist/scaffold/containers/createContainers.d.ts +6 -0
- package/dist/scaffold/containers/createContainers.d.ts.map +1 -0
- package/dist/scaffold/containers/createContainers.js +48 -0
- package/dist/scaffold/containers/createContainers.js.map +1 -0
- package/dist/scaffold/index.d.ts +2 -0
- package/dist/scaffold/index.d.ts.map +1 -0
- package/dist/scaffold/index.js +6 -0
- package/dist/scaffold/index.js.map +1 -0
- package/dist/scaffold/instance/createBlankInstance.d.ts +8 -0
- package/dist/scaffold/instance/createBlankInstance.d.ts.map +1 -0
- package/dist/scaffold/instance/createBlankInstance.js +51 -0
- package/dist/scaffold/instance/createBlankInstance.js.map +1 -0
- package/dist/scaffold/models/createContentModels.d.ts +6 -0
- package/dist/scaffold/models/createContentModels.d.ts.map +1 -0
- package/dist/scaffold/models/createContentModels.js +70 -0
- package/dist/scaffold/models/createContentModels.js.map +1 -0
- package/dist/templates/copyDirectory.d.ts +5 -0
- package/dist/templates/copyDirectory.d.ts.map +1 -0
- package/dist/templates/copyDirectory.js +28 -0
- package/dist/templates/copyDirectory.js.map +1 -0
- package/dist/templates/copyTemplates.d.ts +8 -0
- package/dist/templates/copyTemplates.d.ts.map +1 -0
- package/dist/templates/copyTemplates.js +58 -0
- package/dist/templates/copyTemplates.js.map +1 -0
- package/dist/templates/index.d.ts +2 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +6 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/types/index.d.ts +50 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/git.d.ts +9 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +71 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/validation.d.ts +45 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +180 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +45 -0
- package/src/agility/api-keys/generateApiKeys.ts +100 -0
- package/src/agility/api-keys/getApiKeys.ts +13 -0
- package/src/agility/index.ts +3 -0
- package/src/agility/instance/createNewInstance.ts +67 -0
- package/src/agility/instance/getAvailableInstances.ts +49 -0
- package/src/agility/instance/manageInstance.ts +90 -0
- package/src/agility/utils/getMgmtAPIUrl.ts +68 -0
- package/src/auth/api-key/authenticateWithApiKey.ts +24 -0
- package/src/auth/index.ts +3 -0
- package/src/auth/oauth/authenticate.ts +165 -0
- package/src/auth/oauth/constants.ts +6 -0
- package/src/auth/oauth/exchangeCodeForToken.ts +43 -0
- package/src/cli/index.ts +281 -0
- package/src/cli/promptForMissingOptions.ts +104 -0
- package/src/config/env/createEnvFile.ts +30 -0
- package/src/config/index.ts +2 -0
- package/src/config/mcp/createMcpConfig.ts +30 -0
- package/src/config/packages/installAgilityPackages.ts +63 -0
- package/src/config/setupProject.ts +31 -0
- package/src/create-next-app/createNextApp.ts +75 -0
- package/src/create-next-app/index.ts +3 -0
- package/src/scaffold/components/createPageComponents.ts +74 -0
- package/src/scaffold/containers/createContainers.ts +55 -0
- package/src/scaffold/index.ts +2 -0
- package/src/scaffold/instance/createBlankInstance.ts +55 -0
- package/src/scaffold/models/createContentModels.ts +83 -0
- package/src/templates/copyDirectory.ts +24 -0
- package/src/templates/copyTemplates.ts +57 -0
- package/src/templates/index.ts +2 -0
- package/src/types/index.ts +55 -0
- package/src/utils/git.ts +74 -0
- package/src/utils/validation.ts +184 -0
- package/templates/.claude/QUICK-START.md +230 -0
- package/templates/.claude/README.md +32 -0
- package/templates/.claude/settings.json +8 -0
- package/templates/BLANK-INSTANCE-SETUP.md +375 -0
- package/templates/DEVELOPMENT.md +160 -0
- package/templates/EXAMPLE-PROMPTS.md +643 -0
- package/templates/PROMPTS.md +410 -0
- package/templates/README.md +281 -0
- package/templates/agents.md +429 -0
- package/templates/app/[locale]/[...slug]/error.tsx +17 -0
- package/templates/app/[locale]/[...slug]/not-found.tsx +9 -0
- package/templates/app/[locale]/[...slug]/page.tsx +102 -0
- package/templates/app/[locale]/layout.tsx +22 -0
- package/templates/app/[locale]/page.tsx +12 -0
- package/templates/app/api/dynamic-redirect/route.ts +24 -0
- package/templates/app/api/preview/exit/route.ts +34 -0
- package/templates/app/api/preview/route.ts +63 -0
- package/templates/app/api/revalidate/route.ts +118 -0
- package/templates/components/agility-components/RichTextArea.tsx +66 -0
- package/templates/components/agility-components/index.ts +30 -0
- package/templates/components/agility-pages/MainTemplate.tsx +36 -0
- package/templates/components/agility-pages/index.ts +11 -0
- package/templates/docs/01-agility-cms-overview.md +139 -0
- package/templates/docs/02-page-routing.md +251 -0
- package/templates/docs/03-creating-components.md +462 -0
- package/templates/docs/04-data-fetching.md +484 -0
- package/templates/docs/05-containers-and-lists.md +596 -0
- package/templates/docs/06-localization.md +561 -0
- package/templates/docs/07-caching-strategies.md +410 -0
- package/templates/docs/08-common-components.md +756 -0
- package/templates/docs/09-whats-included.md +279 -0
- package/templates/docs/10-mcp-server-setup.md +153 -0
- package/templates/docs/11-linked-nested-content.md +611 -0
- package/templates/docs/README.md +164 -0
- package/templates/lib/cms/getAgilityContext.ts +28 -0
- package/templates/lib/cms/getAgilityPage.ts +51 -0
- package/templates/lib/cms/getAgilitySDK.ts +22 -0
- package/templates/lib/cms/getContentItem.ts +20 -0
- package/templates/lib/cms/getContentList.ts +19 -0
- package/templates/lib/cms/getRedirections.ts +85 -0
- package/templates/lib/cms/getSitemapFlat.ts +19 -0
- package/templates/lib/cms/getSitemapNested.ts +19 -0
- package/templates/lib/env.ts +99 -0
- package/templates/lib/i18n/config.ts +28 -0
- package/templates/proxy.ts +101 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
# AI Assistant Guide for Agility CMS Projects
|
|
2
|
+
|
|
3
|
+
This guide helps AI coding assistants (Claude Code, Cursor, GitHub Copilot, Google AI Studio, etc.) understand and work with this Agility CMS + Next.js project.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
This is a Next.js 15 project with Agility CMS headless CMS integration. It uses:
|
|
8
|
+
- **Next.js 15** with App Router
|
|
9
|
+
- **React 19** Server Components
|
|
10
|
+
- **TypeScript** for type safety
|
|
11
|
+
- **Tailwind CSS** for styling
|
|
12
|
+
- **Agility CMS** for content management
|
|
13
|
+
|
|
14
|
+
## Key Architecture Patterns
|
|
15
|
+
|
|
16
|
+
### 1. Headless CMS Flow
|
|
17
|
+
```
|
|
18
|
+
Agility CMS (Content) → API Fetch → Next.js (Server Component) → React Component → Browser
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### 2. File Structure
|
|
22
|
+
```
|
|
23
|
+
src/
|
|
24
|
+
├── app/[locale]/[...slug]/page.tsx ← All pages route here
|
|
25
|
+
├── components/
|
|
26
|
+
│ ├── agility-components/ ← CMS modules (Hero, Blog, etc.)
|
|
27
|
+
│ └── agility-pages/ ← Page templates
|
|
28
|
+
└── lib/
|
|
29
|
+
└── cms/ ← CMS helper functions
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 3. Module Pattern
|
|
33
|
+
Agility CMS "modules" are React components that editors add to pages:
|
|
34
|
+
- Module defined in Agility CMS → React component in `agility-components/`
|
|
35
|
+
- Registered in `agility-components/index.ts`
|
|
36
|
+
- Renders via `<ContentZone>` in page template
|
|
37
|
+
|
|
38
|
+
## 🎯 Understanding User Prompts
|
|
39
|
+
|
|
40
|
+
Users may have varying levels of detail in their prompts. Your job is to:
|
|
41
|
+
1. **Assess prompt quality** - Is it specific enough?
|
|
42
|
+
2. **Ask clarifying questions** - If details are missing
|
|
43
|
+
3. **Suggest best practices** - Guide them to better prompts
|
|
44
|
+
4. **Fill in reasonable defaults** - When appropriate
|
|
45
|
+
|
|
46
|
+
### Prompt Quality Levels
|
|
47
|
+
|
|
48
|
+
**⭐ Poor Prompt** (Too Vague):
|
|
49
|
+
```
|
|
50
|
+
User: "Add a blog"
|
|
51
|
+
```
|
|
52
|
+
**Your Response**: Ask clarifying questions:
|
|
53
|
+
- "I can help you add a blog! To create the best solution, I need some details:
|
|
54
|
+
1. Do you want a blog listing page, or are you building the single post page?
|
|
55
|
+
2. What fields should blog posts have in Agility CMS? (e.g., title, content, featured image, author, date, categories)
|
|
56
|
+
3. How many posts per page?
|
|
57
|
+
4. Any specific design requirements?
|
|
58
|
+
5. Have you already created the 'blogPosts' content model in Agility CMS?"
|
|
59
|
+
|
|
60
|
+
**⭐⭐⭐ Good Prompt** (Specific):
|
|
61
|
+
```
|
|
62
|
+
User: "Add a blog listing component with pagination that shows 9 posts per page"
|
|
63
|
+
```
|
|
64
|
+
**Your Response**: Proceed with reasonable defaults and confirm:
|
|
65
|
+
- "I'll create a blog listing component. I'll assume standard blog fields (title, slug, excerpt, featuredImage, date). Let me know if your Agility CMS blog posts have different field names."
|
|
66
|
+
|
|
67
|
+
**⭐⭐⭐⭐ Excellent Prompt** (Complete):
|
|
68
|
+
```
|
|
69
|
+
User: "Create a blog listing component that fetches from 'blogPosts' content list with fields: title, slug, excerpt, featuredImage, date, author. Show 9 posts in a 3-column grid with pagination."
|
|
70
|
+
```
|
|
71
|
+
**Your Response**: Build it exactly as specified!
|
|
72
|
+
|
|
73
|
+
### When Prompts Lack Detail
|
|
74
|
+
|
|
75
|
+
If a prompt is missing critical information:
|
|
76
|
+
|
|
77
|
+
1. **Check existing files** - Look for similar components to understand patterns
|
|
78
|
+
2. **Check documentation** - Reference PROMPTS.md or EXAMPLE-PROMPTS.md if available
|
|
79
|
+
3. **Ask specific questions** - Don't ask "what do you want?" but rather "Do you want A or B?"
|
|
80
|
+
4. **Suggest the better approach** - Guide them based on best practices
|
|
81
|
+
|
|
82
|
+
Example:
|
|
83
|
+
```
|
|
84
|
+
User: "Add testimonials"
|
|
85
|
+
|
|
86
|
+
You: "I can create a testimonials component! I have a few questions:
|
|
87
|
+
|
|
88
|
+
1. Data source: Should this fetch from a 'testimonials' content list, or will you provide static testimonials in the module fields?
|
|
89
|
+
- Recommended: Content list (more flexible for editors)
|
|
90
|
+
|
|
91
|
+
2. Display style:
|
|
92
|
+
- Grid showing all testimonials
|
|
93
|
+
- Carousel showing one at a time (recommended for homepage)
|
|
94
|
+
|
|
95
|
+
3. What fields does each testimonial have? (typical: quote, author, title, company, photo)
|
|
96
|
+
|
|
97
|
+
Let me know and I'll build it!"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## How to Help Users
|
|
101
|
+
|
|
102
|
+
### When User Wants a New Feature
|
|
103
|
+
|
|
104
|
+
1. **Assess prompt completeness** - Do you have enough information?
|
|
105
|
+
2. **Ask questions if needed** - Get field names, layout requirements, data source
|
|
106
|
+
3. **Identify Type**: Is it a module, page template, or standalone component?
|
|
107
|
+
4. **Create Files**: Generate TypeScript component files
|
|
108
|
+
5. **Register**: Add to appropriate index.ts
|
|
109
|
+
6. **Fetch Data**: Use `getContentList()` or `getContentItem()` for CMS data
|
|
110
|
+
7. **Add Types**: Define TypeScript interfaces
|
|
111
|
+
8. **Provide CMS setup instructions** - Tell them what to create in Agility CMS
|
|
112
|
+
|
|
113
|
+
### When User Wants to Fix a Bug
|
|
114
|
+
|
|
115
|
+
1. **Read Files**: Use Read tool on relevant files
|
|
116
|
+
2. **Understand Pattern**: Check similar working components
|
|
117
|
+
3. **Apply Fix**: Use Edit tool to fix issues
|
|
118
|
+
4. **Verify**: Explain the fix and ask user to test
|
|
119
|
+
|
|
120
|
+
### When User Has a Blank Agility Instance
|
|
121
|
+
|
|
122
|
+
Users may be starting from scratch! Help them plan:
|
|
123
|
+
|
|
124
|
+
1. **Understand their goals** - What type of website?
|
|
125
|
+
2. **Suggest content structure** - What content models to create
|
|
126
|
+
3. **Provide creation order** - Build simple components first
|
|
127
|
+
4. **Reference guides** - Point them to BLANK-INSTANCE-SETUP.md
|
|
128
|
+
5. **Walk through setup** - Guide them step-by-step
|
|
129
|
+
|
|
130
|
+
### When User Wants a Blog/List Feature
|
|
131
|
+
|
|
132
|
+
1. **Content List Module**: Create in `agility-components/`
|
|
133
|
+
2. **Fetch Data**: Use `getContentList({ referenceName: "posts", locale })`
|
|
134
|
+
3. **Display**: Map over items and render
|
|
135
|
+
4. **Pagination**: Use searchParams for page numbers
|
|
136
|
+
|
|
137
|
+
### When User Wants Multi-Language
|
|
138
|
+
|
|
139
|
+
1. **Check Config**: Read `lib/i18n/config.ts`
|
|
140
|
+
2. **Use Locale**: Always pass `locale` prop to CMS functions
|
|
141
|
+
3. **URL Structure**: Default locale has no prefix, others do (/fr/, /es/)
|
|
142
|
+
|
|
143
|
+
## Common Tasks
|
|
144
|
+
|
|
145
|
+
### Task: Create a New Module Component
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
// 1. Create file: src/components/agility-components/MyModule.tsx
|
|
149
|
+
interface MyModuleProps {
|
|
150
|
+
module: {
|
|
151
|
+
fields: {
|
|
152
|
+
title: string;
|
|
153
|
+
// ... other fields
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
locale: string;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export default function MyModule({ module, locale }: MyModuleProps) {
|
|
160
|
+
const { title } = module.fields;
|
|
161
|
+
return <div>{title}</div>;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 2. Register in src/components/agility-components/index.ts
|
|
165
|
+
import MyModule from "./MyModule";
|
|
166
|
+
|
|
167
|
+
export const getModule = (moduleName: string) => {
|
|
168
|
+
switch (moduleName) {
|
|
169
|
+
case "MyModule": // Must match Agility CMS reference name
|
|
170
|
+
return MyModule;
|
|
171
|
+
// ... other cases
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Task: Fetch and Display Content List
|
|
177
|
+
|
|
178
|
+
```tsx
|
|
179
|
+
import { getContentList } from "@/lib/cms/getContentList";
|
|
180
|
+
|
|
181
|
+
export default async function PostListing({ locale }: any) {
|
|
182
|
+
const posts = await getContentList({
|
|
183
|
+
referenceName: "posts",
|
|
184
|
+
locale,
|
|
185
|
+
take: 10,
|
|
186
|
+
sort: "fields.date desc",
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
return (
|
|
190
|
+
<div>
|
|
191
|
+
{posts.map((post) => (
|
|
192
|
+
<article key={post.contentID}>
|
|
193
|
+
<h3>{post.fields.title}</h3>
|
|
194
|
+
<p>{post.fields.excerpt}</p>
|
|
195
|
+
</article>
|
|
196
|
+
))}
|
|
197
|
+
</div>
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Task: Add Interactive Client Component
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
// Server component (data fetching)
|
|
206
|
+
import MyComponentClient from "./MyComponent.client";
|
|
207
|
+
|
|
208
|
+
export default async function MyComponent({ module, locale }: any) {
|
|
209
|
+
const data = await fetchData(locale);
|
|
210
|
+
return <MyComponentClient data={data} />;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Client component (interactivity)
|
|
214
|
+
"use client";
|
|
215
|
+
import { useState } from "react";
|
|
216
|
+
|
|
217
|
+
export default function MyComponentClient({ data }: any) {
|
|
218
|
+
const [state, setState] = useState(0);
|
|
219
|
+
return <button onClick={() => setState(state + 1)}>{state}</button>;
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Task: Add a New Page Template
|
|
224
|
+
|
|
225
|
+
```tsx
|
|
226
|
+
// 1. Create file: src/components/agility-pages/TwoColumn.tsx
|
|
227
|
+
import { ContentZone } from "@agility/nextjs";
|
|
228
|
+
import { getModule } from "../agility-components";
|
|
229
|
+
|
|
230
|
+
export default function TwoColumn(props: any) {
|
|
231
|
+
return (
|
|
232
|
+
<div className="grid md:grid-cols-2 gap-8">
|
|
233
|
+
<ContentZone name="left-zone" {...props} getModule={getModule} />
|
|
234
|
+
<ContentZone name="right-zone" {...props} getModule={getModule} />
|
|
235
|
+
</div>
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// 2. Register in src/components/agility-pages/index.ts
|
|
240
|
+
import TwoColumn from "./TwoColumn";
|
|
241
|
+
|
|
242
|
+
export const getPageTemplate = (templateName: string) => {
|
|
243
|
+
switch (templateName) {
|
|
244
|
+
case "TwoColumn":
|
|
245
|
+
return TwoColumn;
|
|
246
|
+
// ... other cases
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Important Patterns to Remember
|
|
252
|
+
|
|
253
|
+
### 1. Always Use AgilityPic for Images
|
|
254
|
+
|
|
255
|
+
**CRITICAL**: All images from Agility CMS MUST be rendered using the `<AgilityPic>` component from `@agility/nextjs`. Never use Next.js `<Image>` or plain `<img>` tags for Agility CMS images.
|
|
256
|
+
|
|
257
|
+
```tsx
|
|
258
|
+
import { AgilityPic } from "@agility/nextjs";
|
|
259
|
+
import type { ImageField } from "@agility/nextjs";
|
|
260
|
+
|
|
261
|
+
// GOOD - Use AgilityPic for Agility CMS images
|
|
262
|
+
<AgilityPic
|
|
263
|
+
image={imageField}
|
|
264
|
+
fallbackWidth={600}
|
|
265
|
+
className="w-full h-auto rounded-2xl"
|
|
266
|
+
data-agility-field="image"
|
|
267
|
+
/>
|
|
268
|
+
|
|
269
|
+
// BAD - Never use Next.js Image or img for Agility images
|
|
270
|
+
<Image src={imageField.url} alt="..." /> // ❌ WRONG
|
|
271
|
+
<img src={imageField.url} alt="..." /> // ❌ WRONG
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**Key AgilityPic Props**:
|
|
275
|
+
- `image` (required): The ImageField object from Agility CMS
|
|
276
|
+
- `fallbackWidth`: Width in pixels for fallback (e.g., 600)
|
|
277
|
+
- `alt`: Optional alt text (uses CMS alt by default)
|
|
278
|
+
- `className`: CSS classes for styling
|
|
279
|
+
- `priority`: Set true for above-the-fold images
|
|
280
|
+
- `sources`: Array of responsive sources with media queries
|
|
281
|
+
- `data-agility-field`: Field name for inline editing
|
|
282
|
+
|
|
283
|
+
**Example with Responsive Images**:
|
|
284
|
+
```tsx
|
|
285
|
+
<AgilityPic
|
|
286
|
+
image={post.image}
|
|
287
|
+
fallbackWidth={400}
|
|
288
|
+
className="w-full h-full object-cover rounded-2xl"
|
|
289
|
+
sources={[
|
|
290
|
+
{ media: "(max-width: 639px)", width: 640 },
|
|
291
|
+
{ media: "(max-width: 767px)", width: 800 },
|
|
292
|
+
{ media: "(max-width: 1023px)", width: 1200 },
|
|
293
|
+
]}
|
|
294
|
+
data-agility-field="image"
|
|
295
|
+
/>
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### 2. Always Use Locale
|
|
299
|
+
```tsx
|
|
300
|
+
// GOOD
|
|
301
|
+
const data = await getContentList({ referenceName: "posts", locale });
|
|
302
|
+
|
|
303
|
+
// BAD - missing locale
|
|
304
|
+
const data = await getContentList({ referenceName: "posts" });
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### 3. Reference Name for Lists
|
|
308
|
+
```tsx
|
|
309
|
+
// GOOD - use referenceName from fields
|
|
310
|
+
const items = await getContentList({
|
|
311
|
+
referenceName: cards.referenceName,
|
|
312
|
+
locale,
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// BAD - hardcoded string
|
|
316
|
+
const items = await getContentList({
|
|
317
|
+
referenceName: "testimonials",
|
|
318
|
+
locale,
|
|
319
|
+
});
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### 4. Server vs Client Components
|
|
323
|
+
```tsx
|
|
324
|
+
// Server Component (default) - for data fetching
|
|
325
|
+
export default async function MyComponent() {
|
|
326
|
+
const data = await fetchData();
|
|
327
|
+
return <div>{data}</div>;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Client Component - for interactivity
|
|
331
|
+
"use client";
|
|
332
|
+
export default function MyComponent() {
|
|
333
|
+
const [count, setCount] = useState(0);
|
|
334
|
+
return <button onClick={() => setCount(count + 1)}>{count}</button>;
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### 5. Type Safety
|
|
339
|
+
```tsx
|
|
340
|
+
import type { ImageField } from "@agility/nextjs";
|
|
341
|
+
|
|
342
|
+
// GOOD - define types with ImageField for images
|
|
343
|
+
interface Post {
|
|
344
|
+
contentID: number;
|
|
345
|
+
fields: {
|
|
346
|
+
title: string;
|
|
347
|
+
slug: string;
|
|
348
|
+
image: ImageField; // Always use ImageField for Agility images
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const posts: Post[] = await getContentList({ ... });
|
|
353
|
+
|
|
354
|
+
// BAD - using any
|
|
355
|
+
const posts: any = await getContentList({ ... });
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
## File Naming Conventions
|
|
359
|
+
|
|
360
|
+
- `.tsx` - React component (auto-detected as server or client)
|
|
361
|
+
- `.server.tsx` - Explicitly server component (optional)
|
|
362
|
+
- `.client.tsx` - Explicitly client component (must have "use client")
|
|
363
|
+
- `index.ts` - Registry/barrel file for exports
|
|
364
|
+
|
|
365
|
+
## Common Gotchas
|
|
366
|
+
|
|
367
|
+
1. **Image Components**: ALWAYS use `<AgilityPic>` for Agility CMS images, NEVER use `<Image>` or `<img>`
|
|
368
|
+
2. **Module Reference Names**: Must match EXACTLY between Agility CMS and code
|
|
369
|
+
3. **Locale Param**: Always pass locale to CMS functions
|
|
370
|
+
4. **ContentID vs ReferenceName**: Use referenceName for lists, contentID for single items
|
|
371
|
+
5. **Preview Mode**: Uses different API key (PREVIEW vs FETCH)
|
|
372
|
+
6. **Cache Tags**: Automatically added, no manual setup needed
|
|
373
|
+
7. **Search Params**: Encoded with ~~~ in URLs for static generation
|
|
374
|
+
|
|
375
|
+
## CMS Helper Functions Reference
|
|
376
|
+
|
|
377
|
+
| Function | Purpose | Example |
|
|
378
|
+
|----------|---------|---------|
|
|
379
|
+
| `getAgilityPage()` | Full page data | `await getAgilityPage({ params })` |
|
|
380
|
+
| `getContentItem()` | Single content | `await getContentItem({ contentID: 123, locale })` |
|
|
381
|
+
| `getContentList()` | Content collection | `await getContentList({ referenceName: "posts", locale })` |
|
|
382
|
+
| `getSitemapFlat()` | Flat sitemap | `await getSitemapFlat({ locale })` |
|
|
383
|
+
| `getSitemapNested()` | Nested sitemap | `await getSitemapNested({ locale })` |
|
|
384
|
+
|
|
385
|
+
## When in Doubt
|
|
386
|
+
|
|
387
|
+
1. **Read existing code**: Check similar components in `agility-components/`
|
|
388
|
+
2. **Check documentation**: Read the relevant `.md` file in `.claude/docs/`
|
|
389
|
+
3. **Ask clarifying questions**: If requirements are unclear, ask the user
|
|
390
|
+
4. **Keep it simple**: Don't over-engineer, follow existing patterns
|
|
391
|
+
5. **Type everything**: Use TypeScript interfaces for all props
|
|
392
|
+
|
|
393
|
+
## Documentation Index
|
|
394
|
+
|
|
395
|
+
- [01-agility-cms-overview.md](./01-agility-cms-overview.md) - CMS concepts
|
|
396
|
+
- [02-page-routing.md](./02-page-routing.md) - Routing and URL structure
|
|
397
|
+
- [03-creating-components.md](./03-creating-components.md) - Module components
|
|
398
|
+
- [04-data-fetching.md](./04-data-fetching.md) - Fetching CMS data
|
|
399
|
+
- [05-containers-and-lists.md](./05-containers-and-lists.md) - Content lists
|
|
400
|
+
- [06-localization.md](./06-localization.md) - Multi-language
|
|
401
|
+
- [07-caching-strategies.md](./07-caching-strategies.md) - Caching patterns
|
|
402
|
+
- [08-common-components.md](./08-common-components.md) - Component examples
|
|
403
|
+
- [11-linked-nested-content.md](./11-linked-nested-content.md) - Linked/nested content patterns
|
|
404
|
+
|
|
405
|
+
## Goal: Enable Vibe Coding
|
|
406
|
+
|
|
407
|
+
This documentation enables users to build complete websites by:
|
|
408
|
+
1. **Describing what they want** ("Add a blog with categories and pagination")
|
|
409
|
+
2. **AI generates code** using patterns from docs
|
|
410
|
+
3. **User configures in Agility CMS** (creates modules, adds content)
|
|
411
|
+
4. **Site works immediately** following established patterns
|
|
412
|
+
|
|
413
|
+
The key is that AI assistants should be able to:
|
|
414
|
+
- Generate new components following the patterns
|
|
415
|
+
- Fetch data correctly using helper functions
|
|
416
|
+
- Handle locales, caching, and routing automatically
|
|
417
|
+
- Create type-safe, production-ready code
|
|
418
|
+
|
|
419
|
+
## Success Criteria
|
|
420
|
+
|
|
421
|
+
You're helping effectively when:
|
|
422
|
+
1. ✅ Generated code follows existing patterns
|
|
423
|
+
2. ✅ All CMS functions include locale parameter
|
|
424
|
+
3. ✅ TypeScript types are defined
|
|
425
|
+
4. ✅ Server/client split is correct
|
|
426
|
+
5. ✅ Module names match Agility CMS reference names
|
|
427
|
+
6. ✅ Code is production-ready and maintainable
|
|
428
|
+
|
|
429
|
+
Good luck building amazing websites! 🚀
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
export default function Error({
|
|
4
|
+
error,
|
|
5
|
+
reset,
|
|
6
|
+
}: {
|
|
7
|
+
error: Error & { digest?: string }
|
|
8
|
+
reset: () => void
|
|
9
|
+
}) {
|
|
10
|
+
return (
|
|
11
|
+
<div>
|
|
12
|
+
<h2>Something went wrong!</h2>
|
|
13
|
+
<button onClick={() => reset()}>Try again</button>
|
|
14
|
+
</div>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { getPageTemplate } from "@/components/agility-pages"
|
|
2
|
+
import { type PageProps, getAgilityPage } from "@/lib/cms/getAgilityPage"
|
|
3
|
+
import { getAgilityContext } from "@/lib/cms/getAgilityContext"
|
|
4
|
+
import agilitySDK from "@agility/content-fetch"
|
|
5
|
+
|
|
6
|
+
import type { Metadata, ResolvingMetadata } from "next"
|
|
7
|
+
|
|
8
|
+
import { notFound } from "next/navigation"
|
|
9
|
+
import { locales } from "@/lib/i18n/config"
|
|
10
|
+
|
|
11
|
+
export const revalidate = 60
|
|
12
|
+
export const runtime = "nodejs"
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Generate the list of pages that we want to generate at build time.
|
|
16
|
+
*/
|
|
17
|
+
export async function generateStaticParams() {
|
|
18
|
+
const isDevelopmentMode = process.env.NODE_ENV === "development";
|
|
19
|
+
const isPreview = isDevelopmentMode;
|
|
20
|
+
const apiKey = isPreview ? process.env.AGILITY_API_PREVIEW_KEY : process.env.AGILITY_API_FETCH_KEY;
|
|
21
|
+
const agilityClient = agilitySDK.getApi({
|
|
22
|
+
guid: process.env.AGILITY_GUID,
|
|
23
|
+
apiKey,
|
|
24
|
+
isPreview,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const allPaths: { locale: string; slug: string[] }[] = [];
|
|
28
|
+
|
|
29
|
+
// Generate paths for each locale
|
|
30
|
+
for (const locale of locales) {
|
|
31
|
+
agilityClient.config.fetchConfig = {
|
|
32
|
+
next: {
|
|
33
|
+
tags: [`agility-sitemap-flat-${locale}`],
|
|
34
|
+
revalidate: 60,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Get the flat sitemap for this locale
|
|
39
|
+
const sitemap: { [path: string]: any } = await agilityClient.getSitemapFlat({
|
|
40
|
+
channelName: process.env.AGILITY_SITEMAP || "website",
|
|
41
|
+
languageCode: locale,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const localePaths = Object.values(sitemap)
|
|
45
|
+
.filter((node: any) => {
|
|
46
|
+
if (node.redirect !== null || node.isFolder === true) return false;
|
|
47
|
+
return true;
|
|
48
|
+
})
|
|
49
|
+
.map((node: any) => {
|
|
50
|
+
return {
|
|
51
|
+
locale,
|
|
52
|
+
slug: node.path.split("/").slice(1),
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
allPaths.push(...localePaths);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
console.log("Pre-rendering", allPaths.length, "static paths across", locales.length, "locales.");
|
|
60
|
+
return allPaths;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Generate metadata for this page
|
|
65
|
+
*/
|
|
66
|
+
export async function generateMetadata(
|
|
67
|
+
props: PageProps,
|
|
68
|
+
parent: ResolvingMetadata
|
|
69
|
+
): Promise<Metadata> {
|
|
70
|
+
const { params } = props;
|
|
71
|
+
const awaitedParams = await params;
|
|
72
|
+
|
|
73
|
+
const agilityData = await getAgilityPage({ params });
|
|
74
|
+
if (!agilityData.page) return {};
|
|
75
|
+
|
|
76
|
+
// Basic metadata - can be enhanced with SEO fields from Agility
|
|
77
|
+
return {
|
|
78
|
+
title: agilityData.page?.seo?.metaTitle || 'Page',
|
|
79
|
+
description: agilityData.page?.seo?.metaDescription || '',
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default async function Page({ params }: PageProps) {
|
|
84
|
+
const agilityData = await getAgilityPage({ params });
|
|
85
|
+
if (!agilityData.page) notFound();
|
|
86
|
+
|
|
87
|
+
const AgilityPageTemplate = getPageTemplate(agilityData.pageTemplateName || "MainTemplate");
|
|
88
|
+
|
|
89
|
+
//get the search params from global data (since they are added in getAgilityPage)
|
|
90
|
+
const globalSearchParams = agilityData.globalData?.["searchParams"] || {};
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<div data-agility-page={agilityData.page?.pageID} data-agility-dynamic-content={agilityData.sitemapNode.contentID}>
|
|
94
|
+
{AgilityPageTemplate ? (
|
|
95
|
+
<AgilityPageTemplate {...agilityData} searchParams={globalSearchParams} />
|
|
96
|
+
) : (
|
|
97
|
+
<div>No template found for page template name: {agilityData.pageTemplateName}</div>
|
|
98
|
+
)}
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type React from 'react'
|
|
2
|
+
import { getAgilityContext } from '@/lib/cms/getAgilityContext'
|
|
3
|
+
|
|
4
|
+
interface LayoutProps {
|
|
5
|
+
children: React.ReactNode
|
|
6
|
+
params: Promise<{ locale: string }>
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default async function LocaleLayout({
|
|
10
|
+
children,
|
|
11
|
+
params,
|
|
12
|
+
}: LayoutProps) {
|
|
13
|
+
const { locale } = await params
|
|
14
|
+
const { isDevelopmentMode, isPreview } = await getAgilityContext(locale)
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<>
|
|
18
|
+
{children}
|
|
19
|
+
</>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { redirect } from 'next/navigation'
|
|
2
|
+
import { defaultLocale } from '@/lib/i18n/config'
|
|
3
|
+
|
|
4
|
+
export default function LocaleHomePage({
|
|
5
|
+
params,
|
|
6
|
+
}: {
|
|
7
|
+
params: Promise<{ locale: string }>
|
|
8
|
+
}) {
|
|
9
|
+
// Redirect to home page - this will be handled by the [...slug] route
|
|
10
|
+
redirect('/')
|
|
11
|
+
}
|
|
12
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { getDynamicPageURL } from "@agility/nextjs/node";
|
|
2
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
3
|
+
import { draftMode } from "next/headers"
|
|
4
|
+
|
|
5
|
+
export async function GET(req: NextRequest) {
|
|
6
|
+
|
|
7
|
+
const searchParams = req.nextUrl.searchParams
|
|
8
|
+
const contentIDStr = searchParams.get("ContentID") as string
|
|
9
|
+
const contentID = parseInt(contentIDStr)
|
|
10
|
+
const { isEnabled: preview } = await draftMode()
|
|
11
|
+
|
|
12
|
+
if (!isNaN(contentID) && contentID > 0) {
|
|
13
|
+
//*** this is a dynamic page request ***
|
|
14
|
+
//get the slug for this page based on the sitemap and redirect there
|
|
15
|
+
const redirectUrl = await getDynamicPageURL({ contentID, preview, slug: "" })
|
|
16
|
+
if (redirectUrl) {
|
|
17
|
+
return NextResponse.redirect(redirectUrl, { status: 307, headers: { "Location": redirectUrl } })
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
//if we get here, it's a 404
|
|
22
|
+
return NextResponse.json({ message: "Not Found" }, { status: 404 })
|
|
23
|
+
|
|
24
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
import { getDynamicPageURL } from '@agility/nextjs/node';
|
|
3
|
+
import { draftMode } from 'next/headers'
|
|
4
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
5
|
+
|
|
6
|
+
export async function GET(request: NextRequest) {
|
|
7
|
+
|
|
8
|
+
const searchParams = request.nextUrl.searchParams
|
|
9
|
+
|
|
10
|
+
const slug = searchParams.get('slug');
|
|
11
|
+
const ContentID = searchParams.get('ContentID');
|
|
12
|
+
|
|
13
|
+
//disable draft/preview mode
|
|
14
|
+
(await draftMode()).disable()
|
|
15
|
+
|
|
16
|
+
let url = new URL(slug || '', request.nextUrl.origin).toString();
|
|
17
|
+
|
|
18
|
+
if (ContentID) {
|
|
19
|
+
const dynamicPath = await getDynamicPageURL({ contentID: Number(ContentID), preview: false, slug: slug || undefined });
|
|
20
|
+
if (dynamicPath) {
|
|
21
|
+
url = dynamicPath;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Remove the preview URL param if it exists
|
|
26
|
+
const urlObj = new URL(url);
|
|
27
|
+
urlObj.searchParams.delete('preview');
|
|
28
|
+
url = urlObj.toString();
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
// Redirect to the url
|
|
32
|
+
return NextResponse.redirect(url, { status: 307, headers: { "Location": url } })
|
|
33
|
+
|
|
34
|
+
}
|