| "use client"; |
|
|
| import * as React from "react"; |
| import { |
| FileText, |
| Quote, |
| ExternalLink, |
| Plus, |
| Calendar, |
| User |
| } from "lucide-react"; |
| import { Button } from "@/components/atoms/Button"; |
| import { Badge } from "@/components/atoms/Badge"; |
| import { Icon } from "@/components/atoms/Icon"; |
| import { cn } from "@/cn"; |
|
|
| export interface PaperCardProps { |
| title: string; |
| authors: string[]; |
| year: number; |
| journal?: string; |
| citationCount?: number; |
| isOpenAccess?: boolean; |
| doi?: string; |
| onAdd?: () => void; |
| className?: string; |
| } |
|
|
| |
| |
| |
| |
| |
| export function PaperCard({ |
| title, |
| authors, |
| year, |
| journal, |
| citationCount, |
| isOpenAccess, |
| doi, |
| onAdd, |
| className, |
| }: PaperCardProps) { |
| |
| const displayAuthors = authors.length > 3 |
| ? `${authors.slice(0, 3).join(", ")} et al.` |
| : authors.join(", "); |
|
|
| return ( |
| <div className={cn( |
| "group relative flex flex-col gap-3 rounded-lg border bg-card p-5 transition-all hover:shadow-md", |
| className |
| )}> |
| {/* 1. Status Badges */} |
| <div className="flex items-center justify-between"> |
| <div className="flex gap-2"> |
| {isOpenAccess && ( |
| <Badge variant="success" className="text-[10px] h-5">Open Access</Badge> |
| )} |
| {journal && ( |
| <Badge variant="secondary" className="max-w-[150px] truncate text-[10px] h-5"> |
| {journal} |
| </Badge> |
| )} |
| </div> |
| <div className="flex items-center gap-1 text-xs text-muted-foreground"> |
| <Icon icon={Calendar} size={12} /> |
| {year} |
| </div> |
| </div> |
| |
| {/* 2. Paper Content */} |
| <div className="space-y-1"> |
| <h3 className="line-clamp-2 text-base font-semibold leading-snug tracking-tight text-foreground group-hover:text-primary transition-colors"> |
| {title} |
| </h3> |
| <div className="flex items-center gap-1 text-sm text-muted-foreground"> |
| <Icon icon={User} size={14} className="mt-0.5" /> |
| <span className="line-clamp-1">{displayAuthors}</span> |
| </div> |
| </div> |
| |
| {/* 3. Metrics & Metadata */} |
| <div className="flex items-center gap-4 text-xs font-medium text-muted-foreground"> |
| <div className="flex items-center gap-1"> |
| <Icon icon={Quote} size={12} /> |
| {citationCount ?? 0} Citations |
| </div> |
| {doi && ( |
| <a |
| href={`https://doi.org/${doi}`} |
| target="_blank" |
| rel="noopener noreferrer" |
| className="flex items-center gap-1 hover:text-primary transition-colors" |
| > |
| <Icon icon={ExternalLink} size={12} /> |
| DOI |
| </a> |
| )} |
| </div> |
| |
| {/* 4. Action Bar */} |
| <div className="mt-2 flex items-center gap-2"> |
| <Button variant="default" size="sm" className="h-8 w-full gap-2" onClick={onAdd}> |
| <Icon icon={Plus} size={14} /> |
| Add to Library |
| </Button> |
| <Button variant="outline" size="sm" className="h-8 px-2" asChild title="View Details"> |
| <a href={`/explore/${doi || 'details'}`}> |
| <Icon icon={FileText} size={14} /> |
| </a> |
| </Button> |
| </div> |
| </div> |
| ); |
| } |
|
|