import type { Metadata } from "next"; import { redirect } from "next/navigation"; import ArticleDetailPage from "./ArticleDetailClient"; import type { Article } from "@/lib/articles/types"; import { getLegacyArticleBySlug, getLegacyRecentSlugs, getLegacyRelatedArticles, } from "@/lib/articles/legacy-source"; export const revalidate = 3600; export const dynamicParams = true; interface PageProps { params: Promise<{ slug: string }>; } const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://broadcastbeat.com"; function toIso(d: string | null | undefined): string { if (!d) return new Date().toISOString(); const parsed = new Date(d); return isNaN(parsed.getTime()) ? new Date().toISOString() : parsed.toISOString(); } function absoluteImage(src: string | undefined | null): string { if (!src) return `${SITE_URL}/assets/images/article-placeholder.jpg`; if (/^https?:\/\//i.test(src)) return src; return `${SITE_URL}${src.startsWith("/") ? "" : "/"}${src}`; } async function loadArticle(slug: string): Promise<{ article: Article | null; related: Article[]; }> { const imported = await getLegacyArticleBySlug(slug); if (!imported) return { article: null, related: [] }; const related = await getLegacyRelatedArticles(slug, imported.category, 3); return { article: imported, related }; } export async function generateMetadata({ params }: PageProps): Promise { const { slug } = await params; const { article } = await loadArticle(slug); if (!article) { return { title: "Article Not Found", description: "The article you are looking for does not exist." }; } return { title: article.title, description: article.excerpt, alternates: { canonical: `/articles/${article.slug}` }, openGraph: { type: "article", title: article.title, description: article.excerpt, url: `/articles/${article.slug}`, images: [{ url: article.image, width: 1200, height: 630, alt: article.alt }], authors: [article.author], publishedTime: article.date, tags: article.tags, }, twitter: { card: "summary_large_image", title: article.title, description: article.excerpt, images: [article.image], creator: "@Broadcast Beat", }, }; } export async function generateStaticParams() { const recent = await getLegacyRecentSlugs(50); return recent.map((slug) => ({ slug })); } export default async function ArticlePage({ params }: PageProps) { const { slug } = await params; const { article, related } = await loadArticle(slug); if (!article) { // Slug isn't in any of our article tables — send the reader to the news // index so a click from the homepage/ticker doesn't dead-end on 404. redirect("/news"); } const articleSchema = { "@context": "https://schema.org", "@type": "Article", headline: article.title, description: article.excerpt, image: [absoluteImage(article.image)], author: { "@type": "Person", name: article.author }, publisher: { "@type": "Organization", name: "Broadcast Beat", logo: { "@type": "ImageObject", url: `${SITE_URL}/assets/images/logo.png` }, }, datePublished: toIso(article.date), dateModified: new Date().toISOString(), mainEntityOfPage: { "@type": "WebPage", "@id": `${SITE_URL}/articles/${article.slug}` }, }; return ( <>