'use client'; import { useState, useEffect, use } from 'react'; import Image from 'next/image'; import Link from 'next/link'; import { useRouter, useParams } from 'next/navigation'; import { User, Trophy, Star, Calendar, Users, Flag, Award, TrendingUp, UserPlus, ExternalLink, Target, Zap, Clock, Medal, Crown, ChevronRight, Globe, Twitter, Youtube, Twitch, MessageCircle, ArrowLeft, BarChart3, History, Shield, Percent, Activity, } from 'lucide-react'; import { getDriverRepository, getDriverStats, getAllDriverRankings, getGetDriverTeamQuery, getSocialRepository, getImageService, getGetAllTeamsQuery, getGetTeamMembersQuery, } from '@/lib/di-container'; import { Driver, EntityMappers, type Team } from '@gridpilot/racing'; import type { DriverDTO } from '@gridpilot/racing'; import Button from '@/components/ui/Button'; import Card from '@/components/ui/Card'; import Breadcrumbs from '@/components/layout/Breadcrumbs'; import type { GetDriverTeamQueryResultDTO } from '@gridpilot/racing/application/dto/TeamCommandAndQueryDTO'; // ============================================================================ // TYPES // ============================================================================ type ProfileTab = 'overview' | 'stats'; interface SocialHandle { platform: 'twitter' | 'youtube' | 'twitch' | 'discord'; handle: string; url: string; } interface Achievement { id: string; title: string; description: string; icon: 'trophy' | 'medal' | 'star' | 'crown' | 'target' | 'zap'; rarity: 'common' | 'rare' | 'epic' | 'legendary'; earnedAt: Date; } interface DriverExtendedProfile { socialHandles: SocialHandle[]; achievements: Achievement[]; racingStyle: string; favoriteTrack: string; favoriteCar: string; timezone: string; availableHours: string; lookingForTeam: boolean; openToRequests: boolean; } interface TeamMembershipInfo { team: Team; role: string; joinedAt: Date; } // ============================================================================ // DEMO DATA // ============================================================================ function getDemoExtendedProfile(driverId: string): DriverExtendedProfile { const hash = driverId.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0); const socialOptions: SocialHandle[][] = [ [ { platform: 'twitter', handle: '@speedracer', url: 'https://twitter.com/speedracer' }, { platform: 'youtube', handle: 'SpeedRacer Racing', url: 'https://youtube.com/@speedracer' }, { platform: 'twitch', handle: 'speedracer_live', url: 'https://twitch.tv/speedracer_live' }, ], [ { platform: 'twitter', handle: '@racingpro', url: 'https://twitter.com/racingpro' }, { platform: 'discord', handle: 'RacingPro#1234', url: '#' }, ], [ { platform: 'twitch', handle: 'simracer_elite', url: 'https://twitch.tv/simracer_elite' }, { platform: 'youtube', handle: 'SimRacer Elite', url: 'https://youtube.com/@simracerelite' }, ], ]; const achievementSets: Achievement[][] = [ [ { id: '1', title: 'First Victory', description: 'Win your first race', icon: 'trophy', rarity: 'common', earnedAt: new Date(Date.now() - 90 * 24 * 60 * 60 * 1000) }, { id: '2', title: 'Clean Racer', description: '10 races without incidents', icon: 'star', rarity: 'rare', earnedAt: new Date(Date.now() - 60 * 24 * 60 * 60 * 1000) }, { id: '3', title: 'Podium Streak', description: '5 consecutive podium finishes', icon: 'medal', rarity: 'epic', earnedAt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) }, ], [ { id: '1', title: 'Rookie No More', description: 'Complete 25 races', icon: 'target', rarity: 'common', earnedAt: new Date(Date.now() - 120 * 24 * 60 * 60 * 1000) }, { id: '2', title: 'Consistent Performer', description: 'Maintain 80%+ consistency rating', icon: 'zap', rarity: 'rare', earnedAt: new Date(Date.now() - 45 * 24 * 60 * 60 * 1000) }, ], [ { id: '1', title: 'Welcome Racer', description: 'Join GridPilot', icon: 'star', rarity: 'common', earnedAt: new Date(Date.now() - 180 * 24 * 60 * 60 * 1000) }, { id: '2', title: 'Team Player', description: 'Join a racing team', icon: 'medal', rarity: 'rare', earnedAt: new Date(Date.now() - 80 * 24 * 60 * 60 * 1000) }, ], ]; const tracks = ['Spa-Francorchamps', 'Nürburgring Nordschleife', 'Suzuka', 'Monza', 'Interlagos', 'Silverstone']; const cars = ['Porsche 911 GT3 R', 'Ferrari 488 GT3', 'Mercedes-AMG GT3', 'BMW M4 GT3', 'Audi R8 LMS']; const styles = ['Aggressive Overtaker', 'Consistent Pacer', 'Strategic Calculator', 'Late Braker', 'Smooth Operator']; const timezones = ['EST (UTC-5)', 'CET (UTC+1)', 'PST (UTC-8)', 'GMT (UTC+0)', 'JST (UTC+9)']; const hours = ['Evenings (18:00-23:00)', 'Weekends only', 'Late nights (22:00-02:00)', 'Flexible schedule']; return { socialHandles: socialOptions[hash % socialOptions.length], achievements: achievementSets[hash % achievementSets.length], racingStyle: styles[hash % styles.length], favoriteTrack: tracks[hash % tracks.length], favoriteCar: cars[hash % cars.length], timezone: timezones[hash % timezones.length], availableHours: hours[hash % hours.length], lookingForTeam: hash % 3 === 0, openToRequests: hash % 2 === 0, }; } // ============================================================================ // HELPERS // ============================================================================ function getCountryFlag(countryCode: string): string { const code = countryCode.toUpperCase(); if (code.length === 2) { const codePoints = [...code].map(char => 127397 + char.charCodeAt(0)); return String.fromCodePoint(...codePoints); } return '🏁'; } function getRarityColor(rarity: Achievement['rarity']) { switch (rarity) { case 'common': return 'text-gray-400 bg-gray-400/10 border-gray-400/30'; case 'rare': return 'text-primary-blue bg-primary-blue/10 border-primary-blue/30'; case 'epic': return 'text-purple-400 bg-purple-400/10 border-purple-400/30'; case 'legendary': return 'text-yellow-400 bg-yellow-400/10 border-yellow-400/30'; } } function getAchievementIcon(icon: Achievement['icon']) { switch (icon) { case 'trophy': return Trophy; case 'medal': return Medal; case 'star': return Star; case 'crown': return Crown; case 'target': return Target; case 'zap': return Zap; } } function getSocialIcon(platform: SocialHandle['platform']) { switch (platform) { case 'twitter': return Twitter; case 'youtube': return Youtube; case 'twitch': return Twitch; case 'discord': return MessageCircle; } } function getSocialColor(platform: SocialHandle['platform']) { switch (platform) { case 'twitter': return 'hover:text-sky-400 hover:bg-sky-400/10'; case 'youtube': return 'hover:text-red-500 hover:bg-red-500/10'; case 'twitch': return 'hover:text-purple-400 hover:bg-purple-400/10'; case 'discord': return 'hover:text-indigo-400 hover:bg-indigo-400/10'; } } // ============================================================================ // STAT DIAGRAM COMPONENTS // ============================================================================ interface CircularProgressProps { value: number; max: number; label: string; color: string; size?: number; } function CircularProgress({ value, max, label, color, size = 80 }: CircularProgressProps) { const percentage = Math.min((value / max) * 100, 100); const strokeWidth = 6; const radius = (size - strokeWidth) / 2; const circumference = radius * 2 * Math.PI; const strokeDashoffset = circumference - (percentage / 100) * circumference; return (
{percentage.toFixed(0)}%
{label}
); } interface BarChartProps { data: { label: string; value: number; color: string }[]; maxValue: number; } function HorizontalBarChart({ data, maxValue }: BarChartProps) { return (
{data.map((item) => (
{item.label} {item.value}
))}
); } // ============================================================================ // MAIN PAGE // ============================================================================ export default function DriverDetailPage({ searchParams, }: { searchParams: any; }) { const router = useRouter(); const params = useParams(); const driverId = params.id as string; const [driver, setDriver] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [activeTab, setActiveTab] = useState('overview'); const [teamData, setTeamData] = useState(null); const [allTeamMemberships, setAllTeamMemberships] = useState([]); const [friends, setFriends] = useState([]); const [friendRequestSent, setFriendRequestSent] = useState(false); const unwrappedSearchParams = use(searchParams) as URLSearchParams | undefined; const from = typeof unwrappedSearchParams?.get === 'function' ? unwrappedSearchParams.get('from') ?? undefined : undefined; const leagueId = typeof unwrappedSearchParams?.get === 'function' ? unwrappedSearchParams.get('leagueId') ?? undefined : undefined; const raceId = typeof unwrappedSearchParams?.get === 'function' ? unwrappedSearchParams.get('raceId') ?? undefined : undefined; let backLink: string | null = null; if (from === 'league-standings' && leagueId) { backLink = `/leagues/${leagueId}/standings`; } else if (from === 'league' && leagueId) { backLink = `/leagues/${leagueId}`; } else if (from === 'league-members' && leagueId) { backLink = `/leagues/${leagueId}`; } else if (from === 'league-race' && leagueId && raceId) { backLink = `/leagues/${leagueId}/races/${raceId}`; } else { backLink = null; } useEffect(() => { loadDriver(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [driverId]); const loadDriver = async () => { try { const driverRepo = getDriverRepository(); const driverEntity = await driverRepo.findById(driverId); if (!driverEntity) { setError('Driver not found'); setLoading(false); return; } const driverDto = EntityMappers.toDriverDTO(driverEntity); if (!driverDto) { setError('Driver not found'); setLoading(false); return; } setDriver(driverDto); // Load team data const teamQuery = getGetDriverTeamQuery(); const teamResult = await teamQuery.execute({ driverId }); setTeamData(teamResult); // Load ALL team memberships const allTeamsQuery = getGetAllTeamsQuery(); const allTeams = await allTeamsQuery.execute(); const membershipsQuery = getGetTeamMembersQuery(); const memberships: TeamMembershipInfo[] = []; for (const team of allTeams) { const members = await membershipsQuery.execute({ teamId: team.id }); const membership = members.find((m) => m.driverId === driverId); if (membership) { memberships.push({ team, role: membership.role, joinedAt: membership.joinedAt, }); } } setAllTeamMemberships(memberships); // Load friends const socialRepo = getSocialRepository(); const friendsList = await socialRepo.getFriends(driverId); setFriends(friendsList); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load driver'); } finally { setLoading(false); } }; const handleAddFriend = () => { setFriendRequestSent(true); }; if (loading) { return (

