This commit is contained in:
@@ -11,10 +11,13 @@ import {
|
||||
} from "@chakra-ui/react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { motion } from "framer-motion";
|
||||
import { slideUpVariants } from "@/components/motion";
|
||||
import type { MatchResponseDto } from "@/lib/api/matches/types";
|
||||
import { useColorModeValue } from "@/components/ui/color-mode";
|
||||
import { useState } from "react";
|
||||
import { LoginModal } from "@/components/auth/login-modal";
|
||||
|
||||
interface MatchCardProps {
|
||||
match: MatchResponseDto;
|
||||
@@ -24,7 +27,10 @@ const MotionBox = motion.create(Box);
|
||||
|
||||
export default function MatchCard({ match }: MatchCardProps) {
|
||||
const t = useTranslations("matches");
|
||||
const tAuth = useTranslations("auth");
|
||||
const router = useRouter();
|
||||
const { data: session } = useSession();
|
||||
const [loginModalOpen, setLoginModalOpen] = useState(false);
|
||||
|
||||
const cardBg = useColorModeValue("white", "gray.800");
|
||||
const cardBorder = useColorModeValue("gray.100", "gray.700");
|
||||
@@ -42,6 +48,10 @@ export default function MatchCard({ match }: MatchCardProps) {
|
||||
: t("not-started");
|
||||
|
||||
const handleClick = () => {
|
||||
if (!session) {
|
||||
setLoginModalOpen(true);
|
||||
return;
|
||||
}
|
||||
router.push(`/matches/${match.id}`);
|
||||
};
|
||||
|
||||
@@ -49,180 +59,205 @@ export default function MatchCard({ match }: MatchCardProps) {
|
||||
const matchDate = new Date(match.mstUtc);
|
||||
|
||||
return (
|
||||
<MotionBox
|
||||
variants={slideUpVariants}
|
||||
bg={cardBg}
|
||||
borderWidth="1px"
|
||||
borderColor={cardBorder}
|
||||
borderRadius="xl"
|
||||
p={4}
|
||||
cursor="pointer"
|
||||
onClick={handleClick}
|
||||
transition={{ duration: 0.25 }}
|
||||
_hover={{
|
||||
bg: hoverBg,
|
||||
borderColor: hoverBorder,
|
||||
transform: "translateY(-3px)",
|
||||
shadow: "xl",
|
||||
}}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label={`${match.homeTeamName} vs ${match.awayTeamName}`}
|
||||
>
|
||||
{/* Status Badge */}
|
||||
<Flex justify="space-between" align="center" mb={3}>
|
||||
<Badge
|
||||
colorPalette={statusColor}
|
||||
variant="subtle"
|
||||
px={2}
|
||||
py={0.5}
|
||||
borderRadius="full"
|
||||
fontSize="xs"
|
||||
fontWeight="bold"
|
||||
>
|
||||
{isLive && (
|
||||
<Box
|
||||
as="span"
|
||||
display="inline-block"
|
||||
w="6px"
|
||||
h="6px"
|
||||
borderRadius="full"
|
||||
bg="red.500"
|
||||
mr={1.5}
|
||||
animation="pulse 1.5s ease-in-out infinite"
|
||||
/>
|
||||
)}
|
||||
{statusText}
|
||||
</Badge>
|
||||
|
||||
<Text fontSize="xs" color="fg.muted">
|
||||
{matchDate.toLocaleDateString("tr-TR", {
|
||||
day: "2-digit",
|
||||
month: "short",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
})}
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
{/* Teams */}
|
||||
<HStack gap={3} justify="space-between">
|
||||
{/* Home Team */}
|
||||
<VStack gap={1} flex={1} align="center" minW={0}>
|
||||
{match.homeTeamLogo ? (
|
||||
<Image
|
||||
src={match.homeTeamLogo}
|
||||
alt={match.homeTeamName}
|
||||
boxSize="40px"
|
||||
objectFit="contain"
|
||||
/>
|
||||
) : (
|
||||
<Flex
|
||||
boxSize="40px"
|
||||
bg="primary.subtle"
|
||||
borderRadius="full"
|
||||
align="center"
|
||||
justify="center"
|
||||
>
|
||||
<Text fontSize="lg" fontWeight="bold" color="primary.fg">
|
||||
{match.homeTeamName?.charAt(0) || "H"}
|
||||
</Text>
|
||||
</Flex>
|
||||
)}
|
||||
<Text
|
||||
fontSize="sm"
|
||||
fontWeight="semibold"
|
||||
textAlign="center"
|
||||
truncate
|
||||
maxW="100%"
|
||||
<>
|
||||
<MotionBox
|
||||
variants={slideUpVariants}
|
||||
bg={cardBg}
|
||||
borderWidth="1px"
|
||||
borderColor={cardBorder}
|
||||
borderRadius="xl"
|
||||
p={4}
|
||||
cursor="pointer"
|
||||
onClick={handleClick}
|
||||
transition={{ duration: 0.25 }}
|
||||
_hover={{
|
||||
bg: hoverBg,
|
||||
borderColor: hoverBorder,
|
||||
transform: "translateY(-3px)",
|
||||
shadow: "xl",
|
||||
}}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label={`${match.homeTeamName} vs ${match.awayTeamName}`}
|
||||
>
|
||||
{/* Status Badge */}
|
||||
<Flex justify="space-between" align="center" mb={3}>
|
||||
<Badge
|
||||
colorPalette={statusColor}
|
||||
variant="subtle"
|
||||
px={2}
|
||||
py={0.5}
|
||||
borderRadius="full"
|
||||
fontSize="xs"
|
||||
fontWeight="bold"
|
||||
>
|
||||
{match.homeTeamName}
|
||||
</Text>
|
||||
</VStack>
|
||||
{isLive && (
|
||||
<Box
|
||||
as="span"
|
||||
display="inline-block"
|
||||
w="6px"
|
||||
h="6px"
|
||||
borderRadius="full"
|
||||
bg="red.500"
|
||||
mr={1.5}
|
||||
animation="pulse 1.5s ease-in-out infinite"
|
||||
/>
|
||||
)}
|
||||
{statusText}
|
||||
</Badge>
|
||||
|
||||
{/* Score or VS */}
|
||||
<VStack gap={0} flexShrink={0}>
|
||||
{(isLive || isFinished) &&
|
||||
match.scoreHome !== undefined &&
|
||||
match.scoreAway !== undefined ? (
|
||||
<HStack gap={2}>
|
||||
<Text
|
||||
fontSize="2xl"
|
||||
fontWeight="900"
|
||||
color={isLive ? "red.500" : "fg"}
|
||||
>
|
||||
{match.scoreHome}
|
||||
</Text>
|
||||
<Text fontSize="lg" color="fg.muted">
|
||||
-
|
||||
</Text>
|
||||
<Text
|
||||
fontSize="2xl"
|
||||
fontWeight="900"
|
||||
color={isLive ? "red.500" : "fg"}
|
||||
>
|
||||
{match.scoreAway}
|
||||
</Text>
|
||||
</HStack>
|
||||
) : (
|
||||
<Text fontSize="md" fontWeight="bold" color="fg.muted">
|
||||
{t("vs")}
|
||||
</Text>
|
||||
)}
|
||||
</VStack>
|
||||
|
||||
{/* Away Team */}
|
||||
<VStack gap={1} flex={1} align="center" minW={0}>
|
||||
{match.awayTeamLogo ? (
|
||||
<Image
|
||||
src={match.awayTeamLogo}
|
||||
alt={match.awayTeamName}
|
||||
boxSize="40px"
|
||||
objectFit="contain"
|
||||
/>
|
||||
) : (
|
||||
<Flex
|
||||
boxSize="40px"
|
||||
bg="primary.subtle"
|
||||
borderRadius="full"
|
||||
align="center"
|
||||
justify="center"
|
||||
>
|
||||
<Text fontSize="lg" fontWeight="bold" color="primary.fg">
|
||||
{match.awayTeamName?.charAt(0) || "A"}
|
||||
</Text>
|
||||
</Flex>
|
||||
)}
|
||||
<Text
|
||||
fontSize="sm"
|
||||
fontWeight="semibold"
|
||||
textAlign="center"
|
||||
truncate
|
||||
maxW="100%"
|
||||
>
|
||||
{match.awayTeamName}
|
||||
</Text>
|
||||
</VStack>
|
||||
</HStack>
|
||||
|
||||
{/* League Info */}
|
||||
{(match.leagueName || match.countryName) && (
|
||||
<Flex
|
||||
mt={3}
|
||||
pt={2}
|
||||
borderTopWidth="1px"
|
||||
borderColor={cardBorder}
|
||||
justify="center"
|
||||
align="center"
|
||||
gap={1.5}
|
||||
>
|
||||
{/* Flag handling if available in flat response, otherwise skip or pass from parent */}
|
||||
<Text fontSize="xs" color="fg.muted" truncate>
|
||||
{match.countryName && `${match.countryName} • `}
|
||||
{match.leagueName}
|
||||
<Text fontSize="xs" color="fg.muted">
|
||||
{matchDate.toLocaleDateString("tr-TR", {
|
||||
day: "2-digit",
|
||||
month: "short",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
})}
|
||||
</Text>
|
||||
</Flex>
|
||||
)}
|
||||
</MotionBox>
|
||||
|
||||
{/* Teams */}
|
||||
<HStack gap={3} justify="space-between">
|
||||
{/* Home Team */}
|
||||
<VStack gap={1} flex={1} align="center" minW={0}>
|
||||
{match.homeTeamLogo ? (
|
||||
<Image
|
||||
src={match.homeTeamLogo}
|
||||
alt={match.homeTeamName}
|
||||
boxSize="40px"
|
||||
objectFit="contain"
|
||||
/>
|
||||
) : (
|
||||
<Flex
|
||||
boxSize="40px"
|
||||
bg="primary.subtle"
|
||||
borderRadius="full"
|
||||
align="center"
|
||||
justify="center"
|
||||
>
|
||||
<Text fontSize="lg" fontWeight="bold" color="primary.fg">
|
||||
{match.homeTeamName?.charAt(0) || "H"}
|
||||
</Text>
|
||||
</Flex>
|
||||
)}
|
||||
<Text
|
||||
fontSize="sm"
|
||||
fontWeight="semibold"
|
||||
textAlign="center"
|
||||
truncate
|
||||
maxW="100%"
|
||||
>
|
||||
{match.homeTeamName}
|
||||
</Text>
|
||||
</VStack>
|
||||
|
||||
{/* Score or VS */}
|
||||
<VStack gap={0} flexShrink={0}>
|
||||
{(isLive || isFinished) &&
|
||||
match.scoreHome !== undefined &&
|
||||
match.scoreAway !== undefined ? (
|
||||
<HStack gap={2}>
|
||||
<Text
|
||||
fontSize="2xl"
|
||||
fontWeight="900"
|
||||
color={isLive ? "red.500" : "fg"}
|
||||
>
|
||||
{match.scoreHome}
|
||||
</Text>
|
||||
<Text fontSize="lg" color="fg.muted">
|
||||
-
|
||||
</Text>
|
||||
<Text
|
||||
fontSize="2xl"
|
||||
fontWeight="900"
|
||||
color={isLive ? "red.500" : "fg"}
|
||||
>
|
||||
{match.scoreAway}
|
||||
</Text>
|
||||
</HStack>
|
||||
) : (
|
||||
<Text fontSize="md" fontWeight="bold" color="fg.muted">
|
||||
{t("vs")}
|
||||
</Text>
|
||||
)}
|
||||
</VStack>
|
||||
|
||||
{/* Away Team */}
|
||||
<VStack gap={1} flex={1} align="center" minW={0}>
|
||||
{match.awayTeamLogo ? (
|
||||
<Image
|
||||
src={match.awayTeamLogo}
|
||||
alt={match.awayTeamName}
|
||||
boxSize="40px"
|
||||
objectFit="contain"
|
||||
/>
|
||||
) : (
|
||||
<Flex
|
||||
boxSize="40px"
|
||||
bg="primary.subtle"
|
||||
borderRadius="full"
|
||||
align="center"
|
||||
justify="center"
|
||||
>
|
||||
<Text fontSize="lg" fontWeight="bold" color="primary.fg">
|
||||
{match.awayTeamName?.charAt(0) || "A"}
|
||||
</Text>
|
||||
</Flex>
|
||||
)}
|
||||
<Text
|
||||
fontSize="sm"
|
||||
fontWeight="semibold"
|
||||
textAlign="center"
|
||||
truncate
|
||||
maxW="100%"
|
||||
>
|
||||
{match.awayTeamName}
|
||||
</Text>
|
||||
</VStack>
|
||||
</HStack>
|
||||
|
||||
{/* League Info */}
|
||||
{(match.leagueName || match.countryName) && (
|
||||
<Flex
|
||||
mt={3}
|
||||
pt={2}
|
||||
borderTopWidth="1px"
|
||||
borderColor={cardBorder}
|
||||
justify="center"
|
||||
align="center"
|
||||
gap={1.5}
|
||||
>
|
||||
{/* Flag handling if available in flat response, otherwise skip or pass from parent */}
|
||||
<Text fontSize="xs" color="fg.muted" truncate>
|
||||
{match.countryName && `${match.countryName} • `}
|
||||
{match.leagueName}
|
||||
</Text>
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{/* Auth hint for unauthenticated users */}
|
||||
{!session && (
|
||||
<Flex
|
||||
mt={2}
|
||||
pt={2}
|
||||
borderTopWidth="1px"
|
||||
borderColor={cardBorder}
|
||||
justify="center"
|
||||
align="center"
|
||||
>
|
||||
<Text fontSize="xs" color="orange.500" fontWeight="semibold">
|
||||
🔒 {tAuth("login-required-title")}
|
||||
</Text>
|
||||
</Flex>
|
||||
)}
|
||||
</MotionBox>
|
||||
|
||||
{/* Login Modal — shown when unauthenticated user clicks a match */}
|
||||
<LoginModal
|
||||
open={loginModalOpen}
|
||||
onOpenChange={setLoginModalOpen}
|
||||
initialMode="login"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,17 +12,50 @@ import {
|
||||
Spinner,
|
||||
Button,
|
||||
Card,
|
||||
Grid,
|
||||
SimpleGrid,
|
||||
} 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 { SlideUp, FadeIn } 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";
|
||||
import { LuArrowLeft, LuRefreshCw, LuShield, LuFlag, LuUser } from "react-icons/lu";
|
||||
import type { MatchResponseDto } from "@/lib/api/matches/types";
|
||||
|
||||
// ─────────────────────────────────────────────────
|
||||
// Type helpers for sidelined data
|
||||
// ─────────────────────────────────────────────────
|
||||
|
||||
interface SidelinedPlayer {
|
||||
playerName: string;
|
||||
position?: string;
|
||||
positionShort?: string;
|
||||
description?: string;
|
||||
type?: string;
|
||||
matchesMissed?: number;
|
||||
reasonIcon?: string;
|
||||
}
|
||||
|
||||
interface SidelinedTeam {
|
||||
teamId?: string;
|
||||
teamName?: string;
|
||||
totalSidelined?: number;
|
||||
players?: SidelinedPlayer[];
|
||||
}
|
||||
|
||||
interface SidelinedData {
|
||||
homeTeam?: SidelinedTeam;
|
||||
awayTeam?: SidelinedTeam;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────
|
||||
// Main Component
|
||||
// ─────────────────────────────────────────────────
|
||||
|
||||
export default function MatchDetailContent() {
|
||||
const t = useTranslations("matches");
|
||||
@@ -41,9 +74,13 @@ export default function MatchDetailContent() {
|
||||
} = usePrediction(matchId);
|
||||
|
||||
const headerBg = useColorModeValue("white", "gray.800");
|
||||
const cardBg = useColorModeValue("white", "gray.800");
|
||||
const borderColor = useColorModeValue("gray.100", "gray.700");
|
||||
const subtleBg = useColorModeValue("gray.50", "gray.900");
|
||||
const injuryBg = useColorModeValue("red.50", "rgba(254, 178, 178, 0.08)");
|
||||
const injuryBorder = useColorModeValue("red.100", "red.800");
|
||||
|
||||
const match = matchData?.data;
|
||||
const match = matchData?.data as MatchResponseDto | undefined;
|
||||
const prediction = predictionData?.data;
|
||||
|
||||
if (matchLoading) {
|
||||
@@ -70,6 +107,10 @@ export default function MatchDetailContent() {
|
||||
|
||||
const isLive = match.status === "LIVE";
|
||||
const isFinished = match.status === "Finished";
|
||||
const sidelined = (match.sidelined || {}) as SidelinedData;
|
||||
const hasSidelined =
|
||||
(sidelined.homeTeam?.players?.length || 0) > 0 ||
|
||||
(sidelined.awayTeam?.players?.length || 0) > 0;
|
||||
|
||||
return (
|
||||
<SlideUp>
|
||||
@@ -85,26 +126,32 @@ export default function MatchDetailContent() {
|
||||
<LuArrowLeft />
|
||||
{tCommon("back")}
|
||||
</Button>
|
||||
{/* Match Header */}
|
||||
|
||||
{/* ═══════════════════════════════════════════ */}
|
||||
{/* Match Header Card */}
|
||||
{/* ═══════════════════════════════════════════ */}
|
||||
<Card.Root
|
||||
bg={headerBg}
|
||||
borderColor={borderColor}
|
||||
borderRadius="xl"
|
||||
mb={6}
|
||||
overflow="hidden"
|
||||
>
|
||||
<Card.Body>
|
||||
{/* League Info */}
|
||||
{match.league && (
|
||||
<Flex justify="center" align="center" gap={2} mb={4}>
|
||||
{/* League Banner */}
|
||||
{match.league && (
|
||||
<Box bg={subtleBg} px={4} py={2.5} borderBottomWidth="1px" borderColor={borderColor}>
|
||||
<Flex justify="center" align="center" gap={2}>
|
||||
{match.league.country?.flag && (
|
||||
<Image
|
||||
src={match.league.country.flag}
|
||||
alt={match.league.country.name || ""}
|
||||
boxSize="18px"
|
||||
objectFit="contain"
|
||||
borderRadius="sm"
|
||||
/>
|
||||
)}
|
||||
<Text fontSize="sm" color="fg.muted" fontWeight="medium">
|
||||
<Text fontSize="sm" fontWeight="semibold" color="fg.muted">
|
||||
{match.league.country?.name && `${match.league.country.name} • `}
|
||||
{match.league.name}
|
||||
</Text>
|
||||
<Badge
|
||||
@@ -132,8 +179,10 @@ export default function MatchDetailContent() {
|
||||
: t("not-started")}
|
||||
</Badge>
|
||||
</Flex>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Card.Body py={6}>
|
||||
{/* Teams & Score */}
|
||||
<HStack gap={6} justify="center" align="center">
|
||||
{/* Home Team */}
|
||||
@@ -142,12 +191,12 @@ export default function MatchDetailContent() {
|
||||
<Image
|
||||
src={match.homeTeam.logo}
|
||||
alt={match.homeTeam.name}
|
||||
boxSize="64px"
|
||||
boxSize="72px"
|
||||
objectFit="contain"
|
||||
/>
|
||||
) : (
|
||||
<Flex
|
||||
boxSize="64px"
|
||||
boxSize="72px"
|
||||
bg="primary.subtle"
|
||||
borderRadius="full"
|
||||
align="center"
|
||||
@@ -161,9 +210,9 @@ export default function MatchDetailContent() {
|
||||
<Text fontSize="md" fontWeight="bold" textAlign="center">
|
||||
{match.homeTeam?.name}
|
||||
</Text>
|
||||
<Text fontSize="xs" color="fg.muted">
|
||||
<Badge variant="subtle" colorPalette="blue" fontSize="2xs">
|
||||
{t("home-team")}
|
||||
</Text>
|
||||
</Badge>
|
||||
</VStack>
|
||||
|
||||
{/* Score */}
|
||||
@@ -171,18 +220,20 @@ export default function MatchDetailContent() {
|
||||
{match.score && (isLive || isFinished) ? (
|
||||
<HStack gap={3}>
|
||||
<Text
|
||||
fontSize="4xl"
|
||||
fontSize="5xl"
|
||||
fontWeight="900"
|
||||
lineHeight="1"
|
||||
color={isLive ? "red.500" : "fg"}
|
||||
>
|
||||
{match.score.home}
|
||||
</Text>
|
||||
<Text fontSize="2xl" color="fg.muted">
|
||||
-
|
||||
<Text fontSize="2xl" color="fg.muted" fontWeight="300">
|
||||
:
|
||||
</Text>
|
||||
<Text
|
||||
fontSize="4xl"
|
||||
fontSize="5xl"
|
||||
fontWeight="900"
|
||||
lineHeight="1"
|
||||
color={isLive ? "red.500" : "fg"}
|
||||
>
|
||||
{match.score.away}
|
||||
@@ -211,12 +262,12 @@ export default function MatchDetailContent() {
|
||||
<Image
|
||||
src={match.awayTeam.logo}
|
||||
alt={match.awayTeam.name}
|
||||
boxSize="64px"
|
||||
boxSize="72px"
|
||||
objectFit="contain"
|
||||
/>
|
||||
) : (
|
||||
<Flex
|
||||
boxSize="64px"
|
||||
boxSize="72px"
|
||||
bg="primary.subtle"
|
||||
borderRadius="full"
|
||||
align="center"
|
||||
@@ -230,18 +281,77 @@ export default function MatchDetailContent() {
|
||||
<Text fontSize="md" fontWeight="bold" textAlign="center">
|
||||
{match.awayTeam?.name}
|
||||
</Text>
|
||||
<Text fontSize="xs" color="fg.muted">
|
||||
<Badge variant="subtle" colorPalette="orange" fontSize="2xs">
|
||||
{t("away-team")}
|
||||
</Text>
|
||||
</Badge>
|
||||
</VStack>
|
||||
</HStack>
|
||||
|
||||
{/* Referee Info */}
|
||||
{match.refereeName && (
|
||||
<Flex
|
||||
justify="center"
|
||||
align="center"
|
||||
gap={1.5}
|
||||
mt={4}
|
||||
pt={3}
|
||||
borderTopWidth="1px"
|
||||
borderColor={borderColor}
|
||||
>
|
||||
<LuUser size={14} />
|
||||
<Text fontSize="xs" color="fg.muted">
|
||||
{t("referee")}: <Text as="span" fontWeight="semibold" color="fg">{match.refereeName}</Text>
|
||||
</Text>
|
||||
</Flex>
|
||||
)}
|
||||
</Card.Body>
|
||||
</Card.Root>
|
||||
|
||||
{/* Lineups Section */}
|
||||
{/* ═══════════════════════════════════════════ */}
|
||||
{/* Sidelined / Injuries Card */}
|
||||
{/* ═══════════════════════════════════════════ */}
|
||||
{hasSidelined && (
|
||||
<FadeIn>
|
||||
<Card.Root bg={cardBg} borderColor={borderColor} borderRadius="xl" mb={6}>
|
||||
<Card.Body>
|
||||
<Heading as="h2" size="md" mb={4}>
|
||||
🏥 {t("sidelined")}
|
||||
</Heading>
|
||||
|
||||
<SimpleGrid columns={{ base: 1, md: 2 }} gap={4}>
|
||||
{/* Home Team Sidelined */}
|
||||
<SidelinedColumn
|
||||
team={sidelined.homeTeam}
|
||||
teamName={match.homeTeam?.name || ""}
|
||||
teamLogo={match.homeTeam?.logo}
|
||||
injuryBg={injuryBg}
|
||||
injuryBorder={injuryBorder}
|
||||
t={t}
|
||||
/>
|
||||
|
||||
{/* Away Team Sidelined */}
|
||||
<SidelinedColumn
|
||||
team={sidelined.awayTeam}
|
||||
teamName={match.awayTeam?.name || ""}
|
||||
teamLogo={match.awayTeam?.logo}
|
||||
injuryBg={injuryBg}
|
||||
injuryBorder={injuryBorder}
|
||||
t={t}
|
||||
/>
|
||||
</SimpleGrid>
|
||||
</Card.Body>
|
||||
</Card.Root>
|
||||
</FadeIn>
|
||||
)}
|
||||
|
||||
{/* ═══════════════════════════════════════════ */}
|
||||
{/* Lineups Section */}
|
||||
{/* ═══════════════════════════════════════════ */}
|
||||
<LineupsCard match={match} prediction={prediction} />
|
||||
|
||||
{/* Prediction Section */}
|
||||
{/* ═══════════════════════════════════════════ */}
|
||||
{/* Prediction Section */}
|
||||
{/* ═══════════════════════════════════════════ */}
|
||||
<Box>
|
||||
<Flex justify="space-between" align="center" mb={4}>
|
||||
<Heading as="h2" size="lg">
|
||||
@@ -275,7 +385,9 @@ export default function MatchDetailContent() {
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* Odds Section */}
|
||||
{/* ═══════════════════════════════════════════ */}
|
||||
{/* Odds Section */}
|
||||
{/* ═══════════════════════════════════════════ */}
|
||||
{match.odds && Object.keys(match.odds).length > 0 && (
|
||||
<OddsCard odds={match.odds} />
|
||||
)}
|
||||
@@ -283,3 +395,88 @@ export default function MatchDetailContent() {
|
||||
</SlideUp>
|
||||
);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────
|
||||
// Sidelined Column Sub-Component
|
||||
// ─────────────────────────────────────────────────
|
||||
|
||||
interface SidelinedColumnProps {
|
||||
team?: SidelinedTeam;
|
||||
teamName: string;
|
||||
teamLogo?: string;
|
||||
injuryBg: string;
|
||||
injuryBorder: string;
|
||||
t: ReturnType<typeof useTranslations>;
|
||||
}
|
||||
|
||||
function SidelinedColumn({ team, teamName, teamLogo, injuryBg, injuryBorder, t }: SidelinedColumnProps) {
|
||||
const players = team?.players || [];
|
||||
|
||||
if (players.length === 0) {
|
||||
return (
|
||||
<Box>
|
||||
<HStack gap={2} mb={3}>
|
||||
{teamLogo && <Image src={teamLogo} alt={teamName} boxSize="20px" objectFit="contain" />}
|
||||
<Text fontSize="sm" fontWeight="bold">{teamName}</Text>
|
||||
</HStack>
|
||||
<Text fontSize="xs" color="fg.muted" fontStyle="italic">
|
||||
{t("no-sidelined")}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<HStack gap={2} mb={3}>
|
||||
{teamLogo && <Image src={teamLogo} alt={teamName} boxSize="20px" objectFit="contain" />}
|
||||
<Text fontSize="sm" fontWeight="bold">{teamName}</Text>
|
||||
<Badge colorPalette="red" variant="subtle" fontSize="2xs" borderRadius="full">
|
||||
{players.length}
|
||||
</Badge>
|
||||
</HStack>
|
||||
<VStack gap={2} align="stretch">
|
||||
{players.map((player, idx) => (
|
||||
<Box
|
||||
key={`${player.playerName}-${idx}`}
|
||||
bg={injuryBg}
|
||||
borderWidth="1px"
|
||||
borderColor={injuryBorder}
|
||||
borderRadius="lg"
|
||||
px={3}
|
||||
py={2}
|
||||
>
|
||||
<Flex justify="space-between" align="center">
|
||||
<VStack gap={0} align="start">
|
||||
<Text fontSize="sm" fontWeight="semibold">
|
||||
{player.playerName}
|
||||
</Text>
|
||||
<HStack gap={1.5}>
|
||||
{player.positionShort && (
|
||||
<Badge variant="outline" fontSize="2xs" borderRadius="full">
|
||||
{player.positionShort}
|
||||
</Badge>
|
||||
)}
|
||||
<Text fontSize="xs" color="fg.muted">
|
||||
{player.description || (
|
||||
player.type === "injury"
|
||||
? t("injury")
|
||||
: player.type === "suspended"
|
||||
? t("suspended")
|
||||
: t("other-reason")
|
||||
)}
|
||||
</Text>
|
||||
</HStack>
|
||||
</VStack>
|
||||
{player.matchesMissed !== undefined && player.matchesMissed > 0 && (
|
||||
<Badge colorPalette="red" variant="subtle" fontSize="2xs">
|
||||
{player.matchesMissed} {t("matches-missed")}
|
||||
</Badge>
|
||||
)}
|
||||
</Flex>
|
||||
</Box>
|
||||
))}
|
||||
</VStack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user