Deploy
This commit is contained in:
@@ -0,0 +1,70 @@
|
|||||||
|
import { notFound } from 'next/navigation'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import Image from 'next/image'
|
||||||
|
import { getBlogPosts } from '@/lib/cms'
|
||||||
|
|
||||||
|
export const revalidate = 60
|
||||||
|
|
||||||
|
export async function generateStaticParams() {
|
||||||
|
const posts = await getBlogPosts(50)
|
||||||
|
return posts.map(p => ({ slug: (p.data.slug as string) ?? p.id }))
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function BlogPostPage({ params }: { params: Promise<{ slug: string }> }) {
|
||||||
|
const { slug } = await params
|
||||||
|
const posts = await getBlogPosts(50)
|
||||||
|
const post = posts.find(p => (p.data.slug as string) === slug || p.id === slug)
|
||||||
|
|
||||||
|
if (!post) notFound()
|
||||||
|
|
||||||
|
const { title, excerpt, body, cover_image, author, category } = post.data as {
|
||||||
|
title?: string; excerpt?: string; body?: string;
|
||||||
|
cover_image?: string; author?: string; category?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const paragraphs = (body || excerpt || '').split(/\n\n+/).filter(Boolean)
|
||||||
|
const date = post.publishedAt
|
||||||
|
? new Date(post.publishedAt).toLocaleDateString('en-ZA', { year: 'numeric', month: 'long', day: 'numeric' })
|
||||||
|
: ''
|
||||||
|
|
||||||
|
return (
|
||||||
|
<article className="min-h-screen bg-white">
|
||||||
|
{cover_image && (
|
||||||
|
<div className="relative h-72 sm:h-96 w-full">
|
||||||
|
<Image src={cover_image} alt={title || ''} fill className="object-cover" />
|
||||||
|
<div className="absolute inset-0 bg-black/30" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="max-w-2xl mx-auto px-4 py-10">
|
||||||
|
<Link href="/blog" className="text-sm text-zinc-500 hover:text-zinc-800 transition-colors mb-6 inline-block">
|
||||||
|
← Back to blog
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
{category && (
|
||||||
|
<span className="text-xs font-semibold uppercase tracking-wider text-indigo-600">{category}</span>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<h1 className="text-3xl sm:text-4xl font-bold text-zinc-900 mt-2 mb-4 leading-tight">
|
||||||
|
{title}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-3 text-sm text-zinc-500 mb-8 pb-6 border-b border-zinc-100">
|
||||||
|
{author && <span>{author}</span>}
|
||||||
|
{author && date && <span>·</span>}
|
||||||
|
{date && <span>{date}</span>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{excerpt && paragraphs.length <= 1 && (
|
||||||
|
<p className="text-lg text-zinc-600 mb-6 leading-relaxed font-light">{excerpt}</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="space-y-5">
|
||||||
|
{paragraphs.map((para, i) => (
|
||||||
|
<p key={i} className="text-zinc-700 leading-relaxed">{para}</p>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user