website refactor
This commit is contained in:
118
apps/website/ui/ProtestListItem.tsx
Normal file
118
apps/website/ui/ProtestListItem.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
|
||||
|
||||
import { AlertCircle, AlertTriangle, Video } from 'lucide-react';
|
||||
import { Badge } from './Badge';
|
||||
import { Box } from './Box';
|
||||
import { Button } from './Button';
|
||||
import { Card } from './Card';
|
||||
import { Icon } from './Icon';
|
||||
import { Link } from './Link';
|
||||
import { Stack } from './Stack';
|
||||
import { Text } from './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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user