177 lines
6.0 KiB
TypeScript
177 lines
6.0 KiB
TypeScript
'use client';
|
|
|
|
import type { DriverViewModel } from '@/lib/view-models/DriverViewModel';
|
|
import { Card } from '@/ui/Card';
|
|
import { Stack } from '@/ui/Stack';
|
|
import { Text } from '@/ui/Text';
|
|
import { Heading } from '@/ui/Heading';
|
|
import { StatCard } from '@/ui/StatCard';
|
|
import { ProfileHeader } from '@/components/drivers/ProfileHeader';
|
|
import { ProfileStats } from './ProfileStats';
|
|
import { CareerHighlights } from '@/components/drivers/CareerHighlights';
|
|
import { DriverRankings } from '@/components/drivers/DriverRankings';
|
|
import { PerformanceMetrics } from '@/components/drivers/PerformanceMetrics';
|
|
import { useDriverProfile } from "@/hooks/driver/useDriverProfile";
|
|
|
|
interface DriverProfileProps {
|
|
driver: DriverViewModel;
|
|
isOwnProfile?: boolean;
|
|
onEditClick?: () => void;
|
|
}
|
|
|
|
interface DriverTeamViewModel {
|
|
team: {
|
|
name: string;
|
|
tag: string;
|
|
};
|
|
}
|
|
|
|
export function DriverProfile({ driver, isOwnProfile = false, onEditClick }: DriverProfileProps) {
|
|
const { data: profileData } = useDriverProfile(driver.id);
|
|
|
|
// Extract team data from profile
|
|
const teamData: DriverTeamViewModel | null = (() => {
|
|
if (!profileData?.teamMemberships || profileData.teamMemberships.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
const currentTeam = profileData.teamMemberships.find((m: { isCurrent: boolean }) => m.isCurrent) || profileData.teamMemberships[0];
|
|
if (!currentTeam) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
team: {
|
|
name: currentTeam.teamName,
|
|
tag: currentTeam.teamTag ?? ''
|
|
}
|
|
};
|
|
})();
|
|
|
|
const driverStats = profileData?.stats ?? null;
|
|
const globalRank = driverStats?.overallRank ?? null;
|
|
const totalDrivers = 1000; // Placeholder
|
|
|
|
const performanceStats = driverStats ? {
|
|
winRate: driverStats.totalRaces > 0 ? (driverStats.wins / driverStats.totalRaces) * 100 : 0,
|
|
podiumRate: driverStats.totalRaces > 0 ? (driverStats.podiums / driverStats.totalRaces) * 100 : 0,
|
|
dnfRate: driverStats.totalRaces > 0 ? (driverStats.dnfs / driverStats.totalRaces) * 100 : 0,
|
|
avgFinish: driverStats.avgFinish ?? 0,
|
|
consistency: driverStats.consistency ?? 0,
|
|
bestFinish: driverStats.bestFinish ?? 0,
|
|
worstFinish: driverStats.worstFinish ?? 0,
|
|
} : null;
|
|
|
|
const rankings = driverStats ? [
|
|
{
|
|
type: 'overall' as const,
|
|
name: 'Overall Ranking',
|
|
rank: globalRank ?? driverStats.overallRank ?? 0,
|
|
totalDrivers,
|
|
percentile: driverStats.percentile ?? 0,
|
|
rating: driverStats.rating ?? 0,
|
|
},
|
|
] : [];
|
|
|
|
return (
|
|
<Stack gap={6}>
|
|
<Card>
|
|
<ProfileHeader
|
|
driver={driver}
|
|
rating={driverStats?.rating ?? null}
|
|
rank={driverStats?.overallRank ?? null}
|
|
isOwnProfile={isOwnProfile}
|
|
onEditClick={onEditClick ?? (() => {})}
|
|
teamName={teamData?.team.name ?? null}
|
|
teamTag={teamData?.team.tag ?? null}
|
|
/>
|
|
</Card>
|
|
|
|
{driver.bio && (
|
|
<Card>
|
|
<Heading level={3} mb={4}>About</Heading>
|
|
<Text color="text-gray-300" leading="relaxed" block>{driver.bio}</Text>
|
|
</Card>
|
|
)}
|
|
|
|
{driverStats && (
|
|
<Stack display="grid" responsiveGridCols={{ base: 1, lg: 3 }} gap={6}>
|
|
<Stack responsiveColSpan={{ lg: 2 }}>
|
|
<Stack gap={6}>
|
|
<Card>
|
|
<Heading level={3} mb={4}>Career Statistics</Heading>
|
|
<Stack display="grid" gridCols={2} gap={4}>
|
|
<StatCard
|
|
label="Rating"
|
|
value={driverStats.rating ?? 0}
|
|
variant="blue"
|
|
/>
|
|
<StatCard label="Total Races" value={driverStats.totalRaces} variant="blue" />
|
|
<StatCard label="Wins" value={driverStats.wins} variant="green" />
|
|
<StatCard label="Podiums" value={driverStats.podiums} variant="orange" />
|
|
</Stack>
|
|
</Card>
|
|
|
|
{performanceStats && <PerformanceMetrics stats={performanceStats} />}
|
|
</Stack>
|
|
</Stack>
|
|
|
|
<DriverRankings rankings={rankings} />
|
|
</Stack>
|
|
)}
|
|
|
|
{!driverStats && (
|
|
<Stack display="grid" responsiveGridCols={{ base: 1, lg: 3 }} gap={6}>
|
|
<Card responsiveColSpan={{ lg: 3 }}>
|
|
<Heading level={3} mb={4}>Career Statistics</Heading>
|
|
<Text color="text-gray-400" size="sm" block>
|
|
No statistics available yet. Compete in races to start building your record.
|
|
</Text>
|
|
</Card>
|
|
</Stack>
|
|
)}
|
|
|
|
<Card>
|
|
<Heading level={3} mb={4}>Performance by Class</Heading>
|
|
{driverStats && (
|
|
<ProfileStats
|
|
stats={{
|
|
totalRaces: driverStats.totalRaces,
|
|
wins: driverStats.wins,
|
|
podiums: driverStats.podiums,
|
|
dnfs: driverStats.dnfs,
|
|
avgFinish: driverStats.avgFinish ?? 0,
|
|
completionRate:
|
|
driverStats.totalRaces > 0
|
|
? ((driverStats.totalRaces - driverStats.dnfs) / driverStats.totalRaces) * 100
|
|
: 0,
|
|
}}
|
|
/>
|
|
)}
|
|
</Card>
|
|
|
|
<CareerHighlights />
|
|
|
|
<Card bg="bg-charcoal-200/50" borderColor="border-primary-blue/30">
|
|
<Stack display="flex" alignItems="center" gap={3} mb={3}>
|
|
<Text size="2xl">🔒</Text>
|
|
<Heading level={3}>Private Information</Heading>
|
|
</Stack>
|
|
<Text color="text-gray-400" size="sm" block>
|
|
Detailed race history, settings, and preferences are only visible to the driver.
|
|
</Text>
|
|
</Card>
|
|
|
|
<Card bg="bg-charcoal-200/50" borderColor="border-primary-blue/30">
|
|
<Stack display="flex" alignItems="center" gap={3} mb={3}>
|
|
<Text size="2xl">📊</Text>
|
|
<Heading level={3}>Coming Soon</Heading>
|
|
</Stack>
|
|
<Text color="text-gray-400" size="sm" block>
|
|
Per-car statistics, per-track performance, and head-to-head comparisons will be available in production.
|
|
</Text>
|
|
</Card>
|
|
</Stack>
|
|
);
|
|
}
|