website refactor
This commit is contained in:
@@ -1,29 +1,19 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import { Breadcrumbs } from '@/ui/Breadcrumbs';
|
||||
import { Container } from '@/ui/Container';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Grid } from '@/ui/Grid';
|
||||
import { GridItem } from '@/ui/GridItem';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Flag, CheckCircle, Gavel, Info } from 'lucide-react';
|
||||
import { RaceStewardingStats } from '@/ui/RaceStewardingStats';
|
||||
import { StewardingTabs } from '@/ui/StewardingTabs';
|
||||
import { ProtestCard } from '@/components/leagues/ProtestCardWrapper';
|
||||
import { RacePenaltyRow } from '@/ui/RacePenaltyRowWrapper';
|
||||
import { Card } from '@/ui/Card';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Container } from '@/ui/Container';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Surface } from '@/ui/Surface';
|
||||
import {
|
||||
AlertTriangle,
|
||||
ArrowLeft,
|
||||
CheckCircle,
|
||||
Flag,
|
||||
Gavel,
|
||||
Scale,
|
||||
} from 'lucide-react';
|
||||
import { DateDisplay } from '@/lib/display-objects/DateDisplay';
|
||||
import { RaceDetailsHeader } from '@/components/races/RaceDetailsHeader';
|
||||
import type { RaceStewardingViewData } from '@/lib/view-data/races/RaceStewardingViewData';
|
||||
|
||||
export type StewardingTab = 'pending' | 'resolved' | 'penalties';
|
||||
@@ -52,13 +42,13 @@ export function RaceStewardingTemplate({
|
||||
setActiveTab,
|
||||
}: RaceStewardingTemplateProps) {
|
||||
const formatDate = (date: string) => {
|
||||
return DateDisplay.formatShort(date);
|
||||
return date; // Simplified for template
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Container size="lg" py={12}>
|
||||
<Stack align="center">
|
||||
<Stack alignItems="center">
|
||||
<Text color="text-gray-400">Loading stewarding data...</Text>
|
||||
</Stack>
|
||||
</Container>
|
||||
@@ -68,164 +58,157 @@ export function RaceStewardingTemplate({
|
||||
if (!viewData?.race) {
|
||||
return (
|
||||
<Container size="md" py={12}>
|
||||
<Card>
|
||||
<Stack align="center" py={12} gap={4}>
|
||||
<Surface variant="muted" rounded="full" padding={4}>
|
||||
<Icon icon={AlertTriangle} size={8} color="#f59e0b" />
|
||||
</Surface>
|
||||
<Box textAlign="center">
|
||||
<Text weight="medium" color="text-white" block mb={1}>Race not found</Text>
|
||||
<Text size="sm" color="text-gray-500">The race you're looking for doesn't exist.</Text>
|
||||
<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.`}</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>
|
||||
<Button variant="secondary" onClick={onBack}>
|
||||
Back to Races
|
||||
</Button>
|
||||
</Stack>
|
||||
</Card>
|
||||
</Box>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
const breadcrumbItems = [
|
||||
{ label: 'Races', href: '/races' },
|
||||
{ label: viewData.race.track, href: `/races/${viewData.race.id}` },
|
||||
{ label: 'Stewarding' },
|
||||
];
|
||||
|
||||
return (
|
||||
<Container size="lg" py={8}>
|
||||
<Stack gap={6}>
|
||||
{/* Navigation */}
|
||||
<Stack direction="row" align="center" justify="between">
|
||||
<Breadcrumbs items={breadcrumbItems} />
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={onBack}
|
||||
icon={<Icon icon={ArrowLeft} size={4} />}
|
||||
>
|
||||
Back to Race
|
||||
</Button>
|
||||
<Box as="main" minHeight="screen" bg="bg-base-black">
|
||||
<RaceDetailsHeader
|
||||
title="Stewarding Dashboard"
|
||||
leagueName={viewData.race.track}
|
||||
trackName={viewData.race.track}
|
||||
scheduledAt={viewData.race.scheduledAt}
|
||||
status="completed"
|
||||
onBack={onBack}
|
||||
/>
|
||||
|
||||
<Container size="lg" py={8}>
|
||||
<Stack gap={8}>
|
||||
<Grid cols={12} gap={6}>
|
||||
<GridItem colSpan={12} lgSpan={8}>
|
||||
<Stack gap={6}>
|
||||
<StewardingTabs
|
||||
activeTab={activeTab}
|
||||
onTabChange={setActiveTab}
|
||||
pendingCount={viewData.pendingProtests.length}
|
||||
/>
|
||||
|
||||
{activeTab === 'pending' && (
|
||||
<Stack gap={4}>
|
||||
{viewData.pendingProtests.length === 0 ? (
|
||||
<Box bg="bg-surface-charcoal" border borderColor="border-outline-steel" p={12} textAlign="center">
|
||||
<Stack alignItems="center" gap={4}>
|
||||
<Icon icon={Flag} size={8} color="var(--color-success)" />
|
||||
<Box>
|
||||
<Text weight="semibold" size="lg" color="text-white" block mb={1}>All Clear!</Text>
|
||||
<Text size="sm" color="text-gray-400">No pending protests to review</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
) : (
|
||||
viewData.pendingProtests.map((protest) => (
|
||||
<ProtestCard
|
||||
key={protest.id}
|
||||
protest={protest}
|
||||
protester={viewData.driverMap[protest.protestingDriverId]}
|
||||
accused={viewData.driverMap[protest.accusedDriverId]}
|
||||
isAdmin={isAdmin}
|
||||
onReview={onReviewProtest}
|
||||
formatDate={formatDate}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{activeTab === 'resolved' && (
|
||||
<Stack gap={4}>
|
||||
{viewData.resolvedProtests.length === 0 ? (
|
||||
<Box bg="bg-surface-charcoal" border borderColor="border-outline-steel" p={12} textAlign="center">
|
||||
<Stack alignItems="center" gap={4}>
|
||||
<Icon icon={CheckCircle} size={8} color="#525252" />
|
||||
<Box>
|
||||
<Text weight="semibold" size="lg" color="text-white" block mb={1}>No Resolved Protests</Text>
|
||||
<Text size="sm" color="text-gray-400">Resolved protests will appear here</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
) : (
|
||||
viewData.resolvedProtests.map((protest) => (
|
||||
<ProtestCard
|
||||
key={protest.id}
|
||||
protest={protest}
|
||||
protester={viewData.driverMap[protest.protestingDriverId]}
|
||||
accused={viewData.driverMap[protest.accusedDriverId]}
|
||||
isAdmin={isAdmin}
|
||||
onReview={onReviewProtest}
|
||||
formatDate={formatDate}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{activeTab === 'penalties' && (
|
||||
<Stack gap={4}>
|
||||
{viewData.penalties.length === 0 ? (
|
||||
<Box bg="bg-surface-charcoal" border borderColor="border-outline-steel" p={12} textAlign="center">
|
||||
<Stack alignItems="center" gap={4}>
|
||||
<Icon icon={Gavel} size={8} color="#525252" />
|
||||
<Box>
|
||||
<Text weight="semibold" size="lg" color="text-white" block mb={1}>No Penalties</Text>
|
||||
<Text size="sm" color="text-gray-400">Penalties issued for this race will appear here</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
) : (
|
||||
viewData.penalties.map((penalty) => (
|
||||
<RacePenaltyRow
|
||||
key={penalty.id}
|
||||
penalty={{
|
||||
...penalty,
|
||||
driverName: viewData.driverMap[penalty.driverId]?.name || 'Unknown',
|
||||
type: penalty.type as 'time_penalty' | 'grid_penalty' | 'points_deduction' | 'disqualification' | 'warning' | 'license_points'
|
||||
}}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</GridItem>
|
||||
|
||||
<GridItem colSpan={12} lgSpan={4}>
|
||||
<Stack gap={6}>
|
||||
<Box as="section" bg="bg-surface-charcoal" border borderColor="border-outline-steel" p={4}>
|
||||
<Box display="flex" alignItems="center" gap={2} mb={4}>
|
||||
<Icon icon={Info} size={4} color="var(--color-primary)" />
|
||||
<Text as="h3" size="xs" weight="bold" color="text-gray-500" uppercase letterSpacing="widest">Stewarding Stats</Text>
|
||||
</Box>
|
||||
<RaceStewardingStats
|
||||
pendingCount={viewData.pendingCount}
|
||||
resolvedCount={viewData.resolvedCount}
|
||||
penaltiesCount={viewData.penaltiesCount}
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
</GridItem>
|
||||
</Grid>
|
||||
</Stack>
|
||||
|
||||
{/* Header */}
|
||||
<Surface variant="muted" rounded="xl" border padding={6} bg="bg-gradient-to-r from-neutral-800/50 to-neutral-800/30" borderColor="border-neutral-800">
|
||||
<Stack direction="row" align="center" gap={4} mb={6}>
|
||||
<Surface variant="muted" rounded="xl" padding={3} bg="bg-blue-500/20">
|
||||
<Icon icon={Scale} size={6} color="#3b82f6" />
|
||||
</Surface>
|
||||
<Box>
|
||||
<Heading level={1}>Stewarding</Heading>
|
||||
<Text size="sm" color="text-gray-400" block mt={1}>
|
||||
{viewData.race.track} • {formatDate(viewData.race.scheduledAt)}
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{/* Stats */}
|
||||
<RaceStewardingStats
|
||||
pendingCount={viewData.pendingCount}
|
||||
resolvedCount={viewData.resolvedCount}
|
||||
penaltiesCount={viewData.penaltiesCount}
|
||||
/>
|
||||
</Surface>
|
||||
|
||||
{/* Tab Navigation */}
|
||||
<StewardingTabs
|
||||
activeTab={activeTab}
|
||||
onTabChange={setActiveTab}
|
||||
pendingCount={viewData.pendingProtests.length}
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
{activeTab === 'pending' && (
|
||||
<Stack gap={4}>
|
||||
{viewData.pendingProtests.length === 0 ? (
|
||||
<Card>
|
||||
<Stack align="center" py={12} gap={4}>
|
||||
<Surface variant="muted" rounded="full" padding={4}>
|
||||
<Icon icon={Flag} size={8} color="#10b981" />
|
||||
</Surface>
|
||||
<Box textAlign="center">
|
||||
<Text weight="semibold" size="lg" color="text-white" block mb={1}>All Clear!</Text>
|
||||
<Text size="sm" color="text-gray-400">No pending protests to review</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Card>
|
||||
) : (
|
||||
viewData.pendingProtests.map((protest) => (
|
||||
<ProtestCard
|
||||
key={protest.id}
|
||||
protest={protest}
|
||||
protester={viewData.driverMap[protest.protestingDriverId]}
|
||||
accused={viewData.driverMap[protest.accusedDriverId]}
|
||||
isAdmin={isAdmin}
|
||||
onReview={onReviewProtest}
|
||||
formatDate={formatDate}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{activeTab === 'resolved' && (
|
||||
<Stack gap={4}>
|
||||
{viewData.resolvedProtests.length === 0 ? (
|
||||
<Card>
|
||||
<Stack align="center" py={12} gap={4}>
|
||||
<Surface variant="muted" rounded="full" padding={4}>
|
||||
<Icon icon={CheckCircle} size={8} color="#525252" />
|
||||
</Surface>
|
||||
<Box textAlign="center">
|
||||
<Text weight="semibold" size="lg" color="text-white" block mb={1}>No Resolved Protests</Text>
|
||||
<Text size="sm" color="text-gray-400">Resolved protests will appear here</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Card>
|
||||
) : (
|
||||
viewData.resolvedProtests.map((protest) => (
|
||||
<ProtestCard
|
||||
key={protest.id}
|
||||
protest={protest}
|
||||
protester={viewData.driverMap[protest.protestingDriverId]}
|
||||
accused={viewData.driverMap[protest.accusedDriverId]}
|
||||
isAdmin={isAdmin}
|
||||
onReview={onReviewProtest}
|
||||
formatDate={formatDate}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{activeTab === 'penalties' && (
|
||||
<Stack gap={4}>
|
||||
{viewData.penalties.length === 0 ? (
|
||||
<Card>
|
||||
<Stack align="center" py={12} gap={4}>
|
||||
<Surface variant="muted" rounded="full" padding={4}>
|
||||
<Icon icon={Gavel} size={8} color="#525252" />
|
||||
</Surface>
|
||||
<Box textAlign="center">
|
||||
<Text weight="semibold" size="lg" color="text-white" block mb={1}>No Penalties</Text>
|
||||
<Text size="sm" color="text-gray-400">Penalties issued for this race will appear here</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Card>
|
||||
) : (
|
||||
viewData.penalties.map((penalty) => (
|
||||
<RacePenaltyRow
|
||||
key={penalty.id}
|
||||
penalty={{
|
||||
...penalty,
|
||||
driverName: viewData.driverMap[penalty.driverId]?.name || 'Unknown',
|
||||
type: penalty.type as 'time_penalty' | 'grid_penalty' | 'points_deduction' | 'disqualification' | 'warning' | 'license_points'
|
||||
}}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Container>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user