diff --git a/src/app/blog/[slug]/page.tsx b/src/app/blog/[slug]/page.tsx new file mode 100644 index 0000000..23fa461 --- /dev/null +++ b/src/app/blog/[slug]/page.tsx @@ -0,0 +1,116 @@ +import React from "react"; +import type { Metadata } from "next"; +import { notFound } from "next/navigation"; +import SingleBlog from "@/components/sections/SingleBlog"; +import BlogPostFooter from "@/components/sections/BlogPostFooter"; +import { getPostBySlug, getAllSlugs, SITE_URL } from "@/data/blog"; + +type Params = { slug: string }; + +// Required for `output: "export"` — prerender every post at build time. +export function generateStaticParams(): Params[] { + return getAllSlugs().map((slug) => ({ slug })); +} + +export async function generateMetadata({ + params, +}: { + params: Promise; +}): Promise { + const { slug } = await params; + const post = getPostBySlug(slug); + + if (!post) { + return { title: "Article Not Found – Doormile" }; + } + + const url = `${SITE_URL}/blog/${post.slug}`; + const image = `${SITE_URL}${post.image}`; + + return { + title: `${post.title} – Doormile`, + description: post.excerpt, + keywords: [post.category, "last-mile logistics", "EV fleet", "MileTruth", "route optimisation"], + authors: [{ name: post.author }], + alternates: { canonical: url }, + openGraph: { + type: "article", + title: post.title, + description: post.excerpt, + url, + siteName: "Doormile", + images: [{ url: image, alt: post.title }], + publishedTime: new Date(`${post.date}T00:00:00Z`).toISOString(), + authors: [post.author], + }, + twitter: { + card: "summary_large_image", + title: post.title, + description: post.excerpt, + images: [image], + }, + }; +} + +export default async function BlogPostPage({ + params, +}: { + params: Promise; +}) { + const { slug } = await params; + const post = getPostBySlug(slug); + + if (!post) notFound(); + + const url = `${SITE_URL}/blog/${post.slug}`; + + const articleSchema = { + "@context": "https://schema.org", + "@type": "Article", + headline: post.title, + description: post.excerpt, + image: [`${SITE_URL}${post.image}`], + datePublished: new Date(`${post.date}T00:00:00Z`).toISOString(), + dateModified: new Date(`${post.date}T00:00:00Z`).toISOString(), + author: { "@type": "Organization", name: post.author, url: SITE_URL }, + publisher: { + "@type": "Organization", + name: "Doormile", + logo: { + "@type": "ImageObject", + url: `${SITE_URL}/images/cropped-image-2.png`, + }, + }, + mainEntityOfPage: { "@type": "WebPage", "@id": url }, + articleSection: post.category, + }; + + const breadcrumbSchema = { + "@context": "https://schema.org", + "@type": "BreadcrumbList", + itemListElement: [ + { "@type": "ListItem", position: 1, name: "Home", item: SITE_URL }, + { "@type": "ListItem", position: 2, name: "Blog", item: `${SITE_URL}/blog` }, + { "@type": "ListItem", position: 3, name: post.title, item: url }, + ], + }; + + return ( +
+