website refactor
This commit is contained in:
@@ -3,9 +3,8 @@
|
||||
import { useState } from 'react';
|
||||
import { Modal } from '@/ui/Modal';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { InfoBox } from '@/ui/InfoBox';
|
||||
import { Input } from '@/ui/Input';
|
||||
@@ -112,10 +111,10 @@ export function FileProtestModal({
|
||||
onOpenChange={handleClose}
|
||||
title="Protest Filed Successfully"
|
||||
>
|
||||
<Box display="flex" flexDirection="col" alignItems="center" py={6} textAlign="center">
|
||||
<Box p={4} bg="bg-performance-green/20" rounded="full" mb={4}>
|
||||
<Stack display="flex" flexDirection="col" alignItems="center" py={6} textAlign="center">
|
||||
<Stack p={4} bg="bg-performance-green/20" rounded="full" mb={4}>
|
||||
<Icon icon={CheckCircle2} size={8} color="rgb(16, 185, 129)" />
|
||||
</Box>
|
||||
</Stack>
|
||||
<Text color="text-white" weight="medium" mb={2} block>Your protest has been submitted</Text>
|
||||
<Text size="sm" color="text-gray-400" mb={6} block>
|
||||
The stewards will review your protest and make a decision.
|
||||
@@ -124,7 +123,7 @@ export function FileProtestModal({
|
||||
<Button variant="primary" onClick={handleClose}>
|
||||
Close
|
||||
</Button>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -147,7 +146,7 @@ export function FileProtestModal({
|
||||
)}
|
||||
|
||||
{/* Driver Selection */}
|
||||
<Box>
|
||||
<Stack>
|
||||
<Select
|
||||
label="Driver involved *"
|
||||
options={[
|
||||
@@ -158,10 +157,10 @@ export function FileProtestModal({
|
||||
onChange={(e) => setAccusedDriverId(e.target.value)}
|
||||
disabled={fileProtestMutation.isPending}
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{/* Lap and Time */}
|
||||
<Box display="grid" gridCols={2} gap={4}>
|
||||
<Stack display="grid" gridCols={2} gap={4}>
|
||||
<Input
|
||||
label="Lap number *"
|
||||
type="number"
|
||||
@@ -180,7 +179,7 @@ export function FileProtestModal({
|
||||
disabled={fileProtestMutation.isPending}
|
||||
placeholder="Optional"
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{/* Incident Description */}
|
||||
<TextArea
|
||||
@@ -203,7 +202,7 @@ export function FileProtestModal({
|
||||
/>
|
||||
|
||||
{/* Video Proof */}
|
||||
<Box>
|
||||
<Stack>
|
||||
<Input
|
||||
label="Video proof URL"
|
||||
type="url"
|
||||
@@ -215,19 +214,19 @@ export function FileProtestModal({
|
||||
<Text size="xs" color="text-gray-500" mt={1.5} block>
|
||||
Providing video evidence significantly helps the stewards review your protest.
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{/* Info Box */}
|
||||
<Box p={3} bg="bg-iron-gray" rounded="lg" border borderColor="border-charcoal-outline">
|
||||
<Stack p={3} bg="bg-iron-gray" rounded="lg" border borderColor="border-charcoal-outline">
|
||||
<Text size="xs" color="text-gray-400" block>
|
||||
<Text as="strong" color="text-gray-300">Note:</Text> Filing a protest does not guarantee action.
|
||||
The stewards will review the incident and may apply penalties ranging from time penalties
|
||||
to grid penalties for future races, depending on the severity.
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{/* Actions */}
|
||||
<Box display="flex" gap={3} pt={2}>
|
||||
<Stack display="flex" gap={3} pt={2}>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={handleClose}
|
||||
@@ -244,7 +243,7 @@ export function FileProtestModal({
|
||||
>
|
||||
{fileProtestMutation.isPending ? 'Submitting...' : 'Submit Protest'}
|
||||
</Button>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
@@ -24,26 +23,26 @@ export function FinishDistributionChart({ wins, podiums, topTen, total }: Finish
|
||||
|
||||
return (
|
||||
<Stack gap={3}>
|
||||
<Box h="4" rounded="full" overflow="hidden" display="flex" bg="bg-charcoal-outline">
|
||||
<Stack h="4" rounded="full" overflow="hidden" display="flex" bg="bg-charcoal-outline">
|
||||
{segments.map((segment) => (
|
||||
<Box
|
||||
<Stack
|
||||
key={segment.label}
|
||||
bg={segment.color}
|
||||
transition
|
||||
style={{ width: `${(segment.value / total) * 100}%` }}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
<Box display="flex" flexWrap="wrap" gap={4} justifyContent="center">
|
||||
</Stack>
|
||||
<Stack display="flex" flexWrap="wrap" gap={4} justifyContent="center">
|
||||
{segments.map((segment) => (
|
||||
<Box key={segment.label} display="flex" alignItems="center" gap={2}>
|
||||
<Box w="3" h="3" rounded="full" bg={segment.color} />
|
||||
<Stack key={segment.label} display="flex" alignItems="center" gap={2}>
|
||||
<Stack w="3" h="3" rounded="full" bg={segment.color} />
|
||||
<Text size="xs" color={segment.textColor}>
|
||||
{segment.label}: {segment.value} ({((segment.value / total) * 100).toFixed(0)}%)
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
))}
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -5,9 +5,8 @@ import { AlertCircle } from 'lucide-react';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { RACE_RESULTS_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { FilePicker } from '@/ui/FilePicker';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { InfoBox } from '@/ui/InfoBox';
|
||||
|
||||
interface ImportResultsFormProps {
|
||||
@@ -69,15 +68,15 @@ export function ImportResultsForm({ raceId, onSuccess, onError }: ImportResultsF
|
||||
</Text>
|
||||
)}
|
||||
|
||||
<Box p={4} bg="bg-iron-gray/20" rounded="lg">
|
||||
<Stack p={4} bg="bg-iron-gray/20" rounded="lg">
|
||||
<Text weight="semibold" block mb={2} size="xs" color="text-gray-500">CSV Example:</Text>
|
||||
<Box as="pre" color="text-gray-400">
|
||||
<Stack as="pre" color="text-gray-400">
|
||||
{`driverId,position,fastestLap,incidents,startPosition
|
||||
550e8400-e29b-41d4-a716-446655440001,1,92.456,0,3
|
||||
550e8400-e29b-41d4-a716-446655440002,2,92.789,1,1
|
||||
550e8400-e29b-41d4-a716-446655440003,3,93.012,2,2`}
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
|
||||
|
||||
import type { RaceViewData } from '@/lib/view-data/RacesViewData';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { LiveRaceItem } from '@/components/races/LiveRaceItem';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { LiveRaceItem } from '@/components/races/LiveRaceItem';
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
interface LiveRacesBannerProps {
|
||||
@@ -15,7 +14,7 @@ export function LiveRacesBanner({ liveRaces, onRaceClick }: LiveRacesBannerProps
|
||||
if (liveRaces.length === 0) return null;
|
||||
|
||||
return (
|
||||
<Box
|
||||
<Stack
|
||||
position="relative"
|
||||
overflow="hidden"
|
||||
rounded="xl"
|
||||
@@ -24,7 +23,7 @@ export function LiveRacesBanner({ liveRaces, onRaceClick }: LiveRacesBannerProps
|
||||
borderColor="border-performance-green/30"
|
||||
bg="linear-gradient(to right, rgba(16, 185, 129, 0.2), rgba(16, 185, 129, 0.1), transparent)"
|
||||
>
|
||||
<Box
|
||||
<Stack
|
||||
position="absolute"
|
||||
top="0"
|
||||
right="0"
|
||||
@@ -35,13 +34,13 @@ export function LiveRacesBanner({ liveRaces, onRaceClick }: LiveRacesBannerProps
|
||||
blur="xl"
|
||||
/>
|
||||
|
||||
<Box position="relative" zIndex={10}>
|
||||
<Box mb={4}>
|
||||
<Stack position="relative" zIndex={10}>
|
||||
<Stack mb={4}>
|
||||
<Stack direction="row" align="center" gap={2} bg="bg-performance-green/20" px={3} py={1} rounded="full" w="fit">
|
||||
<Box w="2" h="2" bg="bg-performance-green" rounded="full" />
|
||||
<Stack w="2" h="2" bg="bg-performance-green" rounded="full" />
|
||||
<Text weight="semibold" size="sm" color="text-performance-green">LIVE NOW</Text>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<Stack gap={3}>
|
||||
{liveRaces.map((race) => (
|
||||
@@ -53,7 +52,7 @@ export function LiveRacesBanner({ liveRaces, onRaceClick }: LiveRacesBannerProps
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
|
||||
import { Calendar, ChevronRight, Clock } from 'lucide-react';
|
||||
import { Badge } from '@/ui/Badge';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Button } from '@/ui/Button';
|
||||
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';
|
||||
|
||||
@@ -43,7 +42,7 @@ export function NextRaceCard({
|
||||
borderColor: 'rgba(59, 130, 246, 0.3)',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
<Stack
|
||||
position="absolute"
|
||||
top="0"
|
||||
right="0"
|
||||
@@ -54,7 +53,7 @@ export function NextRaceCard({
|
||||
borderBottomLeftRadius: '9999px',
|
||||
}}
|
||||
/>
|
||||
<Box position="relative">
|
||||
<Stack position="relative">
|
||||
<Stack direction="row" align="center" gap={2} mb={4}>
|
||||
<Badge variant="primary" style={{ textTransform: 'uppercase', letterSpacing: '0.05em' }}>
|
||||
Next Race
|
||||
@@ -67,7 +66,7 @@ export function NextRaceCard({
|
||||
</Stack>
|
||||
|
||||
<Stack direction="row" align="end" justify="between" wrap gap={4}>
|
||||
<Box>
|
||||
<Stack>
|
||||
<Heading level={2} style={{ fontSize: '1.5rem', marginBottom: '0.5rem' }}>
|
||||
{track}
|
||||
</Heading>
|
||||
@@ -88,10 +87,10 @@ export function NextRaceCard({
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<Stack align="end" gap={3}>
|
||||
<Box textAlign="right">
|
||||
<Stack textAlign="right">
|
||||
<Text
|
||||
size="xs"
|
||||
color="text-gray-500"
|
||||
@@ -104,8 +103,8 @@ export function NextRaceCard({
|
||||
<Text size="3xl" weight="bold" color="text-primary-blue" font="mono">
|
||||
{timeUntil}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Link href={href}>
|
||||
<Button
|
||||
variant="primary"
|
||||
@@ -114,10 +113,10 @@ export function NextRaceCard({
|
||||
View Details
|
||||
</Button>
|
||||
</Link>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Surface>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
|
||||
import { Badge } from '@/ui/Badge';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Surface } from '@/ui/Surface';
|
||||
import { Text } from '@/ui/Text';
|
||||
@@ -26,7 +25,7 @@ export function PenaltyRow({
|
||||
return (
|
||||
<Surface variant="dark" rounded="lg" p={3} border borderColor="border-charcoal-outline/50">
|
||||
<Stack direction="row" align="center" gap={3}>
|
||||
<Box
|
||||
<Stack
|
||||
display="flex"
|
||||
h="10"
|
||||
w="10"
|
||||
@@ -36,8 +35,8 @@ export function PenaltyRow({
|
||||
bg="bg-red-600/20"
|
||||
>
|
||||
<Text color="text-red-500" weight="bold">!</Text>
|
||||
</Box>
|
||||
<Box flexGrow={1}>
|
||||
</Stack>
|
||||
<Stack flexGrow={1}>
|
||||
<Stack direction="row" align="center" gap={2} mb={1}>
|
||||
<Text weight="medium" color="text-white">{driverName}</Text>
|
||||
<Badge variant="danger">
|
||||
@@ -50,12 +49,12 @@ export function PenaltyRow({
|
||||
{notes}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
<Box textAlign="right">
|
||||
</Stack>
|
||||
<Stack textAlign="right">
|
||||
<Text size="2xl" weight="bold" color="text-red-500">
|
||||
{value} {valueLabel}
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Surface>
|
||||
);
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
'use client';
|
||||
|
||||
import { ArrowRight, Car, ChevronRight, LucideIcon, Trophy, Zap } from 'lucide-react';
|
||||
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 { Surface } from '@/ui/Surface';
|
||||
import { Card } from '@/ui/Card';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { routes } from '@/lib/routing/RouteConfig';
|
||||
|
||||
@@ -42,12 +41,12 @@ export function RaceCard({
|
||||
const scheduledAtDate = new Date(scheduledAt);
|
||||
|
||||
return (
|
||||
<Surface
|
||||
<Card
|
||||
bg="bg-surface-charcoal"
|
||||
rounded="xl"
|
||||
border
|
||||
borderColor="border-outline-steel"
|
||||
padding={4}
|
||||
p={4}
|
||||
onClick={onClick}
|
||||
cursor={onClick ? 'pointer' : 'default'}
|
||||
hoverBorderColor="border-primary-accent"
|
||||
@@ -58,7 +57,7 @@ export function RaceCard({
|
||||
>
|
||||
{/* Live indicator */}
|
||||
{status === 'running' && (
|
||||
<Box
|
||||
<Stack
|
||||
position="absolute"
|
||||
top="0"
|
||||
left="0"
|
||||
@@ -66,31 +65,31 @@ export function RaceCard({
|
||||
h="1"
|
||||
bg="bg-success-green"
|
||||
animate="pulse"
|
||||
/>
|
||||
>{null}</Stack>
|
||||
)}
|
||||
|
||||
<Stack direction="row" align="start" gap={4}>
|
||||
{/* Time Column */}
|
||||
<Box textAlign="center" flexShrink={0} width="16">
|
||||
<Stack textAlign="center" flexShrink={0} w="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>
|
||||
|
||||
{/* Divider */}
|
||||
<Box
|
||||
<Stack
|
||||
w="px"
|
||||
bg="border-outline-steel"
|
||||
alignSelf="stretch"
|
||||
/>
|
||||
>{null}</Stack>
|
||||
|
||||
{/* Main Content */}
|
||||
<Box flex={1} minWidth="0">
|
||||
<Stack flex={1} minWidth="0">
|
||||
<Stack direction="row" align="start" justify="between" gap={4}>
|
||||
<Box minWidth="0">
|
||||
<Stack minWidth="0">
|
||||
<Heading
|
||||
level={3}
|
||||
truncate
|
||||
@@ -115,12 +114,12 @@ export function RaceCard({
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{/* Status Badge */}
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
<Stack
|
||||
direction="row"
|
||||
align="center"
|
||||
gap={1.5}
|
||||
px={2.5}
|
||||
py={1}
|
||||
@@ -136,11 +135,11 @@ export function RaceCard({
|
||||
<Text size="xs" weight="medium" color={statusConfig.color}>
|
||||
{statusConfig.label}
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
{/* League Link */}
|
||||
<Box mt={3} pt={3} borderTop borderColor="border-outline-steel" borderOpacity={0.3}>
|
||||
<Stack mt={3} pt={3} borderTop borderStyle="solid" borderColor="border-outline-steel">
|
||||
<Link
|
||||
href={routes.league.detail(leagueId ?? '')}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
@@ -153,8 +152,8 @@ export function RaceCard({
|
||||
<Icon icon={ArrowRight} size={3} color="var(--primary-accent)" />
|
||||
</Stack>
|
||||
</Link>
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
{/* Arrow */}
|
||||
<Icon
|
||||
@@ -166,6 +165,6 @@ export function RaceCard({
|
||||
flexShrink={0}
|
||||
/>
|
||||
</Stack>
|
||||
</Surface>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import { Heading } from '@/ui/Heading';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { SessionStatusBadge, type SessionStatus } from './SessionStatusBadge';
|
||||
|
||||
interface RaceDetailsHeaderProps {
|
||||
@@ -27,9 +26,9 @@ export function RaceDetailsHeader({
|
||||
onBack,
|
||||
}: RaceDetailsHeaderProps) {
|
||||
return (
|
||||
<Box as="header" bg="bg-surface-charcoal" borderBottom borderColor="border-outline-steel" p={6}>
|
||||
<Stack as="header" bg="bg-surface-charcoal" borderBottom borderColor="border-outline-steel" p={6}>
|
||||
<Stack gap={6}>
|
||||
<Box
|
||||
<Stack
|
||||
as="button"
|
||||
onClick={onBack}
|
||||
display="flex"
|
||||
@@ -42,7 +41,7 @@ export function RaceDetailsHeader({
|
||||
>
|
||||
<Icon icon={ChevronLeft} size={4} groupHoverScale />
|
||||
<Text size="xs" weight="bold" uppercase>Back to Schedule</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<Stack direction="row" justifyContent="between" alignItems="end">
|
||||
<Stack gap={2}>
|
||||
@@ -51,7 +50,7 @@ export function RaceDetailsHeader({
|
||||
</Text>
|
||||
<Heading level={1}>{title}</Heading>
|
||||
|
||||
<Box display="flex" flexWrap="wrap" gap={6} mt={2}>
|
||||
<Stack display="flex" flexWrap="wrap" gap={6} mt={2}>
|
||||
<Stack direction="row" alignItems="center" gap={2}>
|
||||
<Icon icon={MapPin} size={4} color="#6b7280" />
|
||||
<Text size="sm" color="text-gray-300">{trackName}</Text>
|
||||
@@ -60,14 +59,14 @@ export function RaceDetailsHeader({
|
||||
<Icon icon={Calendar} size={4} color="#6b7280" />
|
||||
<Text size="sm" color="text-gray-300">{scheduledAt}</Text>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
<Box pb={2}>
|
||||
<Stack pb={2}>
|
||||
<SessionStatusBadge status={status} />
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
|
||||
|
||||
import { Card } from '@/ui/Card';
|
||||
import { Card , Card as Surface } from '@/ui/Card';
|
||||
import { DriverEntryRow } from '@/components/drivers/DriverEntryRow';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Surface } from '@/ui/Surface';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Users } from 'lucide-react';
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
'use client';
|
||||
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Input } from '@/ui/Input';
|
||||
import { Modal } from '@/ui/Modal';
|
||||
import { Select } from '@/ui/Select';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Filter, Search } from 'lucide-react';
|
||||
|
||||
@@ -66,9 +65,9 @@ export function RaceFilterModal({
|
||||
|
||||
{/* Time Filter */}
|
||||
{showTimeFilter && (
|
||||
<Box>
|
||||
<Stack>
|
||||
<Text as="label" size="sm" color="text-gray-400" block mb={2}>Time</Text>
|
||||
<Box display="flex" flexWrap="wrap" gap={2}>
|
||||
<Stack display="flex" flexWrap="wrap" gap={2}>
|
||||
{(['upcoming', 'live', 'past', 'all'] as TimeFilter[]).map(filter => (
|
||||
<Button
|
||||
key={filter}
|
||||
@@ -76,12 +75,12 @@ export function RaceFilterModal({
|
||||
size="sm"
|
||||
onClick={() => setTimeFilter(filter)}
|
||||
>
|
||||
{filter === 'live' && <Box as="span" width="2" height="2" bg="bg-success-green" rounded="full" mr={1.5} animate="pulse" />}
|
||||
{filter === 'live' && <Stack as="span" width="2" height="2" bg="bg-success-green" rounded="full" mr={1.5} animate="pulse" />}
|
||||
{filter.charAt(0).toUpperCase() + filter.slice(1)}
|
||||
</Button>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{/* Status Filter */}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { RaceStatusBadge } from './RaceStatusBadge';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Calendar, MapPin, Car } from 'lucide-react';
|
||||
@@ -24,7 +23,7 @@ export function RaceHeaderPanel({
|
||||
actions
|
||||
}: RaceHeaderPanelProps) {
|
||||
return (
|
||||
<Box
|
||||
<Stack
|
||||
bg="bg-panel-gray"
|
||||
rounded="xl"
|
||||
border
|
||||
@@ -33,7 +32,7 @@ export function RaceHeaderPanel({
|
||||
position="relative"
|
||||
>
|
||||
{/* Background Accent */}
|
||||
<Box
|
||||
<Stack
|
||||
position="absolute"
|
||||
top={0}
|
||||
left={0}
|
||||
@@ -43,10 +42,10 @@ export function RaceHeaderPanel({
|
||||
opacity={0.5}
|
||||
/>
|
||||
|
||||
<Box p={6} position="relative">
|
||||
<Stack p={6} position="relative">
|
||||
<Stack direction={{ base: 'col', md: 'row' }} gap={6} align="start" className="md:items-center">
|
||||
{/* Info */}
|
||||
<Box flexGrow={1}>
|
||||
<Stack flexGrow={1}>
|
||||
<Stack gap={3}>
|
||||
<Stack direction="row" align="center" gap={3} wrap>
|
||||
<Text as="h1" size="3xl" weight="bold" color="text-white">
|
||||
@@ -80,16 +79,16 @@ export function RaceHeaderPanel({
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{/* Actions */}
|
||||
{actions && (
|
||||
<Box flexShrink={0} width={{ base: 'full', md: 'auto' }}>
|
||||
<Stack flexShrink={0} width={{ base: 'full', md: 'auto' }}>
|
||||
{actions}
|
||||
</Box>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
|
||||
import { Calendar, Car, Clock, LucideIcon } from 'lucide-react';
|
||||
import { Badge } from '@/ui/Badge';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Hero } from '@/ui/Hero';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
interface RaceHeroProps {
|
||||
@@ -28,7 +27,7 @@ export function RaceHero({ track, scheduledAt, car, status, statusConfig }: Race
|
||||
return (
|
||||
<Hero variant="primary">
|
||||
{status === 'running' && (
|
||||
<Box
|
||||
<Stack
|
||||
position="absolute"
|
||||
top="0"
|
||||
left="0"
|
||||
@@ -43,7 +42,7 @@ export function RaceHero({ track, scheduledAt, car, status, statusConfig }: Race
|
||||
<Stack direction="row" align="center" gap={3}>
|
||||
<Badge variant={statusConfig.variant}>
|
||||
{status === 'running' && (
|
||||
<Box w="2" h="2" bg="bg-performance-green" rounded="full" animate="pulse" mr={1.5} />
|
||||
<Stack w="2" h="2" bg="bg-performance-green" rounded="full" animate="pulse" mr={1.5} />
|
||||
)}
|
||||
<Icon icon={StatusIcon} size={4} />
|
||||
{statusConfig.label}
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
|
||||
import { routes } from '@/lib/routing/RouteConfig';
|
||||
import type { RaceViewData } from '@/lib/view-data/RacesViewData';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Card } from '@/ui/Card';
|
||||
import { DateHeader } from '@/ui/DateHeader';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { RaceListItem } from '@/components/races/RaceListItem';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Calendar, CheckCircle2, Clock, PlayCircle, XCircle } from 'lucide-react';
|
||||
|
||||
@@ -49,17 +48,17 @@ export function RaceList({ racesByDate, totalCount, onRaceClick }: RaceListProps
|
||||
return (
|
||||
<Card py={12} textAlign="center" bg="bg-surface-charcoal" border borderColor="border-outline-steel">
|
||||
<Stack align="center" gap={4}>
|
||||
<Box p={4} bg="bg-base-black" rounded="full" border borderColor="border-outline-steel">
|
||||
<Stack p={4} bg="bg-base-black" rounded="full" border borderColor="border-outline-steel">
|
||||
<Icon icon={Calendar} size={8} color="var(--text-gray-500)" />
|
||||
</Box>
|
||||
<Box>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Text weight="medium" color="text-white" block mb={1}>No races found</Text>
|
||||
<Text size="sm" color="text-gray-500">
|
||||
{totalCount === 0
|
||||
? 'No races have been scheduled yet'
|
||||
: 'Try adjusting your filters'}
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Card>
|
||||
);
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
|
||||
import { ArrowRight, Car, ChevronRight, LucideIcon, Trophy, Zap } from 'lucide-react';
|
||||
import { Badge } from '@/ui/Badge';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
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 {
|
||||
@@ -45,7 +44,7 @@ export function RaceListItem({
|
||||
const StatusIcon = statusConfig.icon;
|
||||
|
||||
return (
|
||||
<Box
|
||||
<Stack
|
||||
onClick={onClick}
|
||||
position="relative"
|
||||
overflow="hidden"
|
||||
@@ -61,7 +60,7 @@ export function RaceListItem({
|
||||
>
|
||||
{/* Live indicator */}
|
||||
{status === 'running' && (
|
||||
<Box
|
||||
<Stack
|
||||
position="absolute"
|
||||
top="0"
|
||||
left="0"
|
||||
@@ -74,7 +73,7 @@ export function RaceListItem({
|
||||
|
||||
<Stack direction="row" align="center" gap={4}>
|
||||
{/* Time/Date Column */}
|
||||
<Box flexShrink={0} textAlign="center" width="16">
|
||||
<Stack flexShrink={0} textAlign="center" width="16">
|
||||
{dateLabel && (
|
||||
<Text size="xs" color="text-gray-500" block uppercase>
|
||||
{dateLabel}
|
||||
@@ -86,15 +85,15 @@ export function RaceListItem({
|
||||
<Text size="xs" color={status === 'running' ? 'text-success-green' : 'text-gray-400'} block>
|
||||
{status === 'running' ? 'LIVE' : relativeTimeLabel || timeLabel}
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{/* Divider */}
|
||||
<Box w="px" h="10" alignSelf="stretch" bg="border-outline-steel" />
|
||||
<Stack w="px" h="10" alignSelf="stretch" bg="border-outline-steel" />
|
||||
|
||||
{/* Main Content */}
|
||||
<Box flexGrow={1} minWidth="0">
|
||||
<Stack flexGrow={1} minWidth="0">
|
||||
<Stack direction="row" align="start" justify="between" gap={4}>
|
||||
<Box minWidth="0">
|
||||
<Stack minWidth="0">
|
||||
<Heading level={3} truncate groupHoverTextColor="text-primary-accent" transition>
|
||||
{track}
|
||||
</Heading>
|
||||
@@ -110,7 +109,7 @@ export function RaceListItem({
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{/* Status Badge */}
|
||||
<Badge variant={statusConfig.variant}>
|
||||
@@ -121,7 +120,7 @@ export function RaceListItem({
|
||||
|
||||
{/* League Link */}
|
||||
{leagueName && leagueHref && (
|
||||
<Box mt={3} pt={3} borderTop borderColor="border-outline-steel" borderOpacity={0.3}>
|
||||
<Stack mt={3} pt={3} borderTop borderColor="border-outline-steel" borderOpacity={0.3}>
|
||||
<Link
|
||||
href={leagueHref}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
@@ -132,13 +131,13 @@ export function RaceListItem({
|
||||
<Text as="span" color="text-primary-accent">{leagueName}</Text>
|
||||
<Icon icon={ArrowRight} size={3} ml={2} color="var(--primary-accent)" />
|
||||
</Link>
|
||||
</Box>
|
||||
</Stack>
|
||||
)}
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{/* Arrow */}
|
||||
<Icon icon={ChevronRight} size={5} color="var(--text-gray-500)" flexShrink={0} groupHoverTextColor="text-primary-accent" transition />
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import { Flag, CalendarDays, Clock, Zap, Trophy, LucideIcon } from 'lucide-react
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Grid } from '@/ui/Grid';
|
||||
import { Surface } from '@/ui/Surface';
|
||||
@@ -34,7 +33,7 @@ export function RacePageHeader({
|
||||
overflow="hidden"
|
||||
>
|
||||
{/* Background Accent */}
|
||||
<Box
|
||||
<Stack
|
||||
position="absolute"
|
||||
top={0}
|
||||
left={0}
|
||||
@@ -67,7 +66,7 @@ export function RacePageHeader({
|
||||
|
||||
function StatItem({ icon, label, value, color = 'text-white' }: { icon: LucideIcon, label: string, value: number, color?: string }) {
|
||||
return (
|
||||
<Box p={4} bg="bg-base-black" bgOpacity={0.5} border borderColor="border-outline-steel">
|
||||
<Stack p={4} bg="bg-base-black" bgOpacity={0.5} border borderColor="border-outline-steel">
|
||||
<Stack gap={1}>
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Icon icon={icon} size={3} color={color === 'text-white' ? '#9ca3af' : undefined} groupHoverTextColor={color !== 'text-white' ? color : undefined} />
|
||||
@@ -75,6 +74,6 @@ function StatItem({ icon, label, value, color = 'text-white' }: { icon: LucideIc
|
||||
</Stack>
|
||||
<Text size="2xl" weight="bold" color={color}>{value}</Text>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
'use client';
|
||||
|
||||
import { ChevronRight } from 'lucide-react';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Card } from '@/ui/Card';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Link } from '@/ui/Link';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { routes } from '@/lib/routing/RouteConfig';
|
||||
|
||||
@@ -51,9 +50,9 @@ export function RaceResultCard({
|
||||
onClick={onClick}
|
||||
>
|
||||
<Card p={4} hoverBorderColor="border-primary-accent" transition group bg="bg-surface-charcoal" border borderColor="border-outline-steel">
|
||||
<Box display="flex" alignItems="center" justifyContent="between" mb={2}>
|
||||
<Stack display="flex" alignItems="center" justifyContent="between" mb={2}>
|
||||
<Stack direction="row" align="center" gap={3}>
|
||||
<Box
|
||||
<Stack
|
||||
width="8"
|
||||
height="8"
|
||||
rounded="md"
|
||||
@@ -68,16 +67,16 @@ export function RaceResultCard({
|
||||
borderColor="border-outline-steel"
|
||||
>
|
||||
P{position}
|
||||
</Box>
|
||||
<Box>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Text color="text-white" weight="medium" block groupHoverTextColor="text-primary-accent" transition>
|
||||
{track}
|
||||
</Text>
|
||||
<Text size="sm" color="text-gray-400" block>{car}</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack direction="row" align="center" gap={3}>
|
||||
<Box textAlign="right">
|
||||
<Stack textAlign="right">
|
||||
<Text size="sm" color="text-gray-400" block>
|
||||
{new Date(scheduledAt).toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
@@ -88,10 +87,10 @@ export function RaceResultCard({
|
||||
{showLeague && leagueName && (
|
||||
<Text size="xs" color="text-gray-500" block>{leagueName}</Text>
|
||||
)}
|
||||
</Box>
|
||||
</Stack>
|
||||
<Icon icon={ChevronRight} size={5} color="text-gray-500" groupHoverTextColor="text-primary-accent" transition />
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
<Stack direction="row" align="center" gap={4}>
|
||||
<Text size="xs" color="text-gray-500">Started P{startPosition}</Text>
|
||||
<Text size="xs" color="text-gray-500">•</Text>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
|
||||
|
||||
import { Trophy } from 'lucide-react';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { DecorativeBlur } from '@/ui/DecorativeBlur';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Surface } from '@/ui/Surface';
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
@@ -57,7 +56,7 @@ export function RaceResultHero({
|
||||
|
||||
<Stack direction="row" align="center" justify="between" wrap gap={6} position="relative" zIndex={10}>
|
||||
<Stack direction="row" align="center" gap={5}>
|
||||
<Box
|
||||
<Stack
|
||||
position="relative"
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
@@ -84,9 +83,9 @@ export function RaceResultHero({
|
||||
/>
|
||||
)}
|
||||
P{position}
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<Box>
|
||||
<Stack>
|
||||
<Text
|
||||
size="3xl"
|
||||
weight="bold"
|
||||
@@ -98,12 +97,12 @@ export function RaceResultHero({
|
||||
</Text>
|
||||
<Stack direction="row" align="center" gap={3}>
|
||||
<Text size="sm" color="text-gray-400">Started P{startPosition}</Text>
|
||||
<Box w="1" h="1" rounded="full" bg="bg-charcoal-outline" />
|
||||
<Stack w="1" h="1" rounded="full" bg="bg-charcoal-outline" />
|
||||
<Text size="sm" color={isClean ? 'text-performance-green' : 'text-gray-400'}>
|
||||
{incidents}x incidents {isClean && '✨'}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
<Stack direction="row" gap={3} wrap>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
|
||||
|
||||
import { CountryFlagDisplay } from '@/lib/display-objects/CountryFlagDisplay';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Image } from '@/ui/Image';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Image } from '@/ui/Image';
|
||||
import { Surface } from '@/ui/Surface';
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
@@ -50,7 +49,7 @@ export function RaceResultRow({ result, points }: RaceResultRowProps) {
|
||||
>
|
||||
<Stack direction="row" align="center" gap={3}>
|
||||
{/* Position */}
|
||||
<Box
|
||||
<Stack
|
||||
width="10"
|
||||
height="10"
|
||||
rounded="lg"
|
||||
@@ -59,26 +58,26 @@ export function RaceResultRow({ result, points }: RaceResultRowProps) {
|
||||
className={`${posConfig.bg} ${posConfig.color}`}
|
||||
>
|
||||
<Text weight="bold">{position}</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{/* Avatar */}
|
||||
<Box position="relative" flexShrink={0}>
|
||||
<Box width="10" height="10" rounded="full" overflow="hidden" border={isCurrentUser} borderColor="border-primary-blue/50" className={isCurrentUser ? 'border-2' : ''}>
|
||||
<Stack position="relative" flexShrink={0}>
|
||||
<Stack width="10" height="10" rounded="full" overflow="hidden" border={isCurrentUser} borderColor="border-primary-blue/50" className={isCurrentUser ? 'border-2' : ''}>
|
||||
<Image src={driverAvatar} alt={driverName} width={40} height={40} fullWidth fullHeight objectFit="cover" />
|
||||
</Box>
|
||||
<Box position="absolute" bottom="-0.5" right="-0.5" width="5" height="5" rounded="full" bg="bg-deep-graphite" display="flex" center style={{ fontSize: '0.625rem' }}>
|
||||
</Stack>
|
||||
<Stack position="absolute" bottom="-0.5" right="-0.5" width="5" height="5" rounded="full" bg="bg-deep-graphite" display="flex" center style={{ fontSize: '0.625rem' }}>
|
||||
{CountryFlagDisplay.fromCountryCode(country).toString()}
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
{/* Driver Info */}
|
||||
<Box flexGrow={1} minWidth="0">
|
||||
<Stack flexGrow={1} minWidth="0">
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Text weight="semibold" size="sm" color={isCurrentUser ? 'text-primary-blue' : 'text-white'} truncate>{driverName}</Text>
|
||||
{isCurrentUser && (
|
||||
<Box px={2} py={0.5} rounded="full" bg="bg-primary-blue">
|
||||
<Stack px={2} py={0.5} rounded="full" bg="bg-primary-blue">
|
||||
<Text size="xs" weight="bold" color="text-white">YOU</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
<Stack direction="row" align="center" gap={2} mt={1}>
|
||||
@@ -88,19 +87,19 @@ export function RaceResultRow({ result, points }: RaceResultRowProps) {
|
||||
<Text size="xs" color="text-gray-500">•</Text>
|
||||
<Text size="xs" color="text-gray-500">Incidents: {incidents}</Text>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{/* Times */}
|
||||
<Box textAlign="right" style={{ minWidth: '100px' }}>
|
||||
<Stack textAlign="right" style={{ minWidth: '100px' }}>
|
||||
<Text size="sm" font="mono" color="text-white" block>{time}</Text>
|
||||
<Text size="xs" color="text-performance-green" block mt={1}>FL: {fastestLap}</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{/* Points */}
|
||||
<Box p={2} rounded="lg" border={true} borderColor="border-warning-amber/20" bg="bg-warning-amber/10" textAlign="center" style={{ minWidth: '3.5rem' }}>
|
||||
<Stack p={2} rounded="lg" border={true} borderColor="border-warning-amber/20" bg="bg-warning-amber/10" textAlign="center" style={{ minWidth: '3.5rem' }}>
|
||||
<Text size="xs" color="text-gray-500" block>PTS</Text>
|
||||
<Text size="sm" weight="bold" color="text-warning-amber">{points}</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Surface>
|
||||
);
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
import { AlertTriangle, ExternalLink } from 'lucide-react';
|
||||
import { ReactNode } from 'react';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Link } from '@/ui/Link';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/ui/Table';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Badge } from '@/ui/Badge';
|
||||
|
||||
type PenaltyTypeDTO =
|
||||
| 'time_penalty'
|
||||
@@ -111,14 +111,14 @@ export function RaceResultsTable({
|
||||
|
||||
if (results.length === 0) {
|
||||
return (
|
||||
<Box textAlign="center" py={8}>
|
||||
<Stack textAlign="center" py={8} gap={0}>
|
||||
<Text color="text-gray-400">No results available</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box overflow="auto">
|
||||
<Stack overflow="auto" gap={0}>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
@@ -147,14 +147,13 @@ export function RaceResultsTable({
|
||||
variant={isCurrentUser ? 'highlight' : 'default'}
|
||||
>
|
||||
<TableCell>
|
||||
<Box
|
||||
display="inline-flex"
|
||||
center
|
||||
width="8"
|
||||
height="8"
|
||||
<Stack
|
||||
direction="row"
|
||||
align="center"
|
||||
justify="center"
|
||||
w="8"
|
||||
h="8"
|
||||
rounded="lg"
|
||||
weight="bold"
|
||||
size="sm"
|
||||
bg={
|
||||
result.position === 1
|
||||
? 'bg-yellow-500/20'
|
||||
@@ -164,38 +163,46 @@ export function RaceResultsTable({
|
||||
? 'bg-amber-600/20'
|
||||
: undefined
|
||||
}
|
||||
color={
|
||||
result.position === 1
|
||||
? 'text-yellow-400'
|
||||
: result.position === 2
|
||||
? 'text-gray-300'
|
||||
: result.position === 3
|
||||
? 'text-amber-500'
|
||||
: 'text-white'
|
||||
}
|
||||
>
|
||||
{result.position}
|
||||
</Box>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="sm"
|
||||
color={
|
||||
result.position === 1
|
||||
? 'text-yellow-400'
|
||||
: result.position === 2
|
||||
? 'text-gray-300'
|
||||
: result.position === 3
|
||||
? 'text-amber-500'
|
||||
: 'text-white'
|
||||
}
|
||||
>
|
||||
{result.position}
|
||||
</Text>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Stack direction="row" align="center" gap={3}>
|
||||
{driver ? (
|
||||
<>
|
||||
<Box
|
||||
width="8"
|
||||
height="8"
|
||||
<Stack
|
||||
w="8"
|
||||
h="8"
|
||||
rounded="full"
|
||||
display="flex"
|
||||
center
|
||||
size="sm"
|
||||
weight="bold"
|
||||
align="center"
|
||||
justify="center"
|
||||
flexShrink={0}
|
||||
bg={isCurrentUser ? 'bg-primary-blue/30' : 'bg-iron-gray'}
|
||||
color={isCurrentUser ? 'text-primary-blue' : 'text-gray-400'}
|
||||
className={isCurrentUser ? 'ring-2 ring-primary-blue/50' : ''}
|
||||
ring={isCurrentUser ? 'ring-2 ring-primary-blue/50' : undefined}
|
||||
>
|
||||
{driver.name.charAt(0)}
|
||||
</Box>
|
||||
<Text
|
||||
size="sm"
|
||||
weight="bold"
|
||||
color={isCurrentUser ? 'text-primary-blue' : 'text-gray-400'}
|
||||
>
|
||||
{driver.name.charAt(0)}
|
||||
</Text>
|
||||
</Stack>
|
||||
<Link
|
||||
href={`/drivers/${driver.id}`}
|
||||
variant="ghost"
|
||||
@@ -203,9 +210,9 @@ export function RaceResultsTable({
|
||||
>
|
||||
<Text className="group-hover:underline">{driver.name}</Text>
|
||||
{isCurrentUser && (
|
||||
<Box as="span" px={1.5} py={0.5} ml={1.5} bg="bg-primary-blue" color="text-white" rounded="full" uppercase style={{ fontSize: '10px', fontWeight: 'bold' }}>
|
||||
<Badge size="xs" variant="primary" bg="bg-primary-blue" color="text-white" rounded="full" style={{ marginLeft: '6px' }}>
|
||||
You
|
||||
</Box>
|
||||
</Badge>
|
||||
)}
|
||||
<Icon icon={ExternalLink} size={3} className="ml-1.5 opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
</Link>
|
||||
@@ -239,9 +246,9 @@ export function RaceResultsTable({
|
||||
{driverPenalties.length > 0 ? (
|
||||
<Stack gap={1}>
|
||||
{driverPenalties.map((penalty, idx) => (
|
||||
<Stack key={idx} direction="row" align="center" gap={1.5} color="text-red-400">
|
||||
<Icon icon={AlertTriangle} size={3} />
|
||||
<Text size="xs">{getPenaltyDescription(penalty)}</Text>
|
||||
<Stack key={idx} direction="row" align="center" gap={1.5}>
|
||||
<Icon icon={AlertTriangle} size={3} color="var(--critical-red)" />
|
||||
<Text size="xs" color="text-red-400">{getPenaltyDescription(penalty)}</Text>
|
||||
</Stack>
|
||||
))}
|
||||
</Stack>
|
||||
@@ -259,6 +266,6 @@ export function RaceResultsTable({
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import React from 'react';
|
||||
import { Clock, Trophy, Users } from 'lucide-react';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Card } from '@/ui/Card';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { SidebarRaceItem } from '@/components/races/SidebarRaceItem';
|
||||
import { SidebarActionLink } from '@/ui/SidebarActionLink';
|
||||
@@ -31,9 +30,9 @@ export function RaceSidebar({ upcomingRaces, recentResults, onRaceClick }: RaceS
|
||||
</Stack>
|
||||
|
||||
{upcomingRaces.length === 0 ? (
|
||||
<Box py={4} textAlign="center">
|
||||
<Stack py={4} textAlign="center">
|
||||
<Text size="sm" color="text-gray-400">No races scheduled this week</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
) : (
|
||||
<Stack gap={3}>
|
||||
{upcomingRaces.map((race) => (
|
||||
@@ -60,9 +59,9 @@ export function RaceSidebar({ upcomingRaces, recentResults, onRaceClick }: RaceS
|
||||
</Heading>
|
||||
|
||||
{recentResults.length === 0 ? (
|
||||
<Box py={4} textAlign="center">
|
||||
<Stack py={4} textAlign="center">
|
||||
<Text size="sm" color="text-gray-400">No completed races yet</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
) : (
|
||||
<Stack gap={3}>
|
||||
{recentResults.map((race) => (
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
|
||||
@@ -17,24 +16,24 @@ export function RaceSidebarPanel({
|
||||
children
|
||||
}: RaceSidebarPanelProps) {
|
||||
return (
|
||||
<Box
|
||||
<Stack
|
||||
bg="bg-panel-gray"
|
||||
rounded="xl"
|
||||
border
|
||||
borderColor="border-charcoal-outline"
|
||||
overflow="hidden"
|
||||
>
|
||||
<Box p={4} borderBottom="1px solid" borderColor="border-charcoal-outline" bg="bg-graphite-black/30">
|
||||
<Stack p={4} borderBottom="1px solid" borderColor="border-charcoal-outline" bg="bg-graphite-black/30">
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
{icon && <Icon icon={icon} size={4} color="#198CFF" />}
|
||||
<Text weight="bold" size="sm" color="text-white" uppercase>
|
||||
{title}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Box p={4}>
|
||||
</Stack>
|
||||
<Stack p={4}>
|
||||
{children}
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Badge } from '@/ui/Badge';
|
||||
import { Grid } from '@/ui/Grid';
|
||||
import { StatItem } from '@/ui/StatItem';
|
||||
import { Card } from '@/ui/Card';
|
||||
|
||||
interface StandingsItemProps {
|
||||
leagueName: string;
|
||||
@@ -22,11 +22,8 @@ export function StandingsItem({
|
||||
racesCompleted,
|
||||
}: StandingsItemProps) {
|
||||
return (
|
||||
<Box
|
||||
bg="bg-iron-gray/50"
|
||||
rounded="lg"
|
||||
border
|
||||
borderColor="border-charcoal-outline"
|
||||
<Card
|
||||
variant="default"
|
||||
p={4}
|
||||
>
|
||||
<Stack direction="row" align="center" justify="between" mb={3}>
|
||||
@@ -43,6 +40,6 @@ export function StandingsItem({
|
||||
<StatItem label="Wins" value={wins} align="center" />
|
||||
<StatItem label="Races" value={racesCompleted} align="center" />
|
||||
</Grid>
|
||||
</Box>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import { Thermometer, Wind, Droplets, Sun, type LucideIcon } from 'lucide-react'
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Box } from '@/ui/Box';
|
||||
|
||||
interface TrackConditionsPanelProps {
|
||||
airTemp: string;
|
||||
@@ -23,12 +22,12 @@ export function TrackConditionsPanel({
|
||||
weatherType,
|
||||
}: TrackConditionsPanelProps) {
|
||||
return (
|
||||
<Box as="section" bg="bg-surface-charcoal" border borderColor="border-outline-steel" p={4}>
|
||||
<Stack as="section" bg="bg-surface-charcoal" border borderColor="border-outline-steel" p={4}>
|
||||
<Text size="xs" weight="bold" color="text-gray-500" uppercase block mb={4}>
|
||||
Track Conditions
|
||||
</Text>
|
||||
|
||||
<Box display="grid" gridCols={2} mdCols={4} gap={4}>
|
||||
<Stack display="grid" gridCols={2} mdCols={4} gap={4}>
|
||||
<ConditionItem
|
||||
icon={Thermometer}
|
||||
label="Air Temp"
|
||||
@@ -53,13 +52,13 @@ export function TrackConditionsPanel({
|
||||
value={windSpeed}
|
||||
color="text-gray-400"
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<Box mt={4} pt={4} borderTop borderColor="border-outline-steel" bgOpacity={0.5} display="flex" alignItems="center" gap={3}>
|
||||
<Stack mt={4} pt={4} borderTop borderColor="border-outline-steel" bgOpacity={0.5} display="flex" alignItems="center" gap={3}>
|
||||
<Icon icon={Sun} size={4} color="#FFBE4D" />
|
||||
<Text size="sm" color="text-gray-300">{weatherType}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
|
||||
import { Badge } from '@/ui/Badge';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Surface } from '@/ui/Surface';
|
||||
import { Text } from '@/ui/Text';
|
||||
@@ -31,15 +30,15 @@ export function UpcomingRaceItem({
|
||||
className="hover:border-primary-accent/30 transition-colors bg-panel-gray/20 group"
|
||||
>
|
||||
<Stack direction="row" align="center" gap={4}>
|
||||
<Box w="1" h="8" bg="primary-accent" opacity={0.3} className="group-hover:opacity-100 transition-opacity" />
|
||||
<Box flexGrow={1}>
|
||||
<Stack w="1" h="8" bg="primary-accent" opacity={0.3} className="group-hover:opacity-100 transition-opacity" />
|
||||
<Stack flexGrow={1}>
|
||||
<Text color="text-white" weight="bold" block className="tracking-tight">
|
||||
{track}
|
||||
</Text>
|
||||
<Text size="xs" color="text-gray-500" block weight="medium" className="uppercase tracking-widest mt-0.5">
|
||||
{car}
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
<Stack align="end" gap={1}>
|
||||
<Text size="xs" color="text-gray-400" font="mono" weight="bold">
|
||||
{formattedDate}
|
||||
@@ -50,11 +49,11 @@ export function UpcomingRaceItem({
|
||||
</Stack>
|
||||
</Stack>
|
||||
{isMyLeague && (
|
||||
<Box mt={3} display="flex" justifyContent="end">
|
||||
<Stack mt={3} display="flex" justifyContent="end">
|
||||
<Badge variant="success" size="xs">
|
||||
YOUR LEAGUE
|
||||
</Badge>
|
||||
</Box>
|
||||
</Stack>
|
||||
)}
|
||||
</Surface>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user