Files
flxn-app/src/features/players/components/league-head-to-head.tsx
T

198 lines
6.8 KiB
TypeScript

import { Stack, Text, TextInput, Box, Paper, Group, Divider, ActionIcon, Button } from "@mantine/core";
import { useState, useMemo } from "react";
import { MagnifyingGlassIcon, XIcon, ArrowRightIcon, PlusIcon } from "@phosphor-icons/react";
import { Trans, useLingui } from "@lingui/react/macro";
import { useAllPlayerStats } from "../queries";
import { useAuth } from "@/contexts/auth-context";
import { useSheet } from "@/hooks/use-sheet";
import Sheet from "@/components/sheet/sheet";
import PlayerHeadToHeadSheet from "./player-head-to-head-sheet";
import PlayerAvatar from "@/components/player-avatar";
const LeagueHeadToHead = () => {
const { t } = useLingui();
const { user } = useAuth();
const [player1Id, setPlayer1Id] = useState<string | null>(null);
const [player2Id, setPlayer2Id] = useState<string | null>(null);
const [search, setSearch] = useState("");
const { data: allPlayerStats } = useAllPlayerStats();
const h2hSheet = useSheet();
const myName = useMemo(() => {
if (!user) return "";
const fromStats = allPlayerStats?.find((p) => p.player_id === user.id)?.player_name;
return fromStats || [user.first_name, user.last_name].filter(Boolean).join(" ");
}, [user, allPlayerStats]);
const player1Name = useMemo(() => {
if (!player1Id) return "";
if (user && player1Id === user.id) return myName;
return allPlayerStats?.find((p) => p.player_id === player1Id)?.player_name || "";
}, [player1Id, allPlayerStats, user, myName]);
const player2Name = useMemo(() => {
if (!player2Id || !allPlayerStats) return "";
return allPlayerStats.find((p) => p.player_id === player2Id)?.player_name || "";
}, [player2Id, allPlayerStats]);
const filteredPlayers = useMemo(() => {
if (!allPlayerStats) return [];
return allPlayerStats
.filter((stat) => stat.player_id !== player1Id)
.filter((stat) =>
stat.player_name.toLowerCase().includes(search.toLowerCase())
)
.sort((a, b) => b.matches - a.matches);
}, [allPlayerStats, player1Id, search]);
const handlePlayerClick = (playerId: string) => {
if (!player1Id) {
setPlayer1Id(playerId);
} else {
setPlayer2Id(playerId);
h2hSheet.open();
}
};
const handleClearPlayer1 = () => {
setPlayer1Id(null);
setPlayer2Id(null);
};
return (
<>
<Stack gap="md">
<Box px="md" pt="md">
<Paper
p="sm"
withBorder
shadow="sm"
style={{
minHeight: 64,
display: "flex",
alignItems: "center",
borderWidth: 2,
borderColor: !player1Id ? "var(--mantine-primary-color-filled)" : undefined,
transition: "border-color 150ms ease",
}}
>
{player1Id ? (
<Group justify="space-between" wrap="nowrap" style={{ flex: 1 }}>
<Group gap="sm" wrap="nowrap" style={{ minWidth: 0 }}>
<PlayerAvatar name={player1Name} size={36} disableFullscreen />
<Box style={{ minWidth: 0 }}>
<Text size="sm" fw={600} truncate>
{player1Name}
</Text>
<Text size="xs" c="dimmed">
<Trans>Tap an opponent below to compare</Trans>
</Text>
</Box>
</Group>
<ActionIcon
variant="light"
color="gray"
size="sm"
radius="xl"
onClick={handleClearPlayer1}
aria-label={t`Clear selected player`}
>
<XIcon size={12} />
</ActionIcon>
</Group>
) : (
<Group justify="space-between" wrap="nowrap" style={{ flex: 1 }}>
<Group gap="sm" wrap="nowrap">
<PlayerAvatar size={36} disableFullscreen />
<Text size="sm" c="dimmed" fw={500}>
<Trans>Select a player</Trans>
</Text>
</Group>
{user && (
<Button
variant="light"
size="xs"
radius="xl"
leftSection={<PlusIcon size={12} />}
onClick={() => setPlayer1Id(user.id)}
>
<Trans>Me</Trans>
</Button>
)}
</Group>
)}
</Paper>
</Box>
<TextInput
placeholder={t`Search players`}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
leftSection={<MagnifyingGlassIcon size={16} />}
size="md"
px="md"
/>
<Box px="md" pb="md">
<Paper withBorder>
{filteredPlayers.length === 0 && (
<Text size="sm" c="dimmed" ta="center" py="xl">
{search ? t`No players found matching "${search}"` : t`No players available`}
</Text>
)}
{filteredPlayers.map((player, index) => (
<Box key={player.player_id}>
<Group
p="md"
gap="sm"
wrap="nowrap"
className="flxn-press"
style={{
cursor: "pointer",
transition: "background-color 150ms ease",
}}
onClick={() => handlePlayerClick(player.player_id)}
styles={{
root: {
"&:hover": {
backgroundColor: "var(--mantine-color-default-hover)",
},
},
}}
>
<PlayerAvatar name={player.player_name} size={44} disableFullscreen />
<Box style={{ flex: 1, minWidth: 0 }}>
<Text size="sm" fw={600} truncate>
{player.player_name}
</Text>
</Box>
<ActionIcon variant="subtle" color="gray" size="lg" radius="xl">
{player1Id ? <ArrowRightIcon size={18} /> : <PlusIcon size={18} />}
</ActionIcon>
</Group>
{index < filteredPlayers.length - 1 && <Divider />}
</Box>
))}
</Paper>
</Box>
</Stack>
{player1Id && player2Id && (
<Sheet title={t`Head to Head`} {...h2hSheet.props}>
<PlayerHeadToHeadSheet
player1Id={player1Id}
player1Name={player1Name}
player2Id={player2Id}
player2Name={player2Name}
isOpen={h2hSheet.props.opened}
/>
</Sheet>
)}
</>
);
};
export default LeagueHeadToHead;