218 lines
6.7 KiB
TypeScript
218 lines
6.7 KiB
TypeScript
import { Stack, Text, Group, Box, Divider, Paper } from "@mantine/core";
|
|
import { TeamInfo } from "@/features/teams/types";
|
|
import { useTeamHeadToHead } from "../queries";
|
|
import { useMemo, useEffect, useState, Suspense } from "react";
|
|
import { CrownIcon } from "@phosphor-icons/react";
|
|
import MatchList from "./match-list";
|
|
import TeamHeadToHeadSkeleton from "./team-head-to-head-skeleton";
|
|
|
|
interface TeamHeadToHeadSheetProps {
|
|
team1: TeamInfo;
|
|
team2: TeamInfo;
|
|
isOpen?: boolean;
|
|
}
|
|
|
|
const TeamHeadToHeadContent = ({ team1, team2, isOpen = true }: TeamHeadToHeadSheetProps) => {
|
|
const [shouldFetch, setShouldFetch] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (isOpen && !shouldFetch) {
|
|
setShouldFetch(true);
|
|
}
|
|
}, [isOpen, shouldFetch]);
|
|
|
|
const { data: matches, isLoading } = useTeamHeadToHead(team1.id, team2.id, shouldFetch);
|
|
|
|
const stats = useMemo(() => {
|
|
if (!matches || matches.length === 0) {
|
|
return {
|
|
team1Wins: 0,
|
|
team2Wins: 0,
|
|
team1CupsFor: 0,
|
|
team2CupsFor: 0,
|
|
team1CupsAgainst: 0,
|
|
team2CupsAgainst: 0,
|
|
team1AvgMargin: 0,
|
|
team2AvgMargin: 0,
|
|
};
|
|
}
|
|
|
|
let team1Wins = 0;
|
|
let team2Wins = 0;
|
|
let team1CupsFor = 0;
|
|
let team2CupsFor = 0;
|
|
let team1CupsAgainst = 0;
|
|
let team2CupsAgainst = 0;
|
|
let team1TotalWinMargin = 0;
|
|
let team2TotalWinMargin = 0;
|
|
|
|
matches.forEach((match) => {
|
|
const isTeam1Home = match.home?.id === team1.id;
|
|
const team1Cups = isTeam1Home ? match.home_cups : match.away_cups;
|
|
const team2Cups = isTeam1Home ? match.away_cups : match.home_cups;
|
|
|
|
if (team1Cups > team2Cups) {
|
|
team1Wins++;
|
|
team1TotalWinMargin += (team1Cups - team2Cups);
|
|
} else if (team2Cups > team1Cups) {
|
|
team2Wins++;
|
|
team2TotalWinMargin += (team2Cups - team1Cups);
|
|
}
|
|
|
|
team1CupsFor += team1Cups;
|
|
team2CupsFor += team2Cups;
|
|
team1CupsAgainst += team2Cups;
|
|
team2CupsAgainst += team1Cups;
|
|
});
|
|
|
|
const team1AvgMargin = team1Wins > 0
|
|
? team1TotalWinMargin / team1Wins
|
|
: 0;
|
|
const team2AvgMargin = team2Wins > 0
|
|
? team2TotalWinMargin / team2Wins
|
|
: 0;
|
|
|
|
return {
|
|
team1Wins,
|
|
team2Wins,
|
|
team1CupsFor,
|
|
team2CupsFor,
|
|
team1CupsAgainst,
|
|
team2CupsAgainst,
|
|
team1AvgMargin,
|
|
team2AvgMargin,
|
|
};
|
|
}, [matches, team1.id]);
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<Stack p="md" gap="md">
|
|
<Text size="sm" c="dimmed" ta="center">Loading...</Text>
|
|
</Stack>
|
|
);
|
|
}
|
|
|
|
if (!matches || matches.length === 0) {
|
|
return (
|
|
<Stack p="md" gap="md">
|
|
<Text size="sm" c="dimmed" ta="center">
|
|
These teams have not faced each other yet.
|
|
</Text>
|
|
</Stack>
|
|
);
|
|
}
|
|
|
|
const totalMatches = stats.team1Wins + stats.team2Wins;
|
|
const leader = stats.team1Wins > stats.team2Wins ? team1 : stats.team2Wins > stats.team1Wins ? team2 : null;
|
|
|
|
return (
|
|
<Stack gap="md">
|
|
<Paper p="md" withBorder radius="md">
|
|
<Stack gap="sm">
|
|
<Group justify="center" gap="xs">
|
|
<Text size="lg" fw={700}>{team1.name}</Text>
|
|
<Text size="sm" c="dimmed">vs</Text>
|
|
<Text size="lg" fw={700}>{team2.name}</Text>
|
|
</Group>
|
|
|
|
<Group justify="center" gap="lg">
|
|
<Stack gap={0} align="center">
|
|
<Text size="xl" fw={700}>{stats.team1Wins}</Text>
|
|
<Text size="xs" c="dimmed">{team1.name}</Text>
|
|
</Stack>
|
|
<Text size="md" c="dimmed">-</Text>
|
|
<Stack gap={0} align="center">
|
|
<Text size="xl" fw={700}>{stats.team2Wins}</Text>
|
|
<Text size="xs" c="dimmed">{team2.name}</Text>
|
|
</Stack>
|
|
</Group>
|
|
|
|
{leader && (
|
|
<Group justify="center" gap="xs">
|
|
<CrownIcon size={16} weight="fill" color="gold" />
|
|
<Text size="xs" c="dimmed">
|
|
{leader.name} leads the series
|
|
</Text>
|
|
</Group>
|
|
)}
|
|
|
|
{!leader && totalMatches > 0 && (
|
|
<Text size="xs" c="dimmed" ta="center">
|
|
Series is tied
|
|
</Text>
|
|
)}
|
|
</Stack>
|
|
</Paper>
|
|
|
|
<Stack gap={0}>
|
|
<Text size="sm" fw={600} px="md" mb="xs">Stats Comparison</Text>
|
|
|
|
<Paper withBorder>
|
|
<Stack gap={0}>
|
|
<Group justify="space-between" px="md" py="sm">
|
|
<Group gap="xs">
|
|
<Text size="sm" fw={600}>{stats.team1CupsFor}</Text>
|
|
<Text size="xs" c="dimmed">cups</Text>
|
|
</Group>
|
|
<Text size="xs" fw={500}>Total Cups</Text>
|
|
<Group gap="xs">
|
|
<Text size="xs" c="dimmed">cups</Text>
|
|
<Text size="sm" fw={600}>{stats.team2CupsFor}</Text>
|
|
</Group>
|
|
</Group>
|
|
<Divider />
|
|
|
|
<Group justify="space-between" px="md" py="sm">
|
|
<Group gap="xs">
|
|
<Text size="sm" fw={600}>
|
|
{totalMatches > 0 ? (stats.team1CupsFor / totalMatches).toFixed(1) : '0.0'}
|
|
</Text>
|
|
<Text size="xs" c="dimmed">avg</Text>
|
|
</Group>
|
|
<Text size="xs" fw={500}>Avg Cups/Match</Text>
|
|
<Group gap="xs">
|
|
<Text size="xs" c="dimmed">avg</Text>
|
|
<Text size="sm" fw={600}>
|
|
{totalMatches > 0 ? (stats.team2CupsFor / totalMatches).toFixed(1) : '0.0'}
|
|
</Text>
|
|
</Group>
|
|
</Group>
|
|
<Divider />
|
|
|
|
<Group justify="space-between" px="md" py="sm">
|
|
<Group gap="xs">
|
|
<Text size="sm" fw={600}>
|
|
{!isNaN(stats.team1AvgMargin) ? stats.team1AvgMargin.toFixed(1) : '0.0'}
|
|
</Text>
|
|
<Text size="xs" c="dimmed">margin</Text>
|
|
</Group>
|
|
<Text size="xs" fw={500}>Avg Win Margin</Text>
|
|
<Group gap="xs">
|
|
<Text size="xs" c="dimmed">margin</Text>
|
|
<Text size="sm" fw={600}>
|
|
{!isNaN(stats.team2AvgMargin) ? stats.team2AvgMargin.toFixed(1) : '0.0'}
|
|
</Text>
|
|
</Group>
|
|
</Group>
|
|
</Stack>
|
|
</Paper>
|
|
</Stack>
|
|
|
|
<Stack gap="xs">
|
|
<Text size="sm" fw={600} px="md">Match History ({totalMatches} match{totalMatches !== 1 ? 'es' : ''})</Text>
|
|
<MatchList matches={matches} hideH2H />
|
|
</Stack>
|
|
</Stack>
|
|
);
|
|
};
|
|
|
|
const TeamHeadToHeadSheet = (props: TeamHeadToHeadSheetProps) => {
|
|
return (
|
|
<Suspense fallback={<TeamHeadToHeadSkeleton />}>
|
|
<TeamHeadToHeadContent {...props} />
|
|
</Suspense>
|
|
);
|
|
};
|
|
|
|
export default TeamHeadToHeadSheet;
|