Updating
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
"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";
|
||||
@@ -94,73 +95,102 @@ export function LineupClient() {
|
||||
title="Generate Lineup"
|
||||
subtitle="Choose the active group, pick an eligible starter, then adjust the suggested lineup as needed."
|
||||
>
|
||||
{message ? <div className="banner banner-success">{message}</div> : null}
|
||||
{error ? <div className="banner banner-error">{error}</div> : null}
|
||||
{message ? <Alert severity="success">{message}</Alert> : null}
|
||||
{error ? <Alert severity="error">{error}</Alert> : null}
|
||||
|
||||
<SectionCard title="Game Setup" description="Start with the players who are available today.">
|
||||
<div className="form-grid">
|
||||
<label>
|
||||
<span>Game Date</span>
|
||||
<input type="date" value={gameDate} onChange={(event) => setGameDate(event.target.value)} />
|
||||
</label>
|
||||
<label>
|
||||
<span>Starting Pitcher</span>
|
||||
<select value={startingPitcherId} onChange={(event) => setStartingPitcherId(event.target.value)}>
|
||||
{selectablePitchers.map((player) => (
|
||||
<option key={player._id} value={player._id}>
|
||||
{formatPlayerName(player.firstName, player.lastName)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label className="checkbox-row">
|
||||
<input type="checkbox" checked={showAllPitchers} onChange={(event) => setShowAllPitchers(event.target.checked)} />
|
||||
<span>Show pitchers who need rest</span>
|
||||
</label>
|
||||
<div className="full-width action-row">
|
||||
<button className="button" disabled={selectedPlayerIds.length === 0} onClick={() => void generate()}>
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: { xs: "1fr", md: "repeat(2, minmax(0, 1fr))" }, gap: 2 }}>
|
||||
<TextField
|
||||
label="Game Date"
|
||||
type="date"
|
||||
value={gameDate}
|
||||
onChange={(event) => setGameDate(event.target.value)}
|
||||
slotProps={{ inputLabel: { shrink: true } }}
|
||||
/>
|
||||
<TextField select label="Starting Pitcher" value={startingPitcherId} onChange={(event) => setStartingPitcherId(event.target.value)}>
|
||||
{selectablePitchers.map((player) => (
|
||||
<MenuItem key={player._id} value={player._id}>
|
||||
{formatPlayerName(player.firstName, player.lastName)}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
<Box sx={{ gridColumn: "1 / -1" }}>
|
||||
<FormControlLabel
|
||||
control={<Checkbox checked={showAllPitchers} onChange={(event) => setShowAllPitchers(event.target.checked)} />}
|
||||
label="Show pitchers who need rest"
|
||||
/>
|
||||
</Box>
|
||||
<Stack direction={{ xs: "column", sm: "row" }} spacing={1.25} sx={{ gridColumn: "1 / -1" }}>
|
||||
<Button variant="contained" disabled={selectedPlayerIds.length === 0} onClick={() => void generate()}>
|
||||
Generate Suggested Lineup
|
||||
</button>
|
||||
<button className="button button-secondary" disabled={!generated} onClick={() => void save()}>
|
||||
</Button>
|
||||
<Button variant="outlined" color="inherit" disabled={!generated} onClick={() => void save()}>
|
||||
Save Lineup
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
<div className="picker-grid">
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: { xs: "1fr", lg: "repeat(2, 1fr)" }, gap: 1.5, mt: 3 }}>
|
||||
{players.map((player) => (
|
||||
<label key={player._id} className="picker-card">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedPlayerIds.includes(player._id)}
|
||||
onChange={(event) =>
|
||||
setSelectedPlayerIds((current) =>
|
||||
event.target.checked ? [...current, player._id] : current.filter((id) => id !== player._id)
|
||||
)
|
||||
}
|
||||
/>
|
||||
<div>
|
||||
<strong>{formatPlayerName(player.firstName, player.lastName)}</strong>
|
||||
<p>
|
||||
#{player.jerseyNumber} {player.primaryPosition}
|
||||
</p>
|
||||
</div>
|
||||
<StatusBadge status={player.pitcherAvailability.status} />
|
||||
</label>
|
||||
<Card
|
||||
key={player._id}
|
||||
elevation={0}
|
||||
sx={{
|
||||
border: selectedPlayerIds.includes(player._id) ? "1px solid" : "1px solid rgba(15, 38, 47, 0.08)",
|
||||
borderColor: selectedPlayerIds.includes(player._id) ? "primary.main" : "rgba(15, 38, 47, 0.08)",
|
||||
backgroundColor: selectedPlayerIds.includes(player._id) ? "rgba(15, 92, 77, 0.05)" : "transparent"
|
||||
}}
|
||||
>
|
||||
<CardContent
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: { xs: "column", sm: "row" },
|
||||
justifyContent: "space-between",
|
||||
alignItems: { xs: "flex-start", sm: "center" },
|
||||
gap: 1.5
|
||||
}}
|
||||
>
|
||||
<Stack direction="row" spacing={1.25} sx={{ alignItems: "flex-start" }}>
|
||||
<Checkbox
|
||||
checked={selectedPlayerIds.includes(player._id)}
|
||||
onChange={(event) =>
|
||||
setSelectedPlayerIds((current) =>
|
||||
event.target.checked ? [...current, player._id] : current.filter((id) => id !== player._id)
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Box>
|
||||
<Typography variant="h6">{formatPlayerName(player.firstName, player.lastName)}</Typography>
|
||||
<Typography color="text.secondary">
|
||||
#{player.jerseyNumber} {player.primaryPosition}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Stack>
|
||||
<StatusBadge status={player.pitcherAvailability.status} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</Box>
|
||||
</SectionCard>
|
||||
|
||||
{generated ? (
|
||||
<>
|
||||
<SectionCard title="Batting Order" description="Adjust any slot manually before saving.">
|
||||
<div className="list-stack">
|
||||
<Stack divider={<Divider flexItem />} spacing={0}>
|
||||
{generated.battingOrder.map((slot, index) => (
|
||||
<div key={`${slot.playerId}-${index}`} className="list-row">
|
||||
<div>
|
||||
<h3>Spot {index + 1}</h3>
|
||||
</div>
|
||||
<select
|
||||
<Box
|
||||
key={`${slot.playerId}-${index}`}
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: { xs: "1fr", md: "180px 1fr" },
|
||||
alignItems: "center",
|
||||
gap: 2,
|
||||
py: 2
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6">Spot {index + 1}</Typography>
|
||||
<TextField
|
||||
select
|
||||
value={slot.playerId}
|
||||
onChange={(event) => {
|
||||
const nextPlayer = selectedPlayers.find((player) => player._id === event.target.value);
|
||||
@@ -180,26 +210,34 @@ export function LineupClient() {
|
||||
}}
|
||||
>
|
||||
{selectedPlayers.map((player) => (
|
||||
<option key={player._id} value={player._id}>
|
||||
<MenuItem key={player._id} value={player._id}>
|
||||
{formatPlayerName(player.firstName, player.lastName)}
|
||||
</option>
|
||||
</MenuItem>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</TextField>
|
||||
</Box>
|
||||
))}
|
||||
</div>
|
||||
</Stack>
|
||||
</SectionCard>
|
||||
|
||||
<SectionCard title="Defensive Alignment" description="Assignments follow position preferences, but every field is editable.">
|
||||
<div className="list-stack">
|
||||
<Stack divider={<Divider flexItem />} spacing={0}>
|
||||
{FIELD_POSITIONS.map((position) => {
|
||||
const assignment = generated.defensiveAssignments.find((entry) => entry.position === position);
|
||||
return (
|
||||
<div key={position} className="list-row">
|
||||
<div>
|
||||
<h3>{position}</h3>
|
||||
</div>
|
||||
<select
|
||||
<Box
|
||||
key={position}
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: { xs: "1fr", md: "180px 1fr" },
|
||||
alignItems: "center",
|
||||
gap: 2,
|
||||
py: 2
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6">{position}</Typography>
|
||||
<TextField
|
||||
select
|
||||
value={assignment?.playerId ?? ""}
|
||||
onChange={(event) => {
|
||||
const nextPlayer = selectedPlayers.find((player) => player._id === event.target.value);
|
||||
@@ -216,23 +254,25 @@ export function LineupClient() {
|
||||
});
|
||||
}}
|
||||
>
|
||||
<option value="">Unassigned</option>
|
||||
<MenuItem value="">Unassigned</MenuItem>
|
||||
{selectedPlayers.map((player) => (
|
||||
<option key={player._id} value={player._id}>
|
||||
<MenuItem key={player._id} value={player._id}>
|
||||
{formatPlayerName(player.firstName, player.lastName)}
|
||||
</option>
|
||||
</MenuItem>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</TextField>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Stack>
|
||||
{generated.notes.length > 0 ? (
|
||||
<div className="note-block">
|
||||
<Box sx={{ mt: 2, pl: 2, borderLeft: "4px solid", borderColor: "warning.main" }}>
|
||||
{generated.notes.map((note) => (
|
||||
<p key={note}>{note}</p>
|
||||
<Typography key={note} color="text.secondary">
|
||||
{note}
|
||||
</Typography>
|
||||
))}
|
||||
</div>
|
||||
</Box>
|
||||
) : null}
|
||||
</SectionCard>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user