'use client'; import { useState, useEffect } from 'react'; import Image from 'next/image'; import Link from 'next/link'; import { useRouter, useParams } from 'next/navigation'; import SponsorInsightsCard, { useSponsorMode, MetricBuilders, SlotTemplates } from '@/components/sponsors/SponsorInsightsCard'; import { User, Trophy, Star, Calendar, Users, Flag, Award, TrendingUp, UserPlus, ExternalLink, Target, Zap, Clock, Medal, Crown, ChevronRight, Globe, Twitter, Youtube, Twitch, MessageCircle, ArrowLeft, BarChart3, Shield, Percent, Activity, } from 'lucide-react'; import Button from '@/components/ui/Button'; import Card from '@/components/ui/Card'; import Breadcrumbs from '@/components/layout/Breadcrumbs'; import { useServices } from '@/lib/services/ServiceProvider'; import { DriverProfileViewModel } from '@/lib/view-models/DriverProfileViewModel'; // ============================================================================ // TYPES // ============================================================================ type ProfileTab = 'overview' | 'stats'; interface Team { id: string; name: string; tag: string; description: string; ownerId: string; leagues: unknown[]; // TODO: define proper type createdAt: Date; } 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']; const socialHandles = socialOptions[hash % socialOptions.length] ?? []; const achievements = achievementSets[hash % achievementSets.length] ?? []; const racingStyle = styles[hash % styles.length] ?? 'Consistent Pacer'; const favoriteTrack = tracks[hash % tracks.length] ?? 'Unknown Track'; const favoriteCar = cars[hash % cars.length] ?? 'Unknown Car'; const timezone = timezones[hash % timezones.length] ?? 'UTC'; const availableHours = hours[hash % hours.length] ?? 'Flexible schedule'; return { socialHandles, achievements, racingStyle, favoriteTrack, favoriteCar, timezone, availableHours, 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() { const router = useRouter(); const params = useParams(); const driverId = params.id as string; const { driverService, teamService } = useServices(); const [driverProfile, setDriverProfile] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [activeTab, setActiveTab] = useState('overview'); const [allTeamMemberships, setAllTeamMemberships] = useState([]); const [friendRequestSent, setFriendRequestSent] = useState(false); const search = typeof window !== 'undefined' ? new URLSearchParams(window.location.search) : undefined; const from = search?.get('from') ?? undefined; const leagueId = search?.get('leagueId') ?? undefined; const raceId = search?.get('raceId') ?? 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' && raceId) { backLink = `/races/${raceId}`; } else { backLink = null; } const isSponsorMode = useSponsorMode(); useEffect(() => { loadDriver(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [driverId]); const loadDriver = async () => { try { // Get driver profile const profileViewModel = await driverService.getDriverProfile(driverId); if (!profileViewModel.currentDriver) { setError('Driver not found'); setLoading(false); return; } setDriverProfile(profileViewModel); // Load team memberships - get all teams and check memberships const allTeams = await teamService.getAllTeams(); const memberships: TeamMembershipInfo[] = []; for (const team of allTeams) { const teamMembers = await teamService.getTeamMembers(team.id, driverId, ''); // ownerId not available in summary const membership = teamMembers.find(member => member.driverId === driverId); if (membership) { memberships.push({ team: { id: team.id, name: team.name, tag: '', // Not available in summary description: '', // Not available in summary ownerId: '', // Not available in summary leagues: [], // TODO: populate if needed createdAt: new Date(), // TODO: add to API } as Team, role: membership.role, joinedAt: new Date(membership.joinedAt), }); } } setAllTeamMemberships(memberships); } 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 || !driverProfile?.currentDriver) { return (
{error || 'Driver not found'}
); } const demoExtended = getDemoExtendedProfile(driverProfile.currentDriver.id); const extendedProfile: DriverExtendedProfile = { socialHandles: driverProfile?.extendedProfile?.socialHandles ?? demoExtended.socialHandles, achievements: driverProfile?.extendedProfile?.achievements ? driverProfile.extendedProfile.achievements.map((achievement) => ({ id: achievement.id, title: achievement.title, description: achievement.description, icon: achievement.icon, rarity: achievement.rarity, earnedAt: new Date(achievement.earnedAt), })) : demoExtended.achievements, racingStyle: driverProfile?.extendedProfile?.racingStyle ?? demoExtended.racingStyle, favoriteTrack: driverProfile?.extendedProfile?.favoriteTrack ?? demoExtended.favoriteTrack, favoriteCar: driverProfile?.extendedProfile?.favoriteCar ?? demoExtended.favoriteCar, timezone: driverProfile?.extendedProfile?.timezone ?? demoExtended.timezone, availableHours: driverProfile?.extendedProfile?.availableHours ?? demoExtended.availableHours, lookingForTeam: driverProfile?.extendedProfile?.lookingForTeam ?? demoExtended.lookingForTeam, openToRequests: driverProfile?.extendedProfile?.openToRequests ?? demoExtended.openToRequests, }; const stats = driverProfile?.stats || null; const globalRank = driverProfile?.currentDriver?.globalRank || 1; const driver = driverProfile.currentDriver; // Build sponsor insights for driver const friendsCount = driverProfile?.socialSummary?.friends?.length ?? 0; const driverMetrics = [ MetricBuilders.rating(stats?.rating ?? 0, 'Driver Rating'), MetricBuilders.views((friendsCount * 8) + 50), MetricBuilders.engagement(stats?.consistency ?? 75), MetricBuilders.reach((friendsCount * 12) + 100), ]; return (
{/* Back Navigation */} {backLink ? ( Back to league ) : ( )} {/* Breadcrumb */} {/* Sponsor Insights Card - Consistent placement at top */} {isSponsorMode && driver && ( )} {/* Hero Header Section */}
{/* Background Pattern */}
{/* Avatar */}
{driver.name}
{/* Driver Info */}

{driver.name}

{getCountryFlag(driver.country)}
{/* Rating and Rank */}
{stats && ( <>
{stats.rating} Rating
#{globalRank} Global
)}
{/* 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: SocialHandle) => { 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 ?? 0).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: 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 */} {driverProfile.socialSummary.friends.length > 0 && (

Friends ({driverProfile.socialSummary.friends.length})

{driverProfile.socialSummary.friends.slice(0, 8).map((friend) => (
{friend.name}
{friend.name} {getCountryFlag(friend.country)} ))} {driverProfile.socialSummary.friends.length > 8 && (
+{driverProfile.socialSummary.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 ?? 0).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

)}
); }