This commit is contained in:
2026-01-05 19:35:49 +01:00
parent b4b915416b
commit d9e6151ae0
92 changed files with 10964 additions and 7893 deletions

View File

@@ -0,0 +1,202 @@
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import {
Trophy,
Users,
Search,
Crown,
} from 'lucide-react';
import Button from '@/components/ui/Button';
import Input from '@/components/ui/Input';
import Card from '@/components/ui/Card';
import Heading from '@/components/ui/Heading';
import { FeaturedDriverCard } from '@/components/drivers/FeaturedDriverCard';
import { SkillDistribution } from '@/components/drivers/SkillDistribution';
import { CategoryDistribution } from '@/components/drivers/CategoryDistribution';
import { LeaderboardPreview } from '@/components/drivers/LeaderboardPreview';
import { RecentActivity } from '@/components/drivers/RecentActivity';
import type { DriverLeaderboardItemViewModel } from '@/lib/view-models/DriverLeaderboardItemViewModel';
interface DriversTemplateProps {
drivers: DriverLeaderboardItemViewModel[];
totalRaces: number;
totalWins: number;
activeCount: number;
isLoading?: boolean;
}
export function DriversTemplate({
drivers,
totalRaces,
totalWins,
activeCount,
isLoading = false
}: DriversTemplateProps) {
const router = useRouter();
const [searchQuery, setSearchQuery] = useState('');
const handleDriverClick = (driverId: string) => {
router.push(`/drivers/${driverId}`);
};
// Filter by search
const filteredDrivers = drivers.filter((driver) => {
if (!searchQuery) return true;
return (
driver.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
driver.nationality.toLowerCase().includes(searchQuery.toLowerCase())
);
});
// Featured drivers (top 4)
const featuredDrivers = filteredDrivers.slice(0, 4);
if (isLoading) {
return (
<div className="max-w-7xl mx-auto px-4">
<div className="flex items-center justify-center min-h-[400px]">
<div className="flex flex-col items-center gap-4">
<div className="w-10 h-10 border-2 border-primary-blue border-t-transparent rounded-full animate-spin" />
<p className="text-gray-400">Loading drivers...</p>
</div>
</div>
</div>
);
}
return (
<div className="max-w-7xl mx-auto px-4 pb-12">
{/* Hero Section */}
<div className="relative mb-10 py-10 px-8 rounded-2xl bg-gradient-to-br from-primary-blue/20 via-iron-gray/80 to-deep-graphite border border-primary-blue/30 overflow-hidden">
{/* Background decoration */}
<div className="absolute top-0 right-0 w-96 h-96 bg-primary-blue/10 rounded-full blur-3xl" />
<div className="absolute bottom-0 left-0 w-64 h-64 bg-yellow-400/5 rounded-full blur-3xl" />
<div className="absolute top-1/2 right-1/4 w-48 h-48 bg-performance-green/5 rounded-full blur-2xl" />
<div className="relative z-10 flex flex-col lg:flex-row lg:items-center lg:justify-between gap-8">
<div className="max-w-2xl">
<div className="flex items-center gap-3 mb-4">
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-br from-primary-blue/20 to-primary-blue/5 border border-primary-blue/20">
<Users className="w-6 h-6 text-primary-blue" />
</div>
<Heading level={1} className="text-3xl lg:text-4xl">
Drivers
</Heading>
</div>
<p className="text-gray-400 text-lg leading-relaxed mb-6">
Meet the racers who make every lap count. From rookies to champions, track their journey and see who's dominating the grid.
</p>
{/* Quick Stats */}
<div className="flex flex-wrap gap-6">
<div className="flex items-center gap-2">
<div className="w-2 h-2 rounded-full bg-primary-blue" />
<span className="text-sm text-gray-400">
<span className="text-white font-semibold">{drivers.length}</span> drivers
</span>
</div>
<div className="flex items-center gap-2">
<div className="w-2 h-2 rounded-full bg-performance-green animate-pulse" />
<span className="text-sm text-gray-400">
<span className="text-white font-semibold">{activeCount}</span> active
</span>
</div>
<div className="flex items-center gap-2">
<div className="w-2 h-2 rounded-full bg-yellow-400" />
<span className="text-sm text-gray-400">
<span className="text-white font-semibold">{totalWins.toLocaleString()}</span> total wins
</span>
</div>
<div className="flex items-center gap-2">
<div className="w-2 h-2 rounded-full bg-neon-aqua" />
<span className="text-sm text-gray-400">
<span className="text-white font-semibold">{totalRaces.toLocaleString()}</span> races
</span>
</div>
</div>
</div>
{/* CTA */}
<div className="flex flex-col gap-4">
<Button
variant="primary"
onClick={() => router.push('/leaderboards/drivers')}
className="flex items-center gap-2 px-6 py-3"
>
<Trophy className="w-5 h-5" />
View Leaderboard
</Button>
<p className="text-xs text-gray-500 text-center">See full driver rankings</p>
</div>
</div>
</div>
{/* Search */}
<div className="mb-8">
<div className="relative max-w-md">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-500" />
<Input
type="text"
placeholder="Search drivers by name or nationality..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-11"
/>
</div>
</div>
{/* Featured Drivers */}
{!searchQuery && (
<div className="mb-10">
<div className="flex items-center gap-3 mb-4">
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-yellow-400/10 border border-yellow-400/20">
<Crown className="w-5 h-5 text-yellow-400" />
</div>
<div>
<h2 className="text-lg font-semibold text-white">Featured Drivers</h2>
<p className="text-xs text-gray-500">Top performers on the grid</p>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
{featuredDrivers.map((driver, index) => (
<FeaturedDriverCard
key={driver.id}
driver={driver}
position={index + 1}
onClick={() => handleDriverClick(driver.id)}
/>
))}
</div>
</div>
)}
{/* Active Drivers */}
{!searchQuery && <RecentActivity drivers={drivers} onDriverClick={handleDriverClick} />}
{/* Skill Distribution */}
{!searchQuery && <SkillDistribution drivers={drivers} />}
{/* Category Distribution */}
{!searchQuery && <CategoryDistribution drivers={drivers} />}
{/* Leaderboard Preview */}
<LeaderboardPreview drivers={filteredDrivers} onDriverClick={handleDriverClick} />
{/* Empty State */}
{filteredDrivers.length === 0 && (
<Card className="text-center py-12">
<div className="flex flex-col items-center gap-4">
<Search className="w-10 h-10 text-gray-600" />
<p className="text-gray-400">No drivers found matching "{searchQuery}"</p>
<Button variant="secondary" onClick={() => setSearchQuery('')}>
Clear search
</Button>
</div>
</Card>
)}
</div>
);
}