From 6527c26a8166b23f074e85eb335a15800c1947a2 Mon Sep 17 00:00:00 2001 From: Thomas Hallock Date: Thu, 23 Oct 2025 17:21:08 -0500 Subject: [PATCH] feat(card-sorting): add collapsible stats sidebar for spectators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add comprehensive real-time statistics sidebar for spectators with smooth collapse/expand functionality and color-coded metrics. Features: - Collapsible sidebar (280px width, slides in from right) - Collapse toggle button with smooth animations - Real-time statistics cards with gradient backgrounds: - Time Elapsed (blue gradient, MM:SS format) - Cards Placed (green gradient, with completion percentage) - Current Accuracy (yellow gradient, % of correctly positioned cards) - Numbers Revealed status (pink/gray gradient based on state) - Each stat card has emoji icons and descriptive labels - Smooth slide-in/out transition (0.3s ease) - Fixed position below spectator banner - Z-index 90 (below banner which is 100) UI Details: - Sidebar positioned at top: 56px (below banner) - Right slide animation from -280px to 0 - Toggle button extends slightly on hover (40px → 44px) - Arrow indicators (◀ when collapsed, ▶ when expanded) - Semi-transparent white background (95% opacity) - Subtle box shadow for depth - Scrollable content area for long stat lists 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../components/PlayingPhaseDrag.tsx | 176 ++++++++++++++++++ 1 file changed, 176 insertions(+) diff --git a/apps/web/src/arcade-games/card-sorting/components/PlayingPhaseDrag.tsx b/apps/web/src/arcade-games/card-sorting/components/PlayingPhaseDrag.tsx index f93f1792..5881da47 100644 --- a/apps/web/src/arcade-games/card-sorting/components/PlayingPhaseDrag.tsx +++ b/apps/web/src/arcade-games/card-sorting/components/PlayingPhaseDrag.tsx @@ -826,6 +826,8 @@ export function PlayingPhaseDrag() { // Spectator educational mode (show correctness indicators) const [spectatorEducationalMode, setSpectatorEducationalMode] = useState(false) + // Spectator stats sidebar collapsed state + const [spectatorStatsCollapsed, setSpectatorStatsCollapsed] = useState(false) const containerRef = useRef(null) const dragStateRef = useRef<{ @@ -1343,6 +1345,180 @@ export function PlayingPhaseDrag() { )} + {/* Spectator Stats Sidebar */} + {isSpectating && ( +
+ {/* Collapse/Expand Toggle */} + + + {/* Stats Content */} +
+

+ 📊 Live Stats +

+ + {/* Time Elapsed */} +
+
+ ⏱️ Time Elapsed +
+
+ {Math.floor(elapsedTime / 60)}:{(elapsedTime % 60).toString().padStart(2, '0')} +
+
+ + {/* Cards Placed */} +
+
+ 🎯 Cards Placed +
+
+ {state.placedCards.filter((c) => c !== null).length} / {state.cardCount} +
+
+ {Math.round( + (state.placedCards.filter((c) => c !== null).length / state.cardCount) * 100 + )} + % complete +
+
+ + {/* Current Accuracy */} +
+
+ ✨ Current Accuracy +
+
+ {(() => { + const placedCards = state.placedCards.filter((c): c is SortingCard => c !== null) + if (placedCards.length === 0) return '0%' + const correctCount = placedCards.filter( + (c, i) => state.correctOrder[i]?.id === c.id + ).length + return `${Math.round((correctCount / placedCards.length) * 100)}%` + })()} +
+
+ Cards in correct position +
+
+ + {/* Numbers Revealed */} +
+
+ 👁️ Numbers Revealed +
+
+ {state.numbersRevealed ? '✓ Yes' : '✗ No'} +
+
+
+
+ )} + {/* Floating action buttons */} {!isSpectating && (