diff --git a/apps/website/app/leaderboards/page.tsx b/apps/website/app/leaderboards/page.tsx index 1b40a3ebd..f4e1b1ad3 100644 --- a/apps/website/app/leaderboards/page.tsx +++ b/apps/website/app/leaderboards/page.tsx @@ -2,275 +2,19 @@ import { useState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; -import { - Trophy, - Users, - Crown, - Star, - TrendingUp, - Shield, - Medal, - ChevronRight, - Award, - Flag, - Target, - Zap, - Hash, - Percent, -} from 'lucide-react'; +import { Trophy, Users, Award, ChevronRight } from 'lucide-react'; import Button from '@/components/ui/Button'; import Heading from '@/components/ui/Heading'; +import DriverLeaderboardPreview from '@/components/leaderboards/DriverLeaderboardPreview'; +import TeamLeaderboardPreview from '@/components/leaderboards/TeamLeaderboardPreview'; import type { DriverLeaderboardItemViewModel } from '@/lib/view-models/DriverLeaderboardItemViewModel'; import type { TeamSummaryViewModel } from '@/lib/view-models/TeamSummaryViewModel'; import { useServices } from '@/lib/services/ServiceProvider'; -import Image from 'next/image'; // ============================================================================ // TYPES // ============================================================================ -type SkillLevel = 'pro' | 'advanced' | 'intermediate' | 'beginner'; - -type DriverListItem = DriverLeaderboardItemViewModel; - -type TeamDisplayData = TeamSummaryViewModel; - -// ============================================================================ -// SKILL LEVEL CONFIG -// ============================================================================ - -const SKILL_LEVELS: { - id: SkillLevel; - label: string; - icon: React.ElementType; - color: string; - bgColor: string; - borderColor: string; -}[] = [ - { id: 'pro', label: 'Pro', icon: Crown, color: 'text-yellow-400', bgColor: 'bg-yellow-400/10', borderColor: 'border-yellow-400/30' }, - { id: 'advanced', label: 'Advanced', icon: Star, color: 'text-purple-400', bgColor: 'bg-purple-400/10', borderColor: 'border-purple-400/30' }, - { id: 'intermediate', label: 'Intermediate', icon: TrendingUp, color: 'text-primary-blue', bgColor: 'bg-primary-blue/10', borderColor: 'border-primary-blue/30' }, - { id: 'beginner', label: 'Beginner', icon: Shield, color: 'text-green-400', bgColor: 'bg-green-400/10', borderColor: 'border-green-400/30' }, -]; - -// ============================================================================ -// DRIVER LEADERBOARD PREVIEW -// ============================================================================ - -interface DriverLeaderboardPreviewProps { - drivers: DriverListItem[]; - onDriverClick: (id: string) => void; -} - -function DriverLeaderboardPreview({ drivers, onDriverClick }: DriverLeaderboardPreviewProps) { - const router = useRouter(); - const top10 = drivers.slice(0, 10); - - const getMedalColor = (position: number) => { - switch (position) { - case 1: return 'text-yellow-400'; - case 2: return 'text-gray-300'; - case 3: return 'text-amber-600'; - default: return 'text-gray-500'; - } - }; - - const getMedalBg = (position: number) => { - switch (position) { - case 1: return 'bg-yellow-400/10 border-yellow-400/30'; - case 2: return 'bg-gray-300/10 border-gray-300/30'; - case 3: return 'bg-amber-600/10 border-amber-600/30'; - default: return 'bg-iron-gray/50 border-charcoal-outline'; - } - }; - - return ( -
- {/* Header */} -
-
-
- -
-
-

Driver Rankings

-

Top performers across all leagues

-
-
- -
- - {/* Leaderboard Rows */} -
- {top10.map((driver, index) => { - const levelConfig = SKILL_LEVELS.find((l) => l.id === driver.skillLevel); - const position = index + 1; - - return ( - - ); - })} -
-
- ); -} - -// ============================================================================ -// TEAM LEADERBOARD PREVIEW -// ============================================================================ - -interface TeamLeaderboardPreviewProps { - teams: TeamDisplayData[]; - onTeamClick: (id: string) => void; -} - -function TeamLeaderboardPreview({ teams, onTeamClick }: TeamLeaderboardPreviewProps) { - const router = useRouter(); - const top5 = [...teams] - .sort((a, b) => b.memberCount - a.memberCount) - .slice(0, 5); - - const getMedalColor = (position: number) => { - switch (position) { - case 1: return 'text-yellow-400'; - case 2: return 'text-gray-300'; - case 3: return 'text-amber-600'; - default: return 'text-gray-500'; - } - }; - - const getMedalBg = (position: number) => { - switch (position) { - case 1: return 'bg-yellow-400/10 border-yellow-400/30'; - case 2: return 'bg-gray-300/10 border-gray-300/30'; - case 3: return 'bg-amber-600/10 border-amber-600/30'; - default: return 'bg-iron-gray/50 border-charcoal-outline'; - } - }; - - return ( -
- {/* Header */} -
-
-
- -
-
-

Team Rankings

-

Top performing racing teams

