117 lines
3.4 KiB
TypeScript
117 lines
3.4 KiB
TypeScript
import React from 'react';
|
|
import Image from 'next/image';
|
|
import { Crown, Flag } from 'lucide-react';
|
|
import { Box } from './Box';
|
|
import { Text } from './Text';
|
|
import { Stack } from './Stack';
|
|
import { mediaConfig } from '@/lib/config/mediaConfig';
|
|
|
|
interface LeaderboardItemProps {
|
|
position: number;
|
|
name: string;
|
|
avatarUrl?: string;
|
|
nationality: string;
|
|
rating: number;
|
|
wins: number;
|
|
skillLevelLabel?: string;
|
|
skillLevelColor?: string;
|
|
categoryLabel?: string;
|
|
categoryColor?: string;
|
|
onClick: () => void;
|
|
}
|
|
|
|
export function LeaderboardItem({
|
|
position,
|
|
name,
|
|
avatarUrl,
|
|
nationality,
|
|
rating,
|
|
wins,
|
|
skillLevelLabel,
|
|
skillLevelColor,
|
|
categoryLabel,
|
|
categoryColor,
|
|
onClick,
|
|
}: LeaderboardItemProps) {
|
|
const getMedalColor = (pos: number) => {
|
|
switch (pos) {
|
|
case 1: return 'text-yellow-400';
|
|
case 2: return 'text-gray-300';
|
|
case 3: return 'text-amber-600';
|
|
default: return 'text-gray-500';
|
|
}
|
|
};
|
|
|
|
const getMedalBg = (pos: number) => {
|
|
switch (pos) {
|
|
case 1: return 'bg-yellow-400/10 border-yellow-400/30';
|
|
case 2: return 'bg-gray-300/10 border-gray-300/30';
|
|
case 3: return 'bg-amber-600/10 border-amber-600/30';
|
|
default: return 'bg-iron-gray/50 border-charcoal-outline';
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Box
|
|
as="button"
|
|
type="button"
|
|
onClick={onClick}
|
|
display="flex"
|
|
alignItems="center"
|
|
gap={4}
|
|
px={4}
|
|
py={3}
|
|
fullWidth
|
|
textAlign="left"
|
|
className="hover:bg-iron-gray/30 transition-colors group"
|
|
>
|
|
{/* Position */}
|
|
<Box
|
|
width="8"
|
|
height="8"
|
|
display="flex"
|
|
center
|
|
rounded="full"
|
|
border
|
|
className={`${getMedalBg(position)} ${getMedalColor(position)} text-xs font-bold`}
|
|
>
|
|
{position <= 3 ? <Crown className="w-3.5 h-3.5" /> : position}
|
|
</Box>
|
|
|
|
{/* Avatar */}
|
|
<Box position="relative" width="9" height="9" rounded="full" overflow="hidden" border={true} borderColor="border-charcoal-outline">
|
|
<Image src={avatarUrl || mediaConfig.avatars.defaultFallback} alt={name} fill className="object-cover" />
|
|
</Box>
|
|
|
|
{/* Info */}
|
|
<Box flexGrow={1} minWidth="0">
|
|
<Text weight="medium" color="text-white" truncate block className="group-hover:text-primary-blue transition-colors">
|
|
{name}
|
|
</Text>
|
|
<Stack direction="row" align="center" gap={2}>
|
|
<Flag className="w-3 h-3 text-gray-500" />
|
|
<Text size="xs" color="text-gray-500">{nationality}</Text>
|
|
{categoryLabel && (
|
|
<Text size="xs" className={categoryColor}>{categoryLabel}</Text>
|
|
)}
|
|
{skillLevelLabel && (
|
|
<Text size="xs" className={skillLevelColor}>{skillLevelLabel}</Text>
|
|
)}
|
|
</Stack>
|
|
</Box>
|
|
|
|
{/* Stats */}
|
|
<Stack direction="row" align="center" gap={4}>
|
|
<Box textAlign="center">
|
|
<Text color="text-primary-blue" weight="semibold" font="mono" block>{rating.toLocaleString()}</Text>
|
|
<Text size="xs" color="text-gray-500" block style={{ fontSize: '10px' }}>Rating</Text>
|
|
</Box>
|
|
<Box textAlign="center">
|
|
<Text color="text-performance-green" weight="semibold" font="mono" block>{wins}</Text>
|
|
<Text size="xs" color="text-gray-500" block style={{ fontSize: '10px' }}>Wins</Text>
|
|
</Box>
|
|
</Stack>
|
|
</Box>
|
|
);
|
|
}
|