142 lines
5.2 KiB
TypeScript
142 lines
5.2 KiB
TypeScript
import { useQuery } from '@tanstack/react-query';
|
||
import { Link } from '@tanstack/react-router';
|
||
import { fetchHeroArticle } from '@/lib/api';
|
||
import { Button } from '@/components/ui/button';
|
||
import { Calendar, User, ArrowRight, Eye } from 'lucide-react';
|
||
|
||
export function HeroArticle() {
|
||
const { data: article, isLoading, error } = useQuery({
|
||
queryKey: ['hero-article'],
|
||
queryFn: fetchHeroArticle,
|
||
});
|
||
|
||
if (isLoading) {
|
||
return (
|
||
<div className="border-brutal bg-card p-0 animate-pulse">
|
||
<div className="h-80 bg-muted"></div>
|
||
<div className="p-8">
|
||
<div className="h-10 bg-muted rounded w-3/4 mb-4"></div>
|
||
<div className="h-4 bg-muted rounded w-1/2 mb-6"></div>
|
||
<div className="h-20 bg-muted rounded mb-6"></div>
|
||
<div className="h-12 bg-muted rounded w-40"></div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
if (error) {
|
||
return (
|
||
<div className="border-brutal bg-card p-8 text-center">
|
||
<div className="text-destructive text-xl font-display mb-4">ERROR</div>
|
||
<Button variant="brutal" onClick={() => window.location.reload()}>
|
||
Retry
|
||
</Button>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
if (!article) {
|
||
return (
|
||
<div className="border-brutal bg-card p-12 text-center">
|
||
<div className="inline-flex items-center justify-center w-20 h-20 border-4 border-foreground mb-6">
|
||
<span className="font-display text-4xl">?</span>
|
||
</div>
|
||
<h2 className="text-3xl font-display mb-4">NO HERO ARTICLE</h2>
|
||
<p className="font-body text-muted-foreground mb-4">
|
||
Mark an article as "Hero" in the admin panel to feature it here.
|
||
</p>
|
||
<div className="font-body text-xs uppercase tracking-wider text-muted-foreground mt-8 border-t-2 border-foreground/20 pt-4">
|
||
This space will showcase your most important story
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<article className="group border-brutal bg-card hover:shadow-brutal transition-all duration-200 animate-fade-in-up">
|
||
{article.featuredImage && (
|
||
<div className="relative overflow-hidden">
|
||
<div className="absolute top-0 left-0 z-10">
|
||
<span className="inline-block px-4 py-2 bg-accent text-foreground font-body text-sm font-bold uppercase tracking-wider border-b-2 border-r-2 border-foreground">
|
||
Прекршени Вести
|
||
</span>
|
||
</div>
|
||
<div className="relative h-72 md:h-96 overflow-hidden">
|
||
<img
|
||
src={article.featuredImage}
|
||
alt={article.title}
|
||
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
|
||
/>
|
||
<div className="absolute inset-0 bg-gradient-to-t from-foreground/80 via-foreground/20 to-transparent"></div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
<div className="p-6 md:p-8">
|
||
<h2 className="text-3xl md:text-4xl font-display leading-tight mb-4 line-clamp-2 group-hover:text-accent transition-colors">
|
||
{article.title}
|
||
</h2>
|
||
|
||
<div className="flex flex-wrap items-center gap-4 font-body text-sm uppercase tracking-wider text-muted-foreground mb-6 pb-4 border-b-2 border-foreground/10">
|
||
<div className="flex items-center gap-2">
|
||
<Calendar className="w-4 h-4" />
|
||
<span>
|
||
{new Date(article.createdAt).toLocaleDateString('mk-MK', {
|
||
day: 'numeric',
|
||
month: 'short',
|
||
year: 'numeric',
|
||
})}
|
||
</span>
|
||
</div>
|
||
|
||
{article.author && (
|
||
<div className="flex items-center gap-2">
|
||
<User className="w-4 h-4" />
|
||
<span>{article.author.name}</span>
|
||
</div>
|
||
)}
|
||
|
||
<div className="flex items-center gap-2">
|
||
<Eye className="w-4 h-4" />
|
||
<span>{article.views} views</span>
|
||
</div>
|
||
</div>
|
||
|
||
{article.excerpt && (
|
||
<p className="text-muted-foreground mb-6 line-clamp-3 font-body">
|
||
{article.excerpt}
|
||
</p>
|
||
)}
|
||
|
||
{article.tags && article.tags.length > 0 && (
|
||
<div className="flex flex-wrap gap-2 mb-6">
|
||
{article.tags.map((tag) => (
|
||
<span
|
||
key={tag}
|
||
className="px-3 py-1 text-xs font-body uppercase tracking-wider border-2 border-foreground bg-background"
|
||
>
|
||
#{tag}
|
||
</span>
|
||
))}
|
||
</div>
|
||
)}
|
||
|
||
<div className="flex items-center justify-between pt-4 border-t-2 border-foreground/10">
|
||
<Link to="/articles/$id" params={{ id: article.id }}>
|
||
<Button variant="brutalAccent" className="gap-2">
|
||
Read Full Story
|
||
<ArrowRight className="w-4 h-4" />
|
||
</Button>
|
||
</Link>
|
||
|
||
<div className="font-body text-xs uppercase tracking-wider text-muted-foreground">
|
||
<span className="font-bold text-foreground">
|
||
{(article.facebookShares || 0) + (article.twitterShares || 0) + (article.whatsappShares || 0) + (article.telegramShares || 0)}
|
||
</span> shares
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</article>
|
||
);
|
||
}
|