198 lines
6.3 KiB
TypeScript
198 lines
6.3 KiB
TypeScript
import { useRouter } from 'next/navigation';
|
|
import Image from 'next/image';
|
|
import { Award, ChevronRight, Crown, Trophy, Users } from 'lucide-react';
|
|
import Button from '@/components/ui/Button';
|
|
import { getMediaUrl } from '@/lib/utilities/media';
|
|
|
|
const SKILL_LEVELS: {
|
|
id: string;
|
|
label: string;
|
|
icon: React.ElementType;
|
|
color: string;
|
|
bgColor: string;
|
|
borderColor: string;
|
|
}[] = [
|
|
{
|
|
id: 'pro',
|
|
label: 'Pro',
|
|
icon: () => null,
|
|
color: 'text-yellow-400',
|
|
bgColor: 'bg-yellow-400/10',
|
|
borderColor: 'border-yellow-400/30',
|
|
},
|
|
{
|
|
id: 'advanced',
|
|
label: 'Advanced',
|
|
icon: () => null,
|
|
color: 'text-purple-400',
|
|
bgColor: 'bg-purple-400/10',
|
|
borderColor: 'border-purple-400/30',
|
|
},
|
|
{
|
|
id: 'intermediate',
|
|
label: 'Intermediate',
|
|
icon: () => null,
|
|
color: 'text-primary-blue',
|
|
bgColor: 'bg-primary-blue/10',
|
|
borderColor: 'border-primary-blue/30',
|
|
},
|
|
{
|
|
id: 'beginner',
|
|
label: 'Beginner',
|
|
icon: () => null,
|
|
color: 'text-green-400',
|
|
bgColor: 'bg-green-400/10',
|
|
borderColor: 'border-green-400/30',
|
|
},
|
|
];
|
|
|
|
interface TeamLeaderboardPreviewProps {
|
|
topTeams: Array<{
|
|
id: string;
|
|
name: string;
|
|
logoUrl?: string;
|
|
category?: string;
|
|
memberCount: number;
|
|
totalWins: number;
|
|
isRecruiting: boolean;
|
|
rating?: number;
|
|
performanceLevel: string;
|
|
}>;
|
|
onTeamClick: (id: string) => void;
|
|
}
|
|
|
|
export default function TeamLeaderboardPreview({
|
|
topTeams,
|
|
onTeamClick
|
|
}: TeamLeaderboardPreviewProps) {
|
|
const router = useRouter();
|
|
|
|
const getMedalColor = (position: number) => {
|
|
switch (position) {
|
|
case 0:
|
|
return 'text-yellow-400';
|
|
case 1:
|
|
return 'text-gray-300';
|
|
case 2:
|
|
return 'text-amber-600';
|
|
default:
|
|
return 'text-gray-500';
|
|
}
|
|
};
|
|
|
|
const getMedalBg = (position: number) => {
|
|
switch (position) {
|
|
case 0:
|
|
return 'bg-yellow-400/10 border-yellow-400/30';
|
|
case 1:
|
|
return 'bg-gray-300/10 border-gray-300/30';
|
|
case 2:
|
|
return 'bg-amber-600/10 border-amber-600/30';
|
|
default:
|
|
return 'bg-iron-gray/50 border-charcoal-outline';
|
|
}
|
|
};
|
|
|
|
if (topTeams.length === 0) return null;
|
|
|
|
return (
|
|
<div className="mb-12">
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between mb-4">
|
|
<div className="flex items-center gap-3">
|
|
<div className="flex h-11 w-11 items-center justify-center rounded-xl bg-gradient-to-br from-yellow-400/20 to-amber-600/10 border border-yellow-400/30">
|
|
<Award className="w-5 h-5 text-yellow-400" />
|
|
</div>
|
|
<div>
|
|
<h2 className="text-xl font-bold text-white">Top Teams</h2>
|
|
<p className="text-sm text-gray-500">Highest rated racing teams</p>
|
|
</div>
|
|
</div>
|
|
|
|
<Button
|
|
variant="secondary"
|
|
onClick={() => router.push('/teams/leaderboard')}
|
|
className="flex items-center gap-2 text-sm"
|
|
>
|
|
View Full Leaderboard
|
|
<ChevronRight className="w-4 h-4" />
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Compact Leaderboard */}
|
|
<div className="rounded-xl bg-iron-gray/30 border border-charcoal-outline overflow-hidden">
|
|
<div className="divide-y divide-charcoal-outline/50">
|
|
{topTeams.map((team, index) => {
|
|
const levelConfig = SKILL_LEVELS.find((l) => l.id === team.performanceLevel);
|
|
|
|
return (
|
|
<button
|
|
key={team.id}
|
|
type="button"
|
|
onClick={() => onTeamClick(team.id)}
|
|
className="flex items-center gap-4 px-4 py-3 w-full text-left hover:bg-iron-gray/30 transition-colors group"
|
|
>
|
|
{/* Position */}
|
|
<div
|
|
className={`flex h-8 w-8 items-center justify-center rounded-full text-xs font-bold border ${getMedalBg(index)} ${getMedalColor(index)}`}
|
|
>
|
|
{index < 3 ? (
|
|
<Crown className="w-3.5 h-3.5" />
|
|
) : (
|
|
index + 1
|
|
)}
|
|
</div>
|
|
|
|
{/* Team Info */}
|
|
<div className="flex h-9 w-9 items-center justify-center rounded-lg bg-charcoal-outline border border-charcoal-outline overflow-hidden">
|
|
<Image
|
|
src={team.logoUrl || getMediaUrl('team-logo', team.id)}
|
|
alt={team.name}
|
|
width={36}
|
|
height={36}
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<p className="text-white font-medium truncate group-hover:text-purple-400 transition-colors">
|
|
{team.name}
|
|
</p>
|
|
<div className="flex items-center gap-2 text-xs text-gray-500 flex-wrap">
|
|
{team.category && (
|
|
<span className="flex items-center gap-1 text-purple-400">
|
|
<span className="w-1.5 h-1.5 rounded-full bg-purple-400"></span>
|
|
{team.category}
|
|
</span>
|
|
)}
|
|
<span className="flex items-center gap-1">
|
|
<Users className="w-3 h-3" />
|
|
{team.memberCount}
|
|
</span>
|
|
<span className="flex items-center gap-1">
|
|
<Trophy className="w-3 h-3" />
|
|
{team.totalWins} wins
|
|
</span>
|
|
{team.isRecruiting && (
|
|
<span className="flex items-center gap-1 text-performance-green">
|
|
<div className="w-1.5 h-1.5 rounded-full bg-performance-green animate-pulse" />
|
|
Recruiting
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Rating */}
|
|
<div className="text-right">
|
|
<p className="text-purple-400 font-mono font-semibold">
|
|
{typeof team.rating === 'number' ? Math.round(team.rating).toLocaleString() : '—'}
|
|
</p>
|
|
<p className="text-xs text-gray-500">Rating</p>
|
|
</div>
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
} |