tahamajs/IE / Projects /front /src /components /article /ArticleStats.tsx
tahamajs's picture
download
raw
4.6 kB
// src/components/article/ArticleStats.tsx
import { TrendingUp, Award, Star, Users, Eye, Heart, MessageCircle, Bookmark, Share2, Activity, BarChart3 } from "lucide-react";
import { motion } from "framer-motion";
interface ArticleStatsProps {
views: number;
likes: number;
comments: number;
bookmarks: number;
shares: number;
citations: number;
impactScore?: number;
percentile?: number;
trending?: boolean;
className?: string;
variant?: "grid" | "list" | "minimal";
showProgress?: boolean;
}
export function ArticleStats({
views,
likes,
comments,
bookmarks,
shares,
citations,
impactScore,
percentile,
trending,
className = "",
variant = "grid",
showProgress = false
}: ArticleStatsProps) {
const getScoreColor = (score: number) => {
if (score >= 80) return "text-green-600";
if (score >= 60) return "text-blue-600";
if (score >= 40) return "text-yellow-600";
return "text-red-600";
};
const statsItems = [
{ icon: Eye, label: "Views", value: views.toLocaleString(), color: "blue" },
{ icon: Heart, label: "Likes", value: likes.toLocaleString(), color: "pink" },
{ icon: MessageCircle, label: "Comments", value: comments.toLocaleString(), color: "green" },
{ icon: Bookmark, label: "Bookmarks", value: bookmarks.toLocaleString(), color: "purple" },
{ icon: Share2, label: "Shares", value: shares.toLocaleString(), color: "orange" },
{ icon: TrendingUp, label: "Citations", value: citations.toLocaleString(), color: "teal" },
];
if (variant === "list") {
return (
<div className={`space-y-3 ${className}`}>
{statsItems.map((stat) => (
<div key={stat.label} className="flex items-center justify-between">
<div className="flex items-center gap-2 text-gray-600 dark:text-gray-400">
<stat.icon className="h-4 w-4" />
<span className="text-sm">{stat.label}</span>
</div>
<span className="font-semibold text-gray-900 dark:text-white">{stat.value}</span>
</div>
))}
{impactScore && (
<div className="pt-2 border-t border-gray-100 dark:border-gray-800">
<div className="flex items-center justify-between mb-1">
<div className="flex items-center gap-2 text-gray-600 dark:text-gray-400">
<Award className="h-4 w-4" />
<span className="text-sm">Impact Score</span>
</div>
<span className={`font-bold ${getScoreColor(impactScore)}`}>{impactScore}</span>
</div>
{showProgress && (
<div className="h-1.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
<div
className="h-full bg-gradient-to-r from-primary to-secondary rounded-full transition-all"
style={{ width: `${impactScore}%` }}
/>
</div>
)}
</div>
)}
{percentile && (
<div className="text-xs text-gray-500">
<TrendingUp className="inline h-3 w-3 mr-1" />
Top {percentile}% of articles
</div>
)}
{trending && (
<div className="text-xs text-orange-500 flex items-center gap-1">
<Activity className="h-3 w-3 animate-pulse" />
Trending today
</div>
)}
</div>
);
}
if (variant === "minimal") {
return (
<div className={`flex items-center gap-4 text-sm text-gray-500 ${className}`}>
<span className="flex items-center gap-1">
<Eye className="h-4 w-4" />
{views}
</span>
<span className="flex items-center gap-1">
<Heart className="h-4 w-4" />
{likes}
</span>
<span className="flex items-center gap-1">
<MessageCircle className="h-4 w-4" />
{comments}
</span>
</div>
);
}
// Grid variant (default)
return (
<div className={`grid grid-cols-3 gap-4 ${className}`}>
{statsItems.map((stat) => (
<motion.div
key={stat.label}
whileHover={{ y: -2 }}
className="text-center p-3 rounded-xl bg-gray-50 dark:bg-gray-800/50 hover:bg-gray-100 dark:hover:bg-gray-800 transition"
>
<stat.icon className={`h-5 w-5 mx-auto mb-1 text-${stat.color}-500`} />
<div className="text-lg font-bold text-gray-900 dark:text-white">{stat.value}</div>
<div className="text-xs text-gray-500">{stat.label}</div>
</motion.div>
))}
</div>
);
}

Xet Storage Details

Size:
4.6 kB
·
Xet hash:
19caf6df22c3735f5824f415d8eed70ef503d702f87bb0a39326a9c0eb4381ca

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.