163 lines
4.6 KiB
TypeScript
163 lines
4.6 KiB
TypeScript
"use client";
|
|
|
|
import { Box, VStack, Text, Badge, Flex, Image } from "@chakra-ui/react";
|
|
import { useTranslations } from "next-intl";
|
|
import { useColorModeValue } from "@/components/ui/color-mode";
|
|
import type { ActiveLeagueDto } from "@/lib/api/matches/types";
|
|
|
|
interface LeagueSidebarProps {
|
|
leagues: ActiveLeagueDto[];
|
|
selectedLeagueId: string | null;
|
|
onSelect: (leagueId: string | null) => void;
|
|
isLoading?: boolean;
|
|
}
|
|
|
|
export default function LeagueSidebar({
|
|
leagues,
|
|
selectedLeagueId,
|
|
onSelect,
|
|
isLoading,
|
|
}: LeagueSidebarProps) {
|
|
const t = useTranslations("matches");
|
|
|
|
const bg = useColorModeValue("white", "gray.800");
|
|
const borderColor = useColorModeValue("gray.100", "gray.700");
|
|
const activeBg = useColorModeValue("primary.50", "primary.900");
|
|
const hoverBg = useColorModeValue("gray.50", "gray.750");
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<Box
|
|
bg={bg}
|
|
borderRadius="xl"
|
|
borderWidth="1px"
|
|
borderColor={borderColor}
|
|
p={4}
|
|
>
|
|
<VStack gap={3}>
|
|
{Array.from({ length: 6 }).map((_, i) => (
|
|
<Box
|
|
key={i}
|
|
h="40px"
|
|
w="100%"
|
|
bg="bg.muted"
|
|
borderRadius="lg"
|
|
animation="pulse 1.5s ease-in-out infinite"
|
|
/>
|
|
))}
|
|
</VStack>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Box
|
|
bg={bg}
|
|
borderRadius="xl"
|
|
borderWidth="1px"
|
|
borderColor={borderColor}
|
|
overflow="hidden"
|
|
>
|
|
{/* Header */}
|
|
<Box px={4} py={3} borderBottomWidth="1px" borderColor={borderColor}>
|
|
<Text
|
|
fontSize="sm"
|
|
fontWeight="bold"
|
|
textTransform="uppercase"
|
|
letterSpacing="wide"
|
|
color="fg.muted"
|
|
>
|
|
{t("active-leagues")}
|
|
</Text>
|
|
</Box>
|
|
|
|
{/* All Leagues Option */}
|
|
<Box
|
|
px={4}
|
|
py={2.5}
|
|
cursor="pointer"
|
|
bg={selectedLeagueId === null ? activeBg : "transparent"}
|
|
_hover={{ bg: selectedLeagueId === null ? activeBg : hoverBg }}
|
|
onClick={() => onSelect(null)}
|
|
transition="background 0.15s"
|
|
borderBottomWidth="1px"
|
|
borderColor={borderColor}
|
|
>
|
|
<Text
|
|
fontSize="sm"
|
|
fontWeight={selectedLeagueId === null ? "bold" : "medium"}
|
|
color={selectedLeagueId === null ? "primary.fg" : "fg"}
|
|
>
|
|
{t("all-leagues")}
|
|
</Text>
|
|
</Box>
|
|
|
|
{/* League List */}
|
|
<VStack gap={0} align="stretch" maxH="60vh" overflowY="auto">
|
|
{leagues.map((league) => {
|
|
const isActive = selectedLeagueId === league.id;
|
|
return (
|
|
<Box
|
|
key={league.id}
|
|
px={4}
|
|
py={2.5}
|
|
cursor="pointer"
|
|
bg={isActive ? activeBg : "transparent"}
|
|
_hover={{ bg: isActive ? activeBg : hoverBg }}
|
|
onClick={() => onSelect(league.id)}
|
|
transition="background 0.15s"
|
|
borderBottomWidth="1px"
|
|
borderColor={borderColor}
|
|
>
|
|
<Flex justify="space-between" align="center">
|
|
<Flex align="center" gap={2} minW={0} flex={1}>
|
|
{league.countryFlag && (
|
|
<Image
|
|
src={league.countryFlag}
|
|
alt={league.countryName || ""}
|
|
boxSize="16px"
|
|
objectFit="contain"
|
|
flexShrink={0}
|
|
/>
|
|
)}
|
|
<Text
|
|
fontSize="sm"
|
|
fontWeight={isActive ? "bold" : "medium"}
|
|
color={isActive ? "primary.fg" : "fg"}
|
|
truncate
|
|
>
|
|
{league.name}
|
|
</Text>
|
|
</Flex>
|
|
|
|
<Flex gap={1.5} flexShrink={0}>
|
|
{league.liveCount > 0 && (
|
|
<Badge
|
|
colorPalette="red"
|
|
variant="solid"
|
|
borderRadius="full"
|
|
fontSize="2xs"
|
|
px={1.5}
|
|
>
|
|
{league.liveCount}
|
|
</Badge>
|
|
)}
|
|
<Badge
|
|
colorPalette="gray"
|
|
variant="subtle"
|
|
borderRadius="full"
|
|
fontSize="2xs"
|
|
px={1.5}
|
|
>
|
|
{league.matchCount}
|
|
</Badge>
|
|
</Flex>
|
|
</Flex>
|
|
</Box>
|
|
);
|
|
})}
|
|
</VStack>
|
|
</Box>
|
|
);
|
|
}
|