136 lines
5.2 KiB
TypeScript
136 lines
5.2 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect } from "react";
|
|
import { ProtestViewModel } from "@/lib/view-models/ProtestViewModel";
|
|
import { RaceViewModel } from "@/lib/view-models/RaceViewModel";
|
|
import { DriverViewModel } from "@/lib/view-models/DriverViewModel";
|
|
import { Card } from "@/ui/Card";
|
|
import { Stack } from "@/ui/Stack";
|
|
import { Text } from "@/ui/Text";
|
|
import { Heading } from "@/ui/Heading";
|
|
import { Icon } from "@/ui/Icon";
|
|
import { AlertCircle, Flag } from "lucide-react";
|
|
|
|
interface PenaltyHistoryListProps {
|
|
protests: ProtestViewModel[];
|
|
races: Record<string, RaceViewModel>;
|
|
drivers: Record<string, DriverViewModel>;
|
|
}
|
|
|
|
export function PenaltyHistoryList({
|
|
protests,
|
|
races,
|
|
drivers,
|
|
}: PenaltyHistoryListProps) {
|
|
const [filteredProtests, setFilteredProtests] = useState<ProtestViewModel[]>([]);
|
|
|
|
useEffect(() => {
|
|
setFilteredProtests(protests);
|
|
}, [protests]);
|
|
|
|
const getStatusColor = (status: string) => {
|
|
switch (status) {
|
|
case "upheld":
|
|
return { text: "text-red-400", bg: "bg-red-500/20" };
|
|
case "dismissed":
|
|
return { text: "text-gray-400", bg: "bg-gray-500/20" };
|
|
case "withdrawn":
|
|
return { text: "text-blue-400", bg: "bg-blue-500/20" };
|
|
default:
|
|
return { text: "text-orange-400", bg: "bg-orange-500/20" };
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Stack gap={4}>
|
|
{filteredProtests.length === 0 ? (
|
|
<Card py={12} className="text-center">
|
|
<Stack align="center" gap={4}>
|
|
<Icon icon={AlertCircle} size={12} color="text-gray-400" opacity={0.5} />
|
|
<Stack>
|
|
<Text weight="medium" size="lg" color="text-gray-400" block>No Resolved Protests</Text>
|
|
<Text size="sm" color="text-gray-500" mt={1} block>
|
|
No protests have been resolved in this league
|
|
</Text>
|
|
</Stack>
|
|
</Stack>
|
|
</Card>
|
|
) : (
|
|
<Stack gap={3}>
|
|
{filteredProtests.map((protest) => {
|
|
const race = races[protest.raceId];
|
|
const protester = drivers[protest.protestingDriverId];
|
|
const accused = drivers[protest.accusedDriverId];
|
|
const incident = protest.incident;
|
|
const resolvedDate = protest.reviewedAt || protest.filedAt;
|
|
const statusColors = getStatusColor(protest.status);
|
|
|
|
return (
|
|
<Card key={protest.id} p={4}>
|
|
<Stack direction="row" align="start" gap={4}>
|
|
<Stack
|
|
w="10"
|
|
h="10"
|
|
rounded="full"
|
|
align="center"
|
|
justify="center"
|
|
flexShrink={0}
|
|
bg={statusColors.bg}
|
|
color={statusColors.text}
|
|
>
|
|
<Icon icon={Flag} size={5} />
|
|
</Stack>
|
|
<Stack flex={1}>
|
|
<Stack gap={2}>
|
|
<Stack direction="row" align="start" justify="between" gap={4}>
|
|
<Stack>
|
|
<Heading level={3}>
|
|
Protest #{protest.id.substring(0, 8)}
|
|
</Heading>
|
|
<Text size="sm" color="text-gray-400" block>
|
|
{resolvedDate ? `Resolved ${new Date(resolvedDate).toLocaleDateString()}` : 'Resolved'}
|
|
</Text>
|
|
</Stack>
|
|
<Stack
|
|
px={3}
|
|
py={1}
|
|
rounded="full"
|
|
bg={statusColors.bg}
|
|
color={statusColors.text}
|
|
className="text-[12px] font-medium flex-shrink-0"
|
|
>
|
|
{protest.status.toUpperCase()}
|
|
</Stack>
|
|
</Stack>
|
|
<Stack>
|
|
<Text size="sm" color="text-gray-400" block>
|
|
<Text weight="medium" color="text-white">{protester?.name || 'Unknown'}</Text> vs <Text weight="medium" color="text-white">{accused?.name || 'Unknown'}</Text>
|
|
</Text>
|
|
{race && incident && (
|
|
<Text size="sm" color="text-gray-500" block>
|
|
{race.track} ({race.car}) - Lap {incident.lap}
|
|
</Text>
|
|
)}
|
|
</Stack>
|
|
{incident && (
|
|
<Text size="sm" color="text-gray-300" block>{incident.description}</Text>
|
|
)}
|
|
{protest.decisionNotes && (
|
|
<Stack mt={2} p={2} rounded="md" bg="bg-iron-gray/30" border borderColor="border-charcoal-outline/50">
|
|
<Text size="xs" color="text-gray-400" block>
|
|
<Text weight="medium">Steward Notes:</Text> {protest.decisionNotes}
|
|
</Text>
|
|
</Stack>
|
|
)}
|
|
</Stack>
|
|
</Stack>
|
|
</Stack>
|
|
</Card>
|
|
);
|
|
})}
|
|
</Stack>
|
|
)}
|
|
</Stack>
|
|
);
|
|
}
|