117 lines
3.4 KiB
TypeScript
117 lines
3.4 KiB
TypeScript
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<Params>;
|
||
}): Promise<Metadata> {
|
||
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<Params>;
|
||
}) {
|
||
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 (
|
||
<div className="content-wrapper content-wrapper-may-contain-elementor-code content-wrapper-sidebar-position-none">
|
||
<script
|
||
type="application/ld+json"
|
||
dangerouslySetInnerHTML={{ __html: JSON.stringify(articleSchema) }}
|
||
/>
|
||
<script
|
||
type="application/ld+json"
|
||
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbSchema) }}
|
||
/>
|
||
<div className="content">
|
||
<div className="content-inner">
|
||
<SingleBlog post={post} />
|
||
<BlogPostFooter slug={post.slug} />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|