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

118 lines
4.5 KiB
TypeScript

'use client';
import React from 'react';
import { Box } from '@/ui/Box';
import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text';
import { Surface } from '@/ui/Surface';
import { Icon } from '@/ui/Icon';
import { Clock, ShieldAlert, MessageSquare } from 'lucide-react';
import { Button } from '@/ui/Button';
interface Protest {
id: string;
raceName: string;
protestingDriver: string;
accusedDriver: string;
description: string;
submittedAt: string;
status: 'pending' | 'under_review' | 'resolved' | 'rejected';
}
interface StewardingQueuePanelProps {
protests: Protest[];
onReview: (id: string) => void;
}
export function StewardingQueuePanel({ protests, onReview }: StewardingQueuePanelProps) {
return (
<Surface variant="dark" border rounded="lg" overflow="hidden">
<Box px={6} py={4} borderBottom borderColor="border-charcoal-outline" bg="bg-iron-gray/20">
<Stack direction="row" align="center" justify="between">
<Stack direction="row" align="center" gap={2}>
<Icon icon={ShieldAlert} size={4} color="text-error-red" />
<Text weight="bold" letterSpacing="wider" size="sm" display="block">
STEWARDING QUEUE
</Text>
</Stack>
<Box px={2} py={0.5} rounded="md" bg="bg-error-red/10" border borderColor="border-error-red/20">
<Text size="xs" color="text-error-red" weight="bold">
{protests.filter(p => p.status === 'pending').length} Pending
</Text>
</Box>
</Stack>
</Box>
<Stack gap={0}>
{protests.length === 0 ? (
<Box py={12} center>
<Stack align="center" gap={3}>
<Icon icon={ShieldAlert} size={8} color="text-gray-700" />
<Text color="text-gray-500">No active protests in the queue.</Text>
</Stack>
</Box>
) : (
protests.map((protest) => (
<Box key={protest.id} p={6} borderBottom borderColor="border-charcoal-outline" hoverBg="bg-white/5" transition>
<Stack direction={{ base: 'col', md: 'row' }} justify="between" align="start" gap={4}>
<Stack gap={3} flexGrow={1}>
<Stack direction="row" align="center" gap={2}>
<StatusIndicator status={protest.status} />
<Text size="xs" color="text-gray-500" weight="bold" letterSpacing="widest">
{protest.raceName.toUpperCase()}
</Text>
<Box w={1} h={1} rounded="full" bg="bg-gray-700" />
<Stack direction="row" align="center" gap={1.5}>
<Icon icon={Clock} size={3} color="text-gray-600" />
<Text size="xs" color="text-gray-500">
{new Date(protest.submittedAt).toLocaleString()}
</Text>
</Stack>
</Stack>
<Box>
<Stack direction="row" align="center" gap={2} wrap>
<Text weight="bold" color="text-white">{protest.protestingDriver}</Text>
<Text size="xs" color="text-gray-600" weight="bold">VS</Text>
<Text weight="bold" color="text-white">{protest.accusedDriver}</Text>
</Stack>
<Text size="sm" color="text-gray-400" mt={2} lineClamp={2}>
&ldquo;{protest.description}&rdquo;
</Text>
</Box>
</Stack>
<Stack direction="row" align="center" gap={3} w={{ base: 'full', md: 'auto' }}>
<Button
variant="secondary"
size="sm"
onClick={() => onReview(protest.id)}
>
<Stack direction="row" align="center" gap={2}>
<Icon icon={MessageSquare} size={3.5} />
<Text>Review</Text>
</Stack>
</Button>
</Stack>
</Stack>
</Box>
))
)}
</Stack>
</Surface>
);
}
function StatusIndicator({ status }: { status: Protest['status'] }) {
const colors = {
pending: 'bg-warning-amber',
under_review: 'bg-primary-blue',
resolved: 'bg-performance-green',
rejected: 'bg-gray-500',
};
return (
<Box w={2} h={2} rounded="full" bg={colors[status]} animate={status === 'under_review' ? 'pulse' : 'none'} />
);
}