import { useEffect, useState } from "react"; import { useMutation, useQuery } from "@tanstack/react-query"; import { useSearchParams } from "react-router-dom"; import { api } from "../api/client"; import { useWalkupContext } from "../hooks/useWalkupContext"; import { loadPreparedGame, savePreparedGame } from "../lib/offlinePrep"; import { queryClient } from "../lib/queryClient"; import { formatGameDate, formatGameTitle, formatMemberName, formatTeamLabel } from "../lib/teamsnapHelpers"; export function GamePage() { const walkup = useWalkupContext(); const [searchParams, setSearchParams] = useSearchParams(); const [selectedGameId, setSelectedGameId] = useState(searchParams.get("gameId") ?? ""); const [clipId, setClipId] = useState(0); const [slot, setSlot] = useState(1); const [offlineMessage, setOfflineMessage] = useState(null); const teamId = walkup.selectedTeamId; const playerId = walkup.currentPlayerId; useEffect(() => { const requestedGameId = searchParams.get("gameId"); if (requestedGameId) { setSelectedGameId(requestedGameId); return; } if (!selectedGameId && walkup.nextGame) { setSelectedGameId(String(walkup.nextGame.id)); } }, [searchParams, selectedGameId, walkup.nextGame]); const clipsQuery = useQuery({ queryKey: ["clips", teamId, playerId], queryFn: () => api.listClips(teamId, playerId), enabled: Boolean(teamId && playerId), }); const assignmentsQuery = useQuery({ queryKey: ["assignments", selectedGameId, playerId], queryFn: () => api.listAssignments(selectedGameId, playerId), enabled: Boolean(selectedGameId && playerId), }); const prepQuery = useQuery({ queryKey: ["prep", selectedGameId], queryFn: () => api.prepareGame(selectedGameId), enabled: Boolean(selectedGameId), }); const saveMutation = useMutation({ mutationFn: () => api.createAssignment(selectedGameId, { external_team_id: teamId, external_player_id: playerId, clip_id: clipId, batting_slot: slot, status: "ready", }), onSuccess: async () => { await Promise.all([ queryClient.invalidateQueries({ queryKey: ["assignments", selectedGameId, playerId] }), queryClient.invalidateQueries({ queryKey: ["prep", selectedGameId] }), ]); }, }); function selectGame(gameId: string) { setSelectedGameId(gameId); setSearchParams({ gameId }); setOfflineMessage(null); } function cachePreparedGame() { if (!prepQuery.data) { setOfflineMessage("Prepare the game first so there is something to cache locally."); return; } savePreparedGame(selectedGameId, prepQuery.data); setOfflineMessage(`Cached ${prepQuery.data.assignments.length} assignments for offline operator use.`); } const selectedGame = walkup.games.find((game) => String(game.id) === selectedGameId) ?? null; const cachedPrep = selectedGameId ? loadPreparedGame(selectedGameId) : null; if (!walkup.isTeamSnap) { return (
Reconnect with TeamSnap to attach clips to games.
); } if (!teamId || !playerId) { return (
No player record was found on the selected team, so game-specific clip selection is unavailable.
); } return (

Game clips

{selectedGame ? formatGameTitle(selectedGame) : "Select a game"}

{formatMemberName(walkup.currentPlayer)} can attach clips from song files in their own library to any game on{" "} {formatTeamLabel(walkup.selectedTeam)}.

{selectedGame ? formatGameDate(selectedGame) : "Choose a game to attach clips."}
{walkup.nextGame ?
Next game: {formatGameTitle(walkup.nextGame)}
: null}

Attach a clip

{selectedGame ? ( <>
{formatGameDate(selectedGame)}
{saveMutation.error instanceof Error ?
{saveMutation.error.message}
: null} ) : (
Pick a game to attach clips.
)}

Your selected clips

{assignmentsQuery.data?.map((assignment) => (
{assignment.clip_label}
{assignment.asset_title} {assignment.batting_slot ? `| slot ${assignment.batting_slot}` : ""}
{assignment.status}
))} {!assignmentsQuery.isLoading && !assignmentsQuery.data?.length ? (
No clips attached to this game yet.
) : null}

Prepared payload

Prepared at: {prepQuery.data?.prepared_at ?? "Not prepared yet"}
Assignments in package: {prepQuery.data?.assignments.length ?? 0}
Cached locally: {cachedPrep ? `${cachedPrep.assignments.length} assignments` : "No"}
{offlineMessage ?
{offlineMessage}
: null}
); }