Files
gridpilot.gg/apps/website/components/leagues/NextRaceCountdownWidget.tsx
2026-01-21 17:50:02 +01:00

186 lines
5.7 KiB
TypeScript

'use client';
import { Badge } from '@/ui/Badge';
import { Button } from '@/ui/Button';
import { Icon } from '@/ui/Icon';
import { Link } from '@/ui/Link';
import { Stack } from '@/ui/Stack';
import { Surface } from '@/ui/Surface';
import { Text } from '@/ui/Text';
import { Calendar, Clock, MapPin, type LucideIcon } from 'lucide-react';
import { useEffect, useState } from 'react';
interface NextRaceCountdownWidgetProps {
raceId: string;
raceName: string;
date: string;
track?: string;
car?: string;
isRegistered?: boolean;
}
interface CountdownState {
days: number;
hours: number;
minutes: number;
seconds: number;
}
export function NextRaceCountdownWidget({
raceId,
raceName,
date,
track,
car,
isRegistered = false,
}: NextRaceCountdownWidgetProps) {
const [countdown, setCountdown] = useState<CountdownState | null>(null);
const [isExpired, setIsExpired] = useState(false);
useEffect(() => {
const calculateCountdown = () => {
const now = new Date();
const raceDate = new Date(date);
const diff = raceDate.getTime() - now.getTime();
if (diff <= 0) {
setIsExpired(true);
setCountdown({ days: 0, hours: 0, minutes: 0, seconds: 0 });
return;
}
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((diff % (1000 * 60)) / 1000);
setCountdown({ days, hours, minutes, seconds });
setIsExpired(false);
};
calculateCountdown();
const interval = setInterval(calculateCountdown, 1000);
return () => clearInterval(interval);
}, [date]);
const formatTime = (value: number) => value.toString().padStart(2, '0');
return (
<Surface
variant="muted"
rounded="xl"
border
padding={6}
style={{
position: 'relative',
overflow: 'hidden',
background: 'linear-gradient(to bottom right, #262626, rgba(38, 38, 38, 0.8))',
borderColor: 'rgba(59, 130, 246, 0.3)',
}}
>
<Stack
position="absolute"
top="0"
right="0"
w="40"
h="40"
style={{
background: 'linear-gradient(to bottom left, rgba(59, 130, 246, 0.2), transparent)',
borderBottomLeftRadius: '9999px',
}}
/>
<Stack position="relative" gap={4}>
{/* Header */}
<Stack direction="row" align="center" gap={2}>
<Badge variant="primary" style={{ textTransform: 'uppercase', letterSpacing: '0.05em' }}>
Next Race
</Badge>
{isRegistered && (
<Badge variant="success">
Registered
</Badge>
)}
</Stack>
{/* Race Info */}
<Stack gap={2}>
<Text size="xl" weight="bold" color="text-white">
{raceName}
</Text>
{track && (
<Stack direction="row" align="center" gap={1.5}>
<Icon icon={MapPin as LucideIcon} size={4} color="var(--text-gray-500)" />
<Text size="sm" color="text-gray-400">
{track}
</Text>
</Stack>
)}
{car && (
<Stack direction="row" align="center" gap={1.5}>
<Icon icon={Calendar as LucideIcon} size={4} color="var(--text-gray-500)" />
<Text size="sm" color="text-gray-400">
{car}
</Text>
</Stack>
)}
</Stack>
{/* Countdown Timer */}
<Stack gap={2}>
<Text
size="xs"
color="text-gray-500"
style={{ textTransform: 'uppercase', letterSpacing: '0.05em' }}
block
>
{isExpired ? 'Race Started' : 'Starts in'}
</Text>
{countdown && (
<Stack direction="row" gap={2} align="center">
<Stack align="center" gap={0.5}>
<Text size="2xl" weight="bold" color="text-primary-blue" font="mono">
{formatTime(countdown.days)}
</Text>
<Text size="xs" color="text-gray-500">Days</Text>
</Stack>
<Text size="2xl" weight="bold" color="text-gray-600">:</Text>
<Stack align="center" gap={0.5}>
<Text size="2xl" weight="bold" color="text-primary-blue" font="mono">
{formatTime(countdown.hours)}
</Text>
<Text size="xs" color="text-gray-500">Hours</Text>
</Stack>
<Text size="2xl" weight="bold" color="text-gray-600">:</Text>
<Stack align="center" gap={0.5}>
<Text size="2xl" weight="bold" color="text-primary-blue" font="mono">
{formatTime(countdown.minutes)}
</Text>
<Text size="xs" color="text-gray-500">Mins</Text>
</Stack>
<Text size="2xl" weight="bold" color="text-gray-600">:</Text>
<Stack align="center" gap={0.5}>
<Text size="2xl" weight="bold" color="text-primary-blue" font="mono">
{formatTime(countdown.seconds)}
</Text>
<Text size="xs" color="text-gray-500">Secs</Text>
</Stack>
</Stack>
)}
</Stack>
{/* Actions */}
<Stack direction="row" gap={3} mt={2}>
<Link href={`/races/${raceId}`} style={{ flex: 1 }}>
<Button
variant="primary"
style={{ width: '100%' }}
>
{isRegistered ? 'View Details' : 'Register'}
</Button>
</Link>
</Stack>
</Stack>
</Surface>
);
}