255 lines
8.2 KiB
TypeScript
255 lines
8.2 KiB
TypeScript
'use client';
|
|
|
|
import React from 'react';
|
|
import { Box } from '@/ui/Box';
|
|
import { Text } from '@/ui/Text';
|
|
import { Stack } from '@/ui/Stack';
|
|
import { Group } from '@/ui/Group';
|
|
import { Surface } from '@/ui/Surface';
|
|
import { Icon } from '@/ui/Icon';
|
|
import { Button } from '@/ui/Button';
|
|
import { Badge } from '@/ui/Badge';
|
|
import {
|
|
Calendar,
|
|
Clock,
|
|
Car,
|
|
MapPin,
|
|
Thermometer,
|
|
Droplets,
|
|
Wind,
|
|
Cloud,
|
|
X,
|
|
Trophy,
|
|
CheckCircle
|
|
} from 'lucide-react';
|
|
import { DateDisplay } from '@/lib/display-objects/DateDisplay';
|
|
|
|
interface RaceDetailModalProps {
|
|
race: {
|
|
id: string;
|
|
name: string;
|
|
track?: string;
|
|
car?: string;
|
|
sessionType?: string;
|
|
scheduledAt: string;
|
|
status: 'scheduled' | 'completed';
|
|
strengthOfField?: number;
|
|
isUserRegistered?: boolean;
|
|
canRegister?: boolean;
|
|
};
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
onRegister?: () => void;
|
|
onWithdraw?: () => void;
|
|
onResultsClick?: () => void;
|
|
}
|
|
|
|
export function RaceDetailModal({
|
|
race,
|
|
isOpen,
|
|
onClose,
|
|
onRegister,
|
|
onWithdraw,
|
|
onResultsClick,
|
|
}: RaceDetailModalProps) {
|
|
if (!isOpen) return null;
|
|
|
|
const formatTime = (scheduledAt: string) => {
|
|
return DateDisplay.formatDateTime(scheduledAt);
|
|
};
|
|
|
|
const getStatusBadge = (status: 'scheduled' | 'completed') => {
|
|
if (status === 'completed') {
|
|
return <Badge variant="success" size="sm">Completed</Badge>;
|
|
}
|
|
return <Badge variant="primary" size="sm">Scheduled</Badge>;
|
|
};
|
|
|
|
return (
|
|
<Box
|
|
position="fixed"
|
|
top={0}
|
|
left={0}
|
|
right={0}
|
|
bottom={0}
|
|
bg="bg-base-black/80"
|
|
display="flex"
|
|
alignItems="center"
|
|
justifyContent="center"
|
|
zIndex={1000}
|
|
onClick={onClose}
|
|
>
|
|
<Box
|
|
maxWidth="lg"
|
|
width="100%"
|
|
mx={4}
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<Surface variant="precision" overflow="hidden" data-testid="race-detail-modal">
|
|
{/* Header */}
|
|
<Box
|
|
display="flex"
|
|
alignItems="center"
|
|
justifyContent="space-between"
|
|
p={4}
|
|
bg="bg-surface"
|
|
borderBottom
|
|
borderColor="border-default"
|
|
>
|
|
<Group gap={3}>
|
|
<Text size="lg" weight="bold" variant="high">
|
|
{race.name || `Race ${race.id.substring(0, 4)}`}
|
|
</Text>
|
|
{getStatusBadge(race.status)}
|
|
</Group>
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={onClose}
|
|
icon={<Icon icon={X} size={4} />}
|
|
>
|
|
Close
|
|
</Button>
|
|
</Box>
|
|
|
|
{/* Content */}
|
|
<Box p={4}>
|
|
<Stack gap={4}>
|
|
{/* Basic Info */}
|
|
<Surface variant="precision" p={4}>
|
|
<Text as="h3" size="sm" weight="bold" variant="low" uppercase letterSpacing="widest" mb={3}>
|
|
Race Details
|
|
</Text>
|
|
<Stack gap={3}>
|
|
<Group gap={2} align="center" data-testid="race-track">
|
|
<Icon icon={MapPin} size={4} intent="primary" />
|
|
<Text size="md" variant="high" weight="bold">
|
|
{race.track || 'TBA'}
|
|
</Text>
|
|
</Group>
|
|
<Group gap={2} align="center" data-testid="race-car">
|
|
<Icon icon={Car} size={4} intent="primary" />
|
|
<Text size="md" variant="high">
|
|
{race.car || 'TBA'}
|
|
</Text>
|
|
</Group>
|
|
<Group gap={2} align="center" data-testid="race-date">
|
|
<Icon icon={Calendar} size={4} intent="primary" />
|
|
<Text size="md" variant="high">
|
|
{formatTime(race.scheduledAt)}
|
|
</Text>
|
|
</Group>
|
|
{race.sessionType && (
|
|
<Group gap={2} align="center">
|
|
<Icon icon={Clock} size={4} intent="primary" />
|
|
<Text size="md" variant="high">
|
|
{race.sessionType}
|
|
</Text>
|
|
</Group>
|
|
)}
|
|
</Stack>
|
|
</Surface>
|
|
|
|
{/* Weather Info (Mock Data) */}
|
|
<Surface variant="precision" p={4}>
|
|
<Text as="h3" size="sm" weight="bold" variant="low" uppercase letterSpacing="widest" mb={3}>
|
|
Weather Conditions
|
|
</Text>
|
|
<Stack gap={3}>
|
|
<Group gap={2} align="center">
|
|
<Icon icon={Thermometer} size={4} intent="primary" />
|
|
<Text size="md" variant="high">Air: 24°C</Text>
|
|
</Group>
|
|
<Group gap={2} align="center">
|
|
<Icon icon={Thermometer} size={4} intent="primary" />
|
|
<Text size="md" variant="high">Track: 31°C</Text>
|
|
</Group>
|
|
<Group gap={2} align="center">
|
|
<Icon icon={Droplets} size={4} intent="primary" />
|
|
<Text size="md" variant="high">Humidity: 45%</Text>
|
|
</Group>
|
|
<Group gap={2} align="center">
|
|
<Icon icon={Wind} size={4} intent="primary" />
|
|
<Text size="md" variant="high">Wind: 12 km/h NW</Text>
|
|
</Group>
|
|
<Group gap={2} align="center">
|
|
<Icon icon={Cloud} size={4} intent="primary" />
|
|
<Text size="md" variant="high">Partly Cloudy</Text>
|
|
</Group>
|
|
</Stack>
|
|
</Surface>
|
|
|
|
{/* Car Classes */}
|
|
<Surface variant="precision" p={4}>
|
|
<Text as="h3" size="sm" weight="bold" variant="low" uppercase letterSpacing="widest" mb={3}>
|
|
Car Classes
|
|
</Text>
|
|
<Group gap={2} wrap>
|
|
<Badge variant="outline" size="sm">GT3</Badge>
|
|
<Badge variant="outline" size="sm">GT4</Badge>
|
|
<Badge variant="outline" size="sm">TCR</Badge>
|
|
</Group>
|
|
</Surface>
|
|
|
|
{/* Strength of Field */}
|
|
{race.strengthOfField && (
|
|
<Surface variant="precision" p={4}>
|
|
<Text as="h3" size="sm" weight="bold" variant="low" uppercase letterSpacing="widest" mb={3}>
|
|
Strength of Field
|
|
</Text>
|
|
<Group gap={2} align="center">
|
|
<Icon icon={Trophy} size={4} intent="primary" />
|
|
<Text size="md" variant="high">
|
|
{race.strengthOfField.toFixed(1)} / 10.0
|
|
</Text>
|
|
</Group>
|
|
</Surface>
|
|
)}
|
|
|
|
{/* Action Buttons */}
|
|
{race.status === 'scheduled' && (
|
|
<Box display="flex" gap={2} flexWrap="wrap">
|
|
{!race.isUserRegistered && race.canRegister && onRegister && (
|
|
<Button
|
|
variant="primary"
|
|
size="md"
|
|
onClick={onRegister}
|
|
icon={<Icon icon={CheckCircle} size={4} />}
|
|
fullWidth
|
|
>
|
|
Register
|
|
</Button>
|
|
)}
|
|
{race.isUserRegistered && onWithdraw && (
|
|
<Button
|
|
variant="secondary"
|
|
size="md"
|
|
onClick={onWithdraw}
|
|
icon={<Icon icon={X} size={4} />}
|
|
fullWidth
|
|
>
|
|
Withdraw
|
|
</Button>
|
|
)}
|
|
</Box>
|
|
)}
|
|
|
|
{race.status === 'completed' && onResultsClick && (
|
|
<Button
|
|
variant="primary"
|
|
size="md"
|
|
onClick={onResultsClick}
|
|
icon={<Icon icon={Trophy} size={4} />}
|
|
fullWidth
|
|
>
|
|
View Results
|
|
</Button>
|
|
)}
|
|
</Stack>
|
|
</Box>
|
|
</Surface>
|
|
</Box>
|
|
</Box>
|
|
);
|
|
}
|