website refactor

This commit is contained in:
2026-01-17 15:46:55 +01:00
parent 4d5ce9bfd6
commit 72a626ce71
346 changed files with 19308 additions and 8605 deletions

View File

@@ -1,5 +1,6 @@
'use client';
import { StewardingQueuePanel } from '@/components/leagues/StewardingQueuePanel';
import { PenaltyFAB } from '@/ui/PenaltyFAB';
import { QuickPenaltyModal } from '@/components/leagues/QuickPenaltyModal';
import { ReviewProtestModal } from '@/components/leagues/ReviewProtestModal';
@@ -8,12 +9,10 @@ import { Button } from '@/ui/Button';
import { Card } from '@/ui/Card';
import { useLeagueStewardingMutations } from "@/hooks/league/useLeagueStewardingMutations";
import { useMemo, useState } from 'react';
import { PendingProtestsList } from '@/components/leagues/PendingProtestsList';
import { PenaltyHistoryList } from '@/components/leagues/PenaltyHistoryList';
import { Box } from '@/ui/Box';
import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text';
import { Heading } from '@/ui/Heading';
import type { StewardingViewData } from '@/lib/view-data/leagues/StewardingViewData';
import { ProtestViewModel } from '@/lib/view-models/ProtestViewModel';
import { RaceViewModel } from '@/lib/view-models/RaceViewModel';
@@ -26,7 +25,7 @@ interface StewardingTemplateProps {
onRefetch: () => void;
}
export function StewardingPageClient({ data, leagueId, currentDriverId, onRefetch }: StewardingTemplateProps) {
export function StewardingPageClient({ data, currentDriverId, onRefetch }: StewardingTemplateProps) {
const [activeTab, setActiveTab] = useState<'pending' | 'history'>('pending');
const [selectedProtest, setSelectedProtest] = useState<ProtestViewModel | null>(null);
const [showQuickPenaltyModal, setShowQuickPenaltyModal] = useState(false);
@@ -36,19 +35,16 @@ export function StewardingPageClient({ data, leagueId, currentDriverId, onRefetc
// Flatten protests for the specialized list components
const allPendingProtests = useMemo(() => {
return data.races.flatMap(r => r.pendingProtests.map(p => new ProtestViewModel({
return data.races.flatMap(r => r.pendingProtests.map(p => ({
id: p.id,
protestingDriverId: p.protestingDriverId,
accusedDriverId: p.accusedDriverId,
raceName: r.track || 'Unknown Track',
protestingDriver: data.drivers.find(d => d.id === p.protestingDriverId)?.name || 'Unknown',
accusedDriver: data.drivers.find(d => d.id === p.accusedDriverId)?.name || 'Unknown',
description: p.incident.description,
submittedAt: p.filedAt,
status: p.status,
raceId: r.id,
incident: p.incident,
proofVideoUrl: p.proofVideoUrl,
decisionNotes: p.decisionNotes,
} as never)));
}, [data.races]);
status: p.status as 'pending' | 'under_review' | 'resolved' | 'rejected',
})));
}, [data.races, data.drivers]);
const allResolvedProtests = useMemo(() => {
return data.races.flatMap(r => r.resolvedProtests.map(p => new ProtestViewModel({
@@ -131,84 +127,91 @@ export function StewardingPageClient({ data, leagueId, currentDriverId, onRefetc
});
};
const handleReviewProtest = (id: string) => {
// Find the protest in the data
let foundProtest: ProtestViewModel | null = null;
data.races.forEach(r => {
const p = r.pendingProtests.find(p => p.id === id);
if (p) {
foundProtest = new ProtestViewModel({
id: p.id,
protestingDriverId: p.protestingDriverId,
accusedDriverId: p.accusedDriverId,
description: p.incident.description,
submittedAt: p.filedAt,
status: p.status,
raceId: r.id,
incident: p.incident,
proofVideoUrl: p.proofVideoUrl,
decisionNotes: p.decisionNotes,
} as never);
}
});
if (foundProtest) setSelectedProtest(foundProtest);
};
return (
<Stack gap={6}>
<Card>
<Box p={6}>
<Box display="flex" alignItems="center" justifyContent="between" mb={6}>
<Box>
<Heading level={2}>Stewarding</Heading>
<Box mt={1}>
<Text size="sm" color="text-gray-400">
Quick overview of protests and penalties across all races
</Text>
</Box>
</Box>
<StewardingStats
totalPending={data.totalPending}
totalResolved={data.totalResolved}
totalPenalties={data.totalPenalties}
/>
{/* Tab navigation */}
<Box borderBottom borderColor="border-charcoal-outline">
<Stack direction="row" gap={4}>
<Box
borderBottom={activeTab === 'pending'}
borderColor={activeTab === 'pending' ? 'border-primary-blue' : undefined}
>
<Button
variant="ghost"
onClick={() => setActiveTab('pending')}
rounded="none"
>
<Stack direction="row" align="center" gap={2}>
<Text weight="medium" color={activeTab === 'pending' ? 'text-primary-blue' : undefined}>Pending Protests</Text>
{data.totalPending > 0 && (
<Box px={2} py={0.5} fontSize="0.75rem" bg="bg-warning-amber/20" color="text-warning-amber" rounded="full">
{data.totalPending}
</Box>
)}
</Stack>
</Button>
</Box>
{/* Stats summary */}
<StewardingStats
totalPending={data.totalPending}
totalResolved={data.totalResolved}
totalPenalties={data.totalPenalties}
/>
{/* Tab navigation */}
<Box borderBottom borderColor="border-charcoal-outline" mb={6}>
<Stack direction="row" gap={4}>
<Box
borderBottom={activeTab === 'pending'}
borderColor={activeTab === 'pending' ? 'border-primary-blue' : undefined}
>
<Button
variant="ghost"
onClick={() => setActiveTab('pending')}
rounded="none"
>
<Stack direction="row" align="center" gap={2}>
<Text weight="medium" color={activeTab === 'pending' ? 'text-primary-blue' : undefined}>Pending Protests</Text>
{data.totalPending > 0 && (
<Box px={2} py={0.5} fontSize="0.75rem" bg="bg-warning-amber/20" color="text-warning-amber" rounded="full">
{data.totalPending}
</Box>
)}
</Stack>
</Button>
</Box>
<Box
borderBottom={activeTab === 'history'}
borderColor={activeTab === 'history' ? 'border-primary-blue' : undefined}
>
<Button
variant="ghost"
onClick={() => setActiveTab('history')}
rounded="none"
>
<Text weight="medium" color={activeTab === 'history' ? 'text-primary-blue' : undefined}>History</Text>
</Button>
</Box>
</Stack>
<Box
borderBottom={activeTab === 'history'}
borderColor={activeTab === 'history' ? 'border-primary-blue' : undefined}
>
<Button
variant="ghost"
onClick={() => setActiveTab('history')}
rounded="none"
>
<Text weight="medium" color={activeTab === 'history' ? 'text-primary-blue' : undefined}>History</Text>
</Button>
</Box>
</Stack>
</Box>
{/* Content */}
{activeTab === 'pending' ? (
<PendingProtestsList
protests={allPendingProtests}
races={racesMap}
drivers={driverMap}
leagueId={leagueId}
onReviewProtest={setSelectedProtest}
onProtestReviewed={onRefetch}
/>
) : (
{/* Content */}
{activeTab === 'pending' ? (
<StewardingQueuePanel
protests={allPendingProtests}
onReview={handleReviewProtest}
/>
) : (
<Card>
<Box p={6}>
<PenaltyHistoryList
protests={allResolvedProtests}
races={racesMap}
drivers={driverMap}
/>
)}
</Box>
</Card>
</Box>
</Card>
)}
{activeTab === 'history' && (
<PenaltyFAB onClick={() => setShowQuickPenaltyModal(true)} />