diff --git a/app/(main)/admin/page.js b/app/(main)/admin/page.js index 59e66fc..9f9bcbf 100644 --- a/app/(main)/admin/page.js +++ b/app/(main)/admin/page.js @@ -5,6 +5,7 @@ import { useRouter } from "next/navigation"; import { setHuntTimer, clearHuntTimer, + getHuntTimer, formatElapsedTime, } from "@/utils/timerService"; @@ -26,6 +27,7 @@ export default function AdminDashboard() { useEffect(() => { async function loadTimerStatus() { try { + setLoading(true); const response = await fetch("/api/hunt/timer", { credentials: "include", headers: { @@ -39,18 +41,13 @@ export default function AdminDashboard() { // Calculate elapsed time if (data.startTime) { - const startTime = new Date(data.startTime).getTime(); - const currentTime = Date.now(); - const elapsed = Math.floor((currentTime - startTime) / 1000); - setElapsedTime(elapsed); - - // Update global timer data for all clients + // 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()); + // If there's an end time, set it + if (data.endTime) { + setTimerEndTime(new Date(data.endTime).getTime()); + } } } @@ -78,62 +75,39 @@ export default function AdminDashboard() { } }, [session, status, router]); - // Timer update effect + // Timer update effect - use the centralized timer service useEffect(() => { - if (timerActive) { - timerRef.current = setInterval(() => { - setElapsedTime((prev) => { - const newElapsed = prev + 1; + let intervalId; - // If we have an end time and we've reached it, stop the timer + if (timerActive) { + // Use the existing timer data from the centralized service + intervalId = setInterval(() => { + const timerData = getHuntTimer(); + + if (timerData && timerData.startTime) { + // Format the elapsed time using the centralized service + const formattedTime = formatElapsedTime(timerData.startTime); + setTimerDisplay(formattedTime); + + // If there's an end time, check if we've reached it if (timerEndTime && Date.now() >= timerEndTime) { stopTimer(); - return prev; } - - // Update the display - formatTimeDisplay(newElapsed); - return newElapsed; - }); + } else { + setTimerDisplay("--:--:--"); + } }, 1000); } else { - if (timerRef.current) { - clearInterval(timerRef.current); - } + setTimerDisplay("--:--:--"); } return () => { - if (timerRef.current) { - clearInterval(timerRef.current); + if (intervalId) { + clearInterval(intervalId); } }; }, [timerActive, timerEndTime]); - // Format time for display whenever elapsed time changes - useEffect(() => { - formatTimeDisplay(elapsedTime); - }, [elapsedTime]); - - // Format seconds into HH:MM:SS - const formatTimeDisplay = (totalSeconds) => { - if (!timerActive) { - setTimerDisplay("--:--:--"); - return; - } - - const hours = Math.floor(totalSeconds / 3600); - const minutes = Math.floor((totalSeconds % 3600) / 60); - const seconds = totalSeconds % 60; - - const display = [ - hours.toString().padStart(2, "0"), - minutes.toString().padStart(2, "0"), - seconds.toString().padStart(2, "0"), - ].join(":"); - - setTimerDisplay(display); - }; - const startTimer = async (preset = false) => { setLoading(true); try { @@ -182,7 +156,6 @@ export default function AdminDashboard() { if (data.success) { setTimerActive(true); - setElapsedTime(0); if (endTime) { setTimerEndTime(new Date(endTime).getTime()); @@ -221,8 +194,7 @@ export default function AdminDashboard() { if (data.success) { setTimerActive(false); setTimerEndTime(null); - setElapsedTime(0); - formatTimeDisplay(0); + setTimerDisplay("--:--:--"); // Clear timer data for all clients clearHuntTimer(); diff --git a/app/(main)/leaderboard/page.js b/app/(main)/leaderboard/page.js index 906a9d8..c7870a1 100644 --- a/app/(main)/leaderboard/page.js +++ b/app/(main)/leaderboard/page.js @@ -1,15 +1,70 @@ "use client"; import { useState, useEffect } from "react"; import { useSearchParams } from "next/navigation"; -// ...other imports +import { useRouter } from "next/navigation"; +import { useSession } from "next-auth/react"; +import Link from "next/link"; export default function LeaderboardPage() { const [teams, setTeams] = useState([]); const [loading, setLoading] = useState(true); + const [error, setError] = useState(""); const searchParams = useSearchParams(); const huntJustEnded = searchParams.get("huntEnded") === "true"; + const router = useRouter(); + const { status } = useSession(); - // ...existing fetch logic + // Function to fetch leaderboard data + const fetchLeaderboard = async () => { + try { + setLoading(true); + + // Fetch from the public leaderboard endpoint + const response = await fetch("/api/leaderboard", { + headers: { + "Cache-Control": "no-cache, no-store, must-revalidate", + Pragma: "no-cache", + }, + credentials: "include", + }); + + if (!response.ok) { + throw new Error("Failed to fetch leaderboard data"); + } + + const data = await response.json(); + setTeams(data); + setError(""); + } catch (err) { + console.error("Error fetching leaderboard:", err); + setError(err.message); + } finally { + setLoading(false); + } + }; + + // Fetch data on mount and set up refresh interval + useEffect(() => { + // Only proceed if authenticated + if (status === "authenticated") { + fetchLeaderboard(); + + // Refresh data every 30 seconds + const interval = setInterval(fetchLeaderboard, 30000); + return () => clearInterval(interval); + } else if (status === "unauthenticated") { + router.push("/login"); + } + }, [status, router]); + + if (loading) { + return ( +
Loading leaderboard...
+| Rank | +Team | + {!huntJustEnded && ( + <> +Progress | +Last Activity | + > + )} +
|---|---|---|---|
|
+ {index < 3 ? (
+
+ {index === 0 && "π₯"}
+ {index === 1 && "π₯"}
+ {index === 2 && "π₯"}
+
+ ) : (
+ `${index + 1}`
+ )}
+ |
+ {team.name} | + {!huntJustEnded && ( + <> +
+
+
+
+ {team.cluesFound} / {team.totalClues}
+ {team.progress > 0 && ` (${team.progress}%)`}
+
+
+ |
+ + {team.lastActivity + ? new Date(team.lastActivity).toLocaleTimeString([], { + hour: "2-digit", + minute: "2-digit", + }) + : "Never"} + | + > + )} +