Files
gridpilot.gg/apps/website/components/races/RaceResultHero.tsx
2026-01-18 23:24:30 +01:00

158 lines
5.4 KiB
TypeScript

import { DecorativeBlur } from '@/ui/DecorativeBlur';
import { Icon } from '@/ui/Icon';
import { Stack } from '@/ui/Stack';
import { Surface } from '@/ui/Surface';
import { Text } from '@/ui/Text';
import { Trophy } from 'lucide-react';
interface RaceResultHeroProps {
position: number;
startPosition: number;
positionChange: number;
incidents: number;
isClean: boolean;
isPodium: boolean;
ratingChange?: number;
animatedRatingChange: number;
}
export function RaceResultHero({
position,
startPosition,
positionChange,
incidents,
isClean,
isPodium,
ratingChange,
animatedRatingChange,
}: RaceResultHeroProps) {
const isVictory = position === 1;
const isSecond = position === 2;
const isThird = position === 3;
const getPositionBg = () => {
if (isVictory) return 'linear-gradient(to bottom right, #facc15, #d97706)';
if (isSecond) return 'linear-gradient(to bottom right, #d1d5db, #6b7280)';
if (isThird) return 'linear-gradient(to bottom right, #3b82f6, #2563eb)';
return 'linear-gradient(to bottom right, #3b82f6, #2563eb)';
};
const getOuterBg = () => {
if (isVictory) return 'linear-gradient(to right, #eab308, #facc15, #d97706)';
if (isPodium) return 'linear-gradient(to right, #9ca3af, #d1d5db, #6b7280)';
return 'linear-gradient(to right, #3b82f6, #60a5fa, #2563eb)';
};
return (
<Surface
rounded="2xl"
p={1}
style={{ background: getOuterBg() }}
>
<Surface variant="dark" rounded="xl" p={8} position="relative">
<DecorativeBlur color="blue" size="lg" position="top-right" opacity={10} />
<Stack direction="row" align="center" justify="between" wrap gap={6} position="relative" zIndex={10}>
<Stack direction="row" align="center" gap={5}>
<Stack
position="relative"
display="flex"
alignItems="center"
justifyContent="center"
w="28"
h="28"
rounded="2xl"
color={position <= 2 ? 'text-iron-gray' : 'text-white'}
shadow="0 20px 25px -5px rgba(0, 0, 0, 0.1)"
style={{
background: getPositionBg(),
fontWeight: 900,
fontSize: '3rem'
}}
>
{isVictory && (
<Icon
icon={Trophy}
size={8}
color="#fef08a"
position="absolute"
top="-3"
right="-2"
/>
)}
P{position}
</Stack>
<Stack>
<Text
size="3xl"
weight="bold"
block
mb={1}
color={isVictory ? 'text-yellow-400' : isPodium ? 'text-gray-300' : 'text-white'}
>
{isVictory ? '🏆 VICTORY!' : isSecond ? '🥈 Second Place' : isThird ? '🥉 Podium Finish' : `P${position} Finish`}
</Text>
<Stack direction="row" align="center" gap={3}>
<Text size="sm" color="text-gray-400">Started P{startPosition}</Text>
<Stack w="1" h="1" rounded="full" bg="bg-charcoal-outline" />
<Text size="sm" color={isClean ? 'text-performance-green' : 'text-gray-400'}>
{incidents}x incidents {isClean && '✨'}
</Text>
</Stack>
</Stack>
</Stack>
<Stack direction="row" gap={3} wrap>
{positionChange !== 0 && (
<Surface
variant="muted"
rounded="2xl"
border
p={3}
style={{
minWidth: '100px',
textAlign: 'center',
background: positionChange > 0 ? 'rgba(16, 185, 129, 0.1)' : 'rgba(239, 68, 68, 0.1)',
borderColor: positionChange > 0 ? 'rgba(16, 185, 129, 0.3)' : 'rgba(239, 68, 68, 0.3)'
}}
>
<Stack align="center">
<Text size="2xl" weight="bold" color={positionChange > 0 ? 'text-performance-green' : 'text-red-500'}>
{positionChange > 0 ? '↑' : '↓'}{Math.abs(positionChange)}
</Text>
<Text size="xs" color="text-gray-400">{positionChange > 0 ? 'Gained' : 'Lost'}</Text>
</Stack>
</Surface>
)}
{ratingChange !== undefined && (
<Surface
variant="muted"
rounded="2xl"
border
p={3}
style={{
minWidth: '100px',
textAlign: 'center',
background: ratingChange > 0 ? 'rgba(245, 158, 11, 0.1)' : 'rgba(239, 68, 68, 0.1)',
borderColor: ratingChange > 0 ? 'rgba(245, 158, 11, 0.3)' : 'rgba(239, 68, 68, 0.3)'
}}
>
<Stack align="center">
<Text font="mono" size="2xl" weight="bold" color={ratingChange > 0 ? 'text-warning-amber' : 'text-red-500'}>
{animatedRatingChange > 0 ? '+' : ''}{animatedRatingChange}
</Text>
<Text size="xs" color="text-gray-400">Rating</Text>
</Stack>
</Surface>
)}
</Stack>
</Stack>
</Surface>
</Surface>
);
}