This commit is contained in:
@@ -11,7 +11,9 @@ import {
|
||||
Badge,
|
||||
Spinner,
|
||||
Input,
|
||||
Tabs,
|
||||
Grid,
|
||||
GridItem,
|
||||
Icon,
|
||||
} from "@chakra-ui/react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useColorModeValue } from "@/components/ui/color-mode";
|
||||
@@ -22,8 +24,8 @@ import {
|
||||
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 { LuSearch, LuGlobe, LuTrophy, LuUsers, LuArrowRight, LuMapPin } from "react-icons/lu";
|
||||
import { useMemo, useState } from "react";
|
||||
import { useDebounce } from "@/hooks/use-debounce";
|
||||
import { Link } from "@/i18n/navigation";
|
||||
import { InputGroup } from "@/components/ui/forms/input-group";
|
||||
@@ -33,13 +35,24 @@ 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 bgGradient = useColorModeValue(
|
||||
"linear(to-r, primary.500, primary.700)",
|
||||
"linear(to-r, primary.600, primary.900)"
|
||||
);
|
||||
|
||||
const cardBg = useColorModeValue("white", "gray.900");
|
||||
const borderColor = useColorModeValue("gray.200", "gray.800");
|
||||
const hoverBg = useColorModeValue("gray.50", "whiteAlpha.50");
|
||||
|
||||
const [activeTab, setActiveTab] = useState<"leagues" | "teams">("leagues");
|
||||
const [sportFilter, setSportFilter] = useState<string>("");
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const debouncedQuery = useDebounce(searchQuery, 300);
|
||||
const [selectedCountryId, setSelectedCountryId] = useState<string | null>(null);
|
||||
|
||||
const [teamSearchQuery, setTeamSearchQuery] = useState("");
|
||||
const debouncedTeamQuery = useDebounce(teamSearchQuery, 300);
|
||||
|
||||
const [countrySearchQuery, setCountrySearchQuery] = useState("");
|
||||
const debouncedCountryQuery = useDebounce(countrySearchQuery, 300);
|
||||
|
||||
const countries = useCountries();
|
||||
const leagues = useLeagues(
|
||||
@@ -48,288 +61,379 @@ export default function LeaguesContent() {
|
||||
: undefined,
|
||||
);
|
||||
const searchTeams = useSearchTeams(
|
||||
debouncedQuery.length >= 2 ? { q: debouncedQuery } : { q: "" },
|
||||
debouncedTeamQuery.length >= 2 ? { q: debouncedTeamQuery } : { q: "" },
|
||||
);
|
||||
|
||||
const filteredCountries = useMemo(() => {
|
||||
if (!countries.data?.data) return [];
|
||||
if (!debouncedCountryQuery) return countries.data.data;
|
||||
return countries.data.data.filter((c) =>
|
||||
c.name.toLowerCase().includes(debouncedCountryQuery.toLowerCase())
|
||||
);
|
||||
}, [countries.data?.data, debouncedCountryQuery]);
|
||||
|
||||
const displayedLeagues = useMemo(() => {
|
||||
let sourceLeagues: LeagueDto[] = leagues.data?.data || [];
|
||||
|
||||
if (selectedCountryId) {
|
||||
sourceLeagues = sourceLeagues.filter(l => l.countryId === selectedCountryId);
|
||||
}
|
||||
|
||||
// Apply sport filter if selected
|
||||
if (sportFilter) {
|
||||
return sourceLeagues.filter(l => l.sport === sportFilter);
|
||||
}
|
||||
return sourceLeagues;
|
||||
}, [selectedCountryId, leagues.data?.data, sportFilter]);
|
||||
|
||||
return (
|
||||
<SlideUp>
|
||||
<Box maxW="6xl" mx="auto">
|
||||
<Heading as="h1" size="xl" fontWeight="bold" mb={6}>
|
||||
{t("title")}
|
||||
</Heading>
|
||||
<Box minH="calc(100vh - 80px)">
|
||||
{/* Hero Section */}
|
||||
<Box bgGradient={bgGradient} color="white" pt={16} pb={20} px={6} position="relative" overflow="hidden">
|
||||
<Box position="absolute" top="-20%" right="-10%" opacity={0.1} transform="rotate(15deg)">
|
||||
<LuTrophy size={400} />
|
||||
</Box>
|
||||
<Box maxW="7xl" mx="auto" position="relative" zIndex={1}>
|
||||
<SlideUp>
|
||||
<VStack align="center" gap={4} textAlign="center" maxW="3xl" mx="auto">
|
||||
<Badge colorScheme="whiteAlpha" variant="subtle" size="lg" px={4} py={1} rounded="full">
|
||||
{t("title")}
|
||||
</Badge>
|
||||
<Heading as="h1" fontSize={{ base: "3xl", md: "5xl" }} fontWeight="800" letterSpacing="tight">
|
||||
{activeTab === "leagues" ? t("countries-leagues") : tMatches("search-teams")}
|
||||
</Heading>
|
||||
<Text fontSize="lg" color="whiteAlpha.800" maxW="xl">
|
||||
{activeTab === "leagues"
|
||||
? "Explore top football and basketball leagues around the world. Filter by country and analyze historical matches."
|
||||
: "Find your favorite teams across all leagues. Get deep insights and head-to-head statistics."}
|
||||
</Text>
|
||||
</VStack>
|
||||
</SlideUp>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<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"
|
||||
{/* Main Content Area - Pulled up to overlap hero */}
|
||||
<Box maxW="7xl" mx="auto" px={6} mt={-10} position="relative" zIndex={2} pb={20}>
|
||||
<SlideUp transition={{ delay: 0.1, duration: 0.5, ease: [0.25, 0.1, 0.25, 1] }}>
|
||||
<Card.Root bg={cardBg} shadow="xl" borderRadius="2xl" borderWidth="1px" borderColor={borderColor} overflow="hidden">
|
||||
|
||||
{/* Tab Navigation */}
|
||||
<Flex borderBottomWidth="1px" borderColor={borderColor} bg={useColorModeValue("gray.50", "whiteAlpha.50")}>
|
||||
<Flex flex={1}>
|
||||
<Box
|
||||
flex={1} py={4} textAlign="center" cursor="pointer"
|
||||
borderBottomWidth="2px"
|
||||
borderColor={activeTab === "leagues" ? "primary.500" : "transparent"}
|
||||
color={activeTab === "leagues" ? "primary.500" : "fg.muted"}
|
||||
fontWeight={activeTab === "leagues" ? "bold" : "medium"}
|
||||
onClick={() => setActiveTab("leagues")}
|
||||
transition="all 0.2s"
|
||||
_hover={{ bg: hoverBg }}
|
||||
>
|
||||
<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 justify="center" gap={2}>
|
||||
<LuGlobe />
|
||||
<Text>{t("countries-leagues")}</Text>
|
||||
</HStack>
|
||||
</Box>
|
||||
<Box
|
||||
flex={1} py={4} textAlign="center" cursor="pointer"
|
||||
borderBottomWidth="2px"
|
||||
borderColor={activeTab === "teams" ? "primary.500" : "transparent"}
|
||||
color={activeTab === "teams" ? "primary.500" : "fg.muted"}
|
||||
fontWeight={activeTab === "teams" ? "bold" : "medium"}
|
||||
onClick={() => setActiveTab("teams")}
|
||||
transition="all 0.2s"
|
||||
_hover={{ bg: hoverBg }}
|
||||
>
|
||||
<HStack justify="center" gap={2}>
|
||||
<LuUsers />
|
||||
<Text>{tMatches("search-teams")}</Text>
|
||||
</HStack>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
{/* LEAGUES TAB */}
|
||||
{activeTab === "leagues" && (
|
||||
<Flex direction={{ base: "column", lg: "row" }} minH="600px">
|
||||
|
||||
{/* Left Sidebar: Countries */}
|
||||
<Box w={{ base: "full", lg: "320px" }} borderRightWidth={{ lg: "1px" }} borderColor={borderColor} bg={useColorModeValue("gray.50", "whiteAlpha.50")}>
|
||||
<VStack align="stretch" h="full" gap={0}>
|
||||
<Box p={4} borderBottomWidth="1px" borderColor={borderColor} bg={cardBg}>
|
||||
<InputGroup startElement={<LuSearch color="gray.400" />} w="full">
|
||||
<Input
|
||||
placeholder={t("countries") + "..."}
|
||||
variant="subtle"
|
||||
borderRadius="full"
|
||||
value={countrySearchQuery}
|
||||
onChange={(e) => setCountrySearchQuery(e.target.value)}
|
||||
/>
|
||||
</InputGroup>
|
||||
</Box>
|
||||
|
||||
<Box flex={1} overflowY="auto" maxH={{ base: "300px", lg: "600px" }} p={2}>
|
||||
{countries.isLoading ? (
|
||||
<Flex justify="center" py={10}><Spinner color="primary.500" /></Flex>
|
||||
) : (
|
||||
<VStack gap={1} align="stretch">
|
||||
<Box
|
||||
px={4} py={3} borderRadius="lg" cursor="pointer"
|
||||
bg={selectedCountryId === null ? "primary.500" : "transparent"}
|
||||
color={selectedCountryId === null ? "white" : "fg"}
|
||||
_hover={{ bg: selectedCountryId === null ? "primary.600" : hoverBg }}
|
||||
onClick={() => setSelectedCountryId(null)}
|
||||
transition="all 0.2s"
|
||||
>
|
||||
<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 justify="space-between">
|
||||
<HStack gap={3}>
|
||||
<LuGlobe />
|
||||
<Text fontWeight={selectedCountryId === null ? "bold" : "medium"}>{t("all")}</Text>
|
||||
</HStack>
|
||||
<Badge size="sm" bg={selectedCountryId === null ? "whiteAlpha.300" : "gray.100"} color={selectedCountryId === null ? "white" : "fg"}>
|
||||
{leagues.data?.data?.length || 0}
|
||||
</Badge>
|
||||
</HStack>
|
||||
<Badge size="xs" colorScheme="gray">
|
||||
{country.leagues?.length || 0}
|
||||
</Badge>
|
||||
</Flex>
|
||||
))}
|
||||
</VStack>
|
||||
)}
|
||||
</Card.Body>
|
||||
</Card.Root>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{filteredCountries.map((country: CountryDto) => {
|
||||
const isSelected = selectedCountryId === country.id;
|
||||
return (
|
||||
<Box
|
||||
key={country.id}
|
||||
px={4} py={3} borderRadius="lg" cursor="pointer"
|
||||
bg={isSelected ? "primary.500" : "transparent"}
|
||||
color={isSelected ? "white" : "fg"}
|
||||
_hover={{ bg: isSelected ? "primary.600" : hoverBg }}
|
||||
onClick={() => setSelectedCountryId(country.id)}
|
||||
transition="all 0.2s"
|
||||
>
|
||||
<HStack justify="space-between">
|
||||
<HStack gap={3}>
|
||||
{country.flag ? (
|
||||
<img src={country.flag} width="20" height="20" style={{ borderRadius: "50%", objectFit: "cover" }} alt={country.name} />
|
||||
) : <LuMapPin />}
|
||||
<Text fontWeight={isSelected ? "bold" : "medium"}>{country.name}</Text>
|
||||
</HStack>
|
||||
<Badge size="sm" bg={isSelected ? "whiteAlpha.300" : "gray.100"} color={isSelected ? "white" : "fg"}>
|
||||
{leagues.data?.data?.filter(l => l.countryId === country.id).length || 0}
|
||||
</Badge>
|
||||
</HStack>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</VStack>
|
||||
)}
|
||||
</Box>
|
||||
</VStack>
|
||||
</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>
|
||||
{/* Right Area: Leagues Grid */}
|
||||
<Box flex={1} p={{ base: 4, md: 8 }} bg={cardBg}>
|
||||
{/* Top Filters */}
|
||||
<Flex justify="space-between" align="center" mb={6} direction={{ base: "column", sm: "row" }} gap={4}>
|
||||
<Heading size="md" fontWeight="bold">
|
||||
{selectedCountryId
|
||||
? `${countries.data?.data?.find(c => c.id === selectedCountryId)?.name} ${t("leagues")}`
|
||||
: t("leagues")}
|
||||
<Text as="span" color="fg.muted" ml={2} fontWeight="normal" fontSize="sm">
|
||||
({displayedLeagues.length})
|
||||
</Text>
|
||||
</Heading>
|
||||
|
||||
<HStack gap={2} bg={useColorModeValue("gray.100", "gray.800")} p={1} borderRadius="full">
|
||||
<Box
|
||||
px={4} py={1.5} borderRadius="full" cursor="pointer" fontSize="sm" fontWeight="medium"
|
||||
bg={!sportFilter ? "white" : "transparent"}
|
||||
color={!sportFilter ? "black" : "fg.muted"}
|
||||
shadow={!sportFilter ? "sm" : "none"}
|
||||
onClick={() => setSportFilter("")}
|
||||
transition="all 0.2s"
|
||||
_dark={{ bg: !sportFilter ? "gray.600" : "transparent", color: !sportFilter ? "white" : "gray.400" }}
|
||||
>
|
||||
{t("all")}
|
||||
</Box>
|
||||
<Box
|
||||
px={4} py={1.5} borderRadius="full" cursor="pointer" fontSize="sm" fontWeight="medium"
|
||||
bg={sportFilter === "football" ? "green.500" : "transparent"}
|
||||
color={sportFilter === "football" ? "white" : "fg.muted"}
|
||||
shadow={sportFilter === "football" ? "sm" : "none"}
|
||||
onClick={() => setSportFilter(sportFilter === "football" ? "" : "football")}
|
||||
transition="all 0.2s"
|
||||
>
|
||||
{tMatches("football")}
|
||||
</Box>
|
||||
<Box
|
||||
px={4} py={1.5} borderRadius="full" cursor="pointer" fontSize="sm" fontWeight="medium"
|
||||
bg={sportFilter === "basketball" ? "orange.500" : "transparent"}
|
||||
color={sportFilter === "basketball" ? "white" : "fg.muted"}
|
||||
shadow={sportFilter === "basketball" ? "sm" : "none"}
|
||||
onClick={() => setSportFilter(sportFilter === "basketball" ? "" : "basketball")}
|
||||
transition="all 0.2s"
|
||||
>
|
||||
{tMatches("basketball")}
|
||||
</Box>
|
||||
</HStack>
|
||||
</Flex>
|
||||
|
||||
{/* Leagues Grid */}
|
||||
{leagues.isLoading ? (
|
||||
<Flex justify="center" py={20}><Spinner size="xl" color="primary.500" borderWidth="3px" /></Flex>
|
||||
) : displayedLeagues.length === 0 ? (
|
||||
<Flex direction="column" align="center" justify="center" py={20} textAlign="center">
|
||||
<Box bg="gray.100" _dark={{ bg: "gray.800" }} p={6} borderRadius="full" mb={4}>
|
||||
<LuTrophy size={40} color="gray" />
|
||||
</Box>
|
||||
<Heading size="md" mb={2}>Bulunamadı</Heading>
|
||||
<Text color="fg.muted">Seçili kriterlere uygun lig bulunamadı.</Text>
|
||||
</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) => (
|
||||
) : (
|
||||
<Grid templateColumns={{ base: "1fr", md: "repeat(2, 1fr)", xl: "repeat(3, 1fr)" }} gap={4}>
|
||||
{displayedLeagues.map((league: LeagueDto) => (
|
||||
<GridItem key={league.id}>
|
||||
<ChakraLink
|
||||
key={league.id}
|
||||
as={Link}
|
||||
href="/matches"
|
||||
p={3}
|
||||
borderRadius="md"
|
||||
href={`/leagues/${league.id}`}
|
||||
display="block"
|
||||
h="full"
|
||||
p={5}
|
||||
borderRadius="xl"
|
||||
borderWidth="1px"
|
||||
borderColor={borderColor}
|
||||
bg={cardBg}
|
||||
_hover={{
|
||||
borderColor: "primary.300",
|
||||
bg: "primary.50",
|
||||
_dark: { bg: "gray.750" },
|
||||
shadow: "md",
|
||||
transform: "translateY(-2px)",
|
||||
}}
|
||||
display="flex"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
transition="all 0.2s"
|
||||
textDecoration="none"
|
||||
color="inherit"
|
||||
data-group
|
||||
>
|
||||
<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}
|
||||
<Flex justify="space-between" align="flex-start" mb={4}>
|
||||
<Box p={2} borderRadius="lg" bg={league.sport === "football" ? "green.50" : "orange.50"} _dark={{ bg: league.sport === "football" ? "green.900" : "orange.900" }}>
|
||||
<LuTrophy size={20} color={league.sport === "football" ? "var(--chakra-colors-green-500)" : "var(--chakra-colors-orange-500)"} />
|
||||
</Box>
|
||||
<Badge size="sm" variant="subtle" colorScheme={league.sport === "football" ? "green" : "orange"}>
|
||||
{league.sport}
|
||||
</Badge>
|
||||
</Flex>
|
||||
|
||||
<Heading size="sm" mb={1} lineClamp={1} _groupHover={{ color: "primary.500" }}>
|
||||
{league.name}
|
||||
</Heading>
|
||||
<HStack color="fg.muted" fontSize="sm" gap={1}>
|
||||
<LuMapPin size={14} />
|
||||
<Text lineClamp={1}>{league.country?.name || "Global"}</Text>
|
||||
</HStack>
|
||||
|
||||
{league.season && (
|
||||
<Flex mt={4} pt={4} borderTopWidth="1px" borderColor={borderColor} justify="space-between" align="center">
|
||||
<Text fontSize="xs" color="fg.muted" fontWeight="medium">SEZON: {league.season}</Text>
|
||||
<Icon as={LuArrowRight} color="gray.400" _groupHover={{ color: "primary.500", transform: "translateX(4px)" }} transition="all 0.2s" />
|
||||
</Flex>
|
||||
)}
|
||||
</ChakraLink>
|
||||
))}
|
||||
</VStack>
|
||||
)}
|
||||
</Card.Body>
|
||||
</Card.Root>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Tabs.Content>
|
||||
</GridItem>
|
||||
))}
|
||||
</Grid>
|
||||
)}
|
||||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{/* 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>
|
||||
{/* TEAMS TAB */}
|
||||
{activeTab === "teams" && (
|
||||
<Box p={{ base: 4, md: 8 }}>
|
||||
<Box maxW="2xl" mx="auto" mb={10}>
|
||||
<InputGroup startElement={<LuSearch color="gray.400" size={20} />} w="full">
|
||||
<Input
|
||||
placeholder={tMatches("search-teams") + "..."}
|
||||
value={teamSearchQuery}
|
||||
onChange={(e) => setTeamSearchQuery(e.target.value)}
|
||||
variant="outline"
|
||||
borderRadius="xl"
|
||||
fontSize="lg"
|
||||
py={6}
|
||||
boxShadow="sm"
|
||||
_focus={{ boxShadow: "0 0 0 2px var(--chakra-colors-primary-500)" }}
|
||||
/>
|
||||
</InputGroup>
|
||||
</Box>
|
||||
|
||||
{debouncedTeamQuery.length < 2 ? (
|
||||
<Flex direction="column" align="center" justify="center" py={20} textAlign="center">
|
||||
<Box bg="primary.50" _dark={{ bg: "primary.900" }} p={8} borderRadius="full" mb={6}>
|
||||
<LuUsers size={64} color="var(--chakra-colors-primary-500)" />
|
||||
</Box>
|
||||
<Heading size="lg" mb={3}>{t("search-at-least-2")}</Heading>
|
||||
<Text color="fg.muted" maxW="md">
|
||||
Find detailed statistics, upcoming matches, and head-to-head analysis by searching for any team worldwide.
|
||||
</Text>
|
||||
</Flex>
|
||||
) : searchTeams.isLoading ? (
|
||||
<Flex justify="center" py={6}>
|
||||
<Spinner size="md" />
|
||||
<Flex justify="center" py={20}><Spinner size="xl" color="primary.500" borderWidth="3px" /></Flex>
|
||||
) : searchTeams.data?.data?.length === 0 ? (
|
||||
<Flex direction="column" align="center" justify="center" py={20} textAlign="center">
|
||||
<Heading size="md" mb={2}>Takım Bulunamadı</Heading>
|
||||
<Text color="fg.muted">"{debouncedTeamQuery}" aramasıyla eşleşen bir takım bulunamadı.</Text>
|
||||
</Flex>
|
||||
) : (
|
||||
<VStack gap={2}>
|
||||
<Grid templateColumns={{ base: "1fr", md: "repeat(2, 1fr)", xl: "repeat(3, 1fr)" }} gap={4}>
|
||||
{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"
|
||||
}
|
||||
<GridItem key={team.id}>
|
||||
<ChakraLink
|
||||
as={Link}
|
||||
href={`/teams/${team.id}`}
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
p={4}
|
||||
borderRadius="xl"
|
||||
borderWidth="1px"
|
||||
borderColor={borderColor}
|
||||
bg={cardBg}
|
||||
_hover={{
|
||||
borderColor: "primary.300",
|
||||
shadow: "md",
|
||||
transform: "translateY(-2px)",
|
||||
}}
|
||||
transition="all 0.2s"
|
||||
textDecoration="none"
|
||||
color="inherit"
|
||||
data-group
|
||||
>
|
||||
{team.sport}
|
||||
</Badge>
|
||||
</ChakraLink>
|
||||
{team.logo ? (
|
||||
<Box w={12} h={12} borderRadius="full" overflow="hidden" flexShrink={0} mr={4} bg="white" p={1} shadow="sm">
|
||||
<img src={team.logo} width="100%" height="100%" style={{ objectFit: "contain" }} alt={team.name} />
|
||||
</Box>
|
||||
) : (
|
||||
<Flex w={12} h={12} borderRadius="full" bg="gray.100" _dark={{ bg: "gray.700" }} align="center" justify="center" flexShrink={0} mr={4}>
|
||||
<LuUsers size={20} color="gray" />
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
<VStack align="start" gap={0} flex={1}>
|
||||
<Heading size="sm" lineClamp={1} _groupHover={{ color: "primary.500" }}>{team.name}</Heading>
|
||||
<HStack color="fg.muted" fontSize="xs" gap={1}>
|
||||
<LuMapPin size={12} />
|
||||
<Text lineClamp={1}>{team.country || "Global"}</Text>
|
||||
</HStack>
|
||||
</VStack>
|
||||
|
||||
<Badge ml={2} size="sm" colorScheme={team.sport === "football" ? "green" : "orange"} variant="subtle">
|
||||
{team.sport}
|
||||
</Badge>
|
||||
</ChakraLink>
|
||||
</GridItem>
|
||||
))}
|
||||
</VStack>
|
||||
</Grid>
|
||||
)}
|
||||
</Card.Body>
|
||||
</Card.Root>
|
||||
</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
</Box>
|
||||
)}
|
||||
</Card.Root>
|
||||
</SlideUp>
|
||||
</Box>
|
||||
</SlideUp>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user