"use client"; import { useState, useEffect, useRef } from "react"; import { useSession } from "next-auth/react"; import { useRouter } from "next/navigation"; import { setHuntTimer, clearHuntTimer, getHuntTimer, formatElapsedTime, syncTimerWithServer, } 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 [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 timerStartTimeRef = useRef(null); // New ref to store the start time 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) { // Store the start time in the ref to prevent it from being lost on re-renders timerStartTimeRef.current = existingTimer.startTime; // If we have timer data, immediately set the display setTimerActive(existingTimer.isActive !== false); setTimerDisplay(formatElapsedTime(existingTimer.startTime)); 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 ref for persistence across renders timerStartTimeRef.current = data.startTime; // Store the start time in the centralized service setHuntTimer(data.startTime, data.endTime, true); // Explicitly set isActive=true // Update the display immediately with the correct time setTimerDisplay(formatElapsedTime(data.startTime)); 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 if (data.active === false && data.timerActive === false) { clearHuntTimer(); timerStartTimeRef.current = null; // Clear the start time ref setTimerActive(false); setTimerDisplay("--:--:--"); setTimerEndTime(null); } } 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 - but only have one instance useEffect(() => { // Clear any existing interval to avoid duplicates if (timerIntervalRef.current) { clearInterval(timerIntervalRef.current); timerIntervalRef.current = null; } const updateTimer = () => { // Use the ref to access the persisted start time const startTime = timerStartTimeRef.current; if (timerActive && startTime) { // Format time using the start time from our ref, which persists across renders setTimerDisplay(formatElapsedTime(startTime)); // Check if timer has ended if (timerEndTime && Date.now() >= timerEndTime) { stopTimer(); } } else if (timerActive) { // If timer is active but no data, try to restore syncTimerWithServer().then((result) => { if (result.success && result.data) { timerStartTimeRef.current = result.data.startTime; } }); } else { setTimerDisplay("--:--:--"); } }; // Initial update updateTimer(); // Set up interval for timer updates if (timerActive) { timerIntervalRef.current = setInterval(updateTimer, 1000); } return () => { if (timerIntervalRef.current) { clearInterval(timerIntervalRef.current); timerIntervalRef.current = null; } }; }, [timerActive, timerEndTime]); // Include timerEndTime as a dependency // 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); timerStartTimeRef.current = data.startTime; // Update the ref 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); timerStartTimeRef.current = null; // Clear the ref 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); // Store the start time in the ref timerStartTimeRef.current = data.startTime; 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); timerStartTimeRef.current = null; // Clear the ref 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
{error}
}