Files
ASC/asc/src/components/ScoreTracker.js
2025-03-18 23:42:30 -04:00

325 lines
10 KiB
JavaScript

import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import {
Button,
Card,
CardContent,
CardHeader,
Grid,
Typography,
Box,
Paper,
IconButton,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Divider,
ButtonGroup
} from '@mui/material';
import { Remove } from '@mui/icons-material';
import { useScore, ACTIONS } from '../context/ScoreContext';
const ScoreTracker = () => {
const { state, dispatch } = useScore();
const navigate = useNavigate();
// Local state for the current end
const [currentEnd, setCurrentEnd] = useState([]);
// Check if we have an active game, if not redirect to setup
useEffect(() => {
if (!state.currentGame || !state.currentGame.gameType) {
navigate('/');
}
}, [state.currentGame, navigate]);
// Handle adding an arrow score
const handleAddArrow = (score) => {
// Add to current end
setCurrentEnd([...currentEnd, score]);
};
// Handle removing the last arrow
const handleRemoveArrow = () => {
if (currentEnd.length > 0) {
const newEnd = [...currentEnd];
newEnd.pop();
setCurrentEnd(newEnd);
}
};
// Calculate total for current end
const calculateEndTotal = (arrows) => {
return arrows.reduce((total, arrow) => {
if (arrow.toUpperCase() === 'X') {
return total + 10;
} else if (arrow.toUpperCase() === 'M') {
return total;
} else {
return total + parseInt(arrow);
}
}, 0);
};
// Get button color based on score
const getButtonColor = (score) => {
switch(score) {
case 'X':
return { bgcolor: '#FF9800', color: 'white' }; // Orange
case '10':
return { bgcolor: '#9C27B0', color: 'white' }; // Purple
case '9':
case '8':
return { bgcolor: '#2196F3', color: 'white' }; // Blue
case '7':
case '6':
case '5':
case '4':
case '3':
case '2':
case '1':
return { bgcolor: '#4CAF50', color: 'white' }; // Green
case 'M':
return { bgcolor: '#9E9E9E', color: 'white' }; // Gray
default:
return { bgcolor: 'default' };
}
};
// Handle saving the current end
const handleSaveEnd = () => {
if (currentEnd.length > 0) {
// Create the end object
const endObject = {
arrows: [...currentEnd],
total: calculateEndTotal(currentEnd),
timestamp: new Date().toISOString()
};
// Dispatch to context
dispatch({
type: ACTIONS.ADD_END,
payload: endObject
});
// Clear current end
setCurrentEnd([]);
}
};
// Handle ending the game
const handleEndGame = () => {
// Save final scores to history first
dispatch({
type: ACTIONS.SAVE_GAME,
});
// Then end the current game
dispatch({
type: ACTIONS.END_GAME,
});
// Navigate to setup page
navigate('/');
};
// Early return if no game is active
if (!state.currentGame || !state.currentGame.gameType) {
return null; // Will be redirected by useEffect
}
// Get the number of arrows per end based on game type
const arrowsPerEnd = state.currentGame.gameType === '450' ? 6 : 5;
// Score button values
const scoreButtons = ['X', '10', '9', '8', '7', '6', '5', '4', '3', '2', '1', 'M'];
return (
<Grid container spacing={3} justifyContent="center">
<Grid item xs={12} sm={10} md={8}>
<Card>
<CardHeader
title={`${state.currentGame.gameType} Round ${state.currentGame.isLeague ? '(League)' : '(Practice)'}`}
titleTypographyProps={{ align: 'center' }}
/>
<CardContent>
<Typography variant="h6" gutterBottom>
Target Face: {state.currentGame.targetFace || 'Standard'}
</Typography>
{/* Current game stats */}
<Box sx={{ mb: 3 }}>
<Paper elevation={2} sx={{ p: 2 }}>
<Typography variant="h6" gutterBottom>
Current Score: {state.currentGame.ends?.reduce((total, end) => total + end.total, 0) || 0}
</Typography>
<Typography variant="body1">
Ends Completed: {state.currentGame.ends?.length || 0}
</Typography>
</Paper>
</Box>
{/* Score input section */}
<Box sx={{ mb: 3 }}>
<Typography variant="h6" gutterBottom>
Score Current End
</Typography>
{/* Score buttons */}
<Box sx={{ mb: 2 }}>
<Box sx={{ display: 'flex', flexWrap: 'wrap', mb: 1, gap: 0.5 }}>
{scoreButtons.map((score) => {
const colorStyle = getButtonColor(score);
return (
<Button
key={score}
variant="contained"
onClick={() => handleAddArrow(score)}
disabled={currentEnd.length >= arrowsPerEnd}
sx={{
minWidth: '45px',
bgcolor: colorStyle.bgcolor,
color: colorStyle.color,
'&:hover': {
bgcolor: colorStyle.bgcolor,
opacity: 0.9
}
}}
>
{score}
</Button>
);
})}
<IconButton
color="secondary"
onClick={handleRemoveArrow}
disabled={currentEnd.length === 0}
sx={{ ml: 1 }}
>
<Remove />
</IconButton>
</Box>
</Box>
{/* Current end display */}
<Box sx={{ display: 'flex', flexWrap: 'wrap', mb: 2 }}>
{currentEnd.map((arrow, index) => {
let bgColor;
if (arrow.toUpperCase() === 'X') bgColor = '#FF9800'; // Orange
else if (arrow.toUpperCase() === '10') bgColor = '#9C27B0'; // Purple
else if (['9', '8'].includes(arrow)) bgColor = '#2196F3'; // Blue
else if (['7', '6', '5', '4', '3', '2', '1'].includes(arrow)) bgColor = '#4CAF50'; // Green
else if (arrow.toUpperCase() === 'M') bgColor = '#9E9E9E'; // Gray
else bgColor = 'default';
return (
<Paper
key={index}
elevation={1}
sx={{
p: 1.5,
m: 0.5,
minWidth: '40px',
textAlign: 'center',
bgcolor: bgColor,
color: 'white'
}}
>
<Typography variant="h6">{arrow.toUpperCase()}</Typography>
</Paper>
);
})}
{Array(arrowsPerEnd - currentEnd.length).fill(0).map((_, index) => (
<Paper
key={`empty-${index}`}
elevation={1}
sx={{
p: 1.5,
m: 0.5,
minWidth: '40px',
textAlign: 'center',
bgcolor: '#f5f5f5'
}}
>
<Typography variant="h6">-</Typography>
</Paper>
))}
</Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Typography variant="body1">
End Total: {calculateEndTotal(currentEnd)}
</Typography>
<Button
variant="contained"
color="primary"
onClick={handleSaveEnd}
disabled={currentEnd.length === 0 || currentEnd.length < arrowsPerEnd}
>
Save End
</Button>
</Box>
</Box>
<Divider sx={{ my: 3 }} />
{/* Previous ends display */}
{(state.currentGame.ends?.length > 0) && (
<Box sx={{ mb: 3 }}>
<Typography variant="h6" gutterBottom>
Previous Ends
</Typography>
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
<TableCell>End</TableCell>
<TableCell>Arrows</TableCell>
<TableCell>Total</TableCell>
<TableCell>Running Total</TableCell>
</TableRow>
</TableHead>
<TableBody>
{state.currentGame.ends.map((end, endIndex) => {
const runningTotal = state.currentGame.ends
.slice(0, endIndex + 1)
.reduce((total, e) => total + e.total, 0);
return (
<TableRow key={endIndex}>
<TableCell>{endIndex + 1}</TableCell>
<TableCell>
{end.arrows.join(', ')}
</TableCell>
<TableCell>{end.total}</TableCell>
<TableCell>{runningTotal}</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
</Box>
)}
<Button
variant="contained"
color="secondary"
onClick={handleEndGame}
sx={{ mt: 3 }}
>
End Game
</Button>
</CardContent>
</Card>
</Grid>
</Grid>
);
};
export default ScoreTracker;