website refactor

This commit is contained in:
2026-01-15 17:12:24 +01:00
parent c3b308e960
commit f035cfe7ce
468 changed files with 24378 additions and 17324 deletions

View File

@@ -5,8 +5,12 @@ 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 { Button } from "@/ui/Button";
import { Clock, Grid3x3, TrendingDown, AlertCircle, Filter, Flag } from "lucide-react";
import { Box } from "@/ui/Box";
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[];
@@ -20,7 +24,6 @@ export function PenaltyHistoryList({
drivers,
}: PenaltyHistoryListProps) {
const [filteredProtests, setFilteredProtests] = useState<ProtestViewModel[]>([]);
const [filterType, setFilterType] = useState<"all">("all");
useEffect(() => {
setFilteredProtests(protests);
@@ -29,86 +32,108 @@ export function PenaltyHistoryList({
const getStatusColor = (status: string) => {
switch (status) {
case "upheld":
return "text-red-400 bg-red-500/20";
return { text: "text-red-400", bg: "bg-red-500/20" };
case "dismissed":
return "text-gray-400 bg-gray-500/20";
return { text: "text-gray-400", bg: "bg-gray-500/20" };
case "withdrawn":
return "text-blue-400 bg-blue-500/20";
return { text: "text-blue-400", bg: "bg-blue-500/20" };
default:
return "text-orange-400 bg-orange-500/20";
return { text: "text-orange-400", bg: "bg-orange-500/20" };
}
};
return (
<div className="space-y-4">
<Stack gap={4}>
{filteredProtests.length === 0 ? (
<Card className="p-12 text-center">
<div className="flex flex-col items-center gap-4 text-gray-400">
<AlertCircle className="h-12 w-12 opacity-50" />
<div>
<p className="font-medium text-lg">No Resolved Protests</p>
<p className="text-sm mt-1">
<Card py={12} textAlign="center">
<Stack alignItems="center" gap={4}>
<Icon icon={AlertCircle} size={12} color="text-gray-400" opacity={0.5} />
<Box>
<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
</p>
</div>
</div>
</Text>
</Box>
</Stack>
</Card>
) : (
<div className="space-y-3">
<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} className="p-4">
<div className="flex items-start gap-4">
<div className={`h-10 w-10 rounded-full flex items-center justify-center flex-shrink-0 ${getStatusColor(protest.status)}`}>
<Flag className="h-5 w-5" />
</div>
<div className="flex-1 space-y-2">
<div className="flex items-start justify-between gap-4">
<div>
<h3 className="font-semibold text-white">
Protest #{protest.id.substring(0, 8)}
</h3>
<p className="text-sm text-gray-400">
{resolvedDate ? `Resolved ${new Date(resolvedDate).toLocaleDateString()}` : 'Resolved'}
</p>
</div>
<span className={`px-3 py-1 rounded-full text-xs font-medium flex-shrink-0 ${getStatusColor(protest.status)}`}>
{protest.status.toUpperCase()}
</span>
</div>
<div className="space-y-1 text-sm">
<p className="text-gray-400">
<span className="font-medium">{protester?.name || 'Unknown'}</span> vs <span className="font-medium">{accused?.name || 'Unknown'}</span>
</p>
{race && incident && (
<p className="text-gray-500">
{race.track} ({race.car}) - Lap {incident.lap}
</p>
<Card key={protest.id} p={4}>
<Box display="flex" alignItems="start" gap={4}>
<Box
w="10"
h="10"
rounded="full"
display="flex"
alignItems="center"
justifyContent="center"
flexShrink={0}
bg={statusColors.bg}
color={statusColors.text}
>
<Icon icon={Flag} size={5} />
</Box>
<Box flex={1}>
<Stack gap={2}>
<Box display="flex" alignItems="start" justifyContent="between" gap={4}>
<Box>
<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>
</Box>
<Box
px={3}
py={1}
rounded="full"
bg={statusColors.bg}
color={statusColors.text}
fontSize="12px"
weight="medium"
flexShrink={0}
>
{protest.status.toUpperCase()}
</Box>
</Box>
<Box>
<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>
)}
</Box>
{incident && (
<Text size="sm" color="text-gray-300" block>{incident.description}</Text>
)}
</div>
{incident && (
<p className="text-gray-300 text-sm">{incident.description}</p>
)}
{protest.decisionNotes && (
<div className="mt-2 p-2 rounded bg-iron-gray/30 border border-charcoal-outline/50">
<p className="text-xs text-gray-400">
<span className="font-medium">Steward Notes:</span> {protest.decisionNotes}
</p>
</div>
)}
</div>
</div>
{protest.decisionNotes && (
<Box 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>
</Box>
)}
</Stack>
</Box>
</Box>
</Card>
);
})}
</div>
</Stack>
)}
</div>
</Stack>
);
}