first
Deploy Iddaai Frontend / build-and-deploy (push) Successful in 4m0s

This commit is contained in:
2026-04-16 13:36:34 +03:00
parent de5e145c4e
commit fc7a1ba567
218 changed files with 32370 additions and 0 deletions
+335
View File
@@ -0,0 +1,335 @@
"use client";
import {
Box,
Flex,
Heading,
Text,
Card,
VStack,
HStack,
Badge,
Spinner,
Input,
Tabs,
} from "@chakra-ui/react";
import { useTranslations } from "next-intl";
import { useColorModeValue } from "@/components/ui/color-mode";
import { SlideUp } from "@/components/motion";
import {
useCountries,
useLeagues,
useSearchTeams,
} from "@/lib/api/leagues/use-hooks";
import type { CountryDto, LeagueDto, TeamDto } from "@/lib/api/leagues/types";
import { LuSearch, LuGlobe, LuTrophy, LuUsers } from "react-icons/lu";
import { useState } from "react";
import { useDebounce } from "@/hooks/use-debounce";
import { Link } from "@/i18n/navigation";
import { InputGroup } from "@/components/ui/forms/input-group";
import { Link as ChakraLink } from "@chakra-ui/react";
export default function LeaguesContent() {
const t = useTranslations("leagues");
const tMatches = useTranslations("matches");
const cardBg = useColorModeValue("white", "gray.800");
const borderColor = useColorModeValue("gray.100", "gray.700");
const [activeTab, setActiveTab] = useState<"leagues" | "teams">("leagues");
const [sportFilter, setSportFilter] = useState<string>("");
const [searchQuery, setSearchQuery] = useState("");
const debouncedQuery = useDebounce(searchQuery, 300);
const countries = useCountries();
const leagues = useLeagues(
sportFilter
? { sport: sportFilter as "football" | "basketball" }
: undefined,
);
const searchTeams = useSearchTeams(
debouncedQuery.length >= 2 ? { q: debouncedQuery } : { q: "" },
);
return (
<SlideUp>
<Box maxW="6xl" mx="auto">
<Heading as="h1" size="xl" fontWeight="bold" mb={6}>
{t("title")}
</Heading>
<Tabs.Root
value={activeTab}
onValueChange={(e) => setActiveTab(e.value as "leagues" | "teams")}
>
<Tabs.List>
<Tabs.Trigger value="leagues">
<LuGlobe />
{t("countries-leagues")}
</Tabs.Trigger>
<Tabs.Trigger value="teams">
<LuUsers />
{tMatches("search-teams")}
</Tabs.Trigger>
</Tabs.List>
{/* Countries & Leagues Tab */}
<Tabs.Content value="leagues">
<Flex gap={6} direction={{ base: "column", lg: "row" }}>
{/* Countries Sidebar */}
<Box w={{ base: "full", lg: "280px" }} flexShrink={0}>
<Card.Root
bg={cardBg}
borderColor={borderColor}
borderRadius="xl"
>
<Card.Header>
<Heading as="h4" size="sm">
<HStack gap={2}>
<LuGlobe />
<Text>{t("countries")}</Text>
</HStack>
</Heading>
</Card.Header>
<Card.Body pt={0} maxH="600px" overflowY="auto">
{countries.isLoading ? (
<Flex justify="center" py={4}>
<Spinner size="sm" />
</Flex>
) : (
<VStack gap={1} align="stretch">
{countries.data?.data?.map((country: CountryDto) => (
<Flex
key={country.id}
px={3}
py={2}
borderRadius="md"
_hover={{
bg: "gray.50",
_dark: { bg: "gray.750" },
}}
cursor="pointer"
justify="space-between"
align="center"
>
<HStack gap={2}>
{country.flag ? (
<img
src={country.flag}
width="16"
height="16"
style={{ borderRadius: "2px" }}
alt={country.name}
/>
) : null}
<Text fontSize="sm">{country.name}</Text>
</HStack>
<Badge size="xs" colorScheme="gray">
{country.leagues?.length || 0}
</Badge>
</Flex>
))}
</VStack>
)}
</Card.Body>
</Card.Root>
</Box>
{/* Leagues List */}
<Box flex={1}>
<Card.Root
bg={cardBg}
borderColor={borderColor}
borderRadius="xl"
>
<Card.Header>
<Flex justify="space-between" align="center">
<Heading as="h4" size="sm">
<HStack gap={2}>
<LuTrophy />
<Text>{t("leagues")}</Text>
</HStack>
</Heading>
<HStack gap={2}>
<Badge
cursor="pointer"
colorScheme={!sportFilter ? "primary" : "gray"}
onClick={() => setSportFilter("")}
>
{tMatches("all")}
</Badge>
<Badge
cursor="pointer"
colorScheme={
sportFilter === "football" ? "green" : "gray"
}
onClick={() =>
setSportFilter(
sportFilter === "football" ? "" : "football",
)
}
>
{tMatches("football")}
</Badge>
<Badge
cursor="pointer"
colorScheme={
sportFilter === "basketball" ? "orange" : "gray"
}
onClick={() =>
setSportFilter(
sportFilter === "basketball" ? "" : "basketball",
)
}
>
{tMatches("basketball")}
</Badge>
</HStack>
</Flex>
</Card.Header>
<Card.Body pt={0}>
{leagues.isLoading ? (
<Flex justify="center" py={6}>
<Spinner size="sm" />
</Flex>
) : (
<VStack gap={2}>
{leagues.data?.data?.map((league: LeagueDto) => (
<ChakraLink
key={league.id}
as={Link}
href="/matches"
p={3}
borderRadius="md"
borderWidth="1px"
borderColor={borderColor}
_hover={{
borderColor: "primary.300",
bg: "primary.50",
_dark: { bg: "gray.750" },
}}
display="flex"
justifyContent="space-between"
alignItems="center"
textDecoration="none"
color="inherit"
>
<VStack align="start" gap={0}>
<Text fontWeight="semibold">{league.name}</Text>
<Text fontSize="xs" color="fg.muted">
{league.country?.name || ""}
</Text>
</VStack>
<HStack gap={2}>
{league.sport ? (
<Badge
size="xs"
colorScheme={
league.sport === "football"
? "green"
: "orange"
}
>
{league.sport}
</Badge>
) : null}
{league.season ? (
<Text fontSize="xs" color="fg.muted">
{league.season}
</Text>
) : null}
</HStack>
</ChakraLink>
))}
</VStack>
)}
</Card.Body>
</Card.Root>
</Box>
</Flex>
</Tabs.Content>
{/* Teams Search Tab */}
<Tabs.Content value="teams">
<Card.Root bg={cardBg} borderColor={borderColor} borderRadius="xl">
<Card.Body>
<InputGroup startElement={<LuSearch />} mb={4}>
<Input
placeholder={tMatches("search-teams")}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</InputGroup>
{debouncedQuery.length < 2 ? (
<Text color="fg.muted" textAlign="center" py={8}>
{t("search-at-least-2")}
</Text>
) : searchTeams.isLoading ? (
<Flex justify="center" py={6}>
<Spinner size="md" />
</Flex>
) : (
<VStack gap={2}>
{searchTeams.data?.data?.map((team: TeamDto) => (
<ChakraLink
key={team.id}
as={Link}
href={`/teams/${team.id}`}
p={3}
borderRadius="md"
borderWidth="1px"
borderColor={borderColor}
_hover={{
borderColor: "primary.300",
bg: "primary.50",
_dark: { bg: "gray.750" },
}}
display="flex"
alignItems="center"
gap={3}
textDecoration="none"
color="inherit"
>
{team.logo ? (
<img
src={team.logo}
width="32"
height="32"
style={{ borderRadius: "50%" }}
alt={team.name}
/>
) : (
<Box
boxSize="32px"
borderRadius="full"
bg="gray.200"
_dark={{ bg: "gray.600" }}
/>
)}
<VStack align="start" gap={0}>
<Text fontWeight="semibold">{team.name}</Text>
<Text fontSize="xs" color="fg.muted">
{team.country || ""}
</Text>
</VStack>
<Badge
ml="auto"
size="xs"
colorScheme={
team.sport === "football" ? "green" : "orange"
}
>
{team.sport}
</Badge>
</ChakraLink>
))}
</VStack>
)}
</Card.Body>
</Card.Root>
</Tabs.Content>
</Tabs.Root>
</Box>
</SlideUp>
);
}