website refactor

This commit is contained in:
2026-01-19 14:07:49 +01:00
parent 54f42bab9f
commit 6154d54435
88 changed files with 755 additions and 566 deletions

View File

@@ -9,7 +9,7 @@ type RaceWithResults = {
track: string;
car: string;
winnerName: string;
scheduledAt: string | Date;
formattedDate: string;
};
interface LatestResultsSidebarProps {
@@ -28,14 +28,12 @@ export function LatestResultsSidebar({ results }: LatestResultsSidebarProps) {
</Heading>
<RaceResultList>
{results.slice(0, 4).map((result) => {
const scheduledAt = typeof result.scheduledAt === 'string' ? new Date(result.scheduledAt) : result.scheduledAt;
return (
<Box as="li" key={result.raceId}>
<RaceSummaryItem
track={result.track}
meta={`${result.winnerName}${result.car}`}
dateLabel={scheduledAt.toLocaleDateString()}
dateLabel={result.formattedDate}
/>
</Box>
);

View File

@@ -10,7 +10,8 @@ import { Calendar, Car, Clock, LucideIcon } from 'lucide-react';
interface RaceHeroProps {
track: string;
scheduledAt: string;
formattedDate: string;
formattedTime: string;
car: string;
status: 'scheduled' | 'running' | 'completed' | 'cancelled';
statusConfig: {
@@ -20,9 +21,8 @@ interface RaceHeroProps {
};
}
export function RaceHero({ track, scheduledAt, car, status, statusConfig }: RaceHeroProps) {
export function RaceHero({ track, formattedDate, formattedTime, car, status, statusConfig }: RaceHeroProps) {
const StatusIcon = statusConfig.icon;
const date = new Date(scheduledAt);
return (
<Hero variant="primary">
@@ -59,11 +59,11 @@ export function RaceHero({ track, scheduledAt, car, status, statusConfig }: Race
<Stack direction="row" align="center" gap={6} wrap>
<Stack direction="row" align="center" gap={2}>
<Icon icon={Calendar} size={4} color="rgb(156, 163, 175)" />
<Text color="text-gray-400">{date.toLocaleDateString()}</Text>
<Text color="text-gray-400">{formattedDate}</Text>
</Stack>
<Stack direction="row" align="center" gap={2}>
<Icon icon={Clock} size={4} color="rgb(156, 163, 175)" />
<Text color="text-gray-400">{date.toLocaleTimeString()}</Text>
<Text color="text-gray-400">{formattedTime}</Text>
</Stack>
<Stack direction="row" align="center" gap={2}>
<Icon icon={Car} size={4} color="rgb(156, 163, 175)" />

View File

@@ -2,6 +2,7 @@
import { RaceHero as UiRaceHero } from '@/components/races/RaceHero';
import { LucideIcon } from 'lucide-react';
import { DateDisplay } from '@/lib/display-objects/DateDisplay';
interface RaceHeroProps {
track: string;
@@ -17,7 +18,7 @@ interface RaceHeroProps {
}
export function RaceHero(props: RaceHeroProps) {
const { statusConfig, ...rest } = props;
const { statusConfig, scheduledAt, ...rest } = props;
// Map variant to match UI component expectations
const mappedConfig: {
@@ -30,5 +31,12 @@ export function RaceHero(props: RaceHeroProps) {
variant: statusConfig.variant === 'default' ? 'default' : statusConfig.variant
};
return <UiRaceHero {...rest} statusConfig={mappedConfig} />;
return (
<UiRaceHero
{...rest}
formattedDate={DateDisplay.formatShort(scheduledAt)}
formattedTime={DateDisplay.formatTime(scheduledAt)}
statusConfig={mappedConfig}
/>
);
}

View File

@@ -2,7 +2,8 @@
import { routes } from '@/lib/routing/RouteConfig';
import { RaceListItem as UiRaceListItem } from '@/components/races/RaceListItem';
import { CheckCircle2, Clock, PlayCircle, XCircle } from 'lucide-react';
import { DateDisplay } from '@/lib/display-objects/DateDisplay';
import { StatusDisplay } from '@/lib/display-objects/StatusDisplay';
interface Race {
id: string;
@@ -26,45 +27,32 @@ export function RaceListItem({ race, onClick }: RaceListItemProps) {
scheduled: {
iconName: 'Clock',
variant: 'primary' as const,
label: 'Scheduled',
},
running: {
iconName: 'PlayCircle',
variant: 'success' as const,
label: 'LIVE',
},
completed: {
iconName: 'CheckCircle2',
variant: 'default' as const,
label: 'Completed',
},
cancelled: {
iconName: 'XCircle',
variant: 'warning' as const,
label: 'Cancelled',
},
};
const config = statusConfig[race.status];
const formatTime = (date: string) => {
return new Date(date).toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
});
};
const date = new Date(race.scheduledAt);
return (
<UiRaceListItem
track={race.track}
car={race.car}
dateLabel={date.toLocaleDateString('en-US', { month: 'short' })}
dayLabel={date.getDate().toString()}
timeLabel={formatTime(race.scheduledAt)}
dateLabel={DateDisplay.formatMonthDay(race.scheduledAt).split(' ')[0]}
dayLabel={DateDisplay.formatMonthDay(race.scheduledAt).split(' ')[1]}
timeLabel={DateDisplay.formatTime(race.scheduledAt)}
status={race.status}
statusLabel={config.label}
statusLabel={StatusDisplay.raceStatus(race.status)}
statusVariant={config.variant}
statusIconName={config.iconName}
leagueName={race.leagueName}

View File

@@ -12,10 +12,12 @@ interface RaceResultCardProps {
raceId: string;
track: string;
car: string;
scheduledAt: string | Date;
formattedDate: string;
position: number;
startPosition: number;
incidents: number;
positionLabel: string;
startPositionLabel: string;
incidentsLabel: string;
positionsGainedLabel?: string;
leagueName?: string;
showLeague?: boolean;
onClick?: () => void;
@@ -25,10 +27,12 @@ export function RaceResultCard({
raceId,
track,
car,
scheduledAt,
formattedDate,
position,
startPosition,
incidents,
positionLabel,
startPositionLabel,
incidentsLabel,
positionsGainedLabel,
leagueName,
showLeague = true,
onClick,
@@ -66,7 +70,7 @@ export function RaceResultCard({
border
borderColor="border-outline-steel"
>
P{position}
{positionLabel}
</Stack>
<Stack>
<Text color="text-white" weight="medium" block groupHoverTextColor="text-primary-accent" transition>
@@ -78,11 +82,7 @@ export function RaceResultCard({
<Stack direction="row" align="center" gap={3}>
<Stack textAlign="right">
<Text size="sm" color="text-gray-400" block>
{new Date(scheduledAt).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
})}
{formattedDate}
</Text>
{showLeague && leagueName && (
<Text size="xs" color="text-gray-500" block>{leagueName}</Text>
@@ -92,16 +92,16 @@ export function RaceResultCard({
</Stack>
</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">Started {startPositionLabel}</Text>
<Text size="xs" color="text-gray-500"></Text>
<Text size="xs" color={incidents === 0 ? 'text-success-green' : incidents > 2 ? 'text-error-red' : 'text-gray-500'}>
{incidents}x incidents
<Text size="xs" color="text-gray-500">
{incidentsLabel}
</Text>
{position < startPosition && (
{positionsGainedLabel && (
<>
<Text size="xs" color="text-gray-500"></Text>
<Text size="xs" color="text-success-green">
+{startPosition - position} positions
{positionsGainedLabel}
</Text>
</>
)}

View File

@@ -2,6 +2,7 @@
import { RaceResultViewModel } from '@/lib/view-models/RaceResultViewModel';
import { RaceResultCard as UiRaceResultCard } from './RaceResultCard';
import { DateDisplay } from '@/lib/display-objects/DateDisplay';
interface RaceResultCardProps {
race: {
@@ -28,10 +29,12 @@ export function RaceResultCard({
raceId={race.id}
track={race.track}
car={race.car}
scheduledAt={race.scheduledAt}
formattedDate={DateDisplay.formatShort(race.scheduledAt)}
position={result.position}
startPosition={result.startPosition}
incidents={result.incidents}
positionLabel={result.formattedPosition}
startPositionLabel={result.formattedStartPosition}
incidentsLabel={result.formattedIncidents}
positionsGainedLabel={result.formattedPositionsGained}
leagueName={league?.name}
showLeague={showLeague}
/>

View File

@@ -8,7 +8,7 @@ type UpcomingRace = {
id: string;
track: string;
car: string;
scheduledAt: string | Date;
formattedDate: string;
};
interface UpcomingRacesSidebarProps {
@@ -35,14 +35,12 @@ export function UpcomingRacesSidebar({ races }: UpcomingRacesSidebarProps) {
</Stack>
<Stack gap={3}>
{races.slice(0, 4).map((race) => {
const scheduledAt = typeof race.scheduledAt === 'string' ? new Date(race.scheduledAt) : race.scheduledAt;
return (
<RaceSummaryItem
key={race.id}
track={race.track}
meta={race.car}
dateLabel={scheduledAt.toLocaleDateString()}
dateLabel={race.formattedDate}
/>
);
})}