@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,63 @@
|
|
|
1
|
+
import { localizeUrl } from "@/lib/i18n/localizeUrl";
|
|
2
|
+
import { validatePreview, getDynamicPageURL } from "@agility/nextjs/node";
|
|
3
|
+
import { draftMode } from 'next/headers'
|
|
4
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* This endpoint is used as a rewrite for preview requests from middleware.
|
|
9
|
+
* Therefore, the URL will be the original preview request url...
|
|
10
|
+
* @param request
|
|
11
|
+
* @param res
|
|
12
|
+
* @returns
|
|
13
|
+
*/
|
|
14
|
+
export async function GET(request: NextRequest) {
|
|
15
|
+
|
|
16
|
+
const searchParams = request.nextUrl.searchParams
|
|
17
|
+
|
|
18
|
+
const agilityPreviewKey = searchParams.get("agilitypreviewkey") || ""
|
|
19
|
+
|
|
20
|
+
//locale is also passed in the querystring on preview requests
|
|
21
|
+
const locale = searchParams.get("locale") || searchParams.get("lang")
|
|
22
|
+
const slug = searchParams.get("slug") || "/"
|
|
23
|
+
|
|
24
|
+
const ContentID = searchParams.get('ContentID')
|
|
25
|
+
|
|
26
|
+
//validate our preview key, also validate the requested page to preview exists
|
|
27
|
+
const validationResp = await validatePreview({
|
|
28
|
+
agilityPreviewKey,
|
|
29
|
+
slug
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
if (validationResp.error) {
|
|
34
|
+
return NextResponse.json({ message: validationResp.message }, { status: 401 });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let previewUrl = slug
|
|
38
|
+
if (locale) previewUrl = localizeUrl(slug, locale);
|
|
39
|
+
|
|
40
|
+
//if we have a content id, get the dynamic page url for it
|
|
41
|
+
if (ContentID) {
|
|
42
|
+
const dynamicPath = await getDynamicPageURL({ contentID: Number(ContentID), preview: true, slug: slug || undefined });
|
|
43
|
+
if (dynamicPath) {
|
|
44
|
+
previewUrl = dynamicPath;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
//enable draft/preview mode
|
|
50
|
+
(await draftMode()).enable()
|
|
51
|
+
|
|
52
|
+
// Redirect to the slug
|
|
53
|
+
// Construct an absolute URL for the redirect
|
|
54
|
+
const baseUrl = `${request.nextUrl.protocol}//${request.nextUrl.host}`;
|
|
55
|
+
let url = `${baseUrl}${previewUrl}`;
|
|
56
|
+
if (url.includes("?")) {
|
|
57
|
+
url = `${url}&preview=1`;
|
|
58
|
+
} else {
|
|
59
|
+
url = `${url}?preview=1`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return NextResponse.redirect(url, 307);
|
|
63
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import { revalidatePath, revalidateTag } from "next/cache";
|
|
4
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
5
|
+
import agilitySDK from "@agility/content-fetch"
|
|
6
|
+
import type { SitemapNode } from "@/lib/types/SitemapNode";
|
|
7
|
+
|
|
8
|
+
interface IRevalidateRequest {
|
|
9
|
+
state: string,
|
|
10
|
+
instanceGuid: string
|
|
11
|
+
languageCode?: string
|
|
12
|
+
referenceName?: string
|
|
13
|
+
contentID?: number
|
|
14
|
+
contentVersionID?: number
|
|
15
|
+
pageID?: number
|
|
16
|
+
pageVersionID?: number
|
|
17
|
+
changeDateUTC?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function POST(req: NextRequest) {
|
|
21
|
+
|
|
22
|
+
//parse the body
|
|
23
|
+
const data = await req.json() as IRevalidateRequest
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
//only process publish events
|
|
27
|
+
if (data.state === "Published") {
|
|
28
|
+
|
|
29
|
+
let sitemapFlat: {
|
|
30
|
+
[path: string]: SitemapNode
|
|
31
|
+
} = {}
|
|
32
|
+
|
|
33
|
+
//grab the sitemap flat so we can revalidate the full path if needed
|
|
34
|
+
if (data.contentID || data.pageID) {
|
|
35
|
+
const apiKey = process.env.AGILITY_API_FETCH_KEY
|
|
36
|
+
|
|
37
|
+
const agilityClient = agilitySDK.getApi({
|
|
38
|
+
guid: process.env.AGILITY_GUID,
|
|
39
|
+
apiKey
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const languageCode = process.env.AGILITY_LOCALES || "en-us"
|
|
43
|
+
|
|
44
|
+
//don't cache the sitemap here... we want to get the latest
|
|
45
|
+
agilityClient.config.fetchConfig = {
|
|
46
|
+
cache: "no-store"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
sitemapFlat = await agilityClient.getSitemapFlat({
|
|
51
|
+
channelName: process.env.AGILITY_SITEMAP || "website",
|
|
52
|
+
languageCode
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
//revalidate the correct tags based on what changed
|
|
57
|
+
if (data.referenceName) {
|
|
58
|
+
//content item change
|
|
59
|
+
const itemTag = `agility-content-${data.referenceName.toLowerCase()}-${data.languageCode}`
|
|
60
|
+
const listTag = `agility-content-${data.contentID}-${data.languageCode}`
|
|
61
|
+
revalidateTag(itemTag)
|
|
62
|
+
revalidateTag(listTag)
|
|
63
|
+
|
|
64
|
+
console.info("Revalidating content tags:", itemTag, listTag)
|
|
65
|
+
|
|
66
|
+
//grab the sitemap and check if this content is in there so we can revalidate a full path
|
|
67
|
+
if (sitemapFlat) {
|
|
68
|
+
const sitemapNode = Object.values(sitemapFlat).find(s => s.contentID === data.contentID)
|
|
69
|
+
if (sitemapNode) {
|
|
70
|
+
const path = sitemapNode.path
|
|
71
|
+
revalidatePath(path)
|
|
72
|
+
console.info("Revalidating path:", path)
|
|
73
|
+
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
} else if (data.pageID !== undefined && data.pageID > 0) {
|
|
79
|
+
//page change
|
|
80
|
+
const pageTag = `agility-page-${data.pageID}-${data.languageCode}`
|
|
81
|
+
revalidateTag(pageTag)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
//also revalidate the sitemaps
|
|
85
|
+
const sitemapTagFlat = `agility-sitemap-flat-${data.languageCode}`
|
|
86
|
+
const sitemapTagNested = `agility-sitemap-nested-${data.languageCode}`
|
|
87
|
+
revalidateTag(sitemapTagFlat)
|
|
88
|
+
revalidateTag(sitemapTagNested)
|
|
89
|
+
|
|
90
|
+
console.info("Revalidating page and sitemap tags:", pageTag, sitemapTagFlat, sitemapTagNested)
|
|
91
|
+
|
|
92
|
+
if (sitemapFlat) {
|
|
93
|
+
const sitemapNode = Object.values(sitemapFlat).find(s => s.pageID === data.pageID)
|
|
94
|
+
if (sitemapNode) {
|
|
95
|
+
const path = sitemapNode.path
|
|
96
|
+
revalidatePath(path)
|
|
97
|
+
console.info("Revalidating path:", path)
|
|
98
|
+
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
} else if (data.contentID === undefined && data.pageID === undefined) {
|
|
103
|
+
//if no content or page id is provided, it's for a URL redirection
|
|
104
|
+
//trigger the rebuild hook for netlify's rebuild...
|
|
105
|
+
const hookUrl = process.env.BUILD_HOOK_URL
|
|
106
|
+
if (hookUrl) {
|
|
107
|
+
await fetch(hookUrl, {
|
|
108
|
+
method: 'POST'
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return new Response(`OK`, {
|
|
114
|
+
status: 200
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RichTextArea Component
|
|
3
|
+
*
|
|
4
|
+
* An Agility CMS module component that displays rich text content.
|
|
5
|
+
* This component fetches a content item from Agility CMS and renders
|
|
6
|
+
* the HTML content with proper styling and Agility CMS data attributes
|
|
7
|
+
* for in-context editing.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { getContentItem } from "@/lib/cms/getContentItem"
|
|
11
|
+
import { renderHTML, type UnloadedModuleProps } from "@agility/nextjs"
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Interface defining the structure of the RichText content item fields.
|
|
15
|
+
* The field name must match the field reference name in Agility CMS.
|
|
16
|
+
*/
|
|
17
|
+
export interface RichText {
|
|
18
|
+
textblob: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* RichTextArea Component
|
|
23
|
+
*
|
|
24
|
+
* Fetches and renders rich text content from Agility CMS.
|
|
25
|
+
*
|
|
26
|
+
* @param module - The Agility CMS module object containing contentID
|
|
27
|
+
* @param languageCode - The language code for localized content
|
|
28
|
+
* @returns A section element with the rendered rich text content
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* - Uses `data-agility-component` attribute for Agility CMS component identification
|
|
32
|
+
* - Uses `data-agility-field` and `data-agility-html` attributes for in-context editing
|
|
33
|
+
* - Applies Tailwind CSS prose classes for typography styling
|
|
34
|
+
* - Supports responsive typography sizing (sm, lg, xl)
|
|
35
|
+
* - Includes dark mode support via `dark:prose-invert`
|
|
36
|
+
*/
|
|
37
|
+
const RichTextArea = async ({ module, languageCode }: UnloadedModuleProps) => {
|
|
38
|
+
// Fetch the content item from Agility CMS
|
|
39
|
+
const {
|
|
40
|
+
fields: { textblob },
|
|
41
|
+
contentID,
|
|
42
|
+
} = await getContentItem<RichText>({
|
|
43
|
+
contentID: module.contentid,
|
|
44
|
+
languageCode,
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<section id={`${contentID}`} className="relative px-8" data-agility-component={contentID}>
|
|
49
|
+
<div className="max-w-2xl mx-auto my-12 md:mt-18 lg:mt-20">
|
|
50
|
+
{/*
|
|
51
|
+
Rich text content container with Agility CMS editing attributes.
|
|
52
|
+
The renderHTML function sanitizes and prepares the HTML for safe rendering.
|
|
53
|
+
*/}
|
|
54
|
+
<div
|
|
55
|
+
data-agility-field="textblob"
|
|
56
|
+
data-agility-html
|
|
57
|
+
className="my-6 prose prose-sm sm:prose lg:prose-lg xl:prose-xl max-w-full dark:prose-invert"
|
|
58
|
+
dangerouslySetInnerHTML={renderHTML(textblob)}
|
|
59
|
+
></div>
|
|
60
|
+
</div>
|
|
61
|
+
</section>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export default RichTextArea
|
|
66
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agility CMS Module Components
|
|
3
|
+
*
|
|
4
|
+
* This file exports a getModule function that maps Agility CMS module names
|
|
5
|
+
* to their corresponding React components. When a module is rendered in a
|
|
6
|
+
* ContentZone, this function is called to retrieve the appropriate component.
|
|
7
|
+
*
|
|
8
|
+
* To add a new module component:
|
|
9
|
+
* 1. Create a new component file in this directory (e.g., MyComponent.tsx)
|
|
10
|
+
* 2. Import it at the top of this file
|
|
11
|
+
* 3. Add a case in the switch statement below with the module's reference name
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import RichTextArea from "./RichTextArea"
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Returns the React component for a given Agility CMS module name.
|
|
18
|
+
*
|
|
19
|
+
* @param moduleName - The reference name of the module as defined in Agility CMS
|
|
20
|
+
* @returns The React component for the module, or null if not found
|
|
21
|
+
*/
|
|
22
|
+
export const getModule = (moduleName: string) => {
|
|
23
|
+
switch (moduleName) {
|
|
24
|
+
case "RichTextArea":
|
|
25
|
+
return RichTextArea
|
|
26
|
+
default:
|
|
27
|
+
return null
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MainTemplate Component
|
|
3
|
+
*
|
|
4
|
+
* The default page template for Agility CMS pages. This template defines
|
|
5
|
+
* the main content zone where modules can be added and rendered.
|
|
6
|
+
*
|
|
7
|
+
* The ContentZone component handles the rendering of all modules added to
|
|
8
|
+
* the "main-content-zone" in Agility CMS, using the getModule function to
|
|
9
|
+
* map module names to their corresponding React components.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import React from "react"
|
|
13
|
+
import { ContentZone } from "@agility/nextjs"
|
|
14
|
+
import { getModule } from "../agility-components"
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* MainTemplate Component
|
|
18
|
+
*
|
|
19
|
+
* Renders the main content zone for an Agility CMS page.
|
|
20
|
+
*
|
|
21
|
+
* @param props - Agility CMS page props including page data, sitemap, etc.
|
|
22
|
+
* @returns A ContentZone component that renders all modules in the main-content-zone
|
|
23
|
+
*
|
|
24
|
+
* @remarks
|
|
25
|
+
* - The ContentZone name "main-content-zone" must match the zone name in Agility CMS
|
|
26
|
+
* - The getModule function is used to resolve module components by their reference name
|
|
27
|
+
* - All props are spread to the ContentZone for proper context passing
|
|
28
|
+
*/
|
|
29
|
+
const MainTemplate = (props: any) => {
|
|
30
|
+
return (
|
|
31
|
+
<ContentZone name="main-content-zone" {...props} getModule={getModule} />
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default MainTemplate
|
|
36
|
+
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Agility CMS Overview
|
|
2
|
+
|
|
3
|
+
This Next.js project is integrated with Agility CMS, a headless content management system. This document provides an overview of how Agility CMS works in this project.
|
|
4
|
+
|
|
5
|
+
## What is Agility CMS?
|
|
6
|
+
|
|
7
|
+
Agility CMS is a headless CMS that provides:
|
|
8
|
+
- **Content Management**: Create and manage content using a web-based interface
|
|
9
|
+
- **Page Management**: Build pages by combining components (called "modules" in CMS)
|
|
10
|
+
- **Multi-locale Support**: Manage content in multiple languages
|
|
11
|
+
- **API Access**: Fetch content via REST API or SDK
|
|
12
|
+
- **Preview Mode**: Preview draft content before publishing
|
|
13
|
+
|
|
14
|
+
## Key Concepts
|
|
15
|
+
|
|
16
|
+
### 1. Pages
|
|
17
|
+
Pages in Agility CMS are composed of:
|
|
18
|
+
- **Title and SEO**: Meta title, description, keywords
|
|
19
|
+
- **Template**: Defines the layout structure (e.g., MainTemplate, TwoColumnTemplate)
|
|
20
|
+
- **Zones**: Areas where components (modules) can be added (e.g., "main-content-zone", "sidebar")
|
|
21
|
+
- **Path**: The URL path for the page (e.g., `/about`, `/blog/post-1`)
|
|
22
|
+
|
|
23
|
+
### 2. Page Templates
|
|
24
|
+
Templates define the layout structure of a page. Each template has:
|
|
25
|
+
- **Named Zones**: ContentZone components where components (modules) are placed
|
|
26
|
+
- **Layout Structure**: Header, footer, sidebars, etc.
|
|
27
|
+
- **React Component**: A server component that renders the zones
|
|
28
|
+
|
|
29
|
+
Example: `MainTemplate.tsx` has a single zone called "main-content-zone"
|
|
30
|
+
|
|
31
|
+
### 3. Components (Called "Modules" in CMS)
|
|
32
|
+
**Components** (referred to as "modules" in the Agility CMS interface) are reusable building blocks that can be added to page zones:
|
|
33
|
+
- **Reference Name**: Unique identifier (e.g., "RichTextArea", "Hero", "PostListing")
|
|
34
|
+
- **Fields**: Content fields defined in Agility CMS
|
|
35
|
+
- **React Component**: The implementation that renders the component
|
|
36
|
+
|
|
37
|
+
**Note**: While these are called "modules" in the CMS, we refer to them as "components" in code to align with React terminology.
|
|
38
|
+
|
|
39
|
+
### 4. Content Items
|
|
40
|
+
Standalone content that can be:
|
|
41
|
+
- **Referenced by Components**: A component can reference a content item by ID or reference name
|
|
42
|
+
- **Listed in Collections**: Grouped content (e.g., blog posts, team members)
|
|
43
|
+
- **Shared Across Pages**: Reusable content (e.g., site settings, navigation)
|
|
44
|
+
|
|
45
|
+
### 5. Content Lists
|
|
46
|
+
Collections of content items:
|
|
47
|
+
- **Reference Name**: Identifier for the list (e.g., "posts", "testimonials")
|
|
48
|
+
- **Content Type**: Type of items in the list (e.g., "BlogPost", "Testimonial")
|
|
49
|
+
- **Pagination**: Support for paginated lists
|
|
50
|
+
|
|
51
|
+
## How This Project Works
|
|
52
|
+
|
|
53
|
+
### Data Flow
|
|
54
|
+
```
|
|
55
|
+
Agility CMS → API Fetch → Next.js Page → Page Template → Components
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
1. **User visits a page**: Next.js receives the request
|
|
59
|
+
2. **Middleware processes**: Handles preview, redirects, locale routing
|
|
60
|
+
3. **Page fetches data**: `getAgilityPage()` fetches page data from Agility CMS
|
|
61
|
+
4. **Template renders**: The appropriate page template component is selected
|
|
62
|
+
5. **Components render**: ContentZone renders each component in order
|
|
63
|
+
6. **Component receives data**: Each component receives its data and renders
|
|
64
|
+
|
|
65
|
+
### File Organization
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
src/
|
|
69
|
+
├── app/
|
|
70
|
+
│ └── [locale]/
|
|
71
|
+
│ └── [...slug]/
|
|
72
|
+
│ └── page.tsx # Dynamic page route
|
|
73
|
+
├── components/
|
|
74
|
+
│ ├── agility-pages/ # Page templates
|
|
75
|
+
│ │ ├── index.ts # Template registry
|
|
76
|
+
│ │ └── MainTemplate.tsx # Main page template
|
|
77
|
+
│ └── agility-components/ # CMS components (called "modules" in CMS)
|
|
78
|
+
│ ├── index.ts # Component registry
|
|
79
|
+
│ └── RichTextArea.tsx # Example component
|
|
80
|
+
└── lib/
|
|
81
|
+
└── cms/ # CMS helper functions
|
|
82
|
+
├── getAgilitySDK.ts # Initialize SDK
|
|
83
|
+
├── getAgilityPage.ts # Fetch page data
|
|
84
|
+
├── getContentItem.ts # Fetch single content
|
|
85
|
+
├── getContentList.ts # Fetch content lists
|
|
86
|
+
├── getSitemapFlat.ts # Fetch flat sitemap
|
|
87
|
+
├── getSitemapNested.ts # Fetch nested sitemap
|
|
88
|
+
└── getRedirections.ts # Fetch redirects
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Environment Configuration
|
|
92
|
+
|
|
93
|
+
Required environment variables (in `.env.local`):
|
|
94
|
+
|
|
95
|
+
```env
|
|
96
|
+
# Agility Instance
|
|
97
|
+
AGILITY_GUID=your-instance-guid
|
|
98
|
+
|
|
99
|
+
# API Keys
|
|
100
|
+
AGILITY_API_FETCH_KEY=your-fetch-key # Published content
|
|
101
|
+
AGILITY_API_PREVIEW_KEY=your-preview-key # Draft content
|
|
102
|
+
|
|
103
|
+
# Security
|
|
104
|
+
AGILITY_SECURITY_KEY=your-security-key # For webhooks
|
|
105
|
+
|
|
106
|
+
# Locale Settings
|
|
107
|
+
AGILITY_LOCALES=en-us # Comma-separated (e.g., "en-us,fr,es")
|
|
108
|
+
AGILITY_SITEMAP=website # Sitemap name
|
|
109
|
+
|
|
110
|
+
# Caching
|
|
111
|
+
AGILITY_FETCH_CACHE_DURATION=60 # Seconds
|
|
112
|
+
AGILITY_PATH_REVALIDATE_DURATION=60 # Seconds
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Preview Mode
|
|
116
|
+
|
|
117
|
+
To preview draft content:
|
|
118
|
+
1. Add `?agilitypreviewkey=your-preview-key` to any URL
|
|
119
|
+
2. The middleware redirects to `/api/preview`
|
|
120
|
+
3. A cookie is set to enable preview mode
|
|
121
|
+
4. All subsequent requests use the preview API key
|
|
122
|
+
|
|
123
|
+
To exit preview mode:
|
|
124
|
+
1. Add `?AgilityPreview=0` to any URL
|
|
125
|
+
2. The preview cookie is cleared
|
|
126
|
+
|
|
127
|
+
## Caching Strategy
|
|
128
|
+
|
|
129
|
+
The project uses Next.js caching with Agility CMS:
|
|
130
|
+
|
|
131
|
+
1. **Time-based Revalidation**: Pages revalidate every 60 seconds (configurable)
|
|
132
|
+
2. **Cache Tags**: Each piece of content has a tag (e.g., `agility-content-123-en-us`)
|
|
133
|
+
3. **On-demand Revalidation**: Webhook can revalidate specific content via `/api/revalidate`
|
|
134
|
+
|
|
135
|
+
## Next Steps
|
|
136
|
+
|
|
137
|
+
- Read [02-page-routing.md](./02-page-routing.md) to understand page routing
|
|
138
|
+
- Read [03-creating-components.md](./03-creating-components.md) to create new components
|
|
139
|
+
- Read [04-data-fetching.md](./04-data-fetching.md) to fetch content from Agility CMS
|