-
-
- -
- - {/* Leaderboard Rows */} -
- {top5.map((team, index) => { - const levelConfig = SKILL_LEVELS.find((l) => l.id === team.performanceLevel); - const LevelIcon = levelConfig?.icon || Shield; - const position = index + 1; - - return ( - - ); - })} -
-
- ); -} // ============================================================================ // MAIN PAGE COMPONENT diff --git a/apps/website/components/leaderboards/DriverLeaderboardPreview.tsx b/apps/website/components/leaderboards/DriverLeaderboardPreview.tsx new file mode 100644 index 000000000..5e210e391 --- /dev/null +++ b/apps/website/components/leaderboards/DriverLeaderboardPreview.tsx @@ -0,0 +1,117 @@ +import React from 'react'; +import { useRouter } from 'next/navigation'; +import { Trophy, Crown, Flag, ChevronRight } from 'lucide-react'; +import Button from '@/components/ui/Button'; +import Image from 'next/image'; +import type { DriverLeaderboardItemViewModel } from '@/lib/view-models/DriverLeaderboardItemViewModel'; + +interface DriverLeaderboardPreviewProps { + drivers: DriverLeaderboardItemViewModel[]; + onDriverClick: (id: string) => void; +} + +const SKILL_LEVELS = [ + { id: 'pro', label: 'Pro', color: 'text-yellow-400' }, + { id: 'advanced', label: 'Advanced', color: 'text-purple-400' }, + { id: 'intermediate', label: 'Intermediate', color: 'text-primary-blue' }, + { id: 'beginner', label: 'Beginner', color: 'text-green-400' }, +]; + +export default function DriverLeaderboardPreview({ drivers, onDriverClick }: DriverLeaderboardPreviewProps) { + const router = useRouter(); + const top10 = drivers.slice(0, 10); + + const getMedalColor = (position: number) => { + switch (position) { + case 1: return 'text-yellow-400'; + case 2: return 'text-gray-300'; + case 3: return 'text-amber-600'; + default: return 'text-gray-500'; + } + }; + + const getMedalBg = (position: number) => { + switch (position) { + case 1: return 'bg-yellow-400/10 border-yellow-400/30'; + case 2: return 'bg-gray-300/10 border-gray-300/30'; + case 3: return 'bg-amber-600/10 border-amber-600/30'; + default: return 'bg-iron-gray/50 border-charcoal-outline'; + } + }; + + return ( +
+ {/* Header */} +
+
+
+ +
+
+

Driver Rankings

+

Top performers across all leagues

+
+
+ +
+ + {/* Leaderboard Rows */} +
+ {top10.map((driver, index) => { + const levelConfig = SKILL_LEVELS.find((l) => l.id === driver.skillLevel); + const position = index + 1; + + return ( + + ); + })} +
+
+ ); +} \ No newline at end of file diff --git a/apps/website/components/leaderboards/TeamLeaderboardPreview.tsx b/apps/website/components/leaderboards/TeamLeaderboardPreview.tsx new file mode 100644 index 000000000..cc970d3c4 --- /dev/null +++ b/apps/website/components/leaderboards/TeamLeaderboardPreview.tsx @@ -0,0 +1,119 @@ +import React from 'react'; +import { useRouter } from 'next/navigation'; +import { Users, Crown, Shield, ChevronRight } from 'lucide-react'; +import Button from '@/components/ui/Button'; +import type { TeamSummaryViewModel } from '@/lib/view-models/TeamSummaryViewModel'; + +interface TeamLeaderboardPreviewProps { + teams: TeamSummaryViewModel[]; + onTeamClick: (id: string) => void; +} + +const SKILL_LEVELS = [ + { id: 'pro', label: 'Pro', icon: Crown, color: 'text-yellow-400', bgColor: 'bg-yellow-400/10', borderColor: 'border-yellow-400/30' }, + { id: 'advanced', label: 'Advanced', icon: Crown, color: 'text-purple-400', bgColor: 'bg-purple-400/10', borderColor: 'border-purple-400/30' }, + { id: 'intermediate', label: 'Intermediate', icon: Crown, color: 'text-primary-blue', bgColor: 'bg-primary-blue/10', borderColor: 'border-primary-blue/30' }, + { id: 'beginner', label: 'Beginner', icon: Shield, color: 'text-green-400', bgColor: 'bg-green-400/10', borderColor: 'border-green-400/30' }, +]; + +export default function TeamLeaderboardPreview({ teams, onTeamClick }: TeamLeaderboardPreviewProps) { + const router = useRouter(); + const top5 = [...teams] + .sort((a, b) => b.memberCount - a.memberCount) + .slice(0, 5); + + const getMedalColor = (position: number) => { + switch (position) { + case 1: return 'text-yellow-400'; + case 2: return 'text-gray-300'; + case 3: return 'text-amber-600'; + default: return 'text-gray-500'; + } + }; + + const getMedalBg = (position: number) => { + switch (position) { + case 1: return 'bg-yellow-400/10 border-yellow-400/30'; + case 2: return 'bg-gray-300/10 border-gray-300/30'; + case 3: return 'bg-amber-600/10 border-amber-600/30'; + default: return 'bg-iron-gray/50 border-charcoal-outline'; + } + }; + + return ( +
+ {/* Header */} +
+
+
+ +
+
+

Team Rankings

+

Top performing racing teams

+
+
+ +
+ + {/* Leaderboard Rows */} +
+ {top5.map((team, index) => { + const levelConfig = SKILL_LEVELS.find((l) => l.id === team.performanceLevel); + const LevelIcon = levelConfig?.icon || Shield; + const position = index + 1; + + return ( + + ); + })} +
+
+ ); +} \ No newline at end of file