'use client';
import CreateDriverForm from '@/components/drivers/CreateDriverForm';
import ProfileRaceHistory from '@/components/drivers/ProfileRaceHistory';
import ProfileSettings from '@/components/drivers/ProfileSettings';
import Button from '@/components/ui/Button';
import Card from '@/components/ui/Card';
import Heading from '@/components/ui/Heading';
import { useEffectiveDriverId } from "@/lib/hooks/useEffectiveDriverId";
import { useDriverProfile } from "@/lib/hooks/driver/useDriverProfile";
import { useInject } from '@/lib/di/hooks/useInject';
import { DRIVER_SERVICE_TOKEN, MEDIA_SERVICE_TOKEN } from '@/lib/di/tokens';
import type {
DriverProfileAchievementViewModel,
DriverProfileSocialHandleViewModel,
DriverProfileViewModel
} from '@/lib/view-models/DriverProfileViewModel';
import { getMediaUrl } from '@/lib/utilities/media';
// New architecture components
import { StatefulPageWrapper } from '@/components/shared/state/StatefulPageWrapper';
// Icons
import {
Activity,
Award,
BarChart3,
Calendar,
ChevronRight,
Clock,
Crown,
Edit3,
ExternalLink,
Flag,
Globe,
History,
Medal,
MessageCircle,
Percent,
Settings,
Shield,
Star,
Target,
TrendingUp,
Trophy,
Twitch,
Twitter,
User,
UserPlus,
Users,
Youtube,
Zap,
} from 'lucide-react';
import Image from 'next/image';
import Link from 'next/link';
import { useRouter, useSearchParams } from 'next/navigation';
import { useEffect, useState } from 'react';
// ============================================================================
// TYPES
// ============================================================================
type ProfileTab = 'overview' | 'history' | 'stats';
// ============================================================================
// HELPER COMPONENTS
// ============================================================================
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: DriverProfileAchievementViewModel['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: DriverProfileAchievementViewModel['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: DriverProfileSocialHandleViewModel['platform']) {
switch (platform) {
case 'twitter':
return Twitter;
case 'youtube':
return Youtube;
case 'twitch':
return Twitch;
case 'discord':
return MessageCircle;
}
}
function getSocialColor(platform: DriverProfileSocialHandleViewModel['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 (
);
}
interface BarChartProps {
data: { label: string; value: number; color: string }[];
maxValue: number;
}
function HorizontalBarChart({ data, maxValue }: BarChartProps) {
return (
{data.map((item) => (
{item.label}
{item.value}
))}
);
}
interface FinishDistributionProps {
wins: number;
podiums: number;
topTen: number;
total: number;
}
function FinishDistributionChart({ wins, podiums, topTen, total }: FinishDistributionProps) {
const outsideTopTen = total - topTen;
const podiumsNotWins = podiums - wins;
const topTenNotPodium = topTen - podiums;
const segments = [
{ label: 'Wins', value: wins, color: 'bg-performance-green', textColor: 'text-performance-green' },
{ label: 'Podiums', value: podiumsNotWins, color: 'bg-warning-amber', textColor: 'text-warning-amber' },
{ label: 'Top 10', value: topTenNotPodium, color: 'bg-primary-blue', textColor: 'text-primary-blue' },
{ label: 'Other', value: outsideTopTen, color: 'bg-gray-600', textColor: 'text-gray-400' },
].filter(s => s.value > 0);
return (
{segments.map((segment, index) => (
))}
{segments.map((segment) => (
{segment.label}: {segment.value} ({((segment.value / total) * 100).toFixed(0)}%)
))}
);
}
// ============================================================================
// TEMPLATE COMPONENT
// ============================================================================
interface ProfileTemplateProps {
data: DriverProfileViewModel;
onEdit: () => void;
onAddFriend: () => void;
activeTab: ProfileTab;
setActiveTab: (tab: ProfileTab) => void;
friendRequestSent: boolean;
isOwnProfile: boolean;
handleSaveSettings: (updates: { bio?: string; country?: string }) => Promise;
editMode: boolean;
setEditMode: (edit: boolean) => void;
}
function ProfileTemplate({
data,
onEdit,
onAddFriend,
activeTab,
setActiveTab,
friendRequestSent,
isOwnProfile,
handleSaveSettings,
editMode,
setEditMode
}: ProfileTemplateProps) {
const router = useRouter();
const searchParams = useSearchParams();
// Extract data from ViewModel
const currentDriver = data.currentDriver;
if (!currentDriver) {
return (
No driver profile found
Please create a driver profile to continue
);
}
const stats = data.stats;
const teamMemberships = data.teamMemberships;
const socialSummary = data.socialSummary;
const extendedProfile = data.extendedProfile;
const globalRank = currentDriver.globalRank || null;
// Update URL when tab changes
useEffect(() => {
if (searchParams.get('tab') !== activeTab) {
const params = new URLSearchParams(searchParams.toString());
if (activeTab === 'overview') {
params.delete('tab');
} else {
params.set('tab', activeTab);
}
const query = params.toString();
router.replace(`/profile${query ? `?${query}` : ''}`, { scroll: false });
}
}, [activeTab, searchParams, router]);
// Sync tab from URL on mount and param change
useEffect(() => {
const tabParam = searchParams.get('tab') as ProfileTab | null;
if (tabParam && tabParam !== activeTab) {
setActiveTab(tabParam);
}
}, [searchParams]);
// Show edit mode
if (editMode && currentDriver) {
return (
Edit Profile
);
}
return (
{/* Hero Header Section */}
{/* Background Pattern */}
{/* Avatar */}
{/* Online status indicator */}
{/* Driver Info */}
{currentDriver.name}
{getCountryFlag(currentDriver.country)}
{teamMemberships.length > 0 && teamMemberships[0] && (
[{teamMemberships[0].teamTag || 'TEAM'}]
)}
{/* Rating and Rank */}
{stats && (
<>
{stats.rating ?? 0}
Rating
#{globalRank}
Global
>
)}
{teamMemberships.length > 0 && teamMemberships[0] && (
{teamMemberships[0].teamName}
)}
{/* Meta info */}
iRacing: {currentDriver.iracingId}
Joined {new Date(currentDriver.joinedAt).toLocaleDateString('en-US', { month: 'short', year: 'numeric' })}
{extendedProfile && (
{extendedProfile.timezone}
)}
{/* Action Buttons */}
{isOwnProfile ? (
) : (
<>
>
)}
{/* Social Handles */}
{extendedProfile && extendedProfile.socialHandles.length > 0 && (
Connect:
{extendedProfile.socialHandles.map((social) => {
const Icon = getSocialIcon(social.platform);
return (
{social.handle}
);
})}
)}
{/* Bio Section */}
{currentDriver.bio && (
About
{currentDriver.bio}
)}
{/* Team Memberships */}
{teamMemberships.length > 0 && (
Team Memberships
({teamMemberships.length})
{teamMemberships.map((membership) => (
{membership.teamName}
{membership.role}
Since {new Date(membership.joinedAt).toLocaleDateString('en-US', { month: 'short', year: 'numeric' })}
))}
)}
{/* Key Stats Overview with Diagrams */}
{stats && (
Performance Overview
{/* Circular Progress Charts */}
{/* Finish Distribution */}
Finish Distribution
Best Finish
P{stats.bestFinish}
Avg Finish
P{(stats.avgFinish ?? 0).toFixed(1)}
)}
{/* Tab Navigation */}
{/* Tab Content */}
{activeTab === 'overview' && (
<>
{/* Racing Profile & Quick Stats */}
{/* Career Stats Summary */}
Career Statistics
{stats ? (
{stats.consistency ?? 0}%
Consistency
) : (
No race statistics available yet. Join a league and compete to start building your record!
)}
{/* Racing Preferences */}
{/* Background accent */}
Racing Profile
{extendedProfile && (
<>
{/* Racing Style - Featured */}
Racing Style
{extendedProfile.racingStyle}
{/* Track & Car Grid */}
Track
{extendedProfile.favoriteTrack}
Car
{extendedProfile.favoriteCar}
{/* Availability */}
Available
{extendedProfile.availableHours}
{/* Status badges */}
{extendedProfile.lookingForTeam && (
Looking for Team
Open to recruitment offers
)}
{extendedProfile.openToRequests && (
Open to Requests
Accepting friend invites
)}
>
)}
{/* Achievements */}
{extendedProfile && extendedProfile.achievements.length > 0 && (
Achievements
{extendedProfile.achievements.length} earned
{extendedProfile.achievements.map((achievement) => {
const Icon = getAchievementIcon(achievement.icon);
const rarityClasses = getRarityColor(achievement.rarity);
return (
{achievement.title}
{achievement.description}
{new Date(achievement.earnedAt).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
})}
);
})}
)}
{/* Friends Preview */}
{socialSummary && socialSummary.friends.length > 0 && (
Friends
({socialSummary.friendsCount})
{socialSummary.friends.slice(0, 8).map((friend) => (
{friend.name}
{getCountryFlag(friend.country)}
))}
{socialSummary.friendsCount > 8 && (
+{socialSummary.friendsCount - 8} more
)}
)}
>
)}
{activeTab === 'history' && currentDriver && (
Race History
)}
{activeTab === 'stats' && stats && (
{/* Detailed Performance Metrics */}
Detailed Performance Metrics
{/* Performance Bars */}
Results Breakdown
{/* Key Metrics */}
{((stats.wins / stats.totalRaces) * 100).toFixed(1)}%
{((stats.podiums / stats.totalRaces) * 100).toFixed(1)}%
{stats.consistency ?? 0}%
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
{/* Global Rankings */}
Global Rankings
#{globalRank}
Global Rank
{stats.rating ?? 0}
Rating
Top {stats.percentile}%
Percentile
)}
{activeTab === 'stats' && !stats && (
No statistics available yet
Join a league and complete races to see detailed stats
)}
);
}
// ============================================================================
// MAIN PAGE COMPONENT
// ============================================================================
export default function ProfilePage() {
const router = useRouter();
const searchParams = useSearchParams();
const tabParam = searchParams.get('tab') as ProfileTab | null;
const driverService = useInject(DRIVER_SERVICE_TOKEN);
const mediaService = useInject(MEDIA_SERVICE_TOKEN);
const effectiveDriverId = useEffectiveDriverId();
const isOwnProfile = true; // This page is always your own profile
// Use React-Query hook for profile data
const { data: profileData, isLoading: loading, error, refetch } = useDriverProfile(effectiveDriverId || '');
const [editMode, setEditMode] = useState(false);
const [activeTab, setActiveTab] = useState(tabParam || 'overview');
const [friendRequestSent, setFriendRequestSent] = useState(false);
const handleSaveSettings = async (updates: { bio?: string; country?: string }) => {
if (!profileData?.currentDriver) return;
try {
const updatedProfile = await driverService.updateProfile(updates);
// Update local state
refetch();
setEditMode(false);
} catch (error) {
console.error('Failed to update profile:', error);
}
};
const handleAddFriend = () => {
setFriendRequestSent(true);
// In production, this would call a use case
};
// Show create form if no profile exists
if (!loading && !profileData?.currentDriver && !error) {
return (
Create Your Driver Profile
Join the GridPilot community and start your racing journey
Get Started
Create your driver profile to join leagues, compete in races, and connect with other drivers.
);
}
return (
(
setEditMode(true)}
onAddFriend={handleAddFriend}
activeTab={activeTab}
setActiveTab={setActiveTab}
friendRequestSent={friendRequestSent}
isOwnProfile={isOwnProfile}
handleSaveSettings={handleSaveSettings}
editMode={editMode}
setEditMode={setEditMode}
/>
)}
loading={{ variant: 'full-screen', message: 'Loading profile...' }}
errorConfig={{ variant: 'full-screen' }}
empty={{
icon: User,
title: 'No profile data',
description: 'Unable to load your profile information',
action: { label: 'Retry', onClick: refetch }
}}
/>
);
}