website refactor
This commit is contained in:
@@ -6,7 +6,7 @@ import { Icon } from '@/ui/Icon';
|
||||
import { InfoBox } from '@/ui/InfoBox';
|
||||
import { Input } from '@/ui/Input';
|
||||
import { Modal } from '@/ui/Modal';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Select } from '@/ui/Select';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { TextArea } from '@/ui/TextArea';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
interface FinishDistributionProps {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { RACE_RESULTS_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { FilePicker } from '@/ui/FilePicker';
|
||||
import { InfoBox } from '@/ui/InfoBox';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { AlertCircle } from 'lucide-react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { RaceResultList } from '@/components/races/RaceResultList';
|
||||
import { RaceSummaryItem } from '@/components/races/RaceSummaryItem';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Card } from '@/ui/Card';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Box } from '@/ui/primitives/Box';
|
||||
|
||||
type RaceWithResults = {
|
||||
raceId: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
|
||||
import { LiveRaceItem } from '@/components/races/LiveRaceItem';
|
||||
import { Box } from '@/ui/primitives/Box';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
interface LiveRaceBannerProps {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Box } from '@/ui/primitives/Box';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { ChevronRight, PlayCircle } from 'lucide-react';
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { LiveRaceItem } from '@/components/races/LiveRaceItem';
|
||||
import type { RaceViewData } from '@/lib/view-data/RacesViewData';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
interface LiveRacesBannerProps {
|
||||
|
||||
@@ -5,8 +5,8 @@ import { Button } from '@/ui/Button';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Link } from '@/ui/Link';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Surface } from '@/ui/primitives/Surface';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Surface } from '@/ui/Surface';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Calendar, ChevronRight, Clock } from 'lucide-react';
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Box } from '@/ui/primitives/Box';
|
||||
import { Plus } from 'lucide-react';
|
||||
|
||||
interface PenaltyFABProps {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
|
||||
import { Badge } from '@/ui/Badge';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Surface } from '@/ui/primitives/Surface';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Surface } from '@/ui/Surface';
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
interface PenaltyRowProps {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Card } from '@/ui/Card';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Box } from '@/ui/primitives/Box';
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/ui/Table';
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { ButtonGroup } from '@/ui/ButtonGroup';
|
||||
import { CheckCircle, LogOut, PlayCircle, Scale, Trophy, XCircle } from 'lucide-react';
|
||||
|
||||
interface RaceActionBarProps {
|
||||
@@ -41,7 +41,7 @@ export function RaceActionBar({
|
||||
isLoading = {}
|
||||
}: RaceActionBarProps) {
|
||||
return (
|
||||
<Stack direction="row" gap={3} wrap>
|
||||
<ButtonGroup gap={3}>
|
||||
{status === 'scheduled' && (
|
||||
<>
|
||||
{!isUserRegistered && canRegister && (
|
||||
@@ -140,6 +140,6 @@ export function RaceActionBar({
|
||||
Reopen Race
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
</ButtonGroup>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,9 @@ import { Card } from '@/ui/Card';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Link } from '@/ui/Link';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Badge } from '@/ui/Badge';
|
||||
import { ArrowRight, Car, ChevronRight, LucideIcon, Trophy, Zap } from 'lucide-react';
|
||||
|
||||
interface RaceCardProps {
|
||||
@@ -19,9 +20,7 @@ interface RaceCardProps {
|
||||
strengthOfField?: number | null;
|
||||
onClick?: () => void;
|
||||
statusConfig: {
|
||||
border: string;
|
||||
bg: string;
|
||||
color: string;
|
||||
intent: 'primary' | 'success' | 'warning' | 'critical' | 'default' | 'secondary' | 'info' | 'danger';
|
||||
icon: LucideIcon | null;
|
||||
label: string;
|
||||
};
|
||||
@@ -42,73 +41,40 @@ export function RaceCard({
|
||||
|
||||
return (
|
||||
<Card
|
||||
bg="bg-surface-charcoal"
|
||||
rounded="xl"
|
||||
border
|
||||
borderColor="border-outline-steel"
|
||||
p={4}
|
||||
variant="dark"
|
||||
onClick={onClick}
|
||||
cursor={onClick ? 'pointer' : 'default'}
|
||||
hoverBorderColor="border-primary-accent"
|
||||
transition
|
||||
position="relative"
|
||||
overflow="hidden"
|
||||
group
|
||||
>
|
||||
{/* Live indicator */}
|
||||
{status === 'running' && (
|
||||
<Stack
|
||||
position="absolute"
|
||||
top="0"
|
||||
left="0"
|
||||
right="0"
|
||||
h="1"
|
||||
bg="bg-success-green"
|
||||
animate="pulse"
|
||||
>{null}</Stack>
|
||||
)}
|
||||
|
||||
<Stack direction="row" align="start" gap={4}>
|
||||
{/* Time Column */}
|
||||
<Stack textAlign="center" flexShrink={0} w="16">
|
||||
<Text size="lg" weight="bold" color="text-white" block>
|
||||
<Stack align="center" gap={1}>
|
||||
<Text size="lg" weight="bold" variant="high">
|
||||
{scheduledAtDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
||||
</Text>
|
||||
<Text size="xs" color={statusConfig.color} block>
|
||||
<Text size="xs" variant={statusConfig.intent === 'default' ? 'low' : (statusConfig.intent as any)}>
|
||||
{status === 'running' ? 'LIVE' : scheduledAtDate.toLocaleDateString()}
|
||||
</Text>
|
||||
</Stack>
|
||||
|
||||
{/* Divider */}
|
||||
<Stack
|
||||
w="px"
|
||||
bg="border-outline-steel"
|
||||
alignSelf="stretch"
|
||||
>{null}</Stack>
|
||||
|
||||
{/* Main Content */}
|
||||
<Stack flex={1} minWidth="0">
|
||||
<Stack gap={4} fullWidth>
|
||||
<Stack direction="row" align="start" justify="between" gap={4}>
|
||||
<Stack minWidth="0">
|
||||
<Stack>
|
||||
<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={3}>
|
||||
<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">
|
||||
<Icon icon={Car} size={3.5} intent="low" />
|
||||
<Text size="sm" variant="low">
|
||||
{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">
|
||||
<Icon icon={Zap} size={3.5} intent="warning" />
|
||||
<Text size="sm" variant="low">
|
||||
SOF {strengthOfField}
|
||||
</Text>
|
||||
</Stack>
|
||||
@@ -117,39 +83,23 @@ export function RaceCard({
|
||||
</Stack>
|
||||
|
||||
{/* Status Badge */}
|
||||
<Stack
|
||||
direction="row"
|
||||
align="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>
|
||||
</Stack>
|
||||
<Badge variant={statusConfig.intent}>
|
||||
{statusConfig.label}
|
||||
</Badge>
|
||||
</Stack>
|
||||
|
||||
{/* League Link */}
|
||||
<Stack mt={3} pt={3} borderTop borderStyle="solid" borderColor="border-outline-steel">
|
||||
<Stack>
|
||||
<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">
|
||||
<Icon icon={Trophy} size={3.5} intent="primary" />
|
||||
<Text size="sm" variant="primary">
|
||||
{leagueName}
|
||||
</Text>
|
||||
<Icon icon={ArrowRight} size={3} color="var(--primary-accent)" />
|
||||
<Icon icon={ArrowRight} size={3} intent="primary" />
|
||||
</Stack>
|
||||
</Link>
|
||||
</Stack>
|
||||
@@ -159,10 +109,7 @@ export function RaceCard({
|
||||
<Icon
|
||||
icon={ChevronRight}
|
||||
size={5}
|
||||
color="var(--text-gray-500)"
|
||||
groupHoverTextColor="text-primary-accent"
|
||||
transition
|
||||
flexShrink={0}
|
||||
intent="low"
|
||||
/>
|
||||
</Stack>
|
||||
</Card>
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
|
||||
|
||||
import { Card } from '@/ui/Card';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Grid } from '@/ui/primitives/Grid';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Panel } from '@/ui/Panel';
|
||||
import { Grid } from '@/ui/Grid';
|
||||
import { StatGridItem } from '@/ui/StatGridItem';
|
||||
import { Flag } from 'lucide-react';
|
||||
|
||||
interface RaceDetailCardProps {
|
||||
track: string;
|
||||
@@ -18,16 +14,13 @@ interface RaceDetailCardProps {
|
||||
|
||||
export function RaceDetailCard({ track, car, sessionType, statusLabel, statusColor }: RaceDetailCardProps) {
|
||||
return (
|
||||
<Card>
|
||||
<Stack gap={4}>
|
||||
<Heading level={2} icon={<Icon icon={Flag} size={5} color="text-primary-blue" />}>Race Details</Heading>
|
||||
<Grid cols={2} gap={4}>
|
||||
<StatGridItem label="Track" value={track} />
|
||||
<StatGridItem label="Car" value={car} />
|
||||
<StatGridItem label="Session Type" value={sessionType} />
|
||||
<StatGridItem label="Status" value={statusLabel} color={statusColor} />
|
||||
</Grid>
|
||||
</Stack>
|
||||
</Card>
|
||||
<Panel title="Race Details">
|
||||
<Grid cols={2} gap={4}>
|
||||
<StatGridItem label="Track" value={track} />
|
||||
<StatGridItem label="Car" value={car} />
|
||||
<StatGridItem label="Session Type" value={sessionType} />
|
||||
<StatGridItem label="Status" value={statusLabel} />
|
||||
</Grid>
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Calendar, ChevronLeft, MapPin } from 'lucide-react';
|
||||
import { SessionStatusBadge, type SessionStatus } from './SessionStatusBadge';
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
|
||||
|
||||
import { DriverEntryRow } from '@/components/drivers/DriverEntryRow';
|
||||
import { Card, Card as Surface } from '@/ui/Card';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Panel } from '@/ui/Panel';
|
||||
import { EmptyState } from '@/ui/EmptyState';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Users } from 'lucide-react';
|
||||
|
||||
@@ -24,37 +22,30 @@ interface RaceEntryListProps {
|
||||
|
||||
export function RaceEntryList({ entries, onDriverClick }: RaceEntryListProps) {
|
||||
return (
|
||||
<Card>
|
||||
<Stack gap={4}>
|
||||
<Stack direction="row" align="center" justify="between">
|
||||
<Heading level={2} icon={<Icon icon={Users} size={5} color="rgb(59, 130, 246)" />}>Entry List</Heading>
|
||||
<Text size="sm" color="text-gray-400">{entries.length} drivers</Text>
|
||||
</Stack>
|
||||
|
||||
{entries.length === 0 ? (
|
||||
<Stack align="center" py={8} gap={3}>
|
||||
<Surface variant="muted" rounded="full" p={4}>
|
||||
<Icon icon={Users} size={6} color="rgb(82, 82, 82)" />
|
||||
</Surface>
|
||||
<Text color="text-gray-400">No drivers registered yet</Text>
|
||||
</Stack>
|
||||
) : (
|
||||
<Stack gap={1}>
|
||||
{entries.map((driver, index) => (
|
||||
<DriverEntryRow
|
||||
key={driver.id}
|
||||
index={index}
|
||||
name={driver.name}
|
||||
avatarUrl={driver.avatarUrl}
|
||||
country={driver.country}
|
||||
rating={driver.rating}
|
||||
isCurrentUser={driver.isCurrentUser}
|
||||
onClick={() => onDriverClick(driver.id)}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Card>
|
||||
<Panel
|
||||
title="Entry List"
|
||||
actions={<Text size="sm" variant="low">{entries.length} drivers</Text>}
|
||||
>
|
||||
{entries.length === 0 ? (
|
||||
<EmptyState
|
||||
icon={Users}
|
||||
title="No drivers registered yet"
|
||||
variant="minimal"
|
||||
/>
|
||||
) : (
|
||||
entries.map((driver, index) => (
|
||||
<DriverEntryRow
|
||||
key={driver.id}
|
||||
index={index}
|
||||
name={driver.name}
|
||||
avatarUrl={driver.avatarUrl}
|
||||
country={driver.country}
|
||||
rating={driver.rating}
|
||||
isCurrentUser={driver.isCurrentUser}
|
||||
onClick={() => onDriverClick(driver.id)}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Button } from '@/ui/Button';
|
||||
import { Card } from '@/ui/Card';
|
||||
import { FilterGroup } from '@/ui/FilterGroup';
|
||||
import { Select } from '@/ui/Select';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
|
||||
interface RaceFilterBarProps {
|
||||
timeFilter: TimeFilter;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Button } from '@/ui/Button';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Input } from '@/ui/Input';
|
||||
import { Modal } from '@/ui/Modal';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Select } from '@/ui/Select';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Filter, Search } from 'lucide-react';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Calendar, Car, MapPin } from 'lucide-react';
|
||||
import React from 'react';
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Badge } from '@/ui/Badge';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Hero } from '@/ui/Hero';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Calendar, Car, Clock, LucideIcon } from 'lucide-react';
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
|
||||
|
||||
import { Button } from '@/ui/Button';
|
||||
import { ButtonGroup } from '@/ui/ButtonGroup';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { InfoBanner } from '@/ui/InfoBanner';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { CheckCircle2, PlayCircle, UserMinus, UserPlus, XCircle } from 'lucide-react';
|
||||
import { PlayCircle, UserMinus, UserPlus, XCircle, CheckCircle2 } from 'lucide-react';
|
||||
|
||||
interface RaceJoinButtonProps {
|
||||
raceStatus: 'scheduled' | 'running' | 'completed' | 'cancelled';
|
||||
@@ -56,8 +56,8 @@ export function RaceJoinButton({
|
||||
|
||||
if (isUserRegistered) {
|
||||
return (
|
||||
<Stack gap={3} fullWidth>
|
||||
<InfoBanner type="success" icon={CheckCircle2}>
|
||||
<ButtonGroup alignment="start" gap={3}>
|
||||
<InfoBanner type="success">
|
||||
You're Registered
|
||||
</InfoBanner>
|
||||
<Button
|
||||
@@ -69,7 +69,7 @@ export function RaceJoinButton({
|
||||
>
|
||||
{isLoading.withdraw ? 'Withdrawing...' : 'Withdraw'}
|
||||
</Button>
|
||||
</Stack>
|
||||
</ButtonGroup>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
import { RaceListItem } from '@/components/races/RaceListItem';
|
||||
import { routes } from '@/lib/routing/RouteConfig';
|
||||
import type { RaceViewData } from '@/lib/view-data/RacesViewData';
|
||||
import { Card } from '@/ui/Card';
|
||||
import { DateHeader } from '@/ui/DateHeader';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { EmptyState } from '@/ui/EmptyState';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Calendar, CheckCircle2, Clock, PlayCircle, XCircle } from 'lucide-react';
|
||||
|
||||
interface RaceListProps {
|
||||
@@ -46,21 +44,11 @@ export function RaceList({ racesByDate, totalCount, onRaceClick }: RaceListProps
|
||||
|
||||
if (racesByDate.length === 0) {
|
||||
return (
|
||||
<Card py={12} textAlign="center" bg="bg-surface-charcoal" border borderColor="border-outline-steel">
|
||||
<Stack align="center" gap={4}>
|
||||
<Stack p={4} bg="bg-base-black" rounded="full" border borderColor="border-outline-steel">
|
||||
<Icon icon={Calendar} size={8} color="var(--text-gray-500)" />
|
||||
</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>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Card>
|
||||
<EmptyState
|
||||
icon={Calendar}
|
||||
title="No races found"
|
||||
description={totalCount === 0 ? 'No races have been scheduled yet' : 'Try adjusting your filters'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -69,8 +57,7 @@ export function RaceList({ racesByDate, totalCount, onRaceClick }: RaceListProps
|
||||
{racesByDate.map((group) => (
|
||||
<Stack key={group.dateKey} gap={3}>
|
||||
<DateHeader
|
||||
label={group.dateLabel}
|
||||
count={group.races.length}
|
||||
date={group.dateLabel}
|
||||
/>
|
||||
|
||||
<Stack gap={2}>
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Grid } from '@/ui/primitives/Grid';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Surface } from '@/ui/primitives/Surface';
|
||||
import { Grid } from '@/ui/Grid';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Surface } from '@/ui/Surface';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { CalendarDays, Clock, Flag, LucideIcon, Trophy, Zap } from 'lucide-react';
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { routes } from '@/lib/routing/RouteConfig';
|
||||
import { Card } from '@/ui/Card';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Link } from '@/ui/Link';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { ChevronRight } from 'lucide-react';
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
import { DecorativeBlur } from '@/ui/DecorativeBlur';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Surface } from '@/ui/primitives/Surface';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Surface } from '@/ui/Surface';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Trophy } from 'lucide-react';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
interface RaceResultListProps {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { PageHero } from '@/ui/PageHero';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Calendar, Trophy, Users, Zap } from 'lucide-react';
|
||||
|
||||
@@ -23,42 +23,42 @@ export function RaceResultsHeader({
|
||||
leagueName,
|
||||
raceSOF
|
||||
}: RaceResultsHeaderProps) {
|
||||
const stats = [
|
||||
...(raceScheduledAt ? [{
|
||||
icon: Calendar,
|
||||
value: new Date(raceScheduledAt).toLocaleDateString('en-US', {
|
||||
weekday: 'short',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
}),
|
||||
label: '',
|
||||
color: 'text-gray-400'
|
||||
}] : []),
|
||||
...(totalDrivers !== undefined && totalDrivers !== null ? [{
|
||||
icon: Users,
|
||||
value: totalDrivers,
|
||||
label: 'drivers classified',
|
||||
color: 'text-gray-400'
|
||||
}] : []),
|
||||
...(leagueName ? [{
|
||||
value: leagueName,
|
||||
label: '',
|
||||
color: 'text-primary-blue'
|
||||
}] : [])
|
||||
];
|
||||
|
||||
return (
|
||||
<PageHero
|
||||
title={`${raceTrack || DEFAULT_RACE_TRACK} Results`}
|
||||
icon={Trophy}
|
||||
stats={stats}
|
||||
>
|
||||
{raceSOF && (
|
||||
<Stack direction="row" align="center" gap={1.5} mt={4}>
|
||||
<Icon icon={Zap} size={4} color="text-warning-amber" />
|
||||
<Text size="sm" color="text-warning-amber">SOF {raceSOF}</Text>
|
||||
<Stack gap={4}>
|
||||
<Stack direction="row" align="center" gap={4} wrap>
|
||||
{raceScheduledAt && (
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Icon icon={Calendar} size={4} intent="low" />
|
||||
<Text size="sm" variant="low">
|
||||
{new Date(raceScheduledAt).toLocaleDateString('en-US', {
|
||||
weekday: 'short',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
})}
|
||||
</Text>
|
||||
</Stack>
|
||||
)}
|
||||
{totalDrivers !== undefined && totalDrivers !== null && (
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Icon icon={Users} size={4} intent="low" />
|
||||
<Text size="sm" variant="low">{totalDrivers} drivers classified</Text>
|
||||
</Stack>
|
||||
)}
|
||||
{leagueName && (
|
||||
<Text size="sm" variant="primary">{leagueName}</Text>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{raceSOF && (
|
||||
<Stack direction="row" align="center" gap={1.5}>
|
||||
<Icon icon={Zap} size={4} intent="warning" />
|
||||
<Text size="sm" variant="warning">SOF {raceSOF}</Text>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</PageHero>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Box } from '@/ui/primitives/Box';
|
||||
import { Grid } from '@/ui/Grid';
|
||||
import { StatGridItem } from '@/ui/StatGridItem';
|
||||
import { CheckCircle, Clock, Gavel } from 'lucide-react';
|
||||
|
||||
@@ -10,25 +10,25 @@ interface RaceStewardingStatsProps {
|
||||
|
||||
export function RaceStewardingStats({ pendingCount, resolvedCount, penaltiesCount }: RaceStewardingStatsProps) {
|
||||
return (
|
||||
<Box display="grid" gridCols={3} gap={4}>
|
||||
<Grid cols={3} gap={4}>
|
||||
<StatGridItem
|
||||
label="Pending"
|
||||
value={pendingCount}
|
||||
icon={Clock}
|
||||
color="text-warning-amber"
|
||||
intent="warning"
|
||||
/>
|
||||
<StatGridItem
|
||||
label="Resolved"
|
||||
value={resolvedCount}
|
||||
icon={CheckCircle}
|
||||
color="text-performance-green"
|
||||
intent="success"
|
||||
/>
|
||||
<StatGridItem
|
||||
label="Penalties"
|
||||
value={penaltiesCount}
|
||||
icon={Gavel}
|
||||
color="text-red-400"
|
||||
intent="critical"
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Panel } from '@/ui/Panel';
|
||||
import { Box } from '@/ui/primitives/Box';
|
||||
import { StatusDot } from '@/ui/StatusDot';
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Badge } from '@/ui/Badge';
|
||||
import { Card } from '@/ui/Card';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Grid } from '@/ui/primitives/Grid';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Grid } from '@/ui/Grid';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { StatItem } from '@/ui/StatItem';
|
||||
|
||||
interface StandingsItemProps {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { StandingsItem } from './StandingsItem';
|
||||
|
||||
interface Standing {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Box } from '@/ui/primitives/Box';
|
||||
import { Box } from '@/ui/Box';
|
||||
|
||||
interface TelemetryLineProps {
|
||||
color?: 'primary' | 'aqua' | 'amber' | 'green' | 'red';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Box } from '@/ui/primitives/Box';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
interface TelemetryItem {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Droplets, Sun, Thermometer, Wind, type LucideIcon } from 'lucide-react';
|
||||
|
||||
interface TrackConditionsPanelProps {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Image } from '@/ui/Image';
|
||||
import { ImagePlaceholder } from '@/ui/ImagePlaceholder';
|
||||
import { Box } from '@/ui/primitives/Box';
|
||||
|
||||
export interface TrackImageProps {
|
||||
trackId?: string;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
|
||||
import { Badge } from '@/ui/Badge';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Surface } from '@/ui/primitives/Surface';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Surface } from '@/ui/Surface';
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
interface UpcomingRaceItemProps {
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
import { UpcomingRaceItem } from '@/components/races/UpcomingRaceItem';
|
||||
import { UpcomingRacesList } from '@/components/races/UpcomingRacesList';
|
||||
import { MinimalEmptyState } from '@/components/shared/state/EmptyState';
|
||||
import { MinimalEmptyState } from '@/ui/EmptyState';
|
||||
import { routes } from '@/lib/routing/RouteConfig';
|
||||
import { Card } from '@/ui/Card';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Link } from '@/ui/Link';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Calendar } from 'lucide-react';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
interface UpcomingRacesListProps {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { RaceSummaryItem } from '@/components/races/RaceSummaryItem';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Card } from '@/ui/Card';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
|
||||
type UpcomingRace = {
|
||||
id: string;
|
||||
|
||||
Reference in New Issue
Block a user