Files
flxn-app/src/features/matches/components/match-card.tsx
2026-02-09 23:36:04 -06:00

241 lines
7.8 KiB
TypeScript

import { Text, Group, Stack, Paper, Indicator, Box, Tooltip, ActionIcon } from "@mantine/core";
import { FootballHelmetIcon } from "@phosphor-icons/react";
import { useNavigate } from "@tanstack/react-router";
import { Match } from "../types";
import TeamAvatar from "@/components/team-avatar";
import EmojiBar from "@/features/reactions/components/emoji-bar";
import { Suspense } from "react";
import { useSheet } from "@/hooks/use-sheet";
import Sheet from "@/components/sheet/sheet";
import TeamHeadToHeadSheet from "./team-head-to-head-sheet";
interface MatchCardProps {
match: Match;
hideH2H?: boolean;
}
const MatchCard = ({ match, hideH2H = false }: MatchCardProps) => {
const navigate = useNavigate();
const h2hSheet = useSheet();
const isHomeWin = match.home_cups > match.away_cups;
const isAwayWin = match.away_cups > match.home_cups;
const isStarted = match.status === "started";
const hasPrivate = match.home?.private || match.away?.private;
const handleHomeTeamClick = (e: React.MouseEvent) => {
e.stopPropagation();
if (match.home?.id) {
navigate({ to: `/teams/${match.home.id}` });
}
};
const handleAwayTeamClick = (e: React.MouseEvent) => {
e.stopPropagation();
if (match.away?.id) {
navigate({ to: `/teams/${match.away.id}` });
}
};
const handleH2HClick = (e: React.MouseEvent) => {
e.stopPropagation();
h2hSheet.open();
};
return (
<>
<Box style={{ position: "relative" }}>
<Paper
px="md"
py="md"
withBorder
radius="md"
style={{ position: "relative", zIndex: 2 }}
>
<Stack gap="sm">
<Group gap="xs" justify="space-between">
<Group gap="xs" style={{ flex: 1 }}>
{isStarted && (
<Indicator
size={8}
color="red"
processing
position="middle-start"
offset={0}
/>
)}
<Text size="xs" fw={600} lineClamp={1} c="dimmed">
{match.tournament.name}
</Text>
{!match.tournament.regional && (
<>
<Text c="dimmed">-</Text>
<Text size="xs" c="dimmed">
Round {match.round + 1}
{match.is_losers_bracket && " (Losers)"}
</Text>
</>
)}
</Group>
{match.home && match.away && !hideH2H && !hasPrivate && (
<Tooltip label="Head to Head" withArrow position="left">
<ActionIcon
variant="subtle"
size="sm"
onClick={handleH2HClick}
aria-label="View head-to-head"
w={40}
>
<Group style={{ position: 'relative', width: 27.5, height: 16 }}>
<FootballHelmetIcon
size={14}
style={{
position: 'absolute',
left: 0,
top: 0,
transform: 'rotate(25deg)',
}}
/>
<FootballHelmetIcon
size={14}
style={{
position: 'absolute',
right: 0,
top: 0,
transform: 'scaleX(-1) rotate(25deg)',
}}
/>
</Group>
</ActionIcon>
</Tooltip>
)}
</Group>
<Group justify="space-between" align="center">
<Group gap="sm" style={{ flex: 1 }}>
<Box
style={{ cursor: "pointer" }}
onClick={handleHomeTeamClick}
>
<TeamAvatar
team={match.home!}
size={40}
radius="sm"
winner={isHomeWin}
isRegional={match.tournament.regional}
/>
</Box>
<Tooltip
label={match.home?.name!}
disabled={!match.home?.name}
events={{ hover: true, focus: true, touch: true }}
>
<Text
size="sm"
fw={600}
lineClamp={1}
style={{ minWidth: 0, flex: 1, cursor: 'pointer' }}
>
{match.home?.name!}
</Text>
</Tooltip>
</Group>
<Stack gap={1}>
{match.home?.players.map((p) => (
<Text key={`match-card-p-${p.id}`} size="xs" fw={600} c="dimmed" ta="right">
{p.first_name} {p.last_name}
</Text>
))}
</Stack>
<Text
size="xl"
fw={700}
c={"dimmed"}
display={match.status === "ended" ? undefined : "none"}
>
{match.home_cups}
</Text>
</Group>
<Group justify="space-between" align="center">
<Group gap="sm" style={{ flex: 1 }}>
<Box
style={{ cursor: "pointer" }}
onClick={handleAwayTeamClick}
>
<TeamAvatar
team={match.away!}
size={40}
radius="sm"
winner={isAwayWin}
isRegional={match.tournament.regional}
/>
</Box>
<Tooltip
label={match.away?.name}
disabled={!match.away?.name}
events={{ hover: true, focus: true, touch: true }}
>
<Text
size="sm"
fw={600}
lineClamp={1}
style={{ minWidth: 0, flex: 1, cursor: 'pointer' }}
>
{match.away?.name}
</Text>
</Tooltip>
</Group>
<Stack gap={1}>
{match.away?.players.map((p) => (
<Text key={`match-card-p-${p.id}`} size="xs" fw={600} c="dimmed" ta="right">
{p.first_name} {p.last_name}
</Text>
))}
</Stack>
<Text
size="xl"
fw={700}
c={"dimmed"}
display={match.status === "ended" ? undefined : "none"}
>
{match.away_cups}
</Text>
</Group>
</Stack>
</Paper>
<Paper
px="md"
pb={4}
style={{
position: "relative",
zIndex: 1,
marginTop: -6,
paddingTop: 8,
backgroundColor: "var(--mantine-color-gray-light)",
borderTop: "none",
borderRadius:
"0 0 var(--mantine-radius-md) var(--mantine-radius-md)",
border: "1px solid var(--mantine-color-default-border)",
}}
>
<Suspense>
<EmojiBar matchId={match.id} />
</Suspense>
</Paper>
</Box>
{match.home && match.away && !hideH2H && h2hSheet.isOpen && (
<Sheet
title="Head to Head"
{...h2hSheet.props}
>
<TeamHeadToHeadSheet team1={match.home} team2={match.away} isOpen={h2hSheet.props.opened} />
</Sheet>
)}
</>
);
};
export default MatchCard;