website refactor
This commit is contained in:
@@ -1,35 +1,20 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import { Breadcrumbs } from '@/ui/Breadcrumbs';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Container } from '@/ui/Container';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Grid } from '@/ui/Grid';
|
||||
import { GridItem } from '@/ui/GridItem';
|
||||
import { Skeleton } from '@/ui/Skeleton';
|
||||
import { InfoBox } from '@/ui/InfoBox';
|
||||
import { RaceJoinButton } from '@/ui/RaceJoinButton';
|
||||
import { RaceHero } from '@/ui/RaceHeroWrapper';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { RaceUserResult } from '@/ui/RaceUserResultWrapper';
|
||||
import { RaceEntryList } from '@/components/races/RaceEntryList';
|
||||
import { RaceDetailCard } from '@/ui/RaceDetailCard';
|
||||
import { LeagueSummaryCard } from '@/components/leagues/LeagueSummaryCardWrapper';
|
||||
import {
|
||||
AlertTriangle,
|
||||
ArrowLeft,
|
||||
CheckCircle2,
|
||||
Clock,
|
||||
PlayCircle,
|
||||
Trophy,
|
||||
XCircle,
|
||||
Scale,
|
||||
} from 'lucide-react';
|
||||
import { Surface } from '@/ui/Surface';
|
||||
import { Card } from '@/ui/Card';
|
||||
import { RaceActionBar } from '@/ui/RaceActionBar';
|
||||
import { RaceDetailsHeader } from '@/components/races/RaceDetailsHeader';
|
||||
import { TrackConditionsPanel } from '@/components/races/TrackConditionsPanel';
|
||||
import { EntrantsTable } from '@/components/races/EntrantsTable';
|
||||
import type { SessionStatus } from '@/components/races/SessionStatusBadge';
|
||||
|
||||
export interface RaceDetailEntryViewModel {
|
||||
id: string;
|
||||
@@ -127,7 +112,6 @@ export function RaceDetailTemplate({
|
||||
onFileProtest,
|
||||
onResultsClick,
|
||||
onStewardingClick,
|
||||
onDriverClick,
|
||||
isOwnerOrAdmin = false,
|
||||
animatedRatingChange,
|
||||
mutationLoading = {},
|
||||
@@ -152,174 +136,122 @@ export function RaceDetailTemplate({
|
||||
if (error || !viewData || !viewData.race) {
|
||||
return (
|
||||
<Container size="md" py={8}>
|
||||
<Stack gap={6}>
|
||||
<Breadcrumbs items={[{ label: 'Races', href: '/races' }, { label: 'Error' }]} />
|
||||
|
||||
<Card>
|
||||
<Stack align="center" gap={4} py={12}>
|
||||
<Surface variant="muted" rounded="full" padding={4}>
|
||||
<Icon icon={AlertTriangle} size={8} color="#f59e0b" />
|
||||
</Surface>
|
||||
<Box>
|
||||
<Text weight="medium" color="text-white" block mb={1}>{error instanceof Error ? error.message : error || 'Race not found'}</Text>
|
||||
<Text size="sm" color="text-gray-500">
|
||||
The race you're looking for doesn't exist or has been removed.
|
||||
</Text>
|
||||
</Box>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={onBack}
|
||||
>
|
||||
Back to Races
|
||||
</Button>
|
||||
</Stack>
|
||||
</Card>
|
||||
</Stack>
|
||||
<Box bg="bg-surface-charcoal" border borderColor="border-outline-steel" p={12} textAlign="center" rounded="xl">
|
||||
<Stack alignItems="center" gap={4}>
|
||||
<Text as="h2" size="xl" weight="bold" color="text-white">Race Not Found</Text>
|
||||
<Text color="text-gray-400">{`The race you're looking for doesn't exist or has been removed.`}</Text>
|
||||
<Box
|
||||
as="button"
|
||||
onClick={onBack}
|
||||
mt={4}
|
||||
px={6}
|
||||
py={2}
|
||||
bg="bg-primary-accent"
|
||||
color="text-white"
|
||||
weight="bold"
|
||||
rounded="md"
|
||||
hoverBg="bg-primary-accent"
|
||||
bgOpacity={0.8}
|
||||
transition
|
||||
>
|
||||
Back to Schedule
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
const { race, league, entryList, userResult } = viewData;
|
||||
|
||||
const statusConfig = {
|
||||
scheduled: {
|
||||
icon: Clock,
|
||||
variant: 'primary' as const,
|
||||
label: 'Scheduled',
|
||||
description: 'This race is scheduled and waiting to start',
|
||||
},
|
||||
running: {
|
||||
icon: PlayCircle,
|
||||
variant: 'success' as const,
|
||||
label: 'LIVE NOW',
|
||||
description: 'This race is currently in progress',
|
||||
},
|
||||
completed: {
|
||||
icon: CheckCircle2,
|
||||
variant: 'default' as const,
|
||||
label: 'Completed',
|
||||
description: 'This race has finished',
|
||||
},
|
||||
cancelled: {
|
||||
icon: XCircle,
|
||||
variant: 'warning' as const,
|
||||
label: 'Cancelled',
|
||||
description: 'This race has been cancelled',
|
||||
},
|
||||
};
|
||||
|
||||
const config = statusConfig[race.status] || statusConfig.scheduled;
|
||||
|
||||
const breadcrumbItems = [
|
||||
{ label: 'Races', href: '/races' },
|
||||
...(league ? [{ label: league.name, href: `/leagues/${league.id}` }] : []),
|
||||
{ label: race.track },
|
||||
];
|
||||
|
||||
return (
|
||||
<Container size="lg" py={8}>
|
||||
<Stack gap={6}>
|
||||
{/* Navigation Row */}
|
||||
<Stack direction="row" align="center" justify="between">
|
||||
<Breadcrumbs items={breadcrumbItems} />
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={onBack}
|
||||
size="sm"
|
||||
icon={<Icon icon={ArrowLeft} size={4} />}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
</Stack>
|
||||
<Box as="main" minHeight="screen" bg="bg-base-black">
|
||||
<RaceDetailsHeader
|
||||
title={race.track}
|
||||
leagueName={league?.name || 'Official'}
|
||||
trackName={race.track}
|
||||
scheduledAt={race.scheduledAt}
|
||||
status={race.status as SessionStatus}
|
||||
onBack={onBack}
|
||||
/>
|
||||
|
||||
{/* User Result */}
|
||||
{userResult && (
|
||||
<RaceUserResult
|
||||
{...userResult}
|
||||
animatedRatingChange={animatedRatingChange}
|
||||
/>
|
||||
)}
|
||||
<Container size="lg" py={8}>
|
||||
<Stack gap={8}>
|
||||
{userResult && (
|
||||
<RaceUserResult
|
||||
{...userResult}
|
||||
animatedRatingChange={animatedRatingChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Hero Header */}
|
||||
<RaceHero
|
||||
track={race.track}
|
||||
scheduledAt={race.scheduledAt}
|
||||
car={race.car}
|
||||
status={race.status}
|
||||
statusConfig={config}
|
||||
/>
|
||||
<Box bg="bg-surface-charcoal" border borderColor="border-outline-steel" p={4}>
|
||||
<RaceActionBar
|
||||
status={race.status}
|
||||
isUserRegistered={viewData.registration.isUserRegistered}
|
||||
canRegister={viewData.registration.canRegister}
|
||||
onRegister={onRegister}
|
||||
onWithdraw={onWithdraw}
|
||||
onResultsClick={onResultsClick}
|
||||
onStewardingClick={onStewardingClick}
|
||||
onFileProtest={onFileProtest}
|
||||
isAdmin={isOwnerOrAdmin}
|
||||
onCancel={onCancel}
|
||||
onReopen={onReopen}
|
||||
onEndRace={onEndRace}
|
||||
isLoading={mutationLoading}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Grid cols={12} gap={6}>
|
||||
<GridItem lgSpan={8} colSpan={12}>
|
||||
<Stack gap={6}>
|
||||
<RaceDetailCard
|
||||
track={race.track}
|
||||
car={race.car}
|
||||
sessionType={race.sessionType}
|
||||
statusLabel={config.label}
|
||||
statusColor={config.variant === 'success' ? '#10b981' : config.variant === 'primary' ? '#3b82f6' : '#9ca3af'}
|
||||
/>
|
||||
<Grid cols={12} gap={6}>
|
||||
<GridItem lgSpan={8} colSpan={12}>
|
||||
<Stack gap={6}>
|
||||
<Box as="section" bg="bg-surface-charcoal" border borderColor="border-outline-steel" overflow="hidden">
|
||||
<Box p={4} borderBottom borderColor="border-outline-steel" bg="bg-base-black" bgOpacity={0.2}>
|
||||
<Text size="xs" weight="bold" color="text-gray-500" uppercase letterSpacing="widest">Entry List</Text>
|
||||
</Box>
|
||||
<EntrantsTable
|
||||
entrants={entryList.map(entry => ({
|
||||
id: entry.id,
|
||||
name: entry.name,
|
||||
carName: race.car,
|
||||
rating: entry.rating || 0,
|
||||
status: 'confirmed'
|
||||
}))}
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
</GridItem>
|
||||
|
||||
<RaceEntryList
|
||||
entries={entryList}
|
||||
onDriverClick={onDriverClick}
|
||||
/>
|
||||
</Stack>
|
||||
</GridItem>
|
||||
<GridItem lgSpan={4} colSpan={12}>
|
||||
<Stack gap={6}>
|
||||
{league && <LeagueSummaryCard league={league} />}
|
||||
|
||||
<TrackConditionsPanel
|
||||
airTemp="24°C"
|
||||
trackTemp="31°C"
|
||||
humidity="45%"
|
||||
windSpeed="12 km/h NW"
|
||||
weatherType="Partly Cloudy"
|
||||
/>
|
||||
|
||||
<GridItem lgSpan={4} colSpan={12}>
|
||||
<Stack gap={6}>
|
||||
{league && <LeagueSummaryCard league={league} />}
|
||||
|
||||
{/* Actions Card */}
|
||||
<Card>
|
||||
<Stack gap={4}>
|
||||
<Text size="xl" weight="bold" color="text-white">Actions</Text>
|
||||
<Stack gap={3}>
|
||||
<RaceJoinButton
|
||||
raceStatus={race.status}
|
||||
isUserRegistered={viewData.registration.isUserRegistered}
|
||||
canRegister={viewData.registration.canRegister}
|
||||
onRegister={onRegister}
|
||||
onWithdraw={onWithdraw}
|
||||
onCancel={onCancel}
|
||||
onReopen={onReopen}
|
||||
onEndRace={onEndRace}
|
||||
canReopenRace={viewData.canReopenRace}
|
||||
isOwnerOrAdmin={isOwnerOrAdmin}
|
||||
isLoading={mutationLoading}
|
||||
/>
|
||||
|
||||
{race.status === 'completed' && (
|
||||
<>
|
||||
<Button variant="primary" fullWidth onClick={onResultsClick} icon={<Icon icon={Trophy} size={4} />}>
|
||||
View Results
|
||||
</Button>
|
||||
{userResult && (
|
||||
<Button variant="secondary" fullWidth onClick={onFileProtest} icon={<Icon icon={Scale} size={4} />}>
|
||||
File Protest
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="secondary" fullWidth onClick={onStewardingClick} icon={<Icon icon={Scale} size={4} />}>
|
||||
Stewarding
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<Box as="section" bg="bg-surface-charcoal" border borderColor="border-outline-steel" p={4}>
|
||||
<Text as="h3" size="xs" weight="bold" color="text-gray-500" uppercase letterSpacing="widest" mb={4}>Session Info</Text>
|
||||
<Stack gap={4}>
|
||||
<Stack gap={1}>
|
||||
<Text size="xs" color="text-gray-500" uppercase weight="bold">Format</Text>
|
||||
<Text size="sm" color="text-white">{race.sessionType}</Text>
|
||||
</Stack>
|
||||
<Stack gap={1}>
|
||||
<Text size="xs" color="text-gray-500" uppercase weight="bold">Car Class</Text>
|
||||
<Text size="sm" color="text-white">{race.car}</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Card>
|
||||
|
||||
{/* Status Info */}
|
||||
<InfoBox
|
||||
icon={config.icon}
|
||||
title={config.label}
|
||||
description={config.description}
|
||||
variant={config.variant}
|
||||
/>
|
||||
</Stack>
|
||||
</GridItem>
|
||||
</Grid>
|
||||
</Stack>
|
||||
</Container>
|
||||
</Box>
|
||||
</Stack>
|
||||
</GridItem>
|
||||
</Grid>
|
||||
</Stack>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user