| // src/components/article/ArticleMeta.tsx | |
| import { Calendar, Clock, Eye, Heart, MessageCircle, Bookmark, Share2, TrendingUp, Award, Star } from "lucide-react"; | |
| import { Link } from "react-router-dom"; | |
| interface ArticleMetaProps { | |
| createdAt: string; | |
| updatedAt?: string; | |
| readTime?: number; | |
| views?: number; | |
| likes?: number; | |
| comments?: number; | |
| bookmarks?: number; | |
| shares?: number; | |
| citations?: number; | |
| impactScore?: number; | |
| className?: string; | |
| variant?: "horizontal" | "vertical" | "compact"; | |
| showIcons?: boolean; | |
| showLabels?: boolean; | |
| } | |
| export function ArticleMeta({ | |
| createdAt, | |
| updatedAt, | |
| readTime = 5, | |
| views = 0, | |
| likes = 0, | |
| comments = 0, | |
| bookmarks = 0, | |
| shares = 0, | |
| citations = 0, | |
| impactScore, | |
| className = "", | |
| variant = "horizontal", | |
| showIcons = true, | |
| showLabels = false | |
| }: ArticleMetaProps) { | |
| const formatDate = (date: string) => { | |
| const d = new Date(date); | |
| const now = new Date(); | |
| const diff = now.getTime() - d.getTime(); | |
| const days = Math.floor(diff / (1000 * 60 * 60 * 24)); | |
| if (days === 0) return "Today"; | |
| if (days === 1) return "Yesterday"; | |
| if (days < 7) return `${days} days ago`; | |
| if (days < 30) return `${Math.floor(days / 7)} weeks ago`; | |
| if (days < 365) return `${Math.floor(days / 30)} months ago`; | |
| return `${Math.floor(days / 365)} years ago`; | |
| }; | |
| const metaItems = [ | |
| { icon: Calendar, label: "Published", value: formatDate(createdAt) }, | |
| { icon: Clock, label: "Read time", value: `${readTime} min` }, | |
| { icon: Eye, label: "Views", value: views.toLocaleString() }, | |
| { icon: Heart, label: "Likes", value: likes.toLocaleString() }, | |
| { icon: MessageCircle, label: "Comments", value: comments.toLocaleString() }, | |
| { icon: Bookmark, label: "Bookmarks", value: bookmarks.toLocaleString() }, | |
| { icon: Share2, label: "Shares", value: shares.toLocaleString() }, | |
| { icon: TrendingUp, label: "Citations", value: citations.toLocaleString() }, | |
| ]; | |
| if (variant === "vertical") { | |
| return ( | |
| <div className={`space-y-2 ${className}`}> | |
| {metaItems.map((item) => ( | |
| <div key={item.label} className="flex items-center justify-between text-sm"> | |
| <div className="flex items-center gap-2 text-gray-500"> | |
| <item.icon className="h-4 w-4" /> | |
| {showLabels && <span>{item.label}</span>} | |
| </div> | |
| <span className="font-medium text-gray-900 dark:text-white">{item.value}</span> | |
| </div> | |
| ))} | |
| {impactScore && ( | |
| <div className="flex items-center justify-between text-sm pt-2 border-t border-gray-100 dark:border-gray-700"> | |
| <div className="flex items-center gap-2 text-gray-500"> | |
| <Award className="h-4 w-4" /> | |
| <span>Impact Score</span> | |
| </div> | |
| <div className="flex items-center gap-1"> | |
| <Star className="h-3 w-3 fill-yellow-400 text-yellow-400" /> | |
| <span className="font-bold text-yellow-600">{impactScore}</span> | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| } | |
| if (variant === "compact") { | |
| return ( | |
| <div className={`flex flex-wrap items-center gap-3 text-xs text-gray-500 ${className}`}> | |
| <span className="flex items-center gap-1"> | |
| <Calendar className="h-3 w-3" /> | |
| {formatDate(createdAt)} | |
| </span> | |
| <span className="flex items-center gap-1"> | |
| <Clock className="h-3 w-3" /> | |
| {readTime} min | |
| </span> | |
| <span className="flex items-center gap-1"> | |
| <Eye className="h-3 w-3" /> | |
| {views} | |
| </span> | |
| <span className="flex items-center gap-1"> | |
| <Heart className="h-3 w-3" /> | |
| {likes} | |
| </span> | |
| </div> | |
| ); | |
| } | |
| // Horizontal variant (default) | |
| return ( | |
| <div className={`flex flex-wrap items-center gap-4 text-sm text-gray-500 ${className}`}> | |
| {metaItems.map((item) => ( | |
| <div key={item.label} className="flex items-center gap-1.5"> | |
| {showIcons && <item.icon className="h-4 w-4" />} | |
| <span>{item.value}</span> | |
| {showLabels && <span className="text-xs text-gray-400">{item.label}</span>} | |
| </div> | |
| ))} | |
| </div> | |
| ); | |
| } |
Xet Storage Details
- Size:
- 4.26 kB
- Xet hash:
- 015e6069c34fab5abe2ebecd794c5f6baef6bd2f840e397a674baf6de74f9d52
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.