165 lines
4.0 KiB
TypeScript
165 lines
4.0 KiB
TypeScript
import { useState, useMemo } from "react";
|
|
import {
|
|
Text,
|
|
TextInput,
|
|
Stack,
|
|
Container,
|
|
Box,
|
|
ThemeIcon,
|
|
Title,
|
|
} from "@mantine/core";
|
|
import {
|
|
MagnifyingGlassIcon,
|
|
UsersIcon,
|
|
} from "@phosphor-icons/react";
|
|
import { Tournament } from "@/features/tournaments/types";
|
|
import TeamList from "./team-list";
|
|
import Sheet from "@/components/sheet/sheet";
|
|
import TeamForm from "./team-form";
|
|
import { useSheet } from "@/hooks/use-sheet";
|
|
import { useTeam } from "../queries";
|
|
|
|
interface TeamEditSheetProps {
|
|
teamId: string;
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
}
|
|
|
|
const TeamEditSheet = ({ teamId, isOpen, onClose }: TeamEditSheetProps) => {
|
|
const { data: team } = useTeam(teamId);
|
|
|
|
return (
|
|
<Sheet
|
|
title={team ? `Edit ${team.name}` : "Edit Team"}
|
|
opened={isOpen}
|
|
onChange={onClose}
|
|
>
|
|
{team && (
|
|
<TeamForm
|
|
teamId={team.id}
|
|
initialValues={{
|
|
...team,
|
|
players: team.players ? team.players.map((p) => p.id) : [],
|
|
logo: typeof team.logo === "string" ? undefined : team.logo,
|
|
}}
|
|
close={onClose}
|
|
/>
|
|
)}
|
|
</Sheet>
|
|
);
|
|
};
|
|
|
|
interface ManageTeamsProps {
|
|
tournament: Tournament;
|
|
}
|
|
|
|
const ManageTeams = ({ tournament }: ManageTeamsProps) => {
|
|
const [search, setSearch] = useState("");
|
|
const [selectedTeamId, setSelectedTeamId] = useState<string | null>(null);
|
|
|
|
const {
|
|
isOpen: editTeamOpened,
|
|
open: openEditTeam,
|
|
close: closeEditTeam,
|
|
} = useSheet();
|
|
|
|
const teams = tournament.teams || [];
|
|
|
|
const filteredTeams = useMemo(() => {
|
|
if (!search.trim()) return teams;
|
|
|
|
const searchLower = search.toLowerCase();
|
|
|
|
return teams.filter((team) => {
|
|
if (team.name.toLowerCase().includes(searchLower)) {
|
|
return true;
|
|
}
|
|
|
|
if (team.players) {
|
|
return team.players.some((player) => {
|
|
const firstName = player.first_name?.toLowerCase() || "";
|
|
const lastName = player.last_name?.toLowerCase() || "";
|
|
const fullName = `${firstName} ${lastName}`.toLowerCase();
|
|
|
|
return fullName.includes(searchLower) ||
|
|
firstName.includes(searchLower) ||
|
|
lastName.includes(searchLower);
|
|
});
|
|
}
|
|
|
|
return false;
|
|
});
|
|
}, [teams, search]);
|
|
|
|
const handleTeamClick = (teamId: string) => {
|
|
setSelectedTeamId(teamId);
|
|
openEditTeam();
|
|
};
|
|
|
|
const handleCloseEditTeam = () => {
|
|
setSelectedTeamId(null);
|
|
closeEditTeam();
|
|
};
|
|
|
|
if (!teams.length) {
|
|
return (
|
|
<Container px={0} size="md">
|
|
<Stack align="center" gap="md" py="xl">
|
|
<ThemeIcon size="xl" variant="light" radius="md">
|
|
<UsersIcon size={32} />
|
|
</ThemeIcon>
|
|
<Title order={3} c="dimmed">
|
|
No Teams Enrolled
|
|
</Title>
|
|
<Text c="dimmed" ta="center">
|
|
This tournament has no enrolled teams yet.
|
|
</Text>
|
|
</Stack>
|
|
</Container>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<Container size="100%" px={0}>
|
|
<Stack gap="xs">
|
|
<TextInput
|
|
placeholder="Search teams by name or player..."
|
|
value={search}
|
|
onChange={(e) => setSearch(e.currentTarget.value)}
|
|
leftSection={<MagnifyingGlassIcon size={16} />}
|
|
size="md"
|
|
px="md"
|
|
/>
|
|
|
|
<Box px="md">
|
|
<Text size="xs" c="dimmed">
|
|
{filteredTeams.length} of {teams.length} teams
|
|
</Text>
|
|
</Box>
|
|
|
|
<TeamList
|
|
teams={filteredTeams}
|
|
onTeamClick={handleTeamClick}
|
|
/>
|
|
|
|
{filteredTeams.length === 0 && search && (
|
|
<Text ta="center" c="dimmed" py="xl">
|
|
No teams found matching "{search}"
|
|
</Text>
|
|
)}
|
|
</Stack>
|
|
</Container>
|
|
|
|
{selectedTeamId && (
|
|
<TeamEditSheet
|
|
teamId={selectedTeamId}
|
|
isOpen={editTeamOpened}
|
|
onClose={handleCloseEditTeam}
|
|
/>
|
|
)}
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default ManageTeams; |