Skip to content

Commit

Permalink
Merge pull request #47 from agility/NextJS-15-Update
Browse files Browse the repository at this point in the history
Next 15 Updates
  • Loading branch information
AaronAgility authored Nov 20, 2024
2 parents 86b7edd + 99cb295 commit 474cccc
Show file tree
Hide file tree
Showing 30 changed files with 435 additions and 338 deletions.
5 changes: 4 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
{
"extends": "next/core-web-vitals"
"extends": "next/core-web-vitals",
"rules": {
"no-html-link-for-pages": "off"
}
}
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ This is sample Next.js starter site that uses Agility CMS and aims to be a found

[New to Agility CMS? Sign up for a FREE account](https://agilitycms.com/free)

## 📢 UPDATED FOR NEXT.JS 14 📢
## 📢 UPDATED FOR NEXT.JS 15 📢

- We have updated this starter for Next.js 14. It is built on top of the [@agility/nextjs](https://www.npmjs.com/package/@agility/nextjs) npm package specialized for app router.
- We have updated this starter for Next.js 15.0.3. It is built on top of the [@agility/nextjs](https://www.npmjs.com/package/@agility/nextjs) npm package specialized for app router.

### Caching

Expand Down Expand Up @@ -37,7 +37,7 @@ This starter now relies on component based data-fetching.
## About This Starter

- Uses our [`@agility/nextjs`](https://www.npmjs.com/package/@agility/nextjs) package to make getting started with Agility CMS and Next.js easy
- Support for Next.js 14
- Support for Next.js 15.0.3
- Connected to a sample Agility CMS Instance for sample content & pages
- Supports [`next/image`](https://nextjs.org/docs/api-reference/next/image) for image optimization using the `<Image />` component or the next.js `<Image />` component for images that aren't stored in Agility.
- Supports full [Page Management](https://help.agilitycms.com/hc/en-us/articles/360055805831)
Expand Down
19 changes: 0 additions & 19 deletions app/[...slug]/loading.tsx

This file was deleted.

16 changes: 8 additions & 8 deletions app/[...slug]/not-found.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
export default function NotFound() {
return (
<section className="relative px-8 ">
<div className="max-w-2xl mx-auto my-12 md:mt-18 lg:mt-20 prose prose-sm sm:prose lg:prose-lg xl:prose-xl">
<h1>Page Not Found</h1>
<p>The page you were looking for could not be found</p>
</div>
</section>
)
return (
<section className="relative px-8 ">
<div className="max-w-2xl mx-auto my-12 md:mt-18 lg:mt-20 prose prose-sm sm:prose lg:prose-lg xl:prose-xl">
<h1>Page Not Found</h1>
<p>The page you were looking for could not be found</p>
</div>
</section>
)
}
139 changes: 68 additions & 71 deletions app/[...slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,93 +8,90 @@ import {Metadata, ResolvingMetadata} from "next"
import {resolveAgilityMetaData} from "lib/cms-content/resolveAgilityMetaData"
import NotFound from "./not-found"
import InlineError from "components/common/InlineError"
import {cacheConfig} from "lib/cms/cacheConfig"
import {SitemapNode} from "lib/types/SitemapNode"
import {notFound} from "next/navigation"

export const revalidate = cacheConfig.pathRevalidateDuration
export const revalidate = 60
export const runtime = "nodejs"
export const dynamic = "force-static"

/**
* Generate the list of pages that we want to generate a build time.
*/
export async function generateStaticParams() {
const isDevelopmentMode = process.env.NODE_ENV === "development"
const isPreview = isDevelopmentMode

const apiKey = isPreview ? process.env.AGILITY_API_PREVIEW_KEY : process.env.AGILITY_API_FETCH_KEY

const isDevelopmentMode = process.env.NODE_ENV === "development";
const isPreview = isDevelopmentMode;
const apiKey = isPreview ? process.env.AGILITY_API_PREVIEW_KEY : process.env.AGILITY_API_FETCH_KEY;
const agilityClient = agilitySDK.getApi({
guid: process.env.AGILITY_GUID,
apiKey,
isPreview,
})

const languageCode = process.env.AGILITY_LOCALES || "en-us"

guid: process.env.AGILITY_GUID,
apiKey,
isPreview,
});
const languageCode = process.env.AGILITY_LOCALES || "en-us";

agilityClient.config.fetchConfig = {
next: {
tags: [`agility-sitemap-flat-${languageCode}`],
revalidate: cacheConfig.cacheDuration,
},
}

//get the flat sitemap and generate the paths
// *** NOTE: YOU CAN CUSTOMIZE THIS TO GENERATE ONLY THE PAGES YOU WANT ***
const sitemap: {[path: string]: SitemapNode} = await agilityClient.getSitemapFlat({
channelName: process.env.AGILITY_SITEMAP || "website",
languageCode,
})

next: {
tags: [`agility-sitemap-flat-${languageCode}`],
revalidate: 60,
},
};

// Get the flat sitemap and generate the paths
const sitemap: { [path: string]: SitemapNode } = await agilityClient.getSitemapFlat({
channelName: process.env.AGILITY_SITEMAP || "website",
languageCode,
});

const paths = Object.values(sitemap)
.filter((node, index) => {
//skip folders, redirects, and the home page
if (node.redirect !== null || node.isFolder === true || index === 0) return false
return true
})
.map((node) => {
return {
slug: node.path.split("/").slice(1),
}
})

console.log("Pre-rendering", paths.length, "static paths.")

return paths
}
.filter((node, index) => {
if (node.redirect !== null || node.isFolder === true || index === 0) return false;
return true;
})
.map((node) => {
return {
slug: node.path.split("/").slice(1),
};
});

console.log("Pre-rendering", paths.length, "static paths.");
return paths;
}

/**
* Generate metadata for this page
*/
export async function generateMetadata(
{params, searchParams}: PageProps,
props: PageProps,
parent: ResolvingMetadata
): Promise<Metadata> {
// read route params
const {locale, sitemap, isDevelopmentMode, isPreview} = getAgilityContext()

const agilityData = await getAgilityPage({params})

if (!agilityData.page) return {}
return await resolveAgilityMetaData({agilityData, locale, sitemap, isDevelopmentMode, isPreview, parent})
}

export default async function Page({params, searchParams}: PageProps) {
//const {isPreview} = getAgilityContext()
const agilityData = await getAgilityPage({params})

//if the page is not found...
if (!agilityData.page) return NotFound()

const AgilityPageTemplate = getPageTemplate(agilityData.pageTemplateName || "")

): Promise<Metadata> {
const { params } = props; // Remove the 'await' here

const { locale, sitemap, isDevelopmentMode, isPreview } = await getAgilityContext();
const agilityData = await getAgilityPage({ params });
if (!agilityData.page) return {};
return await resolveAgilityMetaData({
agilityData,
locale,
sitemap,
isDevelopmentMode,
isPreview,
parent,
});
}
export default async function Page({ params }: PageProps) {

const agilityData = await getAgilityPage({ params });
if (!agilityData.page) notFound();

const AgilityPageTemplate = getPageTemplate(agilityData.pageTemplateName || "");

return (
<div data-agility-page={agilityData.page?.pageID} data-agility-dynamic-content={agilityData.sitemapNode.contentID}>
{AgilityPageTemplate && <AgilityPageTemplate {...agilityData} />}
{!AgilityPageTemplate && (
// if we don't have a template for this page, show an error
<InlineError message={`No template found for page template name: ${agilityData.pageTemplateName}`} />
)}
</div>
)
}
<div data-agility-page={agilityData.page?.pageID} data-agility-dynamic-content={agilityData.sitemapNode.contentID}>
{AgilityPageTemplate ? (
<AgilityPageTemplate {...agilityData} />
) : (
<InlineError message={`No template found for page template name: ${agilityData.pageTemplateName}`} />
)}
</div>
);
}
21 changes: 5 additions & 16 deletions app/api/dynamic-redirect/route.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,24 @@
import { getDynamicPageURL } from "@agility/nextjs/node";
import { revalidatePath, revalidateTag } from "next/cache";
import { NextRequest, NextResponse } from "next/server";
import { draftMode } from "next/headers"
import { url } from "inspector";

export async function GET(req: NextRequest, res: NextResponse) {
export async function GET(req: NextRequest) {

const searchParams = req.nextUrl.searchParams
const contentIDStr = searchParams.get("ContentID") as string

const contentID = parseInt(contentIDStr)

const preview = draftMode().isEnabled

const { isEnabled: preview } = await draftMode()

if (!isNaN(contentID) && contentID > 0) {
//*** this is a dynamic page request ***
//get the slug for this page based on the sitemap and redirect there
const redirectUrl = await getDynamicPageURL({ contentID, preview, slug: "" })
if (redirectUrl) {
return new Response(`Redirecting to Dynamic Page Item`, {
status: 307,
headers: {
"Location": redirectUrl,
}
});
return NextResponse.redirect(redirectUrl, { status: 307, headers: { "Location": redirectUrl } })
}
}

//if we get here, it's a 404
return new Response(`Not Found`, {
status: 404
})
return NextResponse.json({ message: "Not Found" }, { status: 404 })

}
32 changes: 12 additions & 20 deletions app/api/preview/exit/route.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,34 @@
"use server";
import { getDynamicPageURL } from '@agility/nextjs/node';
import { draftMode } from 'next/headers'
import { NextRequest, NextResponse } from "next/server";

export async function GET(request: NextRequest, res: NextResponse) {
export async function GET(request: NextRequest) {

const searchParams = request.nextUrl.searchParams

const slug = searchParams.get('slug')
const ContentID = searchParams.get('ContentID')
const slug = searchParams.get('slug');
const ContentID = searchParams.get('ContentID');

//disable draft/preview mode
draftMode().disable()
(await draftMode()).disable()

let url = `${slug}`
let url = new URL(slug || '', request.nextUrl.origin).toString();

if (ContentID) {
const dynamicPath = await getDynamicPageURL({ contentID: Number(ContentID), preview: false, slug: slug || undefined });
if (dynamicPath) {
url = dynamicPath;
}

}

// Redirect to the url
if (url.includes("?")) {
url = `${url}&preview=0`
} else {
url = `${url}?preview=0`
}

return new Response(`Exiting preview mode`, {
status: 307,
headers: {
"Location": url,
}
// Remove the preview URL param if it exists
const urlObj = new URL(url);
urlObj.searchParams.delete('preview');
url = urlObj.toString();

});

NextResponse.redirect(url)
// Redirect to the url
return NextResponse.redirect(url, { status: 307, headers: { "Location": url } })

}
26 changes: 11 additions & 15 deletions app/api/preview/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { NextRequest, NextResponse } from "next/server";
* @param res
* @returns
*/
export async function GET(request: NextRequest, res: NextResponse) {
export async function GET(request: NextRequest) {

const searchParams = request.nextUrl.searchParams

Expand All @@ -28,10 +28,10 @@ export async function GET(request: NextRequest, res: NextResponse) {
slug
});

console.log("validationResp", validationResp)

if (validationResp.error) {
return new Response(`${validationResp.message}`, {
status: 401
});
return NextResponse.json({ message: validationResp.message }, { status: 401 });
}

let previewUrl = slug;
Expand All @@ -46,21 +46,17 @@ export async function GET(request: NextRequest, res: NextResponse) {
}

//enable draft/preview mode
draftMode().enable()
(await draftMode()).enable()

// Redirect to the slug
//Add an extra querystring to the location header - since Netlify will keep the QS for the incoming request by default
let url = `${previewUrl}`
// Construct an absolute URL for the redirect
const baseUrl = `${request.nextUrl.protocol}//${request.nextUrl.host}`;
let url = `${baseUrl}${previewUrl}`;
if (url.includes("?")) {
url = `${url}&preview=1`
url = `${url}&preview=1`;
} else {
url = `${url}?preview=1`
url = `${url}?preview=1`;
}

return new Response(`Initializing preview mode`, {
status: 307,
headers: {
"Location": url,
}
});
return NextResponse.redirect(url, 307);
}
Loading

0 comments on commit 474cccc

Please sign in to comment.