66 lines
2.3 KiB
TypeScript

import Link from 'next/link'
import { formatDate, getExcerpt, getReadingTime, type Post } from '@/app/blog/utils'
import { cn } from '@/lib/utils'
import { Card } from '@/components/ui/card'
type PostCardProps = {
post: Post
className?: string
}
export default function PostCard({ post, className }: PostCardProps) {
const { metadata, slug, content } = post
const excerpt = getExcerpt(metadata.summary, content, 200)
const reading = getReadingTime(content)
return (
<Card
className={cn(
'group relative overflow-hidden transition-colors hover:bg-black/[0.04] dark:hover:bg-white/5',
className
)}
>
<Link
href={`/blog/${slug}`}
aria-label={`Read: ${metadata.title}`}
className="absolute inset-0"
/>
<div className="p-5">
<header className="mb-3">
<h3 className="text-lg font-semibold leading-tight tracking-tight text-neutral-900 dark:text-neutral-100 underline-offset-4 group-hover:underline">
{metadata.title}
</h3>
</header>
{excerpt && (
<p className="mb-4 line-clamp-3 text-sm leading-6 text-neutral-700 dark:text-neutral-300">
{excerpt}
</p>
)}
<footer className="flex flex-wrap items-center gap-2 text-xs text-neutral-600 dark:text-neutral-400">
<time dateTime={metadata.publishedAt}>{formatDate(metadata.publishedAt, false)}</time>
<span aria-hidden="true"></span>
<span>{reading.text}</span>
{Array.isArray(metadata.tags) && metadata.tags.length > 0 && (
<>
<span aria-hidden="true"></span>
<ul className="flex flex-wrap gap-1.5">
{metadata.tags.slice(0, 3).map((tag) => (
<li
key={tag}
className="rounded-full border border-black/10 bg-white/40 px-2 py-0.5 text-[11px] leading-5 dark:border-white/10 dark:bg-white/[0.06]"
>
{tag}
</li>
))}
</ul>
</>
)}
<span className="ml-auto hidden text-neutral-400 transition-colors group-hover:text-neutral-600 dark:group-hover:text-neutral-300 sm:inline">
</span>
</footer>
</div>
</Card>
)
}