website refactor
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
import { PageWrapper } from '@/ui/PageWrapper';
|
import { PageWrapper } from '@/components/shared/state/PageWrapper';
|
||||||
import { RaceStewardingTemplate, type StewardingTab } from '@/templates/RaceStewardingTemplate';
|
import { RaceStewardingTemplate, type StewardingTab } from '@/templates/RaceStewardingTemplate';
|
||||||
import { RaceStewardingPageQuery } from '@/lib/page-queries/races/RaceStewardingPageQuery';
|
import { RaceStewardingPageQuery } from '@/lib/page-queries/races/RaceStewardingPageQuery';
|
||||||
import { type RaceStewardingViewData } from '@/lib/view-data/races/RaceStewardingViewData';
|
import { type RaceStewardingViewData } from '@/lib/view-data/races/RaceStewardingViewData';
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
unpublishScheduleAction,
|
unpublishScheduleAction,
|
||||||
updateRaceAction
|
updateRaceAction
|
||||||
} from '@/app/actions/leagueScheduleActions';
|
} from '@/app/actions/leagueScheduleActions';
|
||||||
import { PageWrapper } from '@/ui/PageWrapper';
|
import { PageWrapper } from '@/components/shared/state/PageWrapper';
|
||||||
import { ConfirmDialog } from '@/ui/ConfirmDialog';
|
import { ConfirmDialog } from '@/ui/ConfirmDialog';
|
||||||
import {
|
import {
|
||||||
useLeagueAdminSchedule,
|
useLeagueAdminSchedule,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { UploadDropzone } from '@/components/media/UploadDropzone';
|
import { UploadDropzone } from '@/ui/UploadDropzone';
|
||||||
import { routes } from '@/lib/routing/RouteConfig';
|
import { routes } from '@/lib/routing/RouteConfig';
|
||||||
import { Box } from '@/ui/Box';
|
import { Box } from '@/ui/Box';
|
||||||
import { Button } from '@/ui/Button';
|
import { Button } from '@/ui/Button';
|
||||||
@@ -78,7 +78,9 @@ export function ProfileLiveryUploadPageClient() {
|
|||||||
{previewUrl ? (
|
{previewUrl ? (
|
||||||
<Box display="flex" flexDirection="col" gap={6}>
|
<Box display="flex" flexDirection="col" gap={6}>
|
||||||
<MediaPreviewCard
|
<MediaPreviewCard
|
||||||
|
type="image"
|
||||||
src={previewUrl}
|
src={previewUrl}
|
||||||
|
alt={selectedFile?.name || 'Livery preview'}
|
||||||
title={selectedFile?.name}
|
title={selectedFile?.name}
|
||||||
subtitle="Preview"
|
subtitle="Preview"
|
||||||
aspectRatio="16/9"
|
aspectRatio="16/9"
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import { useEffect, useMemo, useState } from 'react';
|
|||||||
|
|
||||||
// Shared state components
|
// Shared state components
|
||||||
import { LoadingWrapper } from '@/ui/LoadingWrapper';
|
import { LoadingWrapper } from '@/ui/LoadingWrapper';
|
||||||
import { StateContainer } from '@/ui/StateContainer';
|
import { StateContainer } from '@/components/shared/state/StateContainer';
|
||||||
import { useLeagueAdminStatus } from "@/hooks/league/useLeagueAdminStatus";
|
import { useLeagueAdminStatus } from "@/hooks/league/useLeagueAdminStatus";
|
||||||
import { useProtestDetail } from "@/hooks/league/useProtestDetail";
|
import { useProtestDetail } from "@/hooks/league/useProtestDetail";
|
||||||
import { routes } from '@/lib/routing/RouteConfig';
|
import { routes } from '@/lib/routing/RouteConfig';
|
||||||
@@ -43,10 +43,15 @@ import { Heading } from '@/ui/Heading';
|
|||||||
import { Icon as UIIcon } from '@/ui/Icon';
|
import { Icon as UIIcon } from '@/ui/Icon';
|
||||||
import { Link as UILink } from '@/ui/Link';
|
import { Link as UILink } from '@/ui/Link';
|
||||||
import { Grid } from '@/ui/Grid';
|
import { Grid } from '@/ui/Grid';
|
||||||
import { GridItem } from '@/ui/GridItem';
|
|
||||||
import { Stack } from '@/ui/Stack';
|
import { Stack } from '@/ui/Stack';
|
||||||
import { Text } from '@/ui/Text';
|
import { Text } from '@/ui/Text';
|
||||||
|
|
||||||
|
const GridItem = ({ children, colSpan, lgSpan }: { children: React.ReactNode; colSpan?: number; lgSpan?: number }) => (
|
||||||
|
<Box gridCols={colSpan} style={{ gridColumn: `span ${colSpan} / span ${colSpan}` }} className={lgSpan ? `lg:col-span-${lgSpan}` : ''}>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
|
||||||
type PenaltyUiConfig = {
|
type PenaltyUiConfig = {
|
||||||
label: string;
|
label: string;
|
||||||
description: string;
|
description: string;
|
||||||
@@ -377,7 +382,7 @@ export function ProtestDetailPageClient({ initialViewData }: { initialViewData:
|
|||||||
<Box w={10} h={10} rounded="full" bg="bg-blue-500/20" display="flex" alignItems="center" justifyContent="center" flexShrink={0}>
|
<Box w={10} h={10} rounded="full" bg="bg-blue-500/20" display="flex" alignItems="center" justifyContent="center" flexShrink={0}>
|
||||||
<UIIcon icon={User} size={5} color="text-blue-400" />
|
<UIIcon icon={User} size={5} color="text-blue-400" />
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexGrow={1} minWidth={0}>
|
<Box flexGrow={1} minWidth="0">
|
||||||
<Text size="xs" color="text-blue-400" weight="medium" block>Protesting</Text>
|
<Text size="xs" color="text-blue-400" weight="medium" block>Protesting</Text>
|
||||||
<Text size="sm" weight="semibold" color="text-white" truncate block>{protestingDriver?.name || 'Unknown'}</Text>
|
<Text size="sm" weight="semibold" color="text-white" truncate block>{protestingDriver?.name || 'Unknown'}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -391,7 +396,7 @@ export function ProtestDetailPageClient({ initialViewData }: { initialViewData:
|
|||||||
<Box w={10} h={10} rounded="full" bg="bg-orange-500/20" display="flex" alignItems="center" justifyContent="center" flexShrink={0}>
|
<Box w={10} h={10} rounded="full" bg="bg-orange-500/20" display="flex" alignItems="center" justifyContent="center" flexShrink={0}>
|
||||||
<UIIcon icon={User} size={5} color="text-orange-400" />
|
<UIIcon icon={User} size={5} color="text-orange-400" />
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexGrow={1} minWidth={0}>
|
<Box flexGrow={1} minWidth="0">
|
||||||
<Text size="xs" color="text-orange-400" weight="medium" block>Accused</Text>
|
<Text size="xs" color="text-orange-400" weight="medium" block>Accused</Text>
|
||||||
<Text size="sm" weight="semibold" color="text-white" truncate block>{accusedDriver?.name || 'Unknown'}</Text>
|
<Text size="sm" weight="semibold" color="text-white" truncate block>{accusedDriver?.name || 'Unknown'}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -407,18 +412,19 @@ export function ProtestDetailPageClient({ initialViewData }: { initialViewData:
|
|||||||
<Box p={4}>
|
<Box p={4}>
|
||||||
<Heading level={3} fontSize="xs" weight="semibold" color="text-gray-500" mb={3}>Race Details</Heading>
|
<Heading level={3} fontSize="xs" weight="semibold" color="text-gray-500" mb={3}>Race Details</Heading>
|
||||||
|
|
||||||
<UILink
|
<Box marginBottom={3}>
|
||||||
href={routes.race.detail(race?.id || '')}
|
<UILink
|
||||||
block
|
href={routes.race.detail(race?.id || '')}
|
||||||
mb={3}
|
block
|
||||||
>
|
>
|
||||||
<Box p={3} rounded="lg" bg="bg-deep-graphite" border borderColor="border-charcoal-outline" hoverBorderColor="border-primary-blue/50" hoverBg="bg-primary-blue/5" transition>
|
<Box p={3} rounded="lg" bg="bg-deep-graphite" border borderColor="border-charcoal-outline" hoverBorderColor="border-primary-blue/50" hoverBg="bg-primary-blue/5" transition>
|
||||||
<Box display="flex" alignItems="center" justifyContent="between">
|
<Box display="flex" alignItems="center" justifyContent="between">
|
||||||
<Text size="sm" weight="medium" color="text-white">{race?.name || 'Unknown Race'}</Text>
|
<Text size="sm" weight="medium" color="text-white">{race?.name || 'Unknown Race'}</Text>
|
||||||
<UIIcon icon={ExternalLink} size={3} color="text-gray-500" />
|
<UIIcon icon={ExternalLink} size={3} color="text-gray-500" />
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</UILink>
|
||||||
</UILink>
|
</Box>
|
||||||
|
|
||||||
<Stack gap={2}>
|
<Stack gap={2}>
|
||||||
<Box display="flex" alignItems="center" gap={2}>
|
<Box display="flex" alignItems="center" gap={2}>
|
||||||
@@ -500,7 +506,7 @@ export function ProtestDetailPageClient({ initialViewData }: { initialViewData:
|
|||||||
<Box w={10} h={10} rounded="full" bg="bg-blue-500/20" display="flex" alignItems="center" justifyContent="center" flexShrink={0}>
|
<Box w={10} h={10} rounded="full" bg="bg-blue-500/20" display="flex" alignItems="center" justifyContent="center" flexShrink={0}>
|
||||||
<UIIcon icon={AlertCircle} size={5} color="text-blue-400" />
|
<UIIcon icon={AlertCircle} size={5} color="text-blue-400" />
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexGrow={1} minWidth={0}>
|
<Box flexGrow={1} minWidth="0">
|
||||||
<Box display="flex" alignItems="center" gap={2} mb={1}>
|
<Box display="flex" alignItems="center" gap={2} mb={1}>
|
||||||
<Text weight="semibold" color="text-white" size="sm">{protestingDriver?.name || 'Unknown'}</Text>
|
<Text weight="semibold" color="text-white" size="sm">{protestingDriver?.name || 'Unknown'}</Text>
|
||||||
<Text size="xs" color="text-blue-400" weight="medium">filed protest</Text>
|
<Text size="xs" color="text-blue-400" weight="medium">filed protest</Text>
|
||||||
@@ -544,7 +550,7 @@ export function ProtestDetailPageClient({ initialViewData }: { initialViewData:
|
|||||||
<Box w={10} h={10} rounded="full" display="flex" alignItems="center" justifyContent="center" flexShrink={0} bg={protest.status === 'upheld' ? 'bg-red-500/20' : 'bg-gray-500/20'}>
|
<Box w={10} h={10} rounded="full" display="flex" alignItems="center" justifyContent="center" flexShrink={0} bg={protest.status === 'upheld' ? 'bg-red-500/20' : 'bg-gray-500/20'}>
|
||||||
<UIIcon icon={Gavel} size={5} color={protest.status === 'upheld' ? 'text-red-400' : 'text-gray-400'} />
|
<UIIcon icon={Gavel} size={5} color={protest.status === 'upheld' ? 'text-red-400' : 'text-gray-400'} />
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexGrow={1} minWidth={0}>
|
<Box flexGrow={1} minWidth="0">
|
||||||
<Box display="flex" alignItems="center" gap={2} mb={1}>
|
<Box display="flex" alignItems="center" gap={2} mb={1}>
|
||||||
<Text weight="semibold" color="text-white" size="sm">Steward Decision</Text>
|
<Text weight="semibold" color="text-white" size="sm">Steward Decision</Text>
|
||||||
<Text size="xs" weight="medium" color={protest.status === 'upheld' ? 'text-red-400' : 'text-gray-400'}>
|
<Text size="xs" weight="medium" color={protest.status === 'upheld' ? 'text-red-400' : 'text-gray-400'}>
|
||||||
@@ -575,21 +581,21 @@ export function ProtestDetailPageClient({ initialViewData }: { initialViewData:
|
|||||||
<UIIcon icon={User} size={5} color="text-gray-500" />
|
<UIIcon icon={User} size={5} color="text-gray-500" />
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexGrow={1}>
|
<Box flexGrow={1}>
|
||||||
<Box as="textarea"
|
<Box as="textarea"
|
||||||
value={newComment}
|
value={newComment}
|
||||||
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setNewComment(e.target.value)}
|
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setNewComment(e.target.value)}
|
||||||
placeholder="Add a comment or request more information..."
|
placeholder="Add a comment or request more information..."
|
||||||
rows={2}
|
style={{ height: '4rem' }}
|
||||||
w="full"
|
w="full"
|
||||||
px={4}
|
px={4}
|
||||||
py={3}
|
py={3}
|
||||||
bg="bg-deep-graphite"
|
bg="bg-deep-graphite"
|
||||||
border
|
border
|
||||||
borderColor="border-charcoal-outline"
|
borderColor="border-charcoal-outline"
|
||||||
rounded="lg"
|
rounded="lg"
|
||||||
color="text-white"
|
color="text-white"
|
||||||
fontSize="sm"
|
fontSize="sm"
|
||||||
/>
|
/>
|
||||||
<Box display="flex" justifyContent="end" mt={2}>
|
<Box display="flex" justifyContent="end" mt={2}>
|
||||||
<Button variant="secondary" disabled={!newComment.trim()}>
|
<Button variant="secondary" disabled={!newComment.trim()}>
|
||||||
<UIIcon icon={Send} size={3} mr={1} />
|
<UIIcon icon={Send} size={3} mr={1} />
|
||||||
@@ -651,32 +657,30 @@ export function ProtestDetailPageClient({ initialViewData }: { initialViewData:
|
|||||||
|
|
||||||
{/* Decision Selection */}
|
{/* Decision Selection */}
|
||||||
<Grid cols={2} gap={2} mb={4}>
|
<Grid cols={2} gap={2} mb={4}>
|
||||||
<Button
|
<Box padding={3} border borderColor={decision === 'uphold' ? 'border-racing-red' : 'border-charcoal-outline'} bg={decision === 'uphold' ? 'bg-racing-red/10' : 'transparent'} rounded="lg">
|
||||||
variant="ghost"
|
<Button
|
||||||
onClick={() => setDecision('uphold')}
|
variant="ghost"
|
||||||
p={3}
|
onClick={() => setDecision('uphold')}
|
||||||
border
|
fullWidth
|
||||||
borderColor={decision === 'uphold' ? 'border-racing-red' : 'border-charcoal-outline'}
|
>
|
||||||
bg={decision === 'uphold' ? 'bg-racing-red/10' : 'transparent'}
|
<Stack align="center" gap={1}>
|
||||||
>
|
<UIIcon icon={CheckCircle} size={5} color={decision === 'uphold' ? 'text-red-400' : 'text-gray-500'} />
|
||||||
<Stack align="center" gap={1}>
|
<Text size="xs" weight="medium" color={decision === 'uphold' ? 'text-red-400' : 'text-gray-400'}>Uphold</Text>
|
||||||
<UIIcon icon={CheckCircle} size={5} color={decision === 'uphold' ? 'text-red-400' : 'text-gray-500'} />
|
</Stack>
|
||||||
<Text size="xs" weight="medium" color={decision === 'uphold' ? 'text-red-400' : 'text-gray-400'}>Uphold</Text>
|
</Button>
|
||||||
</Stack>
|
</Box>
|
||||||
</Button>
|
<Box padding={3} border borderColor={decision === 'dismiss' ? 'border-gray-500' : 'border-charcoal-outline'} bg={decision === 'dismiss' ? 'bg-gray-500/10' : 'transparent'} rounded="lg">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={() => setDecision('dismiss')}
|
onClick={() => setDecision('dismiss')}
|
||||||
p={3}
|
fullWidth
|
||||||
border
|
>
|
||||||
borderColor={decision === 'dismiss' ? 'border-gray-500' : 'border-charcoal-outline'}
|
<Stack align="center" gap={1}>
|
||||||
bg={decision === 'dismiss' ? 'bg-gray-500/10' : 'transparent'}
|
<UIIcon icon={XCircle} size={5} color={decision === 'dismiss' ? 'text-gray-300' : 'text-gray-500'} />
|
||||||
>
|
<Text size="xs" weight="medium" color={decision === 'dismiss' ? 'text-gray-300' : 'text-gray-400'}>Dismiss</Text>
|
||||||
<Stack align="center" gap={1}>
|
</Stack>
|
||||||
<UIIcon icon={XCircle} size={5} color={decision === 'dismiss' ? 'text-gray-300' : 'text-gray-500'} />
|
</Button>
|
||||||
<Text size="xs" weight="medium" color={decision === 'dismiss' ? 'text-gray-300' : 'text-gray-400'}>Dismiss</Text>
|
</Box>
|
||||||
</Stack>
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{/* Penalty Selection (if upholding) */}
|
{/* Penalty Selection (if upholding) */}
|
||||||
@@ -696,27 +700,24 @@ export function ProtestDetailPageClient({ initialViewData }: { initialViewData:
|
|||||||
const Icon = penalty.icon;
|
const Icon = penalty.icon;
|
||||||
const isSelected = penaltyType === penalty.type;
|
const isSelected = penaltyType === penalty.type;
|
||||||
return (
|
return (
|
||||||
<Button
|
<Box key={penalty.type} padding={2} border borderColor={isSelected ? undefined : 'border-charcoal-outline'} bg={isSelected ? undefined : 'bg-iron-gray/30'} rounded="lg">
|
||||||
key={penalty.type}
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPenaltyType(penalty.type);
|
setPenaltyType(penalty.type);
|
||||||
setPenaltyValue(penalty.defaultValue);
|
setPenaltyValue(penalty.defaultValue);
|
||||||
}}
|
}}
|
||||||
p={2}
|
fullWidth
|
||||||
border
|
title={penalty.description}
|
||||||
borderColor={isSelected ? undefined : 'border-charcoal-outline'}
|
>
|
||||||
bg={isSelected ? undefined : 'bg-iron-gray/30'}
|
<Stack align="start" gap={0.5}>
|
||||||
color={isSelected ? penalty.color : undefined}
|
<UIIcon icon={Icon} size={3.5} color={isSelected ? penalty.color : 'text-gray-500'} />
|
||||||
title={penalty.description}
|
<Text size="xs" weight="medium" fontSize="10px" color={isSelected ? penalty.color : 'text-gray-500'}>
|
||||||
>
|
{penalty.label}
|
||||||
<Stack align="start" gap={0.5}>
|
</Text>
|
||||||
<UIIcon icon={Icon} size={3.5} color={isSelected ? undefined : 'text-gray-500'} />
|
</Stack>
|
||||||
<Text size="xs" weight="medium" fontSize="10px" color={isSelected ? undefined : 'text-gray-500'}>
|
</Button>
|
||||||
{penalty.label}
|
</Box>
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
</Button>
|
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -755,7 +756,7 @@ export function ProtestDetailPageClient({ initialViewData }: { initialViewData:
|
|||||||
value={stewardNotes}
|
value={stewardNotes}
|
||||||
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setStewardNotes(e.target.value)}
|
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setStewardNotes(e.target.value)}
|
||||||
placeholder="Explain your decision..."
|
placeholder="Explain your decision..."
|
||||||
rows={4}
|
style={{ height: '8rem' }}
|
||||||
w="full"
|
w="full"
|
||||||
px={3}
|
px={3}
|
||||||
py={2}
|
py={2}
|
||||||
|
|||||||
Reference in New Issue
Block a user