Files
sapiens-web/client/src/components/ArticleCard.tsx
kimjaehyeon0101 2cbad88faa Add core UI components and layout for media platform
Initializes the client-side application with fundamental UI components, including navigation, cards for articles and auctions, and various elements for user interaction and display.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 069d4324-6c40-4355-955e-c714a50de1ea
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3df548ff-50ae-432f-9be4-25d34eccc983/069d4324-6c40-4355-955e-c714a50de1ea/bVdKIaU
2025-09-29 14:35:50 +00:00

123 lines
4.1 KiB
TypeScript

import { Card, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import type { Article, MediaOutlet } from "@shared/schema";
interface ArticleCardProps {
article: Article;
outlet: MediaOutlet;
viewMode?: "grid" | "list";
}
export default function ArticleCard({ article, outlet, viewMode = "grid" }: ArticleCardProps) {
const handleClick = () => {
window.location.href = `/articles/${article.slug}`;
};
const formatDate = (date: string | Date) => {
const d = new Date(date);
const now = new Date();
const diffTime = Math.abs(now.getTime() - d.getTime());
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
if (diffDays === 1) return "1 day ago";
if (diffDays < 7) return `${diffDays} days ago`;
return d.toLocaleDateString();
};
if (viewMode === "list") {
return (
<Card
className="card-hover cursor-pointer"
onClick={handleClick}
data-testid={`card-article-${article.slug}`}
>
<CardContent className="p-6">
<div className="flex space-x-6">
{article.imageUrl && (
<img
src={article.imageUrl}
alt={article.title}
className="w-32 h-24 object-cover rounded-lg flex-shrink-0"
/>
)}
<div className="flex-1">
<div className="flex items-center space-x-2 mb-2">
{article.isPinned && (
<Badge variant="destructive" className="text-xs">
<i className="fas fa-thumbtack mr-1"></i>
Pinned
</Badge>
)}
{article.isFeatured && (
<Badge variant="default" className="text-xs">
Featured
</Badge>
)}
</div>
<h3 className="text-lg font-semibold mb-2 line-clamp-2">{article.title}</h3>
<p className="text-sm text-muted-foreground mb-3 line-clamp-2">{article.excerpt}</p>
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2 text-xs text-muted-foreground">
<span>{formatDate(article.publishedAt!)}</span>
{article.tags?.map((tag) => (
<Badge key={tag} variant="outline" className="text-xs">
{tag}
</Badge>
))}
</div>
</div>
</div>
</div>
</CardContent>
</Card>
);
}
return (
<Card
className="card-hover cursor-pointer"
onClick={handleClick}
data-testid={`card-article-${article.slug}`}
>
<CardContent className="p-0">
{article.imageUrl && (
<img
src={article.imageUrl}
alt={article.title}
className="w-full h-48 object-cover rounded-t-xl"
/>
)}
<div className="p-6">
<div className="flex items-center space-x-2 mb-2">
{article.isPinned && (
<Badge variant="destructive" className="text-xs">
<i className="fas fa-thumbtack mr-1"></i>
Pinned
</Badge>
)}
{article.isFeatured && (
<Badge variant="default" className="text-xs">
Featured
</Badge>
)}
</div>
<h3 className="text-lg font-semibold mb-2 line-clamp-2">{article.title}</h3>
<p className="text-sm text-muted-foreground mb-4 line-clamp-3">{article.excerpt}</p>
<div className="flex items-center justify-between">
<span className="text-xs text-muted-foreground">
{formatDate(article.publishedAt!)}
</span>
<div className="flex items-center space-x-1">
{article.tags?.slice(0, 2).map((tag) => (
<Badge key={tag} variant="outline" className="text-xs">
{tag}
</Badge>
))}
</div>
</div>
</div>
</CardContent>
</Card>
);
}