149 lines
5.9 KiB
TypeScript
149 lines
5.9 KiB
TypeScript
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||
|
import { Badge } from '@/components/ui/badge';
|
||
|
import { Button } from '@/components/ui/button';
|
||
|
import { Eye, Calendar, User, ExternalLink, Star, TrendingUp } from 'lucide-react';
|
||
|
import { useNavigate } from 'react-router-dom';
|
||
|
import { formatDate, formatRelativeTime } from '@/lib/utils';
|
||
|
|
||
|
export interface Script {
|
||
|
id: string;
|
||
|
name: string;
|
||
|
description: string;
|
||
|
compatible_os: string[];
|
||
|
categories: string[];
|
||
|
git_repository_url?: string;
|
||
|
author_name: string;
|
||
|
view_count: number;
|
||
|
created_at: string;
|
||
|
updated_at: string;
|
||
|
is_approved: boolean;
|
||
|
}
|
||
|
|
||
|
interface ScriptCardProps {
|
||
|
script: Script;
|
||
|
}
|
||
|
|
||
|
export function ScriptCard({ script }: ScriptCardProps) {
|
||
|
const navigate = useNavigate();
|
||
|
|
||
|
const isPopular = script.view_count > 500;
|
||
|
const isTrending = script.view_count > 200;
|
||
|
|
||
|
return (
|
||
|
<Card className="group h-full hover:shadow-2xl hover:shadow-primary/20 transition-all duration-500 cursor-pointer border-muted/50 hover:border-primary/30 bg-gradient-to-br from-card/80 to-card/40 backdrop-blur-sm hover:scale-[1.02] relative overflow-hidden">
|
||
|
{/* Animated background gradient */}
|
||
|
<div className="absolute inset-0 bg-gradient-to-br from-primary/5 via-transparent to-accent/5 opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
|
||
|
|
||
|
{/* Popular/Trending indicators */}
|
||
|
{isPopular && (
|
||
|
<div className="absolute top-3 right-3 z-10">
|
||
|
<Badge variant="secondary" className="bg-gradient-to-r from-yellow-500/20 to-orange-500/20 text-yellow-600 border-yellow-500/30 animate-pulse">
|
||
|
<Star className="h-3 w-3 mr-1 fill-current" />
|
||
|
Popular
|
||
|
</Badge>
|
||
|
</div>
|
||
|
)}
|
||
|
{isTrending && !isPopular && (
|
||
|
<div className="absolute top-3 right-3 z-10">
|
||
|
<Badge variant="secondary" className="bg-gradient-to-r from-green-500/20 to-emerald-500/20 text-green-600 border-green-500/30">
|
||
|
<TrendingUp className="h-3 w-3 mr-1" />
|
||
|
Trending
|
||
|
</Badge>
|
||
|
</div>
|
||
|
)}
|
||
|
|
||
|
<CardHeader onClick={() => navigate(`/script/${script.id}`)} className="pb-3 relative z-10">
|
||
|
<CardTitle className="line-clamp-2 group-hover:text-transparent group-hover:bg-gradient-to-r group-hover:from-primary group-hover:to-accent group-hover:bg-clip-text transition-all duration-300 text-lg font-bold">
|
||
|
{script.name}
|
||
|
</CardTitle>
|
||
|
<CardDescription className="line-clamp-3 text-muted-foreground group-hover:text-foreground/80 transition-colors duration-300">
|
||
|
{script.description}
|
||
|
</CardDescription>
|
||
|
</CardHeader>
|
||
|
|
||
|
<CardContent className="space-y-4 relative z-10">
|
||
|
{/* OS Badges with enhanced styling */}
|
||
|
<div className="flex flex-wrap gap-1.5">
|
||
|
{script.compatible_os.map(os => (
|
||
|
<Badge
|
||
|
key={os}
|
||
|
variant="outline"
|
||
|
className="text-xs border-primary/40 text-primary/90 hover:bg-primary/15 transition-all duration-200 rounded-full px-2 py-1 font-medium shadow-sm"
|
||
|
>
|
||
|
{os}
|
||
|
</Badge>
|
||
|
))}
|
||
|
</div>
|
||
|
|
||
|
{/* Categories with gradient backgrounds */}
|
||
|
<div className="flex flex-wrap gap-1.5">
|
||
|
{script.categories.slice(0, 2).map((category, index) => (
|
||
|
<Badge
|
||
|
key={category}
|
||
|
variant="secondary"
|
||
|
className={`text-xs rounded-full px-3 py-1 font-medium shadow-sm transition-all duration-200 hover:scale-105 ${
|
||
|
index === 0
|
||
|
? 'bg-gradient-to-r from-accent/20 to-accent/30 text-accent border-accent/30'
|
||
|
: 'bg-gradient-to-r from-primary/20 to-primary/30 text-primary border-primary/30'
|
||
|
}`}
|
||
|
>
|
||
|
{category}
|
||
|
</Badge>
|
||
|
))}
|
||
|
{script.categories.length > 2 && (
|
||
|
<Badge
|
||
|
variant="secondary"
|
||
|
className="text-xs bg-gradient-to-r from-muted to-muted/80 text-muted-foreground border-muted rounded-full px-2 py-1"
|
||
|
>
|
||
|
+{script.categories.length - 2}
|
||
|
</Badge>
|
||
|
)}
|
||
|
</div>
|
||
|
|
||
|
{/* Script metadata */}
|
||
|
<div className="flex items-center justify-between text-xs text-muted-foreground">
|
||
|
<div className="flex items-center space-x-3">
|
||
|
<div className="flex items-center space-x-1">
|
||
|
<User className="h-3 w-3" />
|
||
|
<span>{script.author_name}</span>
|
||
|
</div>
|
||
|
<div className="flex items-center space-x-1">
|
||
|
<Eye className="h-3 w-3" />
|
||
|
<span>{script.view_count.toLocaleString()}</span>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div className="flex items-center space-x-1">
|
||
|
<Calendar className="h-3 w-3" />
|
||
|
<span title={formatDate(script.updated_at)}>
|
||
|
{formatRelativeTime(script.updated_at)}
|
||
|
</span>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
{/* Action buttons */}
|
||
|
<div className="flex space-x-2 pt-2">
|
||
|
<Button
|
||
|
size="sm"
|
||
|
className="flex-1 bg-gradient-to-r from-primary to-accent hover:from-primary/90 hover:to-accent/90"
|
||
|
onClick={() => navigate(`/script/${script.id}`)}
|
||
|
>
|
||
|
View Script
|
||
|
</Button>
|
||
|
{script.git_repository_url && (
|
||
|
<Button
|
||
|
size="sm"
|
||
|
variant="outline"
|
||
|
onClick={(e) => {
|
||
|
e.stopPropagation();
|
||
|
window.open(script.git_repository_url, '_blank');
|
||
|
}}
|
||
|
>
|
||
|
<ExternalLink className="h-3 w-3" />
|
||
|
</Button>
|
||
|
)}
|
||
|
</div>
|
||
|
</CardContent>
|
||
|
</Card>
|
||
|
);
|
||
|
}
|