From 7efabc6465bce31cfb8adbc723bfd84aad84c03c Mon Sep 17 00:00:00 2001 From: Adekunle Date: Thu, 8 Jan 2026 21:37:04 +0100 Subject: [PATCH] feat: add game timer and game history components - Add GameTimer component showing elapsed time during gameplay - Add GameHistory component tracking wins/losses and stats - Include win rate calculation and expandable game list --- src/components/GameHistory.tsx | 131 +++++++++++++++++++++++++++++++++ src/components/GameTimer.tsx | 48 ++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 src/components/GameHistory.tsx create mode 100644 src/components/GameTimer.tsx diff --git a/src/components/GameHistory.tsx b/src/components/GameHistory.tsx new file mode 100644 index 0000000..f0a751a --- /dev/null +++ b/src/components/GameHistory.tsx @@ -0,0 +1,131 @@ +'use client'; + +import { useState } from 'react'; + +interface GameRecord { + id: string; + date: string; + difficulty: 'easy' | 'medium' | 'hard'; + result: 'won' | 'lost'; + time: number; +} + +interface GameHistoryProps { + games: GameRecord[]; + isLoading: boolean; +} + +export function GameHistory({ games, isLoading }: GameHistoryProps) { + const [isExpanded, setIsExpanded] = useState(false); + + if (isLoading) { + return ( +
+
+
+ ); + } + + const formatTime = (seconds: number) => { + const mins = Math.floor(seconds / 60); + const secs = seconds % 60; + return `${mins}:${secs.toString().padStart(2, '0')}`; + }; + + const recentGames = games.slice(-5).reverse(); + const allGames = isExpanded ? games.slice().reverse() : recentGames; + + const stats = { + total: games.length, + wins: games.filter(g => g.result === 'won').length, + losses: games.filter(g => g.result === 'lost').length, + }; + + const winRate = stats.total > 0 ? Math.round((stats.wins / stats.total) * 100) : 0; + + return ( +
+
+

+ 📜 Game History +

+
+ {stats.total} games +
+
+ + {/* Stats Summary */} +
+
+
{stats.wins}
+
Wins 🏆
+
+
+
{stats.losses}
+
Losses 💔
+
+
+
{winRate}%
+
Win Rate 📊
+
+
+ + {/* Games List */} + {games.length === 0 ? ( +
+
🎮
+

No games played yet

+

Start your first game!

+
+ ) : ( + <> +
+ {allGames.map((game) => ( +
+
+ + {game.result === 'won' ? '✅' : '❌'} + +
+
+ {game.difficulty} +
+
+ {new Date(game.date).toLocaleDateString()} +
+
+
+
+
+ {game.result === 'won' ? '+ Won' : '- Lost'} +
+
+ {formatTime(game.time)} +
+
+
+ ))} +
+ + {games.length > 5 && ( + + )} + + )} +
+ ); +} diff --git a/src/components/GameTimer.tsx b/src/components/GameTimer.tsx new file mode 100644 index 0000000..974f782 --- /dev/null +++ b/src/components/GameTimer.tsx @@ -0,0 +1,48 @@ +'use client'; + +import { useState, useEffect } from 'react'; + +interface GameTimerProps { + isRunning: boolean; + onTimeUpdate?: (seconds: number) => void; +} + +export function GameTimer({ isRunning, onTimeUpdate }: GameTimerProps) { + const [seconds, setSeconds] = useState(0); + + useEffect(() => { + let interval: NodeJS.Timeout; + + if (isRunning) { + interval = setInterval(() => { + setSeconds(s => { + const newValue = s + 1; + onTimeUpdate?.(newValue); + return newValue; + }); + }, 1000); + } + + return () => clearInterval(interval); + }, [isRunning, onTimeUpdate]); + + const formatTime = (totalSeconds: number) => { + const hours = Math.floor(totalSeconds / 3600); + const minutes = Math.floor((totalSeconds % 3600) / 60); + const secs = totalSeconds % 60; + + if (hours > 0) { + return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; + } + return `${minutes}:${secs.toString().padStart(2, '0')}`; + }; + + return ( +
+ ⏱️ Time: + + {formatTime(seconds)} + +
+ ); +}