Update README.md to provide a comprehensive overview of the ScriptShare platform, including features, tech stack, setup instructions, admin capabilities, and contribution guidelines.
This commit is contained in:
148
src/components/ScriptCard.tsx
Normal file
148
src/components/ScriptCard.tsx
Normal file
@ -0,0 +1,148 @@
|
||||
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>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user