"use client"; import { useState, useEffect, useRef } from "react"; import { useSession } from "next-auth/react"; import { useRouter } from "next/navigation"; import { setHuntTimer, clearHuntTimer, getHuntTimer, formatElapsedTime, } from "@/utils/timerService"; export default function AdminDashboard() { const { data: session, status } = useSession(); const [timerActive, setTimerActive] = useState(false); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); const [elapsedTime, setElapsedTime] = useState(0); // in seconds const [timerDisplay, setTimerDisplay] = useState("--:--:--"); const [presetHours, setPresetHours] = useState(""); const [presetMinutes, setPresetMinutes] = useState(""); const [presetSeconds, setPresetSeconds] = useState(""); const [timerEndTime, setTimerEndTime] = useState(null); const timerRef = useRef(null); const timerIntervalRef = useRef(null); const router = useRouter(); // Load timer status on page load useEffect(() => { async function loadTimerStatus() { try { setLoading(true); // First, check if we already have timer data in localStorage const existingTimer = getHuntTimer(); if (existingTimer && existingTimer.startTime) { // If we have timer data, immediately set the display setTimerActive(existingTimer.isActive); if (existingTimer.endTime) { setTimerEndTime(new Date(existingTimer.endTime).getTime()); } } // Then, fetch fresh data from the server to ensure we're in sync const response = await fetch("/api/hunt/timer", { credentials: "include", headers: { "Cache-Control": "no-cache, no-store, must-revalidate", Pragma: "no-cache", }, }); if (!response.ok) { throw new Error(`Server responded with status: ${response.status}`); } const data = await response.json(); // Check both activity indicators to handle cases where they might be inconsistent const isActive = data.active || data.timerActive; if (isActive && data.startTime) { setTimerActive(true); // Store the start time in the centralized service setHuntTimer(data.startTime, data.endTime); // If there's an end time, set it if (data.endTime) { setTimerEndTime(new Date(data.endTime).getTime()); } else { setTimerEndTime(null); } } else { // Only clear local timer if server explicitly says timer is not active // This prevents clearing a valid timer if the server response is ambiguous if (data.active === false && data.timerActive === false) { clearHuntTimer(); setTimerActive(false); } } setLoading(false); } catch (err) { console.error("Failed to load timer status:", err); setError(`Failed to load timer status: ${err.message}`); setLoading(false); } } if (status === "authenticated") { loadTimerStatus(); } // Clear interval when component unmounts return () => { if (timerIntervalRef.current) { clearInterval(timerIntervalRef.current); timerIntervalRef.current = null; } }; }, [status]); // Check if user is admin useEffect(() => { if (status === "authenticated") { if (session?.user?.role !== "admin") { router.push("/team-selection"); } } else if (status === "unauthenticated") { router.push("/login"); } }, [session, status, router]); // Create a separate effect for updating the timer display useEffect(() => { let timerId; // Initialize timer async function initializeTimer() { // Set up timer updates const updateTimer = () => { const timerData = getHuntTimer(); if (timerData && timerData.startTime) { setTimerDisplay(formatElapsedTime(timerData.startTime)); // If there's an end time, check if we've reached it if (timerEndTime && Date.now() >= timerEndTime) { stopTimer(); } } else { setTimerDisplay("--:--:--"); } }; // Initial update updateTimer(); // Update every second timerId = setInterval(updateTimer, 1000); } // Only initialize timer if the timer is active if (timerActive) { initializeTimer(); } else { setTimerDisplay("--:--:--"); } // Clean up interval on unmount return () => { if (timerId) { clearInterval(timerId); } }; }, [timerActive, timerEndTime]); // Add a function to fetch timer status directly from the server const fetchTimerStatusFromServer = async () => { try { const response = await fetch("/api/hunt/timer", { credentials: "include", headers: { "Cache-Control": "no-cache, no-store, must-revalidate", Pragma: "no-cache", }, }); if (!response.ok) { console.error("Server responded with status:", response.status); return; } const data = await response.json(); // Check both activity indicators for consistency const isActive = data.active || data.timerActive; if (isActive && data.startTime) { setTimerActive(true); setHuntTimer(data.startTime, data.endTime); if (data.endTime) { setTimerEndTime(new Date(data.endTime).getTime()); } else { setTimerEndTime(null); } setTimerDisplay(formatElapsedTime(data.startTime)); } else if (data.active === false && data.timerActive === false) { // Only clear timer if server explicitly says timer is inactive setTimerActive(false); clearHuntTimer(); setTimerDisplay("--:--:--"); } } catch (err) { console.error("Error fetching timer status:", err); } }; const startTimer = async (preset = false) => { setLoading(true); try { let endTime = null; // If using preset, calculate the end time if (preset) { const hours = parseInt(presetHours || "0"); const minutes = parseInt(presetMinutes || "0"); const seconds = parseInt(presetSeconds || "0"); // Validate input if (isNaN(hours) || isNaN(minutes) || isNaN(seconds)) { setError( "Please enter valid numbers for hours, minutes, and seconds" ); setLoading(false); return; } // Calculate total duration in milliseconds const durationMs = (hours * 3600 + minutes * 60 + seconds) * 1000; if (durationMs <= 0) { setError("Please enter a duration greater than zero"); setLoading(false); return; } // Set end time endTime = new Date(Date.now() + durationMs).toISOString(); } // API call to start timer const response = await fetch("/api/hunt/timer", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ action: "start", endTime: endTime, }), }); if (!response.ok) { throw new Error(`Server responded with status: ${response.status}`); } const data = await response.json(); if (data.success) { setTimerActive(true); if (endTime) { setTimerEndTime(new Date(endTime).getTime()); } else { setTimerEndTime(null); } // Update global timer data for all clients setHuntTimer(data.startTime, data.endTime); // Immediately display the current elapsed time setTimerDisplay(formatElapsedTime(data.startTime)); setError(""); } else { setError(data.error || "Failed to start timer"); } } catch (err) { console.error("Failed to start timer:", err); setError(`Failed to start timer: ${err.message}`); } finally { setLoading(false); } }; const stopTimer = async () => { setLoading(true); try { const response = await fetch("/api/hunt/timer", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ action: "stop" }), }); if (!response.ok) { throw new Error(`Server responded with status: ${response.status}`); } const data = await response.json(); if (data.success) { setTimerActive(false); setTimerEndTime(null); setTimerDisplay("--:--:--"); // Clear timer data for all clients clearHuntTimer(); } else { setError(data.error || "Failed to stop timer"); } } catch (err) { console.error("Failed to stop timer:", err); setError(`Failed to stop timer: ${err.message}`); } finally { setLoading(false); } }; const startPresetTimer = () => { startTimer(true); }; // Handle access denied and loading states if (status === "loading" || loading) { return
Loading...
; } if (status === "unauthenticated") { return null; // Will redirect via useEffect } if (session?.user?.role !== "admin") { return null; // Will redirect via useEffect } return (
Admin Dashboard

Hunt Timer

{timerDisplay}

Set Timer Duration

setPresetHours(e.target.value)} disabled={timerActive || loading} className="admin-input" />
setPresetMinutes(e.target.value)} disabled={timerActive || loading} className="admin-input" />
setPresetSeconds(e.target.value)} disabled={timerActive || loading} className="admin-input" />
{error &&

{error}

}
{/* Admin Navigation Bar */}
); }