import { getArticleBySlug } from '@/lib/feature/article/article.external'; import { ArrowLeftIcon, CalendarIcon, ClockIcon } from 'lucide-react'; import Link from 'next/link'; import { notFound } from 'next/navigation'; import { Suspense } from 'react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; type ArticlePageProps = { params: Promise<{ slug: string }>; }; type ArticleContentProps = { params: Promise<{ slug: string }>; }; function readingTime(text: string): number { const words = text.trim().split(/\s+/).length; return Math.max(1, Math.ceil(words / 200)); } function formatDate(date: Date): string { return new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric', }).format(date); } const ArticleContentSkeleton = () => (

{[...Array(4)].map((_, i) => (
))}
); const ArticleContent = async ({ params }: ArticleContentProps) => { const { slug } = await params; const articleResult = await getArticleBySlug(slug); if (!articleResult.ok) throw articleResult.error; const article = articleResult.value; if (!article) notFound(); const minutes = readingTime(article.content); return (
{article.coverImageUrl && (
{/* eslint-disable-next-line @next/next/no-img-element */} {article.title}
)}
All articles

{article.title}

{article.description}

{minutes} min read

{article.content}
); }; const ArticlePage = ({ params }: ArticlePageProps) => { return ( }> ); }; export default ArticlePage;