Files

115 lines
3.5 KiB
TypeScript

import Image from "next/image";
import Link from "next/link";
import { notFound } from "next/navigation";
import type { Metadata } from "next";
import { getPost, getPosts, postExcerpt, formatDate, readingTime } from "@/lib/ghost";
export const revalidate = 60;
// Pre-render every known post at build time; new ones are handled on demand.
export async function generateStaticParams() {
const posts = await getPosts();
return posts.map((p) => ({ slug: p.slug }));
}
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string }>;
}): Promise<Metadata> {
const { slug } = await params;
const post = await getPost(slug);
if (!post) return { title: "Post not found — UP Parivaar Dallas" };
return {
title: `${post.title} — UP Parivaar Dallas`,
description: postExcerpt(post, 160),
openGraph: {
title: post.title,
description: postExcerpt(post, 160),
type: "article",
images: post.feature_image ? [{ url: post.feature_image }] : undefined,
},
};
}
export default async function BlogPostPage({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = await getPost(slug);
if (!post) notFound();
const author = post.primary_author;
return (
<article className="page-glow blogpost">
<div className="container blogpost__inner">
<Link href="/blog" className="blogpost__back">
All posts
</Link>
<div className="blogpost__head">
{post.primary_tag && <span className="blog-tag">{post.primary_tag.name}</span>}
<h1 className="display-title ts-34-44 blogpost__title">{post.title}</h1>
<div className="blogpost__byline">
{author?.profile_image && (
<span className="blogpost__avatar">
<Image src={author.profile_image} alt={author.name} fill className="img-cover" sizes="44px" />
</span>
)}
<div className="blogpost__byline-text">
{author && <span className="blogpost__author">{author.name}</span>}
<span className="blog-meta">
<span>{formatDate(post.published_at)}</span>
{readingTime(post) && <span className="blog-meta__dot"></span>}
{readingTime(post) && <span>{readingTime(post)}</span>}
</span>
</div>
</div>
</div>
{post.feature_image && (
<div className="blogpost__hero">
<Image
src={post.feature_image}
alt={post.feature_image_alt || post.title}
fill
className="img-cover"
sizes="(max-width: 900px) 100vw, 900px"
priority
/>
</div>
)}
{post.html ? (
<div
className="blogpost__content"
dangerouslySetInnerHTML={{ __html: post.html }}
/>
) : (
<p className="blogpost__content">{postExcerpt(post, 500)}</p>
)}
{post.tags && post.tags.length > 0 && (
<div className="blogpost__tags">
{post.tags.map((t) => (
<span key={t.id} className="blog-tag blog-tag--muted">
{t.name}
</span>
))}
</div>
)}
<div className="blogpost__foot">
<Link href="/blog" className="link-arrow link-arrow--spaced-lg">
Back to all posts
</Link>
</div>
</div>
</article>
);
}