Files
gridpilot.gg/apps/website/components/races/RaceListItem.tsx
2026-01-18 13:26:35 +01:00

145 lines
4.3 KiB
TypeScript

'use client';
import { ArrowRight, Car, ChevronRight, LucideIcon, Trophy, Zap } from 'lucide-react';
import { Badge } from '@/ui/Badge';
import { Box } from '@/ui/Box';
import { Heading } from '@/ui/Heading';
import { Icon } from '@/ui/Icon';
import { Link } from '@/ui/Link';
import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text';
interface RaceListItemProps {
track: string;
car: string;
timeLabel?: string;
relativeTimeLabel?: string;
dateLabel?: string;
dayLabel?: string;
status: string;
leagueName?: string | null;
leagueHref?: string;
strengthOfField?: number | null;
onClick: () => void;
statusConfig: {
icon: LucideIcon;
variant: 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info';
label: string;
};
}
export function RaceListItem({
track,
car,
timeLabel,
relativeTimeLabel,
dateLabel,
dayLabel,
status,
leagueName,
leagueHref,
strengthOfField,
onClick,
statusConfig,
}: RaceListItemProps) {
const StatusIcon = statusConfig.icon;
return (
<Box
onClick={onClick}
position="relative"
overflow="hidden"
rounded="xl"
bg="bg-surface-charcoal"
border
borderColor="border-outline-steel"
p={4}
cursor="pointer"
transition
hoverScale
group
>
{/* Live indicator */}
{status === 'running' && (
<Box
position="absolute"
top="0"
left="0"
right="0"
h="1"
bg="bg-success-green"
animate="pulse"
/>
)}
<Stack direction="row" align="center" gap={4}>
{/* Time/Date Column */}
<Box flexShrink={0} textAlign="center" width="16">
{dateLabel && (
<Text size="xs" color="text-gray-500" block uppercase>
{dateLabel}
</Text>
)}
<Text size={dayLabel ? "2xl" : "lg"} weight="bold" color="text-white" block>
{dayLabel || timeLabel}
</Text>
<Text size="xs" color={status === 'running' ? 'text-success-green' : 'text-gray-400'} block>
{status === 'running' ? 'LIVE' : relativeTimeLabel || timeLabel}
</Text>
</Box>
{/* Divider */}
<Box w="px" h="10" alignSelf="stretch" bg="border-outline-steel" />
{/* Main Content */}
<Box flexGrow={1} minWidth="0">
<Stack direction="row" align="start" justify="between" gap={4}>
<Box minWidth="0">
<Heading level={3} truncate groupHoverTextColor="text-primary-accent" transition>
{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 */}
<Badge variant={statusConfig.variant}>
<Icon icon={StatusIcon} size={3.5} />
{statusConfig.label}
</Badge>
</Stack>
{/* League Link */}
{leagueName && leagueHref && (
<Box mt={3} pt={3} borderTop borderColor="border-outline-steel" borderOpacity={0.3}>
<Link
href={leagueHref}
onClick={(e) => e.stopPropagation()}
variant="primary"
size="sm"
>
<Icon icon={Trophy} size={3.5} mr={2} color="var(--primary-accent)" />
<Text as="span" color="text-primary-accent">{leagueName}</Text>
<Icon icon={ArrowRight} size={3} ml={2} color="var(--primary-accent)" />
</Link>
</Box>
)}
</Box>
{/* Arrow */}
<Icon icon={ChevronRight} size={5} color="var(--text-gray-500)" flexShrink={0} groupHoverTextColor="text-primary-accent" transition />
</Stack>
</Box>
);
}