import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { POSTS, getPost, formatDate } from "../posts"; import { Breadcrumb } from "@/components/marketing/Breadcrumb"; import { blogPostingSchema, breadcrumbSchema, jsonLdScript, } from "@/lib/seo"; // Turn a heading string into a URL-safe anchor ID function slugifyHeading(heading: string): string { return heading .toLowerCase() .replace(/[^a-z0-9\s-]/g, "") .replace(/\s+/g, "-") .replace(/-+/g, "-") .trim(); } export function generateStaticParams() { return POSTS.map((p) => ({ slug: p.slug })); } export async function generateMetadata({ params, }: { params: Promise<{ slug: string }>; }): Promise { const { slug } = await params; const post = getPost(slug); if (!post) return {}; const url = `/blog/${post.slug}`; return { title: post.title, description: post.excerpt, alternates: { canonical: url }, openGraph: { type: "article", url, siteName: "Tia", locale: "en_IN", title: post.title, description: post.excerpt, publishedTime: post.date, modifiedTime: post.date, authors: [post.author], section: post.category, // Uses the per-post opengraph-image.tsx in this segment. }, twitter: { card: "summary_large_image", title: post.title, description: post.excerpt, }, }; } export default async function BlogPostPage({ params, }: { params: Promise<{ slug: string }>; }) { const { slug } = await params; const post = getPost(slug); if (!post) notFound(); // Extract headings for TOC const headings = post.sections.filter((s) => s.heading).map((s) => ({ text: s.heading!, id: slugifyHeading(s.heading!), })); // Other posts (exclude current) const otherPosts = POSTS.filter((p) => p.slug !== slug).slice(0, 3); return (
{/* Structured data — BlogPosting + breadcrumb trail */}