import { ActionIcon, Card, Flex, Text, Stack, Indicator } from "@mantine/core"; import { PlayIcon, PencilIcon, SpeakerHighIcon } from "@phosphor-icons/react"; import React, { useCallback, useMemo } from "react"; import { MatchSlot } from "./match-slot"; import { Match } from "@/features/matches/types"; import { useSheet } from "@/hooks/use-sheet"; import { MatchForm } from "./match-form"; import Sheet from "@/components/sheet/sheet"; import { useServerMutation } from "@/lib/tanstack-query/hooks"; import { endMatch, startMatch } from "@/features/matches/server"; import { tournamentKeys } from "@/features/tournaments/queries"; import { useQueryClient } from "@tanstack/react-query"; interface MatchCardProps { match: Match; orders: Record; showControls?: boolean; } export const MatchCard: React.FC = ({ match, orders, showControls, }) => { const queryClient = useQueryClient(); const editSheet = useSheet(); const homeSlot = useMemo( () => ({ from: orders[match.home_from_lid], from_loser: match.home_from_loser, team: match.home, seed: match.home_seed, cups: match.status === "ended" ? match.home_cups : undefined, isWinner: match.status === "ended" && match.home_cups !== undefined && match.away_cups !== undefined && match.home_cups > match.away_cups, }), [match] ); const awaySlot = useMemo( () => ({ from: orders[match.away_from_lid], from_loser: match.away_from_loser, team: match.away, seed: match.away_seed, cups: match.status === "ended" ? match.away_cups : undefined, isWinner: match.status === "ended" && match.away_cups !== undefined && match.home_cups !== undefined && match.away_cups > match.home_cups, }), [match] ); const showToolbar = useMemo( () => match.status === "ready" && showControls, [match.status, showControls] ); const showEditButton = useMemo( () => showControls && match.status === "started", [showControls, match.status] ); const start = useServerMutation({ mutationFn: startMatch, successMessage: "Match started!", onSuccess: () => { queryClient.invalidateQueries({ queryKey: tournamentKeys.details(match.tournament.id), }); }, }); const end = useServerMutation({ mutationFn: endMatch, onSuccess: () => { queryClient.invalidateQueries({ queryKey: tournamentKeys.details(match.tournament.id), }); }, }); const handleStart = useCallback(async () => { await start.mutate({ data: match.id, }); }, [match]); const handleFormSubmit = useCallback( async (data: { home_cups: number; away_cups: number; ot_count: number; }) => { await end.mutate({ data: { ...data, matchId: match.id, }, }); editSheet.close(); }, [match.id, editSheet] ); const handleSpeakerClick = useCallback(() => { if ("speechSynthesis" in window && match.home?.name && match.away?.name) { const utterance = new SpeechSynthesisUtterance( `${match.home.name} vs. ${match.away.name}` ); const voices = window.speechSynthesis.getVoices(); const preferredVoice = voices.find( (voice) => voice.lang.startsWith("en") && voice.name.includes("Daniel") ) || voices.find((voice) => voice.lang.startsWith("en") && voice.default); if (preferredVoice) { utterance.voice = preferredVoice; } utterance.rate = 0.9; utterance.volume = 0.8; utterance.pitch = 1.0; window.speechSynthesis.speak(utterance); } }, [match.home?.name, match.away?.name]); return ( {match.order} {match.reset && ( * If necessary )} {showControls && ( )} {showToolbar && ( )} {showEditButton && ( )} ); };