Add right/wrong marking with steal mechanism and question stats

Adds Correct/Wrong buttons to admin panel that track question
answer stats across games and implement a steal system where
the next team gets one chance to answer if the first team is wrong.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-03 14:40:58 -04:00
parent cf0937763b
commit 46d45eebb6
7 changed files with 262 additions and 16 deletions

View File

@@ -23,6 +23,7 @@ export default function GameAdminView() {
const [newTeamName, setNewTeamName] = useState("");
const [currentTurnTeamId, setCurrentTurnTeamId] = useState(null);
const [currentTurnTeamName, setCurrentTurnTeamName] = useState(null);
const [stealActive, setStealActive] = useState(false);
useEffect(() => {
loadGameState();
@@ -41,6 +42,7 @@ export default function GameAdminView() {
setTotalQuestions(response.data.total_questions);
setCurrentTurnTeamId(response.data.current_turn_team_id);
setCurrentTurnTeamName(response.data.current_turn_team_name);
setStealActive(response.data.steal_active || false);
} catch (error) {
console.error("Error loading game state:", error);
}
@@ -87,6 +89,13 @@ export default function GameAdminView() {
setCurrentTurnTeamName(data.current_turn_team_name);
});
socket.on("steal_state_changed", (data) => {
console.log("Steal state changed:", data);
setStealActive(data.steal_active);
setCurrentTurnTeamId(data.current_turn_team_id);
setCurrentTurnTeamName(data.current_turn_team_name);
});
return () => {
socket.off("question_with_answer");
socket.off("score_updated");
@@ -94,6 +103,7 @@ export default function GameAdminView() {
socket.off("lifeline_updated");
socket.off("timer_reset");
socket.off("turn_changed");
socket.off("steal_state_changed");
};
}, [socket]);
@@ -220,6 +230,24 @@ export default function GameAdminView() {
}
};
const handleMarkCorrect = async () => {
try {
await adminAPI.markCorrect(gameId);
} catch (error) {
console.error("Error marking correct:", error);
alert("Error marking correct");
}
};
const handleMarkWrong = async () => {
try {
await adminAPI.markWrong(gameId);
} catch (error) {
console.error("Error marking wrong:", error);
alert("Error marking wrong");
}
};
const handleAdvanceTurn = async () => {
try {
await adminAPI.advanceTurn(gameId);
@@ -399,6 +427,20 @@ export default function GameAdminView() {
<div className="answer-box">
<strong>Answer:</strong> {currentQuestion.answer}
</div>
{stealActive && (
<div style={{
marginTop: "0.75rem",
padding: "0.5rem 1rem",
background: "#FFF3E0",
border: "2px solid #FF9800",
borderRadius: "8px",
color: "#E65100",
fontWeight: "bold",
textAlign: "center",
}}>
STEAL OPPORTUNITY {currentTurnTeamName}'s turn to steal
</div>
)}
</div>
) : (
<p>No question selected. Click "Start Game" to begin.</p>
@@ -559,21 +601,57 @@ export default function GameAdminView() {
{/* Button grid - 2 columns on desktop, 1 on mobile */}
<div className="controls-button-grid">
{gameState?.is_active && teams.length > 0 && (
<button
onClick={handleAdvanceTurn}
className="btn-next-turn"
style={{
padding: "0.75rem 1.5rem",
background: "#673AB7",
color: "white",
border: "none",
borderRadius: "4px",
cursor: "pointer",
fontSize: "1rem",
}}
>
Next Turn {currentTurnTeamName ? `(${currentTurnTeamName})` : ""}
</button>
<>
<button
onClick={handleAdvanceTurn}
className="btn-next-turn"
style={{
padding: "0.75rem 1.5rem",
background: "#673AB7",
color: "white",
border: "none",
borderRadius: "4px",
cursor: "pointer",
fontSize: "1rem",
}}
>
Next Turn {currentTurnTeamName ? `(${currentTurnTeamName})` : ""}
</button>
{currentQuestion && (
<>
<button
onClick={handleMarkCorrect}
style={{
padding: "0.75rem 1.5rem",
background: "#4CAF50",
color: "white",
border: stealActive ? "3px solid #FF9800" : "none",
borderRadius: "4px",
cursor: "pointer",
fontSize: "1rem",
fontWeight: "bold",
}}
>
{stealActive ? "Steal Correct (+1)" : "Correct (+1)"}
</button>
<button
onClick={handleMarkWrong}
style={{
padding: "0.75rem 1.5rem",
background: stealActive ? "#b71c1c" : "#f44336",
color: "white",
border: stealActive ? "3px solid #FF9800" : "none",
borderRadius: "4px",
cursor: "pointer",
fontSize: "1rem",
fontWeight: "bold",
}}
>
{stealActive ? "Steal Wrong" : "Wrong"}
</button>
</>
)}
</>
)}
{currentQuestion && (
<button

View File

@@ -120,6 +120,8 @@ export const adminAPI = {
addLifeline: (gameId: number, teamId: number) =>
api.post(`/admin/game/${gameId}/team/${teamId}/add-lifeline`),
advanceTurn: (id: number) => api.post(`/admin/game/${id}/advance-turn`),
markCorrect: (id: number) => api.post(`/admin/game/${id}/mark-correct`),
markWrong: (id: number) => api.post(`/admin/game/${id}/mark-wrong`),
};
// Categories API