import { useState, useRef, useEffect } from "react"; // ============================================= // Constants (DO NOT MODIFY) // ============================================= const INITIAL_MINUTES = 5; const INITIAL_SECONDS = 0; // ============================================= // Helper: format total seconds as MM:SS // ============================================= function formatTime(totalSeconds: number): string { const mins = Math.floor(totalSeconds / 60); const secs = totalSeconds % 60; return `${String(mins).padStart(2, "0")}:${String(secs).padStart(2, "0")}`; } // ============================================= // TODO: Implement Countdown Timer // ============================================= // // Requirements: // 1. Display a countdown starting from 05:00 // 2. Start button begins the countdown (decrement every second) // 3. Pause button pauses the countdown // 4. Reset button stops and resets to the initial time // 5. Timer auto-stops at 00:00 with a visual indicator // 6. Allow adjusting minutes/seconds before starting // 7. Clean up interval on component unmount // // Hints: // - Store remaining time as total seconds in state // - Use useRef (not state) for the interval ID — avoids re-renders // - Start: setInterval that decrements every 1000ms // - Inside the interval callback, use the functional updater: // setTimeLeft(prev => { if (prev <= 1) { stop; return 0; } return prev - 1; }) // This avoids stale closure issues // - Pause: clearInterval via the ref // - Reset: clearInterval + set time back to (minutes * 60 + seconds) // - useEffect cleanup: return () => clearInterval(ref.current) export default function App() { const [timeLeft, setTimeLeft] = useState(INITIAL_MINUTES * 60 + INITIAL_SECONDS); const [isRunning, setIsRunning] = useState(false); const [minutes, setMinutes] = useState(INITIAL_MINUTES); const [seconds, setSeconds] = useState(INITIAL_SECONDS); // TODO: Create a ref to hold the interval ID // const intervalRef = useRef<number | null>(null); // ============================================= // TODO: Implement start() // ============================================= // Should: // - Return early if timeLeft is 0 // - Set isRunning to true // - Create a setInterval (1000ms) that decrements timeLeft // - Inside the callback, check if prev <= 1 → clear interval, // set isRunning false, return 0 // - Store the interval ID in the ref const start = () => { // Your code here }; // ============================================= // TODO: Implement pause() // ============================================= // Should: // - Set isRunning to false // - Clear the interval using the ref const pause = () => { // Your code here }; // ============================================= // TODO: Implement reset() // ============================================= // Should: // - Set isRunning to false // - Clear the interval // - Reset timeLeft to (minutes * 60 + seconds) const reset = () => { // Your code here }; // ============================================= // TODO: Cleanup interval on unmount // ============================================= // useEffect(() => { // return () => { if (intervalRef.current) clearInterval(intervalRef.current); }; // }, []); return ( <div style={{ maxWidth: 480, margin: "0 auto", padding: 24, fontFamily: "system-ui" }}> <h2 style={{ fontSize: 20, fontWeight: 700, marginBottom: 4 }}> Countdown Timer </h2> <p style={{ fontSize: 14, color: "#666", marginBottom: 32 }}> Build a countdown timer with start, pause, and reset controls. </p> {/* Timer display */} <div style={{ textAlign: "center", marginBottom: 32 }}> <div style={{ fontSize: 72, fontFamily: "monospace", fontWeight: 700, letterSpacing: 4, color: timeLeft === 0 ? "#ef4444" : "#111", }}> {formatTime(timeLeft)} </div> {timeLeft === 0 && !isRunning && ( <p style={{ color: "#ef4444", fontWeight: 600, marginTop: 8 }}> Time's up! </p> )} </div> {/* Controls */} <div style={{ display: "flex", justifyContent: "center", gap: 12, marginBottom: 32 }}> {/* TODO: onClick should call start() or pause() based on isRunning */} <button onClick={() => isRunning ? pause() : start()} style={{ padding: "12px 28px", background: "#111", color: "#fff", borderRadius: 8, fontSize: 14, fontWeight: 600, cursor: "pointer", }} > {/* TODO: Toggle label between "Start" and "Pause" */} {isRunning ? "Pause" : "Start"} </button> <button onClick={reset} style={{ padding: "12px 28px", border: "1px solid #d1d5db", borderRadius: 8, fontSize: 14, fontWeight: 600, color: "#555", cursor: "pointer", }} > Reset </button> </div> {/* Time adjuster */} <div style={{ border: "1px solid #e5e7eb", borderRadius: 8, padding: 24, marginBottom: 24, }}> <p style={{ fontSize: 13, color: "#999", marginBottom: 16 }}> Adjust starting time </p> <div style={{ display: "flex", alignItems: "center", justifyContent: "center", gap: 16 }}> <div style={{ textAlign: "center" }}> <label style={{ fontSize: 11, color: "#999", textTransform: "uppercase", letterSpacing: 1 }}> Minutes </label> <input type="number" min={0} max={99} value={minutes} disabled={isRunning} onChange={(e) => { const m = Math.max(0, Math.min(99, Number(e.target.value) || 0)); setMinutes(m); if (!isRunning) setTimeLeft(m * 60 + seconds); }} style={{ display: "block", width: 72, marginTop: 4, padding: "8px 12px", border: "1px solid #e5e7eb", borderRadius: 8, textAlign: "center", fontSize: 18, fontFamily: "monospace", opacity: isRunning ? 0.5 : 1, }} /> </div> <span style={{ fontSize: 24, color: "#ccc", fontFamily: "monospace", marginTop: 20 }}>:</span> <div style={{ textAlign: "center" }}> <label style={{ fontSize: 11, color: "#999", textTransform: "uppercase", letterSpacing: 1 }}> Seconds </label> <input type="number" min={0} max={59} value={seconds} disabled={isRunning} onChange={(e) => { const s = Math.max(0, Math.min(59, Number(e.target.value) || 0)); setSeconds(s); if (!isRunning) setTimeLeft(minutes * 60 + s); }} style={{ display: "block", width: 72, marginTop: 4, padding: "8px 12px", border: "1px solid #e5e7eb", borderRadius: 8, textAlign: "center", fontSize: 18, fontFamily: "monospace", opacity: isRunning ? 0.5 : 1, }} /> </div> </div> </div> {/* Hint */} <div style={{ padding: 16, background: "#f9fafb", borderLeft: "4px solid #111", borderRadius: "0 8px 8px 0", fontSize: 13, color: "#555", }}> <strong>Key concepts:</strong> <ul style={{ margin: "8px 0 0 16px", padding: 0, lineHeight: 1.8 }}> <li>Store interval ID in <code>useRef</code> — not state (avoids re-renders)</li> <li>Use <code>setTimeLeft(prev => prev - 1)</code> to avoid stale closures</li> <li>Check <code>prev <= 1</code> inside the callback to auto-stop</li> <li>Always <code>clearInterval</code> on pause, reset, and unmount</li> </ul> </div> </div> ); }