168 lines
5.1 KiB
TypeScript
168 lines
5.1 KiB
TypeScript
|
|
|
|
import { ArrowRight, Car, ChevronRight, LucideIcon, Trophy, Zap } from 'lucide-react';
|
|
import { Box } from './Box';
|
|
import { Heading } from './Heading';
|
|
import { Icon } from './Icon';
|
|
import { Link } from './Link';
|
|
import { Stack } from './Stack';
|
|
import { Surface } from './Surface';
|
|
import { Text } from './Text';
|
|
|
|
interface RaceCardProps {
|
|
track: string;
|
|
car: string;
|
|
scheduledAt: string;
|
|
status: string;
|
|
leagueName: string;
|
|
leagueId?: string;
|
|
strengthOfField?: number | null;
|
|
onClick?: () => void;
|
|
statusConfig: {
|
|
border: string;
|
|
bg: string;
|
|
color: string;
|
|
icon: LucideIcon | null;
|
|
label: string;
|
|
};
|
|
}
|
|
|
|
export function RaceCard({
|
|
track,
|
|
car,
|
|
scheduledAt,
|
|
status,
|
|
leagueName,
|
|
leagueId,
|
|
strengthOfField,
|
|
onClick,
|
|
statusConfig,
|
|
}: RaceCardProps) {
|
|
const scheduledAtDate = new Date(scheduledAt);
|
|
|
|
return (
|
|
<Surface
|
|
variant="muted"
|
|
rounded="xl"
|
|
border
|
|
padding={4}
|
|
onClick={onClick}
|
|
style={{
|
|
cursor: onClick ? 'pointer' : 'default',
|
|
borderColor: statusConfig.border.includes('/') ? statusConfig.border : undefined, // Handle custom colors if needed
|
|
}}
|
|
className={`group relative overflow-hidden transition-all duration-200 hover:scale-[1.01] hover:border-primary-blue`}
|
|
>
|
|
{/* Live indicator */}
|
|
{status === 'running' && (
|
|
<Box
|
|
position="absolute"
|
|
top="0"
|
|
left="0"
|
|
right="0"
|
|
h="1"
|
|
style={{
|
|
background: 'linear-gradient(to right, var(--performance-green), rgba(16, 185, 129, 0.5), var(--performance-green))',
|
|
}}
|
|
animate="pulse"
|
|
/>
|
|
)}
|
|
|
|
<Stack direction="row" align="start" gap={4}>
|
|
{/* Time Column */}
|
|
<Box textAlign="center" style={{ minWidth: '60px' }} flexShrink={0}>
|
|
<Text size="lg" weight="bold" color="text-white" block>
|
|
{scheduledAtDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
|
</Text>
|
|
<Text size="xs" color={statusConfig.color as any} block>
|
|
{status === 'running' ? 'LIVE' : scheduledAtDate.toLocaleDateString()}
|
|
</Text>
|
|
</Box>
|
|
|
|
{/* Divider */}
|
|
<Box
|
|
w="px"
|
|
style={{ alignSelf: 'stretch', backgroundColor: 'rgba(38, 38, 38, 0.8)' }}
|
|
/>
|
|
|
|
{/* Main Content */}
|
|
<Box style={{ flex: 1, minWidth: 0 }}>
|
|
<Stack direction="row" align="start" justify="between" gap={4}>
|
|
<Box style={{ minWidth: 0 }}>
|
|
<Heading
|
|
level={3}
|
|
className="truncate group-hover:text-primary-blue transition-colors"
|
|
>
|
|
{track}
|
|
</Heading>
|
|
<Stack direction="row" align="center" gap={3} mt={1}>
|
|
<Stack direction="row" align="center" gap={1}>
|
|
<Icon icon={Car} size={3.5} color="var(--text-gray-400)" />
|
|
<Text size="sm" color="text-gray-400">
|
|
{car}
|
|
</Text>
|
|
</Stack>
|
|
{strengthOfField && (
|
|
<Stack direction="row" align="center" gap={1}>
|
|
<Icon icon={Zap} size={3.5} color="var(--warning-amber)" />
|
|
<Text size="sm" color="text-gray-400">
|
|
SOF {strengthOfField}
|
|
</Text>
|
|
</Stack>
|
|
)}
|
|
</Stack>
|
|
</Box>
|
|
|
|
{/* Status Badge */}
|
|
<Box
|
|
display="flex"
|
|
alignItems="center"
|
|
gap={1.5}
|
|
px={2.5}
|
|
py={1}
|
|
rounded="full"
|
|
border
|
|
style={{
|
|
backgroundColor: 'rgba(38, 38, 38, 0.5)',
|
|
borderColor: 'rgba(38, 38, 38, 0.8)',
|
|
}}
|
|
>
|
|
{statusConfig.icon && (
|
|
<Icon icon={statusConfig.icon} size={3.5} color={statusConfig.color as any} />
|
|
)}
|
|
<Text size="xs" weight="medium" color={statusConfig.color as any}>
|
|
{statusConfig.label}
|
|
</Text>
|
|
</Box>
|
|
</Stack>
|
|
|
|
{/* League Link */}
|
|
<Box mt={3} pt={3} borderTop style={{ borderColor: 'rgba(38, 38, 38, 0.3)' }}>
|
|
<Link
|
|
href={`/leagues/${leagueId ?? ''}`}
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<Stack direction="row" align="center" gap={2}>
|
|
<Icon icon={Trophy} size={3.5} color="var(--primary-blue)" />
|
|
<Text size="sm" color="text-primary-blue" className="hover:underline">
|
|
{leagueName}
|
|
</Text>
|
|
<Icon icon={ArrowRight} size={3} color="var(--primary-blue)" />
|
|
</Stack>
|
|
</Link>
|
|
</Box>
|
|
</Box>
|
|
|
|
{/* Arrow */}
|
|
<Icon
|
|
icon={ChevronRight}
|
|
size={5}
|
|
color="var(--text-gray-500)"
|
|
className="group-hover:text-primary-blue transition-colors"
|
|
flexShrink={0}
|
|
/>
|
|
</Stack>
|
|
</Surface>
|
|
);
|
|
}
|