158 lines
5.4 KiB
TypeScript
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>
|
|
);
|
|
}
|