Updated practice view and added score distribution
This commit is contained in:
@@ -17,7 +17,8 @@ import {
|
|||||||
TableHead,
|
TableHead,
|
||||||
TableRow,
|
TableRow,
|
||||||
Divider,
|
Divider,
|
||||||
ButtonGroup
|
ButtonGroup,
|
||||||
|
LinearProgress
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { Remove } from '@mui/icons-material';
|
import { Remove } from '@mui/icons-material';
|
||||||
import { useScore, ACTIONS } from '../context/ScoreContext';
|
import { useScore, ACTIONS } from '../context/ScoreContext';
|
||||||
@@ -28,6 +29,10 @@ const ScoreTracker = () => {
|
|||||||
|
|
||||||
// Local state for the current end
|
// Local state for the current end
|
||||||
const [currentEnd, setCurrentEnd] = useState([]);
|
const [currentEnd, setCurrentEnd] = useState([]);
|
||||||
|
// Local state to track score distribution
|
||||||
|
const [scoreDistribution, setScoreDistribution] = useState({});
|
||||||
|
// Local state to track total arrows
|
||||||
|
const [totalArrows, setTotalArrows] = useState(0);
|
||||||
|
|
||||||
// Check if we have an active game, if not redirect to setup
|
// Check if we have an active game, if not redirect to setup
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -36,6 +41,15 @@ const ScoreTracker = () => {
|
|||||||
}
|
}
|
||||||
}, [state.currentGame, navigate]);
|
}, [state.currentGame, navigate]);
|
||||||
|
|
||||||
|
// Update score distribution whenever the current game changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (state.currentGame) {
|
||||||
|
const distribution = calculateScoreDistribution();
|
||||||
|
setScoreDistribution(distribution);
|
||||||
|
setTotalArrows(calculateTotalArrows(distribution));
|
||||||
|
}
|
||||||
|
}, [state.currentGame]);
|
||||||
|
|
||||||
// Handle adding an arrow score
|
// Handle adding an arrow score
|
||||||
const handleAddArrow = (score) => {
|
const handleAddArrow = (score) => {
|
||||||
// Add to current end
|
// Add to current end
|
||||||
@@ -64,6 +78,42 @@ const ScoreTracker = () => {
|
|||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Calculate score distribution for all completed ends
|
||||||
|
const calculateScoreDistribution = () => {
|
||||||
|
// Initialize distribution object based on game type
|
||||||
|
let distribution = {};
|
||||||
|
|
||||||
|
if (state.currentGame.gameType === '300' && state.currentGame.targetFace === '5-spot') {
|
||||||
|
distribution = { 'X': 0, '5': 0, '4': 0, 'M': 0 };
|
||||||
|
} else if (state.currentGame.gameType === '300' && state.currentGame.targetFace !== '5-spot') {
|
||||||
|
distribution = { 'X': 0, '5': 0, '4': 0, '3': 0, '2': 0, '1': 0, 'M': 0 };
|
||||||
|
} else if (state.currentGame.gameType === '450' && state.currentGame.targetFace === '3-spot') {
|
||||||
|
distribution = { 'X': 0, '10': 0, '9': 0, '8': 0, 'M': 0 };
|
||||||
|
} else {
|
||||||
|
distribution = { 'X': 0, '10': 0, '9': 0, '8': 0, '7': 0, '6': 0, '5': 0, '4': 0, '3': 0, '2': 0, '1': 0, 'M': 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count arrows from all completed ends
|
||||||
|
if (state.currentGame.ends && state.currentGame.ends.length > 0) {
|
||||||
|
state.currentGame.ends.forEach(end => {
|
||||||
|
end.arrows.forEach(arrow => {
|
||||||
|
// Make sure to handle uppercase X and M
|
||||||
|
const normalizedArrow = arrow.toUpperCase();
|
||||||
|
if (distribution.hasOwnProperty(normalizedArrow)) {
|
||||||
|
distribution[normalizedArrow]++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return distribution;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate total arrows shot
|
||||||
|
const calculateTotalArrows = (distribution) => {
|
||||||
|
return Object.values(distribution).reduce((sum, count) => sum + count, 0);
|
||||||
|
};
|
||||||
|
|
||||||
// Get button color based on score
|
// Get button color based on score
|
||||||
const getButtonColor = (score) => {
|
const getButtonColor = (score) => {
|
||||||
// For 450 game with single spot, use a specific color scheme
|
// For 450 game with single spot, use a specific color scheme
|
||||||
@@ -150,6 +200,29 @@ const ScoreTracker = () => {
|
|||||||
payload: endObject
|
payload: endObject
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update score distribution immediately
|
||||||
|
// Create a new temporary game state that includes the new end
|
||||||
|
const updatedEnds = [...(state.currentGame.ends || []), endObject];
|
||||||
|
const tempGameState = {
|
||||||
|
...state.currentGame,
|
||||||
|
ends: updatedEnds
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate distribution based on this updated state
|
||||||
|
let updatedDistribution = { ...scoreDistribution };
|
||||||
|
|
||||||
|
// Add the new arrows to the distribution
|
||||||
|
currentEnd.forEach(arrow => {
|
||||||
|
const normalizedArrow = arrow.toUpperCase();
|
||||||
|
if (updatedDistribution.hasOwnProperty(normalizedArrow)) {
|
||||||
|
updatedDistribution[normalizedArrow]++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update the state
|
||||||
|
setScoreDistribution(updatedDistribution);
|
||||||
|
setTotalArrows(calculateTotalArrows(updatedDistribution));
|
||||||
|
|
||||||
// Clear current end
|
// Clear current end
|
||||||
setCurrentEnd([]);
|
setCurrentEnd([]);
|
||||||
}
|
}
|
||||||
@@ -198,6 +271,12 @@ const ScoreTracker = () => {
|
|||||||
// Use the function to get the appropriate buttons
|
// Use the function to get the appropriate buttons
|
||||||
const scoreButtons = getScoreButtons();
|
const scoreButtons = getScoreButtons();
|
||||||
|
|
||||||
|
// Calculate current game total score
|
||||||
|
const currentGameTotal = state.currentGame.ends?.reduce((total, end) => total + end.total, 0) || 0;
|
||||||
|
|
||||||
|
// Check if this is a practice game (not league)
|
||||||
|
const isPracticeGame = !state.currentGame.isLeague;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={3} justifyContent="center">
|
<Grid container spacing={3} justifyContent="center">
|
||||||
<Grid item xs={12} sm={10} md={8}>
|
<Grid item xs={12} sm={10} md={8}>
|
||||||
@@ -207,19 +286,123 @@ const ScoreTracker = () => {
|
|||||||
titleTypographyProps={{ align: 'center' }}
|
titleTypographyProps={{ align: 'center' }}
|
||||||
/>
|
/>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Typography variant="h6" gutterBottom>
|
{/* Only show target face for league games */}
|
||||||
Target Face: {state.currentGame.targetFace || 'Standard'}
|
{!isPracticeGame && (
|
||||||
</Typography>
|
<Typography variant="h6" gutterBottom>
|
||||||
|
Target Face: {state.currentGame.targetFace || 'Standard'}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Current game stats */}
|
{/* Score Distribution Section - moved to the top and enhanced */}
|
||||||
<Box sx={{ mb: 3 }}>
|
<Box sx={{ mb: 4 }}>
|
||||||
<Paper elevation={2} sx={{ p: 2 }}>
|
<Paper
|
||||||
<Typography variant="h6" gutterBottom>
|
elevation={3}
|
||||||
Current Score: {state.currentGame.ends?.reduce((total, end) => total + end.total, 0) || 0}
|
sx={{
|
||||||
</Typography>
|
p: 3,
|
||||||
<Typography variant="body1">
|
borderRadius: 2,
|
||||||
Ends Completed: {state.currentGame.ends?.length || 0}
|
border: '1px solid #e0e0e0'
|
||||||
</Typography>
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 2 }}>
|
||||||
|
<Typography variant="h5" component="h2" fontWeight="bold">
|
||||||
|
Score Distribution
|
||||||
|
</Typography>
|
||||||
|
{/* Only show Total Score for league games */}
|
||||||
|
{!isPracticeGame && (
|
||||||
|
<Typography variant="h6">
|
||||||
|
Total Score: {currentGameTotal}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Grid container spacing={2} sx={{ mb: 2 }}>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
Arrows Shot: {totalArrows}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
{/* Only show Ends Completed for league games */}
|
||||||
|
{!isPracticeGame && (
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
Ends Completed: {state.currentGame.ends?.length || 0}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Divider sx={{ mb: 3 }} />
|
||||||
|
|
||||||
|
{totalArrows > 0 ? (
|
||||||
|
<Box>
|
||||||
|
{Object.entries(scoreDistribution)
|
||||||
|
// Only display scores that have valid values in the target face
|
||||||
|
.filter(([score, _]) => {
|
||||||
|
const validScores = getScoreButtons();
|
||||||
|
return validScores.includes(score);
|
||||||
|
})
|
||||||
|
// Sort by score value (X first, then 10, 9, etc.)
|
||||||
|
.sort(([scoreA], [scoreB]) => {
|
||||||
|
if (scoreA === 'X') return -1;
|
||||||
|
if (scoreB === 'X') return 1;
|
||||||
|
if (scoreA === 'M') return 1;
|
||||||
|
if (scoreB === 'M') return -1;
|
||||||
|
return parseInt(scoreB) - parseInt(scoreA);
|
||||||
|
})
|
||||||
|
// Only show scores that have at least one arrow
|
||||||
|
.filter(([_, count]) => count > 0)
|
||||||
|
.map(([score, count]) => {
|
||||||
|
const percentage = totalArrows > 0 ? (count / totalArrows) * 100 : 0;
|
||||||
|
const colorStyle = getButtonColor(score);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box key={score} sx={{ mb: 2 }}>
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 0.5, alignItems: 'center' }}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||||
|
<Paper
|
||||||
|
elevation={1}
|
||||||
|
sx={{
|
||||||
|
display: 'inline-block',
|
||||||
|
p: 1,
|
||||||
|
minWidth: '36px',
|
||||||
|
textAlign: 'center',
|
||||||
|
mr: 2,
|
||||||
|
bgcolor: colorStyle.bgcolor,
|
||||||
|
color: colorStyle.color
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body1" fontWeight="bold">{score}</Typography>
|
||||||
|
</Paper>
|
||||||
|
<Typography variant="body1" fontWeight="medium">
|
||||||
|
{count} arrows
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Typography variant="body1" fontWeight="medium">
|
||||||
|
{percentage.toFixed(1)}%
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<LinearProgress
|
||||||
|
variant="determinate"
|
||||||
|
value={percentage}
|
||||||
|
sx={{
|
||||||
|
height: 12,
|
||||||
|
borderRadius: 1,
|
||||||
|
bgcolor: '#f5f5f5',
|
||||||
|
'& .MuiLinearProgress-bar': {
|
||||||
|
bgcolor: colorStyle.bgcolor
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
|
<Typography variant="body1" sx={{ textAlign: 'center', py: 4, color: 'text.secondary' }}>
|
||||||
|
No arrows recorded yet. Start scoring to see your distribution.
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@@ -393,7 +576,7 @@ const ScoreTracker = () => {
|
|||||||
onClick={handleEndGame}
|
onClick={handleEndGame}
|
||||||
sx={{ mt: 3 }}
|
sx={{ mt: 3 }}
|
||||||
>
|
>
|
||||||
End Game
|
{isPracticeGame ? "End Practice" : "End Game"}
|
||||||
</Button>
|
</Button>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
Reference in New Issue
Block a user