player h2h
This commit is contained in:
270
src/features/players/components/player-head-to-head-sheet.tsx
Normal file
270
src/features/players/components/player-head-to-head-sheet.tsx
Normal file
@@ -0,0 +1,270 @@
|
||||
import { Stack, Text, Group, Box, Divider, Paper } from "@mantine/core";
|
||||
import { usePlayerHeadToHead } from "@/features/matches/queries";
|
||||
import { useMemo, useEffect, useState } from "react";
|
||||
import { CrownIcon } from "@phosphor-icons/react";
|
||||
import MatchList from "@/features/matches/components/match-list";
|
||||
|
||||
interface PlayerHeadToHeadSheetProps {
|
||||
player1Id: string;
|
||||
player1Name: string;
|
||||
player2Id: string;
|
||||
player2Name: string;
|
||||
isOpen?: boolean;
|
||||
}
|
||||
|
||||
const PlayerHeadToHeadSheet = ({
|
||||
player1Id,
|
||||
player1Name,
|
||||
player2Id,
|
||||
player2Name,
|
||||
isOpen = true,
|
||||
}: PlayerHeadToHeadSheetProps) => {
|
||||
const [shouldFetch, setShouldFetch] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen && !shouldFetch) {
|
||||
setShouldFetch(true);
|
||||
}
|
||||
}, [isOpen, shouldFetch]);
|
||||
|
||||
const { data: matches, isLoading } = usePlayerHeadToHead(player1Id, player2Id, shouldFetch);
|
||||
|
||||
const stats = useMemo(() => {
|
||||
if (!matches || matches.length === 0) {
|
||||
return {
|
||||
player1Wins: 0,
|
||||
player2Wins: 0,
|
||||
player1CupsFor: 0,
|
||||
player2CupsFor: 0,
|
||||
player1CupsAgainst: 0,
|
||||
player2CupsAgainst: 0,
|
||||
player1AvgMargin: 0,
|
||||
player2AvgMargin: 0,
|
||||
};
|
||||
}
|
||||
|
||||
let player1Wins = 0;
|
||||
let player2Wins = 0;
|
||||
let player1CupsFor = 0;
|
||||
let player2CupsFor = 0;
|
||||
let player1CupsAgainst = 0;
|
||||
let player2CupsAgainst = 0;
|
||||
let player1TotalWinMargin = 0;
|
||||
let player2TotalWinMargin = 0;
|
||||
|
||||
matches.forEach((match) => {
|
||||
const isPlayer1Home = match.home?.players?.some((p) => p.id === player1Id);
|
||||
const player1Cups = isPlayer1Home ? match.home_cups : match.away_cups;
|
||||
const player2Cups = isPlayer1Home ? match.away_cups : match.home_cups;
|
||||
|
||||
if (player1Cups > player2Cups) {
|
||||
player1Wins++;
|
||||
player1TotalWinMargin += (player1Cups - player2Cups);
|
||||
} else if (player2Cups > player1Cups) {
|
||||
player2Wins++;
|
||||
player2TotalWinMargin += (player2Cups - player1Cups);
|
||||
}
|
||||
|
||||
player1CupsFor += player1Cups;
|
||||
player2CupsFor += player2Cups;
|
||||
player1CupsAgainst += player2Cups;
|
||||
player2CupsAgainst += player1Cups;
|
||||
});
|
||||
|
||||
const player1AvgMargin =
|
||||
player1Wins > 0 ? player1TotalWinMargin / player1Wins : 0;
|
||||
const player2AvgMargin =
|
||||
player2Wins > 0 ? player2TotalWinMargin / player2Wins : 0;
|
||||
|
||||
return {
|
||||
player1Wins,
|
||||
player2Wins,
|
||||
player1CupsFor,
|
||||
player2CupsFor,
|
||||
player1CupsAgainst,
|
||||
player2CupsAgainst,
|
||||
player1AvgMargin,
|
||||
player2AvgMargin,
|
||||
};
|
||||
}, [matches, player1Id]);
|
||||
|
||||
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 players have not faced each other yet.
|
||||
</Text>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
const totalGames = stats.player1Wins + stats.player2Wins;
|
||||
const leader =
|
||||
stats.player1Wins > stats.player2Wins
|
||||
? player1Name
|
||||
: stats.player2Wins > stats.player1Wins
|
||||
? player2Name
|
||||
: null;
|
||||
|
||||
return (
|
||||
<Stack gap="md">
|
||||
<Paper p="md" withBorder radius="md">
|
||||
<Stack gap="sm">
|
||||
<Group justify="center" gap="xs">
|
||||
<Text size="lg" fw={700}>
|
||||
{player1Name}
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
vs
|
||||
</Text>
|
||||
<Text size="lg" fw={700}>
|
||||
{player2Name}
|
||||
</Text>
|
||||
</Group>
|
||||
|
||||
<Group justify="center" gap="lg">
|
||||
<Stack gap={0} align="center">
|
||||
<Text size="xl" fw={700}>
|
||||
{stats.player1Wins}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed">
|
||||
{player1Name}
|
||||
</Text>
|
||||
</Stack>
|
||||
<Text size="md" c="dimmed">
|
||||
-
|
||||
</Text>
|
||||
<Stack gap={0} align="center">
|
||||
<Text size="xl" fw={700}>
|
||||
{stats.player2Wins}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed">
|
||||
{player2Name}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Group>
|
||||
|
||||
{leader && (
|
||||
<Group justify="center" gap="xs">
|
||||
<CrownIcon size={16} weight="fill" color="gold" />
|
||||
<Text size="xs" c="dimmed">
|
||||
{leader} leads the series
|
||||
</Text>
|
||||
</Group>
|
||||
)}
|
||||
|
||||
{!leader && totalGames > 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.player1CupsFor}
|
||||
</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.player2CupsFor}
|
||||
</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
<Divider />
|
||||
|
||||
<Group justify="space-between" px="md" py="sm">
|
||||
<Group gap="xs">
|
||||
<Text size="sm" fw={600}>
|
||||
{totalGames > 0
|
||||
? (stats.player1CupsFor / totalGames).toFixed(1)
|
||||
: "0.0"}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed">
|
||||
avg
|
||||
</Text>
|
||||
</Group>
|
||||
<Text size="xs" fw={500}>
|
||||
Avg Cups/Game
|
||||
</Text>
|
||||
<Group gap="xs">
|
||||
<Text size="xs" c="dimmed">
|
||||
avg
|
||||
</Text>
|
||||
<Text size="sm" fw={600}>
|
||||
{totalGames > 0
|
||||
? (stats.player2CupsFor / totalGames).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.player1AvgMargin)
|
||||
? stats.player1AvgMargin.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.player2AvgMargin)
|
||||
? stats.player2AvgMargin.toFixed(1)
|
||||
: "0.0"}
|
||||
</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
|
||||
<Stack gap="xs">
|
||||
<Text size="sm" fw={600} px="md">
|
||||
Match History ({totalGames} games)
|
||||
</Text>
|
||||
<MatchList matches={matches} hideH2H />
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlayerHeadToHeadSheet;
|
||||
Reference in New Issue
Block a user