Erik Sarriegui
commit
5353e5f
import { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { ChevronDown, ExternalLink, Hash, FileText } from 'lucide-react';
import { cn } from '../lib/utils';
export default function ClusterCard({ cluster }) {
const [isExpanded, setIsExpanded] = useState(false);
return (
<motion.div
layout
className="bg-slate-900/50 border border-slate-800 rounded-xl overflow-hidden hover:border-slate-700 transition-colors"
>
<div
onClick={() => setIsExpanded(!isExpanded)}
className="p-5 cursor-pointer flex items-start justify-between gap-4"
>
<div className="flex-1">
<div className="flex items-center gap-2 mb-2">
<span className="bg-blue-500/10 text-blue-400 text-xs px-2 py-0.5 rounded-full border border-blue-500/20 font-medium flex items-center gap-1">
<Hash className="w-3 h-3" />
Cluster {cluster.cluster_id}
</span>
<span className="bg-slate-800 text-slate-400 text-xs px-2 py-0.5 rounded-full font-medium flex items-center gap-1">
<FileText className="w-3 h-3" />
{cluster.size} articles
</span>
</div>
<h3 className="text-lg font-semibold text-slate-200 leading-tight group-hover:text-blue-400 transition-colors">
{cluster.title}
</h3>
</div>
<motion.div
animate={{ rotate: isExpanded ? 180 : 0 }}
transition={{ duration: 0.2 }}
className="bg-slate-800 p-1.5 rounded-lg text-slate-400"
>
<ChevronDown className="w-5 h-5" />
</motion.div>
</div>
<AnimatePresence>
{isExpanded && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: "auto", opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
className="border-t border-slate-800 bg-slate-900/30"
>
<div className="p-4 space-y-3">
{cluster.articles.map((article, idx) => (
<a
key={idx}
href={article.url}
target="_blank"
rel="noopener noreferrer"
className="block p-3 rounded-lg bg-slate-800/40 hover:bg-slate-800 transition-colors group"
>
<div className="flex justify-between items-start gap-3">
<div className="flex-1">
<h4 className="text-sm text-slate-300 font-medium line-clamp-2 group-hover:text-blue-300 mb-1">
{article.title}
</h4>
<div className="flex flex-wrap gap-2">
{article.is_clickbait && (
<span className="text-[10px] uppercase font-bold text-red-400 bg-red-900/30 border border-red-900/50 px-1.5 py-0.5 rounded">
Clickbait
</span>
)}
{article.is_sensationalist && (
<span className="text-[10px] uppercase font-bold text-amber-400 bg-amber-900/30 border border-amber-900/50 px-1.5 py-0.5 rounded">
Sensationalist
</span>
)}
</div>
</div>
<ExternalLink className="w-4 h-4 text-slate-500 shrink-0 group-hover:text-blue-400 mt-1" />
</div>
<div className="mt-2 flex items-center gap-2 text-xs text-slate-500">
<span className="font-semibold text-slate-400">{article.newspaper}</span>
<span></span>
<span className="truncate max-w-[200px]">{article.newspaper_url}</span>
</div>
</a>
))}
</div>
</motion.div>
)}
</AnimatePresence>
</motion.div>
);
}