diff --git a/app/(main)/[teamId]/page.js b/app/(main)/[teamId]/page.js new file mode 100644 index 0000000..52d018e --- /dev/null +++ b/app/(main)/[teamId]/page.js @@ -0,0 +1,489 @@ +"use client"; +import { useState, useEffect, useCallback } from "react"; +import { useSession } from "next-auth/react"; +import { useRouter } from "next/navigation"; +import Link from "next/link"; +import { use } from "react"; // Add this import for React.use() + +export default function TeamPage({ params }) { + const { status, data: session } = useSession(); + const [team, setTeam] = useState(null); + const [teamMembers, setTeamMembers] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(""); + const [huntActive, setHuntActive] = useState(false); + const router = useRouter(); + + // Unwrap the params Promise using React.use() + const unwrappedParams = use(params); + const teamId = unwrappedParams.teamId; + + // Add a function to check if the hunt has been activated by admin + const checkHuntStatus = useCallback(async () => { + try { + const response = await fetch("/api/hunt-status", { + method: "GET", + headers: { "Content-Type": "application/json" }, + credentials: "include", + }); + + if (response.ok) { + const data = await response.json(); + setHuntActive(data.active || false); + } + } catch (err) { + console.error("Failed to check hunt status:", err); + } + }, []); + + const fetchTeamData = useCallback(async () => { + try { + if (!teamId || isNaN(parseInt(teamId, 10))) { + throw new Error("Invalid team ID"); + } + + console.log("Fetching team data for team ID:", teamId); + + const response = await fetch(`/api/teams/${teamId}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + credentials: "include", + // Add cache: 'no-store' to prevent caching of team data + cache: "no-store", + }); + + if (!response.ok) { + const text = await response.text(); + let errorMessage; + try { + const errorData = JSON.parse(text); + errorMessage = errorData.error || "Failed to fetch team data"; + } catch (unused) { + errorMessage = `Failed to fetch team data: ${ + response.status + } ${text.slice(0, 100)}`; + } + throw new Error(errorMessage); + } + + const data = await response.json(); + console.log("Team data received:", data); + + if (!data.team) { + throw new Error("Team not found"); + } + + setTeam(data.team); + setTeamMembers(data.members || []); + setError(""); + + // Check hunt status whenever we fetch team data + await checkHuntStatus(); + } catch (err) { + console.error("Team page error:", err); + setError(err.message); + // If we get a 401 error, redirect to login + if (err.message.includes("401")) { + router.push("/login"); + } + } finally { + setLoading(false); + } + }, [teamId, router, checkHuntStatus]); + + useEffect(() => { + // Only attempt to fetch data when session is ready and we have a teamId + if (status === "authenticated" && teamId) { + console.log("Session authenticated, fetching team data"); + fetchTeamData(); + + // Set up an interval to refresh team data every 10 seconds + const refreshInterval = setInterval(fetchTeamData, 10000); + + // Clean up the interval when component unmounts + return () => clearInterval(refreshInterval); + } else if (status === "unauthenticated") { + console.log("User not authenticated, redirecting to login"); + router.push("/login"); + } + }, [teamId, status, fetchTeamData, router]); + + if (status === "loading" || loading) { + return ( +
+
+
Loading team data...
+
+ ); + } + + if (error) { + return ( +
+
⚠️
+
Error: {error}
+ + + +
+ ); + } + + if (!team) { + return ( +
+
🔍
+
Team not found
+ + + +
+ ); + } + + return ( +
+
+
+

Team Name: {team.name}

+
{team.code}
+
+
+ +
+

Team Members

+ + {teamMembers.length > 0 ? ( +
+ {teamMembers.map((member) => ( +
+
👤
+
+
+ {member.username} + {member.id === session?.user?.id && " (You)"} +
+
+
+ ))} +
+ ) : ( +
No team members found
+ )} + + {/* Remove the add member button - it was here before */} +
+ +
+ + + {/* New Start Hunt button with conditional behavior */} + {session?.user?.role !== "admin" && ( + + )} + + {/* Keep the admin start button which directly starts the hunt */} + {session?.user?.role === "admin" && ( + + )} +
+ +
+ {/* Update the Home link to point to the current team page */} + +
🏠
+
Home
+ + +
📊
+
Stats
+ + +
👥
+
Team
+ + +
👤
+
Profile
+ +
+ + +
+ ); +} diff --git a/app/(main)/hunt-not-started/page.js b/app/(main)/hunt-not-started/page.js new file mode 100644 index 0000000..0726e09 --- /dev/null +++ b/app/(main)/hunt-not-started/page.js @@ -0,0 +1,102 @@ +"use client"; +import { useState, useEffect } from "react"; +import { useRouter, useSearchParams } from "next/navigation"; +import Link from "next/link"; + +export default function HuntNotStarted() { + const [countdown, setCountdown] = useState(10); + const router = useRouter(); + const searchParams = useSearchParams(); + const teamId = searchParams.get("teamId"); + + useEffect(() => { + // Countdown timer + if (countdown > 0) { + const timer = setTimeout(() => { + setCountdown(countdown - 1); + }, 1000); + return () => clearTimeout(timer); + } else { + // Redirect back to team page when countdown reaches 0 + if (teamId) { + router.push(`/${teamId}`); + } else { + router.push("/team-selection"); + } + } + }, [countdown, router, teamId]); + + return ( +
+
+
⏳
+

Hunt Not Started Yet

+

+ The treasure hunt has not been activated by an administrator yet. + Please check back later or contact an admin to start the hunt. +

+

Redirecting back in {countdown} seconds...

+ + + +
+ + +
+ ); +} diff --git a/app/(main)/team/[teamId]/page.js b/app/(main)/team/[teamId]/page.js index a20d048..ae30ddf 100644 --- a/app/(main)/team/[teamId]/page.js +++ b/app/(main)/team/[teamId]/page.js @@ -132,12 +132,9 @@ export default function TeamPage({ params }) { return (
- - ← -
-

{team.name}

-
Team Code: {team.code}
+

Team Name: {team.name}

+
{team.code}
@@ -299,17 +296,11 @@ export default function TeamPage({ params }) { align-items: center; } - .back-button { - text-decoration: none; - color: white; - font-size: 24px; - margin-right: 15px; - display: flex; - align-items: center; - } - .team-info { flex: 1; + display: flex; + justify-content: space-between; + align-items: center; } .team-name { @@ -319,9 +310,9 @@ export default function TeamPage({ params }) { } .team-code { - margin-top: 5px; - font-size: 14px; - opacity: 0.8; + font-size: 24px; + font-weight: bold; + margin-left: auto; } .members-container {