initial commit
This commit is contained in:
101
components/topics/TopicCard.tsx
Normal file
101
components/topics/TopicCard.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Card, CardContent } from '@/components/ui/card'
|
||||
import { Calendar, Clock, User } from 'lucide-react'
|
||||
import { ITopic } from '@/models/topic'
|
||||
|
||||
interface TopicCardProps {
|
||||
topic: ITopic
|
||||
showAuthor?: boolean
|
||||
showReadingTime?: boolean
|
||||
}
|
||||
|
||||
export function TopicCard({ topic, showAuthor = true, showReadingTime = true }: TopicCardProps) {
|
||||
const publishedDate = new Date(topic.publishedAt).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})
|
||||
|
||||
return (
|
||||
<Link href={`/topics/${topic.slug}`} className="group block">
|
||||
<Card className="overflow-hidden transition-all duration-300 hover:shadow-lg hover:scale-[1.02]">
|
||||
<div className="aspect-video relative overflow-hidden">
|
||||
<Image
|
||||
src={topic.coverImage}
|
||||
alt={topic.title}
|
||||
fill
|
||||
className="object-cover transition-transform duration-300 group-hover:scale-105"
|
||||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<CardContent className="p-6">
|
||||
{/* Metadata */}
|
||||
<div className="flex items-center gap-4 text-sm text-muted-foreground mb-3">
|
||||
<div className="flex items-center gap-1">
|
||||
<Calendar className="w-3 h-3" />
|
||||
<time dateTime={new Date(topic.publishedAt).toISOString()}>
|
||||
{publishedDate}
|
||||
</time>
|
||||
</div>
|
||||
|
||||
{showReadingTime && topic.readingTime && (
|
||||
<div className="flex items-center gap-1">
|
||||
<Clock className="w-3 h-3" />
|
||||
<span>{topic.readingTime.text}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showAuthor && (
|
||||
<div className="flex items-center gap-1">
|
||||
<User className="w-3 h-3" />
|
||||
<span>{topic.author}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<h2 className="text-xl font-bold mb-3 line-clamp-2 transition-colors group-hover:text-primary">
|
||||
{topic.title}
|
||||
</h2>
|
||||
|
||||
{/* Excerpt */}
|
||||
<p className="text-muted-foreground mb-4 line-clamp-3 leading-relaxed">
|
||||
{topic.excerpt}
|
||||
</p>
|
||||
|
||||
{/* Tags */}
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{topic.tags.slice(0, 3).map((tag) => (
|
||||
<Badge
|
||||
key={tag.id || tag.name}
|
||||
variant="secondary"
|
||||
className="text-xs hover:bg-primary hover:text-primary-foreground transition-colors"
|
||||
>
|
||||
{tag.name}
|
||||
</Badge>
|
||||
))}
|
||||
{topic.tags.length > 3 && (
|
||||
<Badge variant="outline" className="text-xs">
|
||||
+{topic.tags.length - 3} more
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Featured indicator */}
|
||||
{topic.featured && (
|
||||
<div className="mt-3">
|
||||
<Badge className="text-xs bg-gradient-to-r from-amber-500 to-orange-500 hover:from-amber-600 hover:to-orange-600">
|
||||
Featured
|
||||
</Badge>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
export default TopicCard
|
||||
Reference in New Issue
Block a user