website refactor

This commit is contained in:
2026-01-18 16:18:18 +01:00
parent 0b301feb61
commit 13567d51af
329 changed files with 4701 additions and 4750 deletions

View File

@@ -7,13 +7,13 @@ import { ProtestViewModel } from "../../lib/view-models/ProtestViewModel";
import { Modal } from "@/ui/Modal";
import { Button } from "@/ui/Button";
import { Card } from "@/ui/Card";
import { Box } from "@/ui/Box";
import { Text } from "@/ui/Text";
import { Stack } from "@/ui/Stack";
import { Text } from "@/ui/Text";
import { Heading } from "@/ui/Heading";
import { Icon } from "@/ui/Icon";
import { TextArea } from "@/ui/TextArea";
import { Input } from "@/ui/Input";
import { Grid } from "@/ui/Grid";
import {
AlertCircle,
Video,
@@ -210,39 +210,39 @@ export function ReviewProtestModal({
return (
<Modal title="Confirm Decision" isOpen={true} onOpenChange={() => setShowConfirmation(false)}>
<Stack gap={6} p={6}>
<Box textAlign="center">
<Stack align="center">
<Stack gap={4}>
{decision === "accept" ? (
<Box display="flex" justifyContent="center">
<Box h="16" w="16" rounded="full" bg="bg-orange-500/20" display="flex" alignItems="center" justifyContent="center">
<Stack direction="row" justify="center">
<Stack h="16" w="16" rounded="full" bg="bg-orange-500/20" align="center" justify="center">
<Icon icon={AlertCircle} size={8} color="text-orange-400" />
</Box>
</Box>
</Stack>
</Stack>
) : (
<Box display="flex" justifyContent="center">
<Box h="16" w="16" rounded="full" bg="bg-gray-500/20" display="flex" alignItems="center" justifyContent="center">
<Stack direction="row" justify="center">
<Stack h="16" w="16" rounded="full" bg="bg-gray-500/20" align="center" justify="center">
<Icon icon={XCircle} size={8} color="text-gray-400" />
</Box>
</Box>
</Stack>
</Stack>
)}
<Box>
<Heading level={3} fontSize="xl" weight="bold" color="text-white">Confirm Decision</Heading>
<Text color="text-gray-400" mt={2} block>
<Stack align="center">
<Heading level={3} weight="bold" color="text-white">Confirm Decision</Heading>
<Text color="text-gray-400" mt={2} block textAlign="center">
{decision === "accept"
? (selectedPenalty?.requiresValue
? `Issue ${penaltyValue} ${selectedPenalty.valueLabel} penalty?`
: `Issue ${selectedPenalty?.name ?? penaltyType} penalty?`)
: "Reject this protest?"}
</Text>
</Box>
</Stack>
</Stack>
</Box>
</Stack>
<Card p={4} bg="bg-gray-800/50">
<Card p={4} className="bg-gray-800/50">
<Text size="sm" color="text-gray-300" block>{stewardNotes}</Text>
</Card>
<Box display="flex" gap={3}>
<Stack direction="row" gap={3}>
<Button
variant="secondary"
fullWidth
@@ -259,7 +259,7 @@ export function ReviewProtestModal({
>
{submitting ? "Submitting..." : "Confirm Decision"}
</Button>
</Box>
</Stack>
</Stack>
</Modal>
);
@@ -268,94 +268,95 @@ export function ReviewProtestModal({
return (
<Modal title="Review Protest" isOpen={true} onOpenChange={onClose}>
<Stack gap={6} p={6}>
<Box display="flex" alignItems="start" gap={4}>
<Box h="12" w="12" rounded="full" bg="bg-orange-500/20" display="flex" alignItems="center" justifyContent="center" flexShrink={0}>
<Stack direction="row" align="start" gap={4}>
<Stack h="12" w="12" rounded="full" bg="bg-orange-500/20" align="center" justify="center" flexShrink={0}>
<Icon icon={AlertCircle} size={6} color="text-orange-400" />
</Box>
<Box flexGrow={1}>
<Heading level={2} fontSize="2xl" weight="bold" color="text-white">Review Protest</Heading>
</Stack>
<Stack flexGrow={1}>
<Heading level={2} weight="bold" color="text-white">Review Protest</Heading>
<Text color="text-gray-400" mt={1} block>
Protest #{protest.id.substring(0, 8)}
</Text>
</Box>
</Box>
</Stack>
</Stack>
<Stack gap={4}>
<Card p={4} bg="bg-gray-800/50">
<Card p={4} className="bg-gray-800/50">
<Stack gap={3}>
<Box display="flex" alignItems="center" justifyContent="between">
<Stack direction="row" align="center" justify="between">
<Text size="sm" color="text-gray-400">Filed Date</Text>
<Text size="sm" color="text-white" weight="medium">
{new Date(protest.filedAt || protest.submittedAt).toLocaleString()}
</Text>
</Box>
<Box display="flex" alignItems="center" justifyContent="between">
</Stack>
<Stack direction="row" align="center" justify="between">
<Text size="sm" color="text-gray-400">Incident Lap</Text>
<Text size="sm" color="text-white" weight="medium">
Lap {protest.incident?.lap || 'N/A'}
</Text>
</Box>
<Box display="flex" alignItems="center" justifyContent="between">
</Stack>
<Stack direction="row" align="center" justify="between">
<Text size="sm" color="text-gray-400">Status</Text>
<Box as="span" px={2} py={1} rounded="sm" bg="bg-orange-500/20">
<Stack as="span" px={2} py={1} rounded="sm" bg="bg-orange-500/20">
<Text size="xs" weight="medium" color="text-orange-400">
{protest.status}
</Text>
</Box>
</Box>
</Stack>
</Stack>
</Stack>
</Card>
<Box>
<Stack>
<Text as="label" size="sm" weight="medium" color="text-gray-400" block mb={2}>
Description
</Text>
<Card p={4} bg="bg-gray-800/50">
<Card p={4} className="bg-gray-800/50">
<Text color="text-gray-300" block>{protest.incident?.description || protest.description}</Text>
</Card>
</Box>
</Stack>
{protest.comment && (
<Box>
<Stack>
<Text as="label" size="sm" weight="medium" color="text-gray-400" block mb={2}>
Additional Comment
</Text>
<Card p={4} bg="bg-gray-800/50">
<Card p={4} className="bg-gray-800/50">
<Text color="text-gray-300" block>{protest.comment}</Text>
</Card>
</Box>
</Stack>
)}
{protest.proofVideoUrl && (
<Box>
<Stack>
<Text as="label" size="sm" weight="medium" color="text-gray-400" block mb={2}>
Evidence
</Text>
<Card p={4} bg="bg-gray-800/50">
<Box
<Card p={4} className="bg-gray-800/50">
<Stack
as="a"
href={protest.proofVideoUrl}
target="_blank"
rel="noopener noreferrer"
display="flex"
alignItems="center"
{...({
href: protest.proofVideoUrl,
target: "_blank",
rel: "noopener noreferrer"
} as any)}
direction="row"
align="center"
gap={2}
color="text-orange-400"
hoverTextColor="text-orange-300"
transition
className="transition-all hover:text-orange-300"
>
<Icon icon={Video} size={4} />
<Text size="sm">View video evidence</Text>
</Box>
</Stack>
</Card>
</Box>
</Stack>
)}
</Stack>
<Box borderTop borderColor="border-gray-800" pt={6}>
<Stack borderTop borderColor="border-gray-800" pt={6}>
<Stack gap={4}>
<Heading level={3} fontSize="lg" weight="semibold" color="text-white">Stewarding Decision</Heading>
<Heading level={3} weight="semibold" color="text-white">Stewarding Decision</Heading>
<Box display="grid" gridCols={2} gap={3}>
<Grid cols={2} gap={3}>
<Button
variant={decision === "accept" ? "primary" : "secondary"}
fullWidth
@@ -376,11 +377,11 @@ export function ReviewProtestModal({
Reject Protest
</Stack>
</Button>
</Box>
</Grid>
{decision === "accept" && (
<Stack gap={4}>
<Box>
<Stack>
<Text as="label" size="sm" weight="medium" color="text-gray-400" block mb={2}>
Penalty Type
</Text>
@@ -388,11 +389,11 @@ export function ReviewProtestModal({
{penaltyTypesLoading ? (
<Text size="sm" color="text-gray-500">Loading penalty types</Text>
) : (
<Box display="grid" gridCols={3} gap={2}>
<Grid cols={3} gap={2}>
{penaltyOptions.map(({ type, name, Icon: PenaltyIcon, colorClass, defaultValue }: { type: string; name: string; Icon: LucideIcon; colorClass: string; defaultValue: number }) => {
const isSelected = penaltyType === type;
return (
<Box
<Stack
key={type}
as="button"
onClick={() => {
@@ -402,25 +403,20 @@ export function ReviewProtestModal({
p={3}
rounded="lg"
border
borderWidth={isSelected ? "2px" : "1px"}
transition
borderColor={isSelected ? undefined : "border-charcoal-outline"}
bg={isSelected ? undefined : "bg-iron-gray/50"}
hoverBorderColor={!isSelected ? "border-gray-600" : undefined}
// eslint-disable-next-line gridpilot-rules/component-classification
className={isSelected ? colorClass : ""}
{...({ borderWidth: isSelected ? "2px" : "1px" } as any)}
className={`transition-all ${isSelected ? colorClass : "bg-iron-gray/50 border-charcoal-outline hover:border-gray-600"}`}
>
<Icon icon={PenaltyIcon} size={5} mx="auto" mb={1} color={isSelected ? undefined : "text-gray-400"} />
<Icon icon={PenaltyIcon} size={5} className="mx-auto mb-1" color={isSelected ? undefined : "text-gray-400"} />
<Text size="xs" weight="medium" color={isSelected ? undefined : "text-gray-400"} block textAlign="center">{name}</Text>
</Box>
</Stack>
);
})}
</Box>
</Grid>
)}
</Box>
</Stack>
{selectedPenalty?.requiresValue && (
<Box>
<Stack>
<Text as="label" size="sm" weight="medium" color="text-gray-400" block mb={2}>
Penalty Value ({selectedPenalty.valueLabel})
</Text>
@@ -430,12 +426,12 @@ export function ReviewProtestModal({
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setPenaltyValue(Number(e.target.value))}
min={1}
/>
</Box>
</Stack>
)}
</Stack>
)}
<Box>
<Stack>
<Text as="label" size="sm" weight="medium" color="text-gray-400" block mb={2}>
Steward Notes *
</Text>
@@ -445,11 +441,11 @@ export function ReviewProtestModal({
placeholder="Explain your decision and reasoning..."
rows={4}
/>
</Box>
</Stack>
</Stack>
</Box>
</Stack>
<Box display="flex" gap={3} pt={4} borderTop borderColor="border-gray-800">
<Stack direction="row" gap={3} pt={4} borderTop borderColor="border-gray-800">
<Button
variant="secondary"
fullWidth
@@ -466,7 +462,7 @@ export function ReviewProtestModal({
>
Submit Decision
</Button>
</Box>
</Stack>
</Stack>
</Modal>
);