107 lines
3.3 KiB
TypeScript
107 lines
3.3 KiB
TypeScript
import Image from "next/image";
|
|
import Link from "next/link";
|
|
import { notFound } from "next/navigation";
|
|
import { ContactSection } from "../../../components/contact-section";
|
|
import { DemoCta } from "../../../components/demo-cta";
|
|
import { FaqSection } from "../../../components/faq-section";
|
|
import { PageSchema } from "../../../components/page-schema";
|
|
import { SiteFooter } from "../../../components/site-footer";
|
|
import { SiteHeader } from "../../../components/site-header";
|
|
import { blogPosts } from "../../../data/blog";
|
|
import { defaultFaqs } from "../../../data/faq";
|
|
import { siteInfo } from "../../../data/site";
|
|
|
|
type PageProps = {
|
|
params: { slug: string };
|
|
};
|
|
|
|
export function generateStaticParams() {
|
|
return blogPosts.map((post) => ({ slug: post.slug }));
|
|
}
|
|
|
|
export function generateMetadata({ params }: PageProps) {
|
|
const post = blogPosts.find((item) => item.slug === params.slug);
|
|
if (!post) {
|
|
return { title: "Blog Post" };
|
|
}
|
|
return {
|
|
title: post.title,
|
|
description: post.excerpt,
|
|
keywords: [...siteInfo.keywords, "finance blog", "ledger insights"]
|
|
};
|
|
}
|
|
|
|
export default function BlogPostPage({ params }: PageProps) {
|
|
const post = blogPosts.find((item) => item.slug === params.slug);
|
|
if (!post) {
|
|
notFound();
|
|
}
|
|
|
|
const schema = [
|
|
{
|
|
"@context": "https://schema.org",
|
|
"@type": "Article",
|
|
headline: post.title,
|
|
description: post.excerpt,
|
|
datePublished: post.date,
|
|
author: {
|
|
"@type": "Organization",
|
|
name: siteInfo.name
|
|
},
|
|
image: post.image,
|
|
mainEntityOfPage: `${siteInfo.url}/blog/${post.slug}`
|
|
},
|
|
{
|
|
"@context": "https://schema.org",
|
|
"@type": "FAQPage",
|
|
mainEntity: defaultFaqs.map((item) => ({
|
|
"@type": "Question",
|
|
name: item.question,
|
|
acceptedAnswer: { "@type": "Answer", text: item.answer }
|
|
}))
|
|
}
|
|
];
|
|
|
|
return (
|
|
<div className="marketing min-h-screen">
|
|
<div className="relative overflow-hidden">
|
|
<div className="halo absolute inset-0" />
|
|
<div className="grid-dots absolute inset-0" />
|
|
<SiteHeader />
|
|
|
|
<main className="relative mx-auto max-w-4xl px-6 pb-20 pt-12">
|
|
<Link className="text-sm font-semibold text-ink" href="/blog">
|
|
<- Back to blog
|
|
</Link>
|
|
<article className="mt-6 rounded-3xl border border-ink/10 bg-white/80 p-8 shadow-soft">
|
|
<p className="text-xs uppercase tracking-[0.3em] text-muted">
|
|
{post.date} - {post.readTime}
|
|
</p>
|
|
<h1 className="mt-3 text-3xl font-semibold">{post.title}</h1>
|
|
<p className="mt-4 text-sm text-muted">{post.excerpt}</p>
|
|
<div className="mt-6 overflow-hidden rounded-2xl border border-ink/10">
|
|
<Image
|
|
src={post.image}
|
|
alt={post.title}
|
|
width={1200}
|
|
height={900}
|
|
className="h-64 w-full object-cover"
|
|
/>
|
|
</div>
|
|
<div className="mt-6 space-y-4 text-sm text-muted">
|
|
{post.content.map((paragraph) => (
|
|
<p key={paragraph}>{paragraph}</p>
|
|
))}
|
|
</div>
|
|
</article>
|
|
</main>
|
|
</div>
|
|
<DemoCta />
|
|
<FaqSection limit={8} />
|
|
<ContactSection />
|
|
<PageSchema schema={schema} />
|
|
<SiteFooter />
|
|
</div>
|
|
);
|
|
}
|