website refactor
This commit is contained in:
@@ -1,91 +1,171 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import { Clock, MapPin, Users } from 'lucide-react';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { ArrowRight, Car, ChevronRight, LucideIcon, Trophy, Zap } from 'lucide-react';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { SessionStatusBadge, type SessionStatus } from './SessionStatusBadge';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Link } from '@/ui/Link';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Surface } from '@/ui/Surface';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { routes } from '@/lib/routing/RouteConfig';
|
||||
|
||||
interface RaceCardProps {
|
||||
id: string;
|
||||
title: string;
|
||||
leagueName: string;
|
||||
trackName: string;
|
||||
track: string;
|
||||
car: string;
|
||||
scheduledAt: string;
|
||||
entrantCount: number;
|
||||
status: SessionStatus;
|
||||
onClick: (id: string) => void;
|
||||
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({
|
||||
id,
|
||||
title,
|
||||
leagueName,
|
||||
trackName,
|
||||
track,
|
||||
car,
|
||||
scheduledAt,
|
||||
entrantCount,
|
||||
status,
|
||||
leagueName,
|
||||
leagueId,
|
||||
strengthOfField,
|
||||
onClick,
|
||||
statusConfig,
|
||||
}: RaceCardProps) {
|
||||
const scheduledAtDate = new Date(scheduledAt);
|
||||
|
||||
return (
|
||||
<Box
|
||||
as="article"
|
||||
onClick={() => onClick(id)}
|
||||
<Surface
|
||||
bg="bg-surface-charcoal"
|
||||
rounded="xl"
|
||||
border
|
||||
borderColor="border-outline-steel"
|
||||
p={4}
|
||||
padding={4}
|
||||
onClick={onClick}
|
||||
cursor={onClick ? 'pointer' : 'default'}
|
||||
hoverBorderColor="border-primary-accent"
|
||||
transition
|
||||
cursor="pointer"
|
||||
position="relative"
|
||||
overflow="hidden"
|
||||
group
|
||||
>
|
||||
{/* Hover Glow */}
|
||||
<Box
|
||||
position="absolute"
|
||||
inset="0"
|
||||
bg="bg-primary-accent"
|
||||
bgOpacity={0.05}
|
||||
opacity={0}
|
||||
groupHoverOpacity={1}
|
||||
transition
|
||||
/>
|
||||
|
||||
<Stack gap={4}>
|
||||
<Stack direction="row" justifyContent="between" alignItems="start">
|
||||
<Stack gap={1}>
|
||||
<Text size="xs" color="text-gray-500" weight="bold" uppercase>
|
||||
{leagueName}
|
||||
</Text>
|
||||
<Text size="lg" weight="bold" groupHoverTextColor="text-primary-accent">
|
||||
{title}
|
||||
</Text>
|
||||
</Stack>
|
||||
<SessionStatusBadge status={status} />
|
||||
</Stack>
|
||||
{/* Live indicator */}
|
||||
{status === 'running' && (
|
||||
<Box
|
||||
position="absolute"
|
||||
top="0"
|
||||
left="0"
|
||||
right="0"
|
||||
h="1"
|
||||
bg="bg-success-green"
|
||||
animate="pulse"
|
||||
/>
|
||||
)}
|
||||
|
||||
<Box display="grid" gridCols={2} gap={4}>
|
||||
<Stack direction="row" alignItems="center" gap={2}>
|
||||
<Icon icon={MapPin} size={3} color="#6b7280" />
|
||||
<Text size="xs" color="text-gray-400">{trackName}</Text>
|
||||
</Stack>
|
||||
<Stack direction="row" alignItems="center" gap={2}>
|
||||
<Icon icon={Clock} size={3} color="#6b7280" />
|
||||
<Text size="xs" color="text-gray-400">{scheduledAt}</Text>
|
||||
</Stack>
|
||||
<Stack direction="row" align="start" gap={4}>
|
||||
{/* Time Column */}
|
||||
<Box textAlign="center" flexShrink={0} width="16">
|
||||
<Text size="lg" weight="bold" color="text-white" block>
|
||||
{scheduledAtDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
||||
</Text>
|
||||
<Text size="xs" color={statusConfig.color} block>
|
||||
{status === 'running' ? 'LIVE' : scheduledAtDate.toLocaleDateString()}
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Stack direction="row" alignItems="center" gap={2} pt={2} borderTop borderColor="border-outline-steel" bgOpacity={0.5}>
|
||||
<Icon icon={Users} size={3} color="#4ED4E0" />
|
||||
<Text size="xs" color="text-gray-400">
|
||||
<Text as="span" color="text-telemetry-aqua" weight="bold">{entrantCount}</Text> ENTRANTS
|
||||
</Text>
|
||||
</Stack>
|
||||
{/* Divider */}
|
||||
<Box
|
||||
w="px"
|
||||
bg="border-outline-steel"
|
||||
alignSelf="stretch"
|
||||
/>
|
||||
|
||||
{/* Main Content */}
|
||||
<Box flex={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 */}
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
gap={1.5}
|
||||
px={2.5}
|
||||
py={1}
|
||||
rounded="full"
|
||||
border
|
||||
borderColor="border-outline-steel"
|
||||
bg="bg-base-black"
|
||||
bgOpacity={0.5}
|
||||
>
|
||||
{statusConfig.icon && (
|
||||
<Icon icon={statusConfig.icon} size={3.5} color={statusConfig.color} />
|
||||
)}
|
||||
<Text size="xs" weight="medium" color={statusConfig.color}>
|
||||
{statusConfig.label}
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{/* League Link */}
|
||||
<Box mt={3} pt={3} borderTop borderColor="border-outline-steel" borderOpacity={0.3}>
|
||||
<Link
|
||||
href={routes.league.detail(leagueId ?? '')}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Icon icon={Trophy} size={3.5} color="var(--primary-accent)" />
|
||||
<Text size="sm" color="text-primary-accent">
|
||||
{leagueName}
|
||||
</Text>
|
||||
<Icon icon={ArrowRight} size={3} color="var(--primary-accent)" />
|
||||
</Stack>
|
||||
</Link>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Arrow */}
|
||||
<Icon
|
||||
icon={ChevronRight}
|
||||
size={5}
|
||||
color="var(--text-gray-500)"
|
||||
groupHoverTextColor="text-primary-accent"
|
||||
transition
|
||||
flexShrink={0}
|
||||
/>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Surface>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user