"use client"; import { Alert, Box, Button, Card, CardContent, Checkbox, Divider, FormControlLabel, MenuItem, Stack, TextField, Typography } from "@mui/material"; import { useEffect, useMemo, useState } from "react"; import { AppShell } from "@/components/AppShell"; import { EmptyState } from "@/components/EmptyState"; import { SectionCard } from "@/components/SectionCard"; import { StatusBadge } from "@/components/StatusBadge"; import { FIELD_POSITIONS } from "@/lib/constants"; import { fetchJson } from "@/lib/client"; import { formatPlayerName } from "@/lib/utils"; import type { GeneratedLineup, LineupRecord, PlayerRecord } from "@/lib/types"; export function LineupClient() { const [gameDate, setGameDate] = useState(new Date().toISOString().slice(0, 10)); const [players, setPlayers] = useState([]); const [selectedPlayerIds, setSelectedPlayerIds] = useState([]); const [startingPitcherId, setStartingPitcherId] = useState(""); const [showAllPitchers, setShowAllPitchers] = useState(false); const [generated, setGenerated] = useState(null); const [message, setMessage] = useState(null); const [error, setError] = useState(null); async function loadPlayers(targetDate: string) { try { const response = await fetchJson(`/api/players?gameDate=${targetDate}`); setPlayers(response); const activeIds = response.filter((player) => player.isActive).map((player) => player._id); setSelectedPlayerIds(activeIds); const firstEligible = response.find((player) => player.pitcherAvailability.status === "available"); setStartingPitcherId(firstEligible?._id ?? response[0]?._id ?? ""); } catch (loadError) { setError(loadError instanceof Error ? loadError.message : "Failed to load players."); } } useEffect(() => { void loadPlayers(gameDate); }, [gameDate]); const selectablePitchers = useMemo( () => players.filter((player) => selectedPlayerIds.includes(player._id) && (showAllPitchers || player.pitcherAvailability.status !== "needs-rest") ), [players, selectedPlayerIds, showAllPitchers] ); const selectedPlayers = useMemo( () => players.filter((player) => selectedPlayerIds.includes(player._id)), [players, selectedPlayerIds] ); function updateGenerated(next: GeneratedLineup | null) { setGenerated(next); } async function generate() { try { const response = await fetchJson("/api/lineups/generate", { method: "POST", body: JSON.stringify({ gameDate, playerIds: selectedPlayerIds, startingPitcherId: startingPitcherId || null }) }); setGenerated(response); setMessage("Suggested lineup generated."); } catch (generateError) { setError(generateError instanceof Error ? generateError.message : "Unable to generate lineup."); } } async function save() { if (!generated) return; try { await fetchJson("/api/lineups", { method: "POST", body: JSON.stringify({ ...generated, playerIds: selectedPlayerIds }) }); setMessage("Lineup saved."); } catch (saveError) { setError(saveError instanceof Error ? saveError.message : "Unable to save lineup."); } } return ( {message ? {message} : null} {error ? {error} : null} setGameDate(event.target.value)} slotProps={{ inputLabel: { shrink: true } }} /> setStartingPitcherId(event.target.value)}> {selectablePitchers.map((player) => ( {formatPlayerName(player.firstName, player.lastName)} ))} setShowAllPitchers(event.target.checked)} />} label="Show pitchers who need rest" /> {players.map((player) => ( setSelectedPlayerIds((current) => event.target.checked ? [...current, player._id] : current.filter((id) => id !== player._id) ) } /> {formatPlayerName(player.firstName, player.lastName)} #{player.jerseyNumber} {player.primaryPosition} ))} {generated ? ( <> } spacing={0}> {generated.battingOrder.map((slot, index) => ( Spot {index + 1} { const nextPlayer = selectedPlayers.find((player) => player._id === event.target.value); if (!nextPlayer) return; updateGenerated({ ...generated, battingOrder: generated.battingOrder.map((entry, entryIndex) => entryIndex === index ? { ...entry, playerId: nextPlayer._id, playerName: formatPlayerName(nextPlayer.firstName, nextPlayer.lastName) } : entry ) }); }} > {selectedPlayers.map((player) => ( {formatPlayerName(player.firstName, player.lastName)} ))} ))} } spacing={0}> {FIELD_POSITIONS.map((position) => { const assignment = generated.defensiveAssignments.find((entry) => entry.position === position); return ( {position} { const nextPlayer = selectedPlayers.find((player) => player._id === event.target.value); if (!nextPlayer) return; const nextAssignments = generated.defensiveAssignments.filter((entry) => entry.position !== position); nextAssignments.push({ playerId: nextPlayer._id, playerName: formatPlayerName(nextPlayer.firstName, nextPlayer.lastName), position }); updateGenerated({ ...generated, defensiveAssignments: nextAssignments }); }} > Unassigned {selectedPlayers.map((player) => ( {formatPlayerName(player.firstName, player.lastName)} ))} ); })} {generated.notes.length > 0 ? ( {generated.notes.map((note) => ( {note} ))} ) : null} ) : ( )} ); }