286 lines
8.8 KiB
TypeScript
286 lines
8.8 KiB
TypeScript
"use client";
|
|
|
|
import {
|
|
Box,
|
|
Flex,
|
|
Text,
|
|
Heading,
|
|
Badge,
|
|
VStack,
|
|
HStack,
|
|
Image,
|
|
Spinner,
|
|
Button,
|
|
Card,
|
|
} from "@chakra-ui/react";
|
|
import { useTranslations } from "next-intl";
|
|
import { useParams, useRouter } from "next/navigation";
|
|
import { useColorModeValue } from "@/components/ui/color-mode";
|
|
import { SlideUp } from "@/components/motion";
|
|
import { useMatchDetails } from "@/lib/api/matches/use-hooks";
|
|
import { usePrediction } from "@/lib/api/predictions/use-hooks";
|
|
import PredictionCard from "@/components/matches/prediction-card";
|
|
import OddsCard from "@/components/matches/odds-card";
|
|
import LineupsCard from "@/components/matches/lineups-card";
|
|
import { LuArrowLeft, LuRefreshCw } from "react-icons/lu";
|
|
|
|
export default function MatchDetailContent() {
|
|
const t = useTranslations("matches");
|
|
const tPred = useTranslations("predictions");
|
|
const tCommon = useTranslations("common");
|
|
const params = useParams();
|
|
const router = useRouter();
|
|
|
|
const matchId = params.id as string;
|
|
|
|
const { data: matchData, isLoading: matchLoading } = useMatchDetails(matchId);
|
|
const {
|
|
data: predictionData,
|
|
isLoading: predLoading,
|
|
refetch: refetchPrediction,
|
|
} = usePrediction(matchId);
|
|
|
|
const headerBg = useColorModeValue("white", "gray.800");
|
|
const borderColor = useColorModeValue("gray.100", "gray.700");
|
|
|
|
const match = matchData?.data;
|
|
const prediction = predictionData?.data;
|
|
|
|
if (matchLoading) {
|
|
return (
|
|
<Flex justify="center" align="center" py={20}>
|
|
<Spinner size="lg" color="primary.500" />
|
|
</Flex>
|
|
);
|
|
}
|
|
|
|
if (!match) {
|
|
return (
|
|
<Flex justify="center" align="center" py={20} direction="column" gap={4}>
|
|
<Text color="fg.muted" fontSize="lg">
|
|
{t("no-matches")}
|
|
</Text>
|
|
<Button variant="outline" onClick={() => router.back()}>
|
|
<LuArrowLeft />
|
|
{tCommon("back")}
|
|
</Button>
|
|
</Flex>
|
|
);
|
|
}
|
|
|
|
const isLive = match.status === "LIVE";
|
|
const isFinished = match.status === "Finished";
|
|
|
|
return (
|
|
<SlideUp>
|
|
<Box>
|
|
{/* Back Button */}
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
mb={4}
|
|
onClick={() => router.back()}
|
|
gap={1.5}
|
|
>
|
|
<LuArrowLeft />
|
|
{tCommon("back")}
|
|
</Button>
|
|
{/* Match Header */}
|
|
<Card.Root
|
|
bg={headerBg}
|
|
borderColor={borderColor}
|
|
borderRadius="xl"
|
|
mb={6}
|
|
>
|
|
<Card.Body>
|
|
{/* League Info */}
|
|
{match.league && (
|
|
<Flex justify="center" align="center" gap={2} mb={4}>
|
|
{match.league.country?.flag && (
|
|
<Image
|
|
src={match.league.country.flag}
|
|
alt={match.league.country.name || ""}
|
|
boxSize="18px"
|
|
objectFit="contain"
|
|
/>
|
|
)}
|
|
<Text fontSize="sm" color="fg.muted" fontWeight="medium">
|
|
{match.league.name}
|
|
</Text>
|
|
<Badge
|
|
colorPalette={isLive ? "red" : isFinished ? "gray" : "green"}
|
|
variant="subtle"
|
|
fontSize="xs"
|
|
borderRadius="full"
|
|
>
|
|
{isLive && (
|
|
<Box
|
|
as="span"
|
|
display="inline-block"
|
|
w="6px"
|
|
h="6px"
|
|
borderRadius="full"
|
|
bg="red.500"
|
|
mr={1}
|
|
animation="pulse 1.5s ease-in-out infinite"
|
|
/>
|
|
)}
|
|
{isLive
|
|
? t("live")
|
|
: isFinished
|
|
? t("finished")
|
|
: t("not-started")}
|
|
</Badge>
|
|
</Flex>
|
|
)}
|
|
|
|
{/* Teams & Score */}
|
|
<HStack gap={6} justify="center" align="center">
|
|
{/* Home Team */}
|
|
<VStack gap={2} flex={1} align="center">
|
|
{match.homeTeam?.logo ? (
|
|
<Image
|
|
src={match.homeTeam.logo}
|
|
alt={match.homeTeam.name}
|
|
boxSize="64px"
|
|
objectFit="contain"
|
|
/>
|
|
) : (
|
|
<Flex
|
|
boxSize="64px"
|
|
bg="primary.subtle"
|
|
borderRadius="full"
|
|
align="center"
|
|
justify="center"
|
|
>
|
|
<Text fontSize="2xl" fontWeight="bold" color="primary.fg">
|
|
{match.homeTeam?.name?.charAt(0) || "H"}
|
|
</Text>
|
|
</Flex>
|
|
)}
|
|
<Text fontSize="md" fontWeight="bold" textAlign="center">
|
|
{match.homeTeam?.name}
|
|
</Text>
|
|
<Text fontSize="xs" color="fg.muted">
|
|
{t("home-team")}
|
|
</Text>
|
|
</VStack>
|
|
|
|
{/* Score */}
|
|
<VStack gap={1} flexShrink={0}>
|
|
{match.score && (isLive || isFinished) ? (
|
|
<HStack gap={3}>
|
|
<Text
|
|
fontSize="4xl"
|
|
fontWeight="900"
|
|
color={isLive ? "red.500" : "fg"}
|
|
>
|
|
{match.score.home}
|
|
</Text>
|
|
<Text fontSize="2xl" color="fg.muted">
|
|
-
|
|
</Text>
|
|
<Text
|
|
fontSize="4xl"
|
|
fontWeight="900"
|
|
color={isLive ? "red.500" : "fg"}
|
|
>
|
|
{match.score.away}
|
|
</Text>
|
|
</HStack>
|
|
) : (
|
|
<Text fontSize="xl" fontWeight="bold" color="fg.muted">
|
|
{t("vs")}
|
|
</Text>
|
|
)}
|
|
<Text fontSize="xs" color="fg.muted">
|
|
{new Date(match.mstUtc).toLocaleDateString("tr-TR", {
|
|
weekday: "short",
|
|
day: "2-digit",
|
|
month: "short",
|
|
year: "numeric",
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
})}
|
|
</Text>
|
|
</VStack>
|
|
|
|
{/* Away Team */}
|
|
<VStack gap={2} flex={1} align="center">
|
|
{match.awayTeam?.logo ? (
|
|
<Image
|
|
src={match.awayTeam.logo}
|
|
alt={match.awayTeam.name}
|
|
boxSize="64px"
|
|
objectFit="contain"
|
|
/>
|
|
) : (
|
|
<Flex
|
|
boxSize="64px"
|
|
bg="primary.subtle"
|
|
borderRadius="full"
|
|
align="center"
|
|
justify="center"
|
|
>
|
|
<Text fontSize="2xl" fontWeight="bold" color="primary.fg">
|
|
{match.awayTeam?.name?.charAt(0) || "A"}
|
|
</Text>
|
|
</Flex>
|
|
)}
|
|
<Text fontSize="md" fontWeight="bold" textAlign="center">
|
|
{match.awayTeam?.name}
|
|
</Text>
|
|
<Text fontSize="xs" color="fg.muted">
|
|
{t("away-team")}
|
|
</Text>
|
|
</VStack>
|
|
</HStack>
|
|
</Card.Body>
|
|
</Card.Root>
|
|
|
|
{/* Lineups Section */}
|
|
<LineupsCard match={match} prediction={prediction} />
|
|
|
|
{/* Prediction Section */}
|
|
<Box>
|
|
<Flex justify="space-between" align="center" mb={4}>
|
|
<Heading as="h2" size="lg">
|
|
{tPred("title")}
|
|
</Heading>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => refetchPrediction()}
|
|
gap={1.5}
|
|
>
|
|
<LuRefreshCw />
|
|
{tCommon("refresh")}
|
|
</Button>
|
|
</Flex>
|
|
|
|
{predLoading ? (
|
|
<Flex justify="center" py={10}>
|
|
<Spinner size="md" color="primary.500" />
|
|
</Flex>
|
|
) : prediction ? (
|
|
<PredictionCard prediction={prediction} />
|
|
) : (
|
|
<Card.Root borderColor={borderColor} borderRadius="xl">
|
|
<Card.Body>
|
|
<Flex justify="center" align="center" py={8}>
|
|
<Text color="fg.muted">{tPred("no-predictions")}</Text>
|
|
</Flex>
|
|
</Card.Body>
|
|
</Card.Root>
|
|
)}
|
|
</Box>
|
|
|
|
{/* Odds Section */}
|
|
{match.odds && Object.keys(match.odds).length > 0 && (
|
|
<OddsCard odds={match.odds} />
|
|
)}
|
|
</Box>
|
|
</SlideUp>
|
|
);
|
|
}
|