import type { Metadata } from 'next'; import BlogPostContent from './BlogPostContent'; const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000'; interface BlogFaqItem { question: string; answer: string; } interface BlogPostMeta { title: string; slug: string; excerpt: string; metaTitle?: string; metaDescription?: string; primaryKeyword?: string; secondaryKeywords?: string[]; coverImageUrl?: string; authorName: string; publishedAt?: string; updatedAt: string; aiSummary?: string | null; faq?: BlogFaqItem[]; keyTakeaways?: string[]; } async function fetchPostMeta(slug: string): Promise { try { const res = await fetch(`${API_BASE_URL}/api/v1/blog/${slug}`, { cache: 'no-store', }); if (!res.ok) return null; return (await res.json()) as BlogPostMeta; } catch { return null; } } function buildJsonLd(post: BlogPostMeta, slug: string) { const coverImage = post.coverImageUrl ? post.coverImageUrl.startsWith('http') ? post.coverImageUrl : `${API_BASE_URL}${post.coverImageUrl}` : undefined; const article = { '@context': 'https://schema.org', '@type': 'Article', headline: post.metaTitle || post.title, description: post.metaDescription || post.aiSummary || post.excerpt, image: coverImage ? [coverImage] : undefined, datePublished: post.publishedAt, dateModified: post.updatedAt, author: { '@type': 'Person', name: post.authorName }, publisher: { '@type': 'Organization', name: 'Xpeditis' }, mainEntityOfPage: { '@type': 'WebPage', '@id': `https://xpeditis.com/blog/${slug}` }, keywords: [post.primaryKeyword, ...(post.secondaryKeywords ?? [])] .filter(Boolean) .join(', '), }; const faqPage = post.faq && post.faq.length > 0 ? { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: post.faq.map(item => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer }, })), } : null; return { article, faqPage }; } export async function generateMetadata({ params, }: { params: { slug: string; locale: string }; }): Promise { const post = await fetchPostMeta(params.slug); if (!post) { return { title: 'Blog — Xpeditis' }; } const title = post.metaTitle || post.title; const description = post.metaDescription || post.excerpt; const keywords = [post.primaryKeyword, ...(post.secondaryKeywords ?? [])] .filter(Boolean) .join(', '); const coverImage = post.coverImageUrl ? post.coverImageUrl.startsWith('http') ? post.coverImageUrl : `${API_BASE_URL}${post.coverImageUrl}` : undefined; return { title, description, keywords: keywords || undefined, openGraph: { title, description, type: 'article', publishedTime: post.publishedAt, authors: [post.authorName], images: coverImage ? [{ url: coverImage, alt: post.title }] : [], }, twitter: { card: 'summary_large_image', title, description, images: coverImage ? [coverImage] : [], }, alternates: { canonical: `/blog/${params.slug}`, }, }; } export default async function BlogPostPage({ params, }: { params: { slug: string; locale: string }; }) { const post = await fetchPostMeta(params.slug); const jsonLd = post ? buildJsonLd(post, params.slug) : null; return ( <> {jsonLd && ( <>