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
This commit is contained in:
122
client/src/components/ArticleCard.tsx
Normal file
122
client/src/components/ArticleCard.tsx
Normal file
@ -0,0 +1,122 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user