Broken menu
This commit is contained in:
68
asc/package-lock.json
generated
68
asc/package-lock.json
generated
@@ -17,6 +17,7 @@
|
|||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
|
"react-router-dom": "^7.1.5",
|
||||||
"react-scripts": "^5.0.1",
|
"react-scripts": "^5.0.1",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4"
|
||||||
}
|
}
|
||||||
@@ -4552,6 +4553,12 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/cookie": {
|
||||||
|
"version": "0.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
|
||||||
|
"integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/eslint": {
|
"node_modules/@types/eslint": {
|
||||||
"version": "8.56.12",
|
"version": "8.56.12",
|
||||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz",
|
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz",
|
||||||
@@ -16101,6 +16108,55 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-router": {
|
||||||
|
"version": "7.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.5.tgz",
|
||||||
|
"integrity": "sha512-8BUF+hZEU4/z/JD201yK6S+UYhsf58bzYIDq2NS1iGpwxSXDu7F+DeGSkIXMFBuHZB21FSiCzEcUb18cQNdRkA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/cookie": "^0.6.0",
|
||||||
|
"cookie": "^1.0.1",
|
||||||
|
"set-cookie-parser": "^2.6.0",
|
||||||
|
"turbo-stream": "2.4.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=18",
|
||||||
|
"react-dom": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-router-dom": {
|
||||||
|
"version": "7.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.1.5.tgz",
|
||||||
|
"integrity": "sha512-/4f9+up0Qv92D3bB8iN5P1s3oHAepSGa9h5k6tpTFlixTTskJZwKGhJ6vRJ277tLD1zuaZTt95hyGWV1Z37csQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"react-router": "7.1.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=18",
|
||||||
|
"react-dom": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-router/node_modules/cookie": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-scripts": {
|
"node_modules/react-scripts": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
|
||||||
@@ -17035,6 +17091,12 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/set-cookie-parser": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/set-function-length": {
|
"node_modules/set-function-length": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||||
@@ -18432,6 +18494,12 @@
|
|||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||||
"license": "0BSD"
|
"license": "0BSD"
|
||||||
},
|
},
|
||||||
|
"node_modules/turbo-stream": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/type-check": {
|
"node_modules/type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
|
"react-router-dom": "^7.1.5",
|
||||||
"react-scripts": "^5.0.1",
|
"react-scripts": "^5.0.1",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
// src/App.js
|
// src/App.js
|
||||||
import React, { useState, useMemo, useEffect } from 'react';
|
import React, { useState, useMemo, useEffect } from 'react';
|
||||||
import { CssBaseline, Container, ThemeProvider, createTheme, IconButton } from '@mui/material';
|
import { CssBaseline, Container, IconButton } from '@mui/material';
|
||||||
import { Brightness4, Brightness7 } from '@mui/icons-material';
|
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||||
|
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
|
||||||
import { ScoreProvider } from './context/ScoreContext';
|
import { ScoreProvider } from './context/ScoreContext';
|
||||||
import GameSetup from './components/GameSetup';
|
import GameSetup from './components/GameSetup';
|
||||||
import ScoreTracker from './components/ScoreTracker';
|
import ScoreTracker from './components/ScoreTracker';
|
||||||
|
import GameSummary from './components/GameSummary';
|
||||||
|
import { Brightness7, Brightness4 } from '@mui/icons-material';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
// Initialize theme from localStorage or default to 'light'
|
// Initialize theme from localStorage or default to 'light'
|
||||||
@@ -12,7 +15,7 @@ function App() {
|
|||||||
try {
|
try {
|
||||||
const savedMode = localStorage.getItem('themeMode');
|
const savedMode = localStorage.getItem('themeMode');
|
||||||
return savedMode || 'light';
|
return savedMode || 'light';
|
||||||
} catch {
|
} catch (e) {
|
||||||
return 'light';
|
return 'light';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -69,11 +72,15 @@ function App() {
|
|||||||
>
|
>
|
||||||
{mode === 'dark' ? <Brightness7 /> : <Brightness4 />}
|
{mode === 'dark' ? <Brightness7 /> : <Brightness4 />}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
{!gameStarted ? (
|
<Router>
|
||||||
<GameSetup onGameStart={() => setGameStarted(true)} />
|
<Routes>
|
||||||
) : (
|
<Route
|
||||||
<ScoreTracker />
|
path="/"
|
||||||
)}
|
element={!gameStarted ? <GameSetup onGameStart={() => setGameStarted(true)} /> : <ScoreTracker />}
|
||||||
|
/>
|
||||||
|
<Route path="/summary" element={<GameSummary />} />
|
||||||
|
</Routes>
|
||||||
|
</Router>
|
||||||
</Container>
|
</Container>
|
||||||
</ScoreProvider>
|
</ScoreProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
@@ -81,3 +88,4 @@ function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|
||||||
|
|||||||
58
asc/src/components/GameSummary.js
Normal file
58
asc/src/components/GameSummary.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useScore } from '../context/ScoreContext';
|
||||||
|
import { Button, Grid, Typography, Box } from '@mui/material';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
const GameSummary = () => {
|
||||||
|
const { state } = useScore();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleNewGame = () => {
|
||||||
|
navigate('/');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Main Menu Button */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 16,
|
||||||
|
left: 16,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button variant="outlined" onClick={handleNewGame}>
|
||||||
|
Main Menu
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Grid container spacing={2} justifyContent="center">
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Typography variant="h4" align="center" gutterBottom>
|
||||||
|
Game Summary
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* Total score */}
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Typography variant="h6" align="center">
|
||||||
|
Total Score: {state.currentGame.totalScore}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="h6" align="center">
|
||||||
|
Total Bullseyes: {state.currentGame.totalBullseyes}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* New game button */}
|
||||||
|
<Grid item xs={12} align="center">
|
||||||
|
<Button variant="contained" onClick={handleNewGame}>
|
||||||
|
Start New Game
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GameSummary;
|
||||||
|
|
||||||
@@ -2,13 +2,17 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useScore, ACTIONS } from '../context/ScoreContext';
|
import { useScore, ACTIONS } from '../context/ScoreContext';
|
||||||
import { Button, Grid, Typography, TextField, Box } from '@mui/material';
|
import { Button, Grid, Typography, TextField, Box } from '@mui/material';
|
||||||
|
import { useNavigate } from 'react-router-dom'; // <-- Add this import
|
||||||
|
|
||||||
const ScoreTracker = () => {
|
const ScoreTracker = () => {
|
||||||
const { state, dispatch } = useScore();
|
const { state, dispatch } = useScore();
|
||||||
const [arrowScores, setArrowScores] = useState(['', '', '', '', '']);
|
const navigate = useNavigate(); // <-- Define the navigate hook
|
||||||
const gameType = state.currentGame.gameType;
|
const gameType = state.currentGame.gameType;
|
||||||
const maxArrowsPerRound = gameType === '450' ? 3 : 5;
|
const maxArrowsPerRound = gameType === '450' ? 3 : 5;
|
||||||
const maxScore = gameType === '450' ? 10 : 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 handleScoreChange = (index, value) => {
|
||||||
const updatedScores = [...arrowScores];
|
const updatedScores = [...arrowScores];
|
||||||
@@ -16,7 +20,25 @@ const ScoreTracker = () => {
|
|||||||
setArrowScores(updatedScores);
|
setArrowScores(updatedScores);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle Add Round, Game Completion, etc.
|
||||||
|
|
||||||
|
const handleGameEnd = () => {
|
||||||
|
// Navigate to the summary page after the game ends
|
||||||
|
navigate('/summary');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Rest of your component code (Rendering, Button handlers, etc.)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle adding a round
|
||||||
const handleAddRound = () => {
|
const handleAddRound = () => {
|
||||||
|
// Check if the max number of rounds is reached
|
||||||
|
if (state.currentGame.rounds.length >= maxRounds) {
|
||||||
|
navigate('/summary'); // Navigate to summary page when max rounds are reached
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate Scores
|
||||||
const valid = arrowScores.slice(0, maxArrowsPerRound).every(score =>
|
const valid = arrowScores.slice(0, maxArrowsPerRound).every(score =>
|
||||||
(score >= 0 && score <= maxScore) || score.toUpperCase() === 'X'
|
(score >= 0 && score <= maxScore) || score.toUpperCase() === 'X'
|
||||||
);
|
);
|
||||||
@@ -25,17 +47,23 @@ const ScoreTracker = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
arrowScores.slice(0, maxArrowsPerRound).forEach((score) => {
|
// Add arrows to the current round
|
||||||
|
const roundArrows = arrowScores.slice(0, maxArrowsPerRound).map((score) => {
|
||||||
const arrowScore = score.toUpperCase() === 'X' ? maxScore : parseInt(score, 10);
|
const arrowScore = score.toUpperCase() === 'X' ? maxScore : parseInt(score, 10);
|
||||||
dispatch({
|
return {
|
||||||
type: ACTIONS.ADD_ARROW,
|
|
||||||
payload: {
|
|
||||||
roundIndex: state.currentGame.rounds.length,
|
|
||||||
score: arrowScore,
|
score: arrowScore,
|
||||||
isBullseye: score.toUpperCase() === 'X',
|
isBullseye: score.toUpperCase() === 'X',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.ADD_ROUND,
|
||||||
|
payload: {
|
||||||
|
roundIndex: state.currentGame.rounds.length,
|
||||||
|
arrows: roundArrows,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
|
||||||
setArrowScores(['', '', '', '', '']);
|
setArrowScores(['', '', '', '', '']);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -120,6 +148,13 @@ const ScoreTracker = () => {
|
|||||||
>
|
>
|
||||||
X
|
X
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => navigate('/')}
|
||||||
|
>
|
||||||
|
Main Menu
|
||||||
|
</Button>
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
@@ -150,16 +185,21 @@ const ScoreTracker = () => {
|
|||||||
Bullseyes: {state.currentGame.totalBullseyes}
|
Bullseyes: {state.currentGame.totalBullseyes}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
{/* Round history */}
|
||||||
{/* Round history */}
|
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Box sx={{ maxHeight: '200px', overflow: 'auto' }}>
|
<Box sx={{ maxHeight: '200px', overflow: 'auto' }}>
|
||||||
{state.currentGame.rounds.map((round, roundIndex) => (
|
{state.currentGame && state.currentGame.rounds && state.currentGame.rounds.length > 0 ? (
|
||||||
|
state.currentGame.rounds.map((round, roundIndex) => (
|
||||||
<Typography key={roundIndex} variant="body2" align="center">
|
<Typography key={roundIndex} variant="body2" align="center">
|
||||||
Round {roundIndex + 1}: {round.arrows.join(', ')}
|
Round {roundIndex + 1}: {round.arrows ? round.arrows.join(', ') : 'No arrows'}
|
||||||
(Total: {round.total}, Bullseyes: {round.bullseyes})
|
(Total: {round.total || 0}, Bullseyes: {round.bullseyes || 0})
|
||||||
</Typography>
|
</Typography>
|
||||||
))}
|
))
|
||||||
|
) : (
|
||||||
|
<Typography variant="body2" align="center">
|
||||||
|
No rounds played yet.
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
Reference in New Issue
Block a user