214 lines
8.2 KiB
TypeScript
214 lines
8.2 KiB
TypeScript
'use client';
|
|
|
|
import { ProtestCard } from '@/components/leagues/ProtestCardWrapper';
|
|
import { StewardingTabs } from '@/components/leagues/StewardingTabs';
|
|
import { RaceDetailsHeader } from '@/components/races/RaceDetailsHeader';
|
|
import { RacePenaltyRow } from '@/components/races/RacePenaltyRowWrapper';
|
|
import { RaceStewardingStats } from '@/components/races/RaceStewardingStats';
|
|
import type { RaceStewardingViewData } from '@/lib/view-data/races/RaceStewardingViewData';
|
|
import { Box } from '@/ui/Box';
|
|
import { Container } from '@/ui/Container';
|
|
import { Icon } from '@/ui/Icon';
|
|
import { Grid } from '@/ui/Grid';
|
|
import { GridItem } from '@/ui/GridItem';
|
|
import { Stack } from '@/ui/Stack';
|
|
import { Text } from '@/ui/Text';
|
|
import { CheckCircle, Flag, Gavel, Info } from 'lucide-react';
|
|
|
|
export type StewardingTab = 'pending' | 'resolved' | 'penalties';
|
|
|
|
interface RaceStewardingTemplateProps {
|
|
viewData: RaceStewardingViewData;
|
|
isLoading: boolean;
|
|
error?: Error | null;
|
|
// Actions
|
|
onBack: () => void;
|
|
onReviewProtest: (protestId: string) => void;
|
|
// User state
|
|
isAdmin: boolean;
|
|
// UI State
|
|
activeTab: StewardingTab;
|
|
setActiveTab: (tab: StewardingTab) => void;
|
|
}
|
|
|
|
export function RaceStewardingTemplate({
|
|
viewData,
|
|
isLoading,
|
|
onBack,
|
|
onReviewProtest,
|
|
isAdmin,
|
|
activeTab,
|
|
setActiveTab,
|
|
}: RaceStewardingTemplateProps) {
|
|
const formatDate = (date: string) => {
|
|
return date; // Simplified for template
|
|
};
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<Container size="lg" py={12}>
|
|
<Stack alignItems="center">
|
|
<Text color="text-gray-400">Loading stewarding data...</Text>
|
|
</Stack>
|
|
</Container>
|
|
);
|
|
}
|
|
|
|
if (!viewData?.race) {
|
|
return (
|
|
<Container size="md" py={12}>
|
|
<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>
|
|
</Stack>
|
|
</Box>
|
|
</Container>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<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>
|
|
</Container>
|
|
</Box>
|
|
);
|
|
}
|