Updated ArticleCard component to display thumbnail images on the right for list view. Modified Article page to display the article's image as a hero banner at the top. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 9a264234-c5d7-4dcc-adf3-a954b149b30d Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3df548ff-50ae-432f-9be4-25d34eccc983/9a264234-c5d7-4dcc-adf3-a954b149b30d/6ZMblp5
131 lines
4.4 KiB
TypeScript
131 lines
4.4 KiB
TypeScript
import { Card, CardContent } from "@/components/ui/card";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { useLocation } from "wouter";
|
|
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 [, setLocation] = useLocation();
|
|
|
|
const handleClick = () => {
|
|
setLocation(`/articles/${article.slug}`);
|
|
};
|
|
|
|
const formatPublishedTime = () => {
|
|
if (article.publishedMinutesAgo) {
|
|
return `${article.publishedMinutesAgo} min ago`;
|
|
}
|
|
// Fallback for articles without publishedMinutesAgo
|
|
if (article.publishedAt) {
|
|
const d = new Date(article.publishedAt);
|
|
const now = new Date();
|
|
const diffMinutes = Math.floor((now.getTime() - d.getTime()) / (1000 * 60));
|
|
const clampedMinutes = Math.max(1, Math.min(diffMinutes, 59));
|
|
return `${clampedMinutes} min ago`;
|
|
}
|
|
return "1 min ago";
|
|
};
|
|
|
|
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">
|
|
<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>{formatPublishedTime()}</span>
|
|
{article.tags?.map((tag) => (
|
|
<Badge key={tag} variant="outline" className="text-xs">
|
|
{tag}
|
|
</Badge>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{article.imageUrl && (
|
|
<img
|
|
src={article.imageUrl}
|
|
alt={article.title}
|
|
className="w-32 h-24 object-cover rounded-lg flex-shrink-0"
|
|
data-testid="image-article-thumbnail"
|
|
/>
|
|
)}
|
|
</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">
|
|
{formatPublishedTime()}
|
|
</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>
|
|
);
|
|
}
|