Files
kotobane/app/[category]/[slug]/page.tsx
T

158 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { notFound } from 'next/navigation'
import type { Metadata } from 'next'
import Image from 'next/image'
import Link from 'next/link'
import {
getArticleBySlug,
getRelatedArticles,
getAllCategories,
getArticles,
getAssetUrl,
} from '@/lib/directus'
import ArticleBody from '@/components/article/ArticleBody'
import TagRow from '@/components/article/TagRow'
import ArticleGrid from '@/components/home/ArticleGrid'
interface Props {
params: Promise<{ category: string; slug: string }>
}
export const revalidate = false
export async function generateStaticParams() {
try {
const categories = await getAllCategories()
const articlesByCategory = await Promise.all(
categories.map((cat) => getArticles({ categorySlug: cat.slug, limit: 1000 }))
)
return articlesByCategory.flat().map((article) => ({
category: article.category.slug,
slug: article.slug,
}))
} catch {
return []
}
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params
try {
const article = await getArticleBySlug(slug)
if (!article) return {}
return {
title: article.seo_title ?? article.title,
description: article.seo_description ?? article.excerpt ?? undefined,
openGraph: {
title: article.seo_title ?? article.title,
description: article.seo_description ?? article.excerpt ?? undefined,
images: article.featured_image
? [getAssetUrl(article.featured_image, { width: 1200, quality: 80 })]
: [],
},
}
} catch {
return {}
}
}
export default async function ArticlePage({ params }: Props) {
const { category: categorySlug, slug } = await params
let article = null
let related: import('@/lib/types').Article[] = []
try {
article = await getArticleBySlug(slug)
if (article && article.category.slug === categorySlug) {
related = await getRelatedArticles(categorySlug, slug)
} else {
article = null
}
} catch {
article = null
}
if (!article) notFound()
const dateToFormat = article.published_at ?? article.date_created
const publishedDate = dateToFormat
? new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
}).format(new Date(dateToFormat))
: null
return (
<>
<article className="max-w-[780px] mx-auto px-6 pt-10 pb-8">
<nav
className="flex items-center gap-2 text-xs text-text-disabled mb-7"
aria-label="Breadcrumb"
>
<Link href="/" className="text-text-muted hover:text-accent transition-colors">
Home
</Link>
<span></span>
<Link
href={`/${article.category.slug}`}
className="text-text-muted hover:text-accent transition-colors"
>
{article.category.name}
</Link>
<span></span>
<span className="text-text-muted truncate">{article.title}</span>
</nav>
<p className="text-[10px] font-bold uppercase tracking-[2px] text-text-muted mb-3">
{article.category.name}
</p>
<h1 className="text-3xl lg:text-4xl font-bold text-text-primary leading-tight mb-4">
{article.title}
</h1>
{article.excerpt && (
<p className="text-lg text-text-secondary leading-relaxed mb-6">
{article.excerpt}
</p>
)}
<div className="flex items-center gap-3 py-3.5 border-t border-b border-border mb-8">
{!!article.is_featured && (
<span className="bg-violet text-white text-[9px] font-bold uppercase tracking-widest px-2 py-1 rounded">
Featured
</span>
)}
{publishedDate && (
<span className="text-xs text-text-muted">{publishedDate}</span>
)}
</div>
{article.featured_image && (
<div className="relative aspect-video rounded-md overflow-hidden mb-9">
<Image
src={getAssetUrl(article.featured_image, { width: 1200, quality: 85 })}
alt={article.title}
fill
priority
className="object-cover"
sizes="(max-width: 780px) 100vw, 780px"
/>
</div>
)}
{article.content && <ArticleBody html={article.content} />}
<TagRow tags={article.tags} />
</article>
{related.length > 0 && (
<div className="max-w-[1200px] mx-auto">
<ArticleGrid
articles={related}
title={`More from ${article.category.name}`}
/>
</div>
)}
</>
)
}