fixed 5 spot component

This commit is contained in:
corey@blaishome.online
2025-03-01 21:58:20 -05:00
parent e2931a5cdb
commit f3c86d7d11
2 changed files with 334 additions and 337 deletions

View File

@@ -1,150 +1,216 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import {
Button,
Card,
CardContent,
CardHeader,
Grid,
Typography,
FormControl,
FormControlLabel,
Radio,
RadioGroup,
Divider
} from '@mui/material';
import { useScore, ACTIONS } from '../context/ScoreContext'; import { useScore, ACTIONS } from '../context/ScoreContext';
import { Button, Grid, Typography, TextField, Box } from '@mui/material';
import { useNavigate } from 'react-router-dom';
const GameSetup = ({ onGameStart }) => { const ScoreTracker = () => {
const { dispatch } = useScore(); const { state, dispatch } = useScore();
const [selectedGameType, setSelectedGameType] = useState(''); const navigate = useNavigate();
const [selectedTargetFace, setSelectedTargetFace] = useState('standard');
const handleGameTypeSelect = (gameType) => { const gameType = state.currentGame.gameType;
setSelectedGameType(gameType);
setSelectedTargetFace('standard'); // Reset to standard when game type changes // Determine the number of arrows per round based on game type
const maxArrowsPerRound = gameType === '450' ? 3 : 5;
const maxScore = gameType === '450' ? 10 : 5;
const maxRounds = gameType === '450' ? 16 : 12;
const [arrowScores, setArrowScores] = useState(Array(maxArrowsPerRound).fill(''));
const handleScoreChange = (index, value) => {
const updatedScores = [...arrowScores];
updatedScores[index] = value;
setArrowScores(updatedScores);
}; };
const startGame = () => { const handleAddRound = () => {
if (!selectedGameType) return; const valid = arrowScores.slice(0, maxArrowsPerRound).every(score =>
(score >= 0 && score <= maxScore) || score.toUpperCase() === 'X'
);
if (!valid) {
alert(`Please enter valid scores between 0-${maxScore} or X for bullseyes.`);
return;
}
const roundArrows = arrowScores.slice(0, maxArrowsPerRound).map((score) => {
const arrowScore = score.toUpperCase() === 'X' ? maxScore : parseInt(score, 10);
return {
score: arrowScore,
isBullseye: score.toUpperCase() === 'X',
};
});
const roundTotal = roundArrows.reduce((sum, arrow) => sum + arrow.score, 0);
dispatch({ dispatch({
type: ACTIONS.START_GAME, type: ACTIONS.ADD_ROUND,
payload: { payload: {
gameType: selectedGameType, roundIndex: state.currentGame.rounds.length,
isLeague: false, arrows: roundArrows,
targetFace: selectedTargetFace total: roundTotal
} },
}); });
onGameStart();
setArrowScores(Array(maxArrowsPerRound).fill(''));
if (state.currentGame.rounds.length >= maxRounds - 1) {
navigate('/summary');
}
}; };
// Determine if we should show target face options const handleMainMenu = () => {
const showTargetFaceOptions = selectedGameType !== ''; dispatch({ type: ACTIONS.RESET_GAME });
navigate('/');
};
return ( return (
<Grid container spacing={3} justifyContent="center" alignItems="center" style={{ minHeight: '80vh' }}> <Grid container spacing={2} justifyContent="center">
<Grid item xs={12} sm={8} md={6}> {/* Main Menu Button */}
<Card> <Box
<CardHeader sx={{
title="Start New Game" position: 'absolute',
titleTypographyProps={{ align: 'center' }} top: 16,
/> left: 16,
<CardContent> }}
<Grid container spacing={3}> >
<Grid item xs={12}> <Button
<Typography variant="body1" align="center" gutterBottom> variant="outlined"
Select your game type: onClick={handleMainMenu}
</Typography> >
</Grid> Main Menu
<Grid item xs={6}> </Button>
<Button </Box>
fullWidth
variant="contained"
color="primary"
size={selectedGameType === '450' ? "large" : "medium"}
onClick={() => handleGameTypeSelect('450')}
style={{
backgroundColor: selectedGameType === '450' ? undefined : '#1976d2',
boxShadow: selectedGameType === '450' ? '0 0 10px #1976d2' : undefined
}}
>
450 Round
</Button>
</Grid>
<Grid item xs={6}>
<Button
fullWidth
variant="contained"
color="secondary"
size={selectedGameType === '300' ? "large" : "medium"}
onClick={() => handleGameTypeSelect('300')}
style={{
backgroundColor: selectedGameType === '300' ? undefined : '#9c27b0',
boxShadow: selectedGameType === '300' ? '0 0 10px #9c27b0' : undefined
}}
>
300 Round
</Button>
</Grid>
{showTargetFaceOptions && ( <Grid item xs={12}>
<> <Typography variant="h5" align="center" gutterBottom>
<Grid item xs={12}> {gameType} Round - Round {state.currentGame.rounds.length + 1}
<Divider style={{ margin: '16px 0' }} /> </Typography>
<Typography variant="body1" align="center" gutterBottom> </Grid>
Select your target face:
</Typography>
</Grid>
<Grid item xs={12}>
<FormControl component="fieldset">
<RadioGroup
row
name="targetFace"
value={selectedTargetFace}
onChange={(e) => setSelectedTargetFace(e.target.value)}
>
<FormControlLabel
value="standard"
control={<Radio />}
label="Standard"
/>
{selectedGameType === '300' && (
<FormControlLabel
value="5-spot"
control={<Radio />}
label="5-Spot"
/>
)}
{selectedGameType === '450' && (
<FormControlLabel
value="3-spot"
control={<Radio />}
label="3-Spot"
/>
)}
</RadioGroup>
</FormControl>
</Grid>
<Grid item xs={12} style={{ marginTop: '16px' }}> {/* Score input section */}
<Button <Grid item xs={12}>
fullWidth <Box
variant="contained" sx={{
color="success" display: 'flex',
size="large" justifyContent: 'center',
onClick={startGame} gap: 1,
> mb: 2
Start Game }}
</Button> >
</Grid> {Array.from({ length: maxArrowsPerRound }).map((_, index) => (
</> <TextField
)} key={index}
</Grid> value={arrowScores[index]}
</CardContent> onChange={(e) => handleScoreChange(index, e.target.value)}
</Card> placeholder="0"
size="small"
sx={{
width: '60px',
'& .MuiInputBase-input': {
padding: '8px',
textAlign: 'center'
}
}}
inputProps={{
maxLength: 1,
style: { textAlign: 'center' }
}}
/>
))}
</Box>
</Grid>
{/* Score buttons */}
<Grid item xs={12}>
<Box
sx={{
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'center',
gap: 1,
mb: 2
}}
>
{Array.from({ length: maxScore + 1 }).map((_, i) => (
<Button
key={i}
variant="outlined"
size="small"
sx={{ minWidth: '40px', height: '40px' }}
onClick={() => {
const emptyIndex = arrowScores.findIndex(score => score === '');
if (emptyIndex >= 0 && emptyIndex < maxArrowsPerRound) {
handleScoreChange(emptyIndex, i.toString());
}
}}
>
{i}
</Button>
))}
<Button
variant="outlined"
size="small"
sx={{ minWidth: '40px', height: '40px' }}
onClick={() => {
const emptyIndex = arrowScores.findIndex(score => score === '');
if (emptyIndex >= 0 && emptyIndex < maxArrowsPerRound) {
handleScoreChange(emptyIndex, 'X');
}
}}
>
X
</Button>
</Box>
</Grid>
{/* Control buttons */}
<Grid item xs={12}>
<Box sx={{ display: 'flex', justifyContent: 'center', gap: 2 }}>
<Button
variant="contained"
color="primary"
onClick={handleAddRound}
disabled={!arrowScores.slice(0, maxArrowsPerRound).every(score => score !== '')}
>
Add Round
</Button>
<Button
variant="outlined"
onClick={() => setArrowScores(Array(maxArrowsPerRound).fill(''))}
>
Clear
</Button>
</Box>
</Grid>
{/* Scores display */}
<Grid item xs={12}>
<Typography variant="h6" align="center">
Total Score: {state.currentGame.totalScore} |
Bullseyes: {state.currentGame.totalBullseyes}
</Typography>
</Grid>
{/* Round history */}
<Grid item xs={12}>
<Box sx={{ maxHeight: '200px', overflow: 'auto' }}>
{state.currentGame.rounds.length > 0 ? (
state.currentGame.rounds.map((round, roundIndex) => (
<Typography key={roundIndex} variant="body2" align="center">
Round {roundIndex + 1}: {round.arrows.map(arrow => arrow.score).join(', ')}
(Total: {round.total}, Bullseyes: {round.bullseyes})
</Typography>
))
) : (
<Typography variant="body2" align="center">
No rounds played yet.
</Typography>
)}
</Box>
</Grid> </Grid>
</Grid> </Grid>
); );
}; };
export default GameSetup; export default ScoreTracker;

