fix data flow issues

This commit is contained in:
2025-12-19 21:58:03 +01:00
parent 94fc538f44
commit ec177a75ce
37 changed files with 1336 additions and 534 deletions

View File

@@ -4,8 +4,8 @@ import Breadcrumbs from '@/components/layout/Breadcrumbs';
import Button from '@/components/ui/Button';
import Card from '@/components/ui/Card';
import { useEffectiveDriverId } from '@/hooks/useEffectiveDriverId';
import type { RacePenaltiesViewModel, RaceProtestsViewModel } from '@/lib/apiClient';
import { apiClient } from '@/lib/apiClient';
import { useServices } from '@/lib/services/ServiceProvider';
import { RaceStewardingViewModel } from '@/lib/view-models/RaceStewardingViewModel';
import {
AlertCircle,
AlertTriangle,
@@ -24,13 +24,11 @@ import { useEffect, useState } from 'react';
export default function RaceStewardingPage() {
const params = useParams();
const router = useRouter();
const { raceStewardingService } = useServices();
const raceId = params.id as string;
const currentDriverId = useEffectiveDriverId();
const [race, setRace] = useState<any>(null); // TODO: Define proper race type
const [league, setLeague] = useState<any>(null); // TODO: Define proper league type
const [protestsData, setProtestsData] = useState<RaceProtestsViewModel | null>(null);
const [penaltiesData, setPenaltiesData] = useState<RacePenaltiesViewModel | null>(null);
const [stewardingData, setStewardingData] = useState<RaceStewardingViewModel | null>(null);
const [loading, setLoading] = useState(true);
const [isAdmin, setIsAdmin] = useState(false);
const [activeTab, setActiveTab] = useState<'pending' | 'resolved' | 'penalties'>('pending');
@@ -39,24 +37,13 @@ export default function RaceStewardingPage() {
async function loadData() {
setLoading(true);
try {
// Get race detail for basic info
const raceDetail = await apiClient.races.getDetail(raceId, currentDriverId);
setRace(raceDetail.race);
setLeague(raceDetail.league);
const data = await raceStewardingService.getRaceStewardingData(raceId, currentDriverId);
setStewardingData(data);
if (raceDetail.league) {
if (data.league) {
// TODO: Implement admin check via API
setIsAdmin(true);
}
// Get protests and penalties
const [protestsData, penaltiesData] = await Promise.all([
apiClient.races.getProtests(raceId),
apiClient.races.getPenalties(raceId),
]);
setProtestsData(protestsData);
setPenaltiesData(penaltiesData);
} catch (err) {
console.error('Failed to load data:', err);
} finally {
@@ -65,17 +52,10 @@ export default function RaceStewardingPage() {
}
loadData();
}, [raceId, currentDriverId]);
}, [raceId, currentDriverId, raceStewardingService]);
const pendingProtests = protestsData?.protests.filter(
(p) => p.status === 'pending' || p.status === 'under_review',
) ?? [];
const resolvedProtests = protestsData?.protests.filter(
(p) =>
p.status === 'upheld' ||
p.status === 'dismissed' ||
p.status === 'withdrawn',
) ?? [];
const pendingProtests = stewardingData?.pendingProtests ?? [];
const resolvedProtests = stewardingData?.resolvedProtests ?? [];
const getStatusBadge = (status: string) => {
switch (status) {
@@ -131,7 +111,7 @@ export default function RaceStewardingPage() {
);
}
if (!race) {
if (!stewardingData?.race) {
return (
<div className="min-h-screen bg-deep-graphite py-8 px-4 sm:px-6 lg:px-8">
<div className="max-w-4xl mx-auto">
@@ -158,7 +138,7 @@ export default function RaceStewardingPage() {
const breadcrumbItems = [
{ label: 'Races', href: '/races' },
{ label: race.track, href: `/races/${race.id}` },
{ label: stewardingData?.race?.track || 'Race', href: `/races/${raceId}` },
{ label: 'Stewarding' },
];
@@ -186,9 +166,9 @@ export default function RaceStewardingPage() {
</div>
<div>
<h1 className="text-2xl font-bold text-white">Stewarding</h1>
<p className="text-sm text-gray-400">
{race.track} {formatDate(race.scheduledAt)}
</p>
<p className="text-sm text-gray-400">
{stewardingData?.race?.track} {stewardingData?.race?.scheduledAt ? formatDate(stewardingData.race.scheduledAt) : ''}
</p>
</div>
</div>
@@ -199,21 +179,21 @@ export default function RaceStewardingPage() {
<Clock className="w-4 h-4" />
<span className="text-xs font-medium uppercase">Pending</span>
</div>
<div className="text-2xl font-bold text-white">{pendingProtests.length}</div>
<div className="text-2xl font-bold text-white">{stewardingData?.pendingCount ?? 0}</div>
</div>
<div className="rounded-lg bg-deep-graphite/50 border border-charcoal-outline p-4">
<div className="flex items-center gap-2 text-performance-green mb-1">
<CheckCircle className="w-4 h-4" />
<span className="text-xs font-medium uppercase">Resolved</span>
</div>
<div className="text-2xl font-bold text-white">{resolvedProtests.length}</div>
<div className="text-2xl font-bold text-white">{stewardingData?.resolvedCount ?? 0}</div>
</div>
<div className="rounded-lg bg-deep-graphite/50 border border-charcoal-outline p-4">
<div className="flex items-center gap-2 text-red-400 mb-1">
<Gavel className="w-4 h-4" />
<span className="text-xs font-medium uppercase">Penalties</span>
</div>
<div className="text-2xl font-bold text-white">{penaltiesData?.penalties.length ?? 0}</div>
<div className="text-2xl font-bold text-white">{stewardingData?.penaltiesCount ?? 0}</div>
</div>
</div>
</Card>
@@ -272,8 +252,8 @@ export default function RaceStewardingPage() {
</Card>
) : (
pendingProtests.map((protest) => {
const protester = protestsData?.driverMap[protest.protestingDriverId];
const accused = protestsData?.driverMap[protest.accusedDriverId];
const protester = stewardingData?.driverMap[protest.protestingDriverId];
const accused = stewardingData?.driverMap[protest.accusedDriverId];
const daysSinceFiled = Math.floor(
(Date.now() - new Date(protest.filedAt).getTime()) / (1000 * 60 * 60 * 24)
);
@@ -330,9 +310,9 @@ export default function RaceStewardingPage() {
</div>
<p className="text-sm text-gray-300">{protest.incident.description}</p>
</div>
{isAdmin && league && (
{isAdmin && stewardingData?.league && (
<Link
href={`/leagues/${league.id}/stewarding/protests/${protest.id}`}
href={`/leagues/${stewardingData.league.id}/stewarding/protests/${protest.id}`}
>
<Button variant="primary">Review</Button>
</Link>
@@ -359,8 +339,8 @@ export default function RaceStewardingPage() {
</Card>
) : (
resolvedProtests.map((protest) => {
const protester = protestsData?.driverMap[protest.protestingDriverId];
const accused = protestsData?.driverMap[protest.accusedDriverId];
const protester = stewardingData?.driverMap[protest.protestingDriverId];
const accused = stewardingData?.driverMap[protest.accusedDriverId];
return (
<Card key={protest.id}>
@@ -421,8 +401,8 @@ export default function RaceStewardingPage() {
</p>
</Card>
) : (
penaltiesData?.penalties.map((penalty) => {
const driver = penaltiesData?.driverMap[penalty.driverId];
stewardingData?.penalties.map((penalty) => {
const driver = stewardingData?.driverMap[penalty.driverId];
return (
<Card key={penalty.id}>
<div className="flex items-center gap-4">