Loading driver profile...

); } if (error || !driver) { return (
{error || 'Driver not found'}
); } const extendedProfile = getDemoExtendedProfile(driver.id); const stats = getDriverStats(driver.id); const allRankings = getAllDriverRankings(); const globalRank = stats?.overallRank ?? allRankings.findIndex(r => r.driverId === driver.id) + 1; return (
{/* Back Navigation */} {backLink ? ( Back to league ) : ( )} {/* Breadcrumb */} {/* Hero Header Section */}
{/* Background Pattern */}
{/* Avatar */}
{driver.name}
{/* Driver Info */}

{driver.name}

{getCountryFlag(driver.country)} {teamData?.team.tag && ( [{teamData.team.tag}] )}
{/* Rating and Rank */}
{stats && ( <>
{stats.rating} Rating
#{globalRank} Global
)} {teamData && ( {teamData.team.name} )}
{/* Meta info */}
iRacing: {driver.iracingId} Joined{' '} {new Date(driver.joinedAt).toLocaleDateString('en-US', { month: 'short', year: 'numeric', })} {extendedProfile.timezone}
{/* Action Buttons */}
{/* Social Handles */} {extendedProfile.socialHandles.length > 0 && (
Connect: {extendedProfile.socialHandles.map((social) => { const Icon = getSocialIcon(social.platform); return ( {social.handle} ); })}
)}
{/* Bio Section */} {driver.bio && (

About

{driver.bio}

)} {/* Team Memberships */} {allTeamMemberships.length > 0 && (

Team Memberships ({allTeamMemberships.length})

{allTeamMemberships.map((membership) => (

{membership.team.name}

{membership.role} Since {membership.joinedAt.toLocaleDateString('en-US', { month: 'short', year: 'numeric' })}
))}
)} {/* Performance Overview with Diagrams */} {stats && (

Performance Overview

{/* Circular Progress Charts */}
{/* Bar chart and key metrics */}

Results Breakdown

Best Finish

P{stats.bestFinish}

Avg Finish

P{stats.avgFinish.toFixed(1)}

)} {/* Tab Navigation */}
{/* Tab Content */} {activeTab === 'overview' && ( <> {/* Stats and Profile Grid */}
{/* Career Stats */}

Career Statistics

{stats ? (
{stats.totalRaces}
Races
{stats.wins}
Wins
{stats.podiums}
Podiums
{stats.consistency}%
Consistency
) : (

No race statistics available yet.

)}
{/* Racing Preferences */}

Racing Profile

Racing Style

{extendedProfile.racingStyle}

Favorite Track

{extendedProfile.favoriteTrack}

Favorite Car

{extendedProfile.favoriteCar}

Available

{extendedProfile.availableHours}

{/* Status badges */}
{extendedProfile.lookingForTeam && (
Looking for Team
)} {extendedProfile.openToRequests && (
Open to Friend Requests
)}
{/* Achievements */}

Achievements {extendedProfile.achievements.length} earned

{extendedProfile.achievements.map((achievement) => { const Icon = getAchievementIcon(achievement.icon); const rarityClasses = getRarityColor(achievement.rarity); return (

{achievement.title}

{achievement.description}

{achievement.earnedAt.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', })}

); })}
{/* Friends Preview */} {friends.length > 0 && (

Friends ({friends.length})

{friends.slice(0, 8).map((friend) => (
{friend.name}
{friend.name} {getCountryFlag(friend.country)} ))} {friends.length > 8 && (
+{friends.length - 8} more
)}
)} )} {activeTab === 'stats' && stats && (
{/* Detailed Performance Metrics */}

Detailed Performance Metrics

{/* Performance Bars */}

Results Breakdown

{/* Key Metrics */}
Win Rate

{((stats.wins / stats.totalRaces) * 100).toFixed(1)}%

Podium Rate

{((stats.podiums / stats.totalRaces) * 100).toFixed(1)}%

Consistency

{stats.consistency}%

Finish Rate

{(((stats.totalRaces - stats.dnfs) / stats.totalRaces) * 100).toFixed(1)}%

{/* Position Statistics */}

Position Statistics

P{stats.bestFinish}
Best Finish
P{stats.avgFinish.toFixed(1)}
Avg Finish
P{stats.worstFinish}
Worst Finish
{stats.dnfs}
DNFs
{/* Global Rankings */}

Global Rankings

#{globalRank}
Global Rank
{stats.rating}
Rating
Top {stats.percentile}%
Percentile
)} {activeTab === 'stats' && !stats && (

No statistics available yet

This driver hasn't completed any races yet

)}
); }