Files
gridpilot.gg/apps/website/components/leagues/ProtestListItem.tsx
2026-01-15 19:55:46 +01:00

119 lines
3.6 KiB
TypeScript

import { AlertCircle, AlertTriangle, Video } from 'lucide-react';
import { Badge } from '@/ui/Badge';
import { Box } from '@/ui/Box';
import { Button } from '@/ui/Button';
import { Card } from '@/ui/Card';
import { Icon } from '@/ui/Icon';
import { Link } from '@/ui/Link';
import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text';
interface ProtestListItemProps {
protesterName: string;
protesterHref: string;
accusedName: string;
accusedHref: string;
status: string;
isUrgent: boolean;
daysOld: number;
lap: number;
filedAtLabel: string;
description: string;
proofVideoUrl?: string;
decisionNotes?: string;
isAdmin: boolean;
onReview?: () => void;
}
export function ProtestListItem({
protesterName,
protesterHref,
accusedName,
accusedHref,
status,
isUrgent,
daysOld,
lap,
filedAtLabel,
description,
proofVideoUrl,
decisionNotes,
isAdmin,
onReview,
}: ProtestListItemProps) {
const getStatusVariant = (s: string): any => {
switch (s) {
case 'pending':
case 'under_review': return 'warning';
case 'upheld': return 'danger';
case 'dismissed': return 'default';
case 'withdrawn': return 'primary';
default: return 'warning';
}
};
return (
<Card
borderLeft={isUrgent}
borderColor={isUrgent ? 'border-red-500' : 'border-charcoal-outline'}
style={isUrgent ? { borderLeftWidth: '4px' } : undefined}
>
<Stack direction="row" align="start" justify="between" gap={4}>
<Box flexGrow={1} minWidth="0">
<Stack direction="row" align="center" gap={2} mb={2} wrap>
<Icon icon={AlertCircle} size={4} color="rgb(156, 163, 175)" />
<Link href={protesterHref}>
<Text weight="medium" color="text-white">{protesterName}</Text>
</Link>
<Text size="sm" color="text-gray-500">vs</Text>
<Link href={accusedHref}>
<Text weight="medium" color="text-white">{accusedName}</Text>
</Link>
<Badge variant={getStatusVariant(status)}>
{status.replace('_', ' ')}
</Badge>
{isUrgent && (
<Badge variant="danger" icon={AlertTriangle}>
{daysOld}d old
</Badge>
)}
</Stack>
<Stack direction="row" align="center" gap={4} mb={2} wrap>
<Text size="sm" color="text-gray-400">Lap {lap}</Text>
<Text size="sm" color="text-gray-400"></Text>
<Text size="sm" color="text-gray-400">Filed {filedAtLabel}</Text>
{proofVideoUrl && (
<>
<Text size="sm" color="text-gray-400"></Text>
<Link href={proofVideoUrl} target="_blank">
<Icon icon={Video} size={3.5} mr={1.5} />
<Text size="sm">Video Evidence</Text>
</Link>
</>
)}
</Stack>
<Text size="sm" color="text-gray-300" block>{description}</Text>
{decisionNotes && (
<Box mt={4} p={3} bg="bg-charcoal-outline/30" rounded="lg" border borderColor="border-charcoal-outline/50">
<Text size="xs" color="text-gray-500" uppercase letterSpacing="0.05em" block mb={1}>Steward Decision</Text>
<Text size="sm" color="text-gray-300">{decisionNotes}</Text>
</Box>
)}
</Box>
{isAdmin && status === 'pending' && onReview && (
<Button
variant="primary"
onClick={onReview}
size="sm"
>
Review
</Button>
)}
</Stack>
</Card>
);
}