View File

@@ -1,231 +1,162 @@
import React, { useState } from 'react'; import React, { useState, useEffect } from 'react';
import {
Button,
Card,
CardContent,
CardHeader,
FormControl,
FormControlLabel,
RadioGroup,
Radio,
Grid,
Typography,
Select,
MenuItem
} from '@mui/material';
import { useScore, ACTIONS } from '../context/ScoreContext'; import { useScore, ACTIONS } from '../context/ScoreContext';
import { Button, Grid, Typography, TextField, Box } from '@mui/material';
import { useNavigate } from 'react-router-dom';
const ScoreTracker = () => { const GameSetup = ({ onGameStart }) => {
const { state, dispatch } = useScore(); const { dispatch } = useScore();
const navigate = useNavigate();
const gameType = state.currentGame.gameType; const [selectedGameType, setSelectedGameType] = useState('');
const isLeague = state.currentGame.isLeague; // Check if it's a league game const [isLeague, setIsLeague] = useState(false); // This tracks if it's a league game
const maxArrowsPerRound = gameType === '450' ? 3 : 5; const [selectedTargetFace, setSelectedTargetFace] = useState('single'); // Assuming single as default target face
const maxScore = gameType === '450' ? 10 : 5;
const maxRounds = gameType === '450' ? 16 : 12;
const [arrowScores, setArrowScores] = useState(Array(maxArrowsPerRound).fill('')); // Log isLeague to verify if it changes correctly
useEffect(() => {
console.log("Game mode isLeague:", isLeague); // Check this in the console
}, [isLeague]);
const handleScoreChange = (index, value) => { const handleGameModeChange = (event) => {
const updatedScores = [...arrowScores]; const leagueMode = event.target.value === "league";
updatedScores[index] = value; setIsLeague(leagueMode); // Update isLeague based on user selection
setArrowScores(updatedScores);
}; };
const handleAddRound = () => { const handleTargetFaceChange = (event) => {
const valid = arrowScores.slice(0, maxArrowsPerRound).every(score => setSelectedTargetFace(event.target.value);
(score >= 0 && score <= maxScore) || score.toUpperCase() === 'X' };
);
if (!valid) { const startGame = () => {
alert(`Please enter valid scores between 0-${maxScore} or X for bullseyes.`); if (!selectedGameType) return;
return;
}
const roundArrows = arrowScores.slice(0, maxArrowsPerRound).map((score) => { // Log the values to ensure they're correct
const arrowScore = score.toUpperCase() === 'X' ? maxScore : parseInt(score, 10); console.log("Starting game with settings:", {
return { gameType: selectedGameType,
score: arrowScore, isLeague: isLeague,
isBullseye: score.toUpperCase() === 'X', targetFace: selectedTargetFace
};
}); });
const roundTotal = roundArrows.reduce((sum, arrow) => sum + arrow.score, 0);
dispatch({ dispatch({
type: ACTIONS.ADD_ROUND, type: ACTIONS.START_GAME,
payload: { payload: {
roundIndex: state.currentGame.rounds.length, gameType: selectedGameType,
arrows: roundArrows, isLeague: isLeague, // Pass the league/practice mode correctly
total: roundTotal targetFace: selectedTargetFace
},
});
setArrowScores(Array(maxArrowsPerRound).fill(''));
if (state.currentGame.rounds.length >= maxRounds - 1) {
navigate('/summary');
}
};
const handleMainMenu = () => {
dispatch({ type: ACTIONS.RESET_GAME });
navigate('/');
};
const handleGameStart = (gameType, isLeague) => {
// Dispatch the action to start a new round with the correct game type and isLeague flag
dispatch({
type: ACTIONS.START_NEW_ROUND,
payload: {
gameType, // '450' or '300'
isLeague // true for league games, false for practice games
} }
}); });
navigate('/score-tracker'); // Navigate directly to the score tracker onGameStart();
}; };
return ( return (
<Grid container spacing={2} justifyContent="center"> <Grid container spacing={3} justifyContent="center" alignItems="center" style={{ minHeight: '80vh' }}>
{/* Main Menu Button */} <Grid item xs={12} sm={8} md={6}>
<Box <Card>
sx={{ <CardHeader
position: 'absolute', title="Start New Game"
top: 16, titleTypographyProps={{ align: 'center' }}
left: 16, />
}} <CardContent>
> <Grid container spacing={3}>
<Button <Grid item xs={12}>
variant="outlined" <Typography variant="body1" align="center" gutterBottom>
onClick={handleMainMenu} Select your game type:
> </Typography>
Main Menu </Grid>
</Button> <Grid item xs={6}>
</Box> <Button
fullWidth
variant="contained"
color={selectedGameType === '450' ? 'primary' : 'default'}
size="large"
onClick={() => setSelectedGameType('450')}
>
450 Round
</Button>
</Grid>
<Grid item xs={6}>
<Button
fullWidth
variant="contained"
color={selectedGameType === '300' ? 'secondary' : 'default'}
size="large"
onClick={() => setSelectedGameType('300')}
>
300 Round
</Button>
</Grid>
<Grid item xs={12}> {/* Select League or Practice Mode */}
<Typography variant="h5" align="center" gutterBottom> <Grid item xs={12}>
{gameType} Round - Round {state.currentGame.rounds.length + 1} <FormControl component="fieldset" fullWidth>
</Typography> <Typography variant="body1" align="center" gutterBottom>
<Typography variant="subtitle1" align="center" gutterBottom> Select game mode:
{isLeague ? 'League Game' : 'Practice Game'} {/* Display whether it's a league or practice game */} </Typography>
</Typography> <RadioGroup
</Grid> row
name="gameMode"
value={isLeague ? "league" : "practice"} // Display correct mode
onChange={handleGameModeChange}
>
<FormControlLabel
value="practice"
control={<Radio />}
label="Practice"
/>
<FormControlLabel
value="league"
control={<Radio />}
label="League"
/>
</RadioGroup>
</FormControl>
</Grid>
{/* Score input section */} {/* Target face selection */}
<Grid item xs={12}> <Grid item xs={12}>
<Box <FormControl fullWidth>
sx={{ <Typography variant="body1" align="center" gutterBottom>
display: 'flex', Select target face:
justifyContent: 'center', </Typography>
gap: 1, <Select
mb: 2 value={selectedTargetFace}
}} onChange={handleTargetFaceChange}
> >
{Array.from({ length: maxArrowsPerRound }).map((_, index) => ( <MenuItem value="single">Single Spot</MenuItem>
<TextField <MenuItem value="five">Five Spot</MenuItem>
key={index} </Select>
value={arrowScores[index]} </FormControl>
onChange={(e) => handleScoreChange(index, e.target.value)} </Grid>
placeholder="0"
size="small"
sx={{
width: '60px',
'& .MuiInputBase-input': {
padding: '8px',
textAlign: 'center'
}
}}
inputProps={{
maxLength: 1,
style: { textAlign: 'center' }
}}
/>
))}
</Box>
</Grid>
{/* Score buttons */} {/* Start Game Button */}
<Grid item xs={12}> <Grid item xs={12}>
<Box <Button
sx={{ fullWidth
display: 'flex', variant="contained"
flexWrap: 'wrap', color="primary"
justifyContent: 'center', size="large"
gap: 1, onClick={startGame}
mb: 2 disabled={!selectedGameType} // Disable button if no game type selected
}} >
> Start Game
{Array.from({ length: maxScore + 1 }).map((_, i) => ( </Button>
<Button </Grid>
key={i} </Grid>
variant="outlined" </CardContent>
size="small" </Card>
sx={{ minWidth: '40px', height: '40px' }}
onClick={() => {
const emptyIndex = arrowScores.findIndex(score => score === '');
if (emptyIndex >= 0 && emptyIndex < maxArrowsPerRound) {
handleScoreChange(emptyIndex, i.toString());
}
}}
>
{i}
</Button>
))}
<Button
variant="outlined"
size="small"
sx={{ minWidth: '40px', height: '40px' }}
onClick={() => {
const emptyIndex = arrowScores.findIndex(score => score === '');
if (emptyIndex >= 0 && emptyIndex < maxArrowsPerRound) {
handleScoreChange(emptyIndex, 'X');
}
}}
>
X
</Button>
</Box>
</Grid>
{/* Control buttons */}
<Grid item xs={12}>
<Box sx={{ display: 'flex', justifyContent: 'center', gap: 2 }}>
<Button
variant="contained"
color="primary"
onClick={handleAddRound}
disabled={!arrowScores.slice(0, maxArrowsPerRound).every(score => score !== '')}
>
Add Round
</Button>
<Button
variant="outlined"
onClick={() => setArrowScores(Array(maxArrowsPerRound).fill(''))}
>
Clear
</Button>
</Box>
</Grid>
{/* Scores display */}
<Grid item xs={12}>
<Typography variant="h6" align="center">
Total Score: {state.currentGame.totalScore} |
Bullseyes: {state.currentGame.totalBullseyes}
</Typography>
</Grid>
{/* Round history */}
<Grid item xs={12}>
<Box sx={{ maxHeight: '200px', overflow: 'auto' }}>
{state.currentGame.rounds.length > 0 ? (
state.currentGame.rounds.map((round, roundIndex) => (
<Typography key={roundIndex} variant="body2" align="center">
Round {roundIndex + 1}: {round.arrows.map(arrow => arrow.score).join(', ')}
(Total: {round.total}, Bullseyes: {round.bullseyes})
</Typography>
))
) : (
<Typography variant="body2" align="center">
No rounds played yet.
</Typography>
)}
</Box>
</Grid> </Grid>
</Grid> </Grid>
); );
}; };
export default ScoreTracker; export default GameSetup;