mirror of
https://github.com/mudabbir-ahmad/peworkshop.git
synced 2026-10-08 03:50:21 +00:00
Still trying to get the scroll div to work correctly with the page for all devices, or at the very least desktop devices.
500 lines
13 KiB
JavaScript
500 lines
13 KiB
JavaScript
"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";
|
|
import NavBar from "@/components/NavBar";
|
|
import { getHuntTimer } from "@/utils/timerService";
|
|
|
|
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 [huntInProgress, setHuntInProgress] = useState(false);
|
|
const router = useRouter();
|
|
|
|
const unwrappedParams = use(params);
|
|
const teamId = unwrappedParams.teamId;
|
|
|
|
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");
|
|
}
|
|
|
|
const userResponse = await fetch("/api/user", {
|
|
method: "GET",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
credentials: "include",
|
|
cache: "no-store",
|
|
});
|
|
|
|
const userData = await userResponse.json();
|
|
|
|
if (!userData.teamId || userData.teamId != teamId) {
|
|
console.log("User no longer in team, redirecting to team selection");
|
|
window.location.href = "/team-selection";
|
|
return;
|
|
}
|
|
|
|
console.log("Fetching team data for team ID:", teamId);
|
|
|
|
const timestamp = new Date().getTime();
|
|
const response = await fetch(`/api/teams/${teamId}?_t=${timestamp}`, {
|
|
method: "GET",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"Cache-Control": "no-cache, no-store, must-revalidate",
|
|
Pragma: "no-cache",
|
|
},
|
|
credentials: "include",
|
|
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("");
|
|
|
|
await checkHuntStatus();
|
|
} catch (err) {
|
|
console.error("Team page error:", err);
|
|
setError(err.message);
|
|
if (err.message.includes("401")) {
|
|
router.push("/login");
|
|
}
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [teamId, router, checkHuntStatus]);
|
|
|
|
useEffect(() => {
|
|
if (status === "authenticated" && teamId) {
|
|
console.log("Session authenticated, fetching team data");
|
|
fetchTeamData();
|
|
|
|
const refreshInterval = setInterval(fetchTeamData, 3000);
|
|
|
|
return () => clearInterval(refreshInterval);
|
|
} else if (status === "unauthenticated") {
|
|
console.log("User not authenticated, redirecting to login");
|
|
router.push("/login");
|
|
}
|
|
}, [teamId, status, fetchTeamData, router]);
|
|
|
|
useEffect(() => {
|
|
const timerData = getHuntTimer();
|
|
setHuntInProgress(timerData && timerData.startTime);
|
|
}, []);
|
|
|
|
if (status === "loading" || loading) {
|
|
return (
|
|
<div className="loading-container">
|
|
<div className="loading-spinner"></div>
|
|
<div className="loading-text">Loading team data...</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<div className="error-container">
|
|
<div className="error-icon">⚠️</div>
|
|
<div className="error-message">Error: {error}</div>
|
|
<Link href="/team-selection">
|
|
<button className="back-button">Back to Team Selection</button>
|
|
</Link>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!team) {
|
|
return (
|
|
<div className="error-container">
|
|
<div className="error-icon">🔍</div>
|
|
<div className="error-message">Team not found</div>
|
|
<Link href="/team-selection">
|
|
<button className="back-button">Back to Team Selection</button>
|
|
</Link>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const handleStartHunt = () => {
|
|
router.push("/map");
|
|
};
|
|
|
|
return (
|
|
<div className="team-page">
|
|
<div className="team-banner">
|
|
<div className="team-info">
|
|
<h1 className="team-name">Team Name: {team.name}</h1>
|
|
<div className="team-code">{team.code}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="content-wrapper">
|
|
<div className="waves-container">
|
|
<img src="/images/Waves.svg" alt="Waves" className="waves-image" />
|
|
</div>
|
|
|
|
<div className="scroll-background">
|
|
<div className="members-container">
|
|
<h2 className="members-title">Team Members</h2>
|
|
|
|
<div className="members-content">
|
|
{teamMembers.length > 0 ? (
|
|
<div className="members-list">
|
|
{teamMembers.map((member) => (
|
|
<div key={member.id} className="member-card">
|
|
<div className="member-avatar">👤</div>
|
|
<div className="member-details">
|
|
<div className="member-name">
|
|
{member.username}
|
|
{member.id === session?.user?.id && " (You)"}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div className="empty-state">No team members found</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="team-actions">
|
|
<button
|
|
className="action-button leave-button"
|
|
onClick={async () => {
|
|
if (confirm("Are you sure you want to leave this team?")) {
|
|
try {
|
|
setLoading(true);
|
|
const response = await fetch(`/api/teams/${teamId}/leave`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
credentials: "include",
|
|
});
|
|
|
|
if (response.ok) {
|
|
localStorage.removeItem("teamData");
|
|
sessionStorage.removeItem("teamData");
|
|
localStorage.setItem("userLeftTeam", "true");
|
|
window.location.href = "/team-selection";
|
|
} else {
|
|
const errorData = await response.json();
|
|
throw new Error(errorData.error || "Failed to leave team");
|
|
}
|
|
} catch (err) {
|
|
setError(err.message);
|
|
setLoading(false);
|
|
}
|
|
}
|
|
}}
|
|
>
|
|
Leave Team
|
|
</button>
|
|
|
|
<button
|
|
className="action-button start-button"
|
|
onClick={handleStartHunt}
|
|
>
|
|
{huntInProgress ? "Continue Hunt" : "Start Hunt"}
|
|
</button>
|
|
</div>
|
|
|
|
<NavBar activeLink="team" teamData={team} />
|
|
|
|
<style jsx>{`
|
|
.team-page {
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-height: 100vh;
|
|
background-color: #69b5a8;
|
|
font-family: Arial, sans-serif;
|
|
position: relative;
|
|
overflow-x: hidden;
|
|
width: 100%;
|
|
padding-bottom: 70px;
|
|
}
|
|
|
|
.content-wrapper {
|
|
position: relative;
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
width: 100%;
|
|
padding: 80px 0 100px 0;
|
|
}
|
|
|
|
.waves-container {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 120px;
|
|
overflow: hidden;
|
|
z-index: 1;
|
|
}
|
|
|
|
.waves-image {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
object-position: center top;
|
|
}
|
|
|
|
.team-banner {
|
|
background-image: url("/images/Bottom-Bar.svg");
|
|
background-repeat: no-repeat;
|
|
background-size: cover;
|
|
background-position: center;
|
|
color: white;
|
|
padding: 20px;
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
z-index: 3;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.team-info {
|
|
flex: 1;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.team-name {
|
|
margin: 0;
|
|
font-size: 24px;
|
|
font-weight: bold;
|
|
color: white;
|
|
}
|
|
|
|
.team-code {
|
|
font-size: 24px;
|
|
font-weight: bold;
|
|
color: white;
|
|
margin-left: auto;
|
|
}
|
|
|
|
.scroll-background {
|
|
background-image: url("/images/scroll.svg");
|
|
background-repeat: no-repeat;
|
|
background-size: 100% 100%;
|
|
background-position: center;
|
|
display: flex;
|
|
flex-direction: column;
|
|
position: relative;
|
|
z-index: 2;
|
|
padding: 60px 20px 80px 20px;
|
|
margin: 0 auto;
|
|
width: 90%;
|
|
max-width: 800px;
|
|
flex: 1;
|
|
min-height: 60vh;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.scroll-background {
|
|
width: 95%;
|
|
padding: 50px 15px 70px 15px;
|
|
min-height: 50vh;
|
|
}
|
|
|
|
.members-title {
|
|
margin-top: -20px;
|
|
}
|
|
|
|
.team-actions {
|
|
width: 90%;
|
|
bottom: 70px;
|
|
}
|
|
}
|
|
|
|
.members-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
width: 100%;
|
|
flex: 1;
|
|
}
|
|
|
|
.members-title {
|
|
font-size: 20px;
|
|
font-weight: bold;
|
|
margin-bottom: 15px;
|
|
color: #68543c;
|
|
text-align: center;
|
|
margin-top: -30px;
|
|
}
|
|
|
|
.members-content {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: 10px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.members-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
width: 100%;
|
|
align-items: center;
|
|
flex: 1;
|
|
}
|
|
|
|
.member-card {
|
|
background-color: white;
|
|
border-radius: 6px;
|
|
padding: 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
border: 1px solid #eaeaea;
|
|
width: 100%;
|
|
max-width: 320px;
|
|
margin: 0 auto 8px auto;
|
|
}
|
|
|
|
.member-avatar {
|
|
font-size: 20px;
|
|
margin-right: 12px;
|
|
}
|
|
|
|
.member-name {
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
text-align: center;
|
|
}
|
|
|
|
.empty-state {
|
|
text-align: center;
|
|
padding: 20px;
|
|
color: #888;
|
|
width: 80%;
|
|
max-width: 400px;
|
|
background-color: rgba(255, 255, 255, 0.7);
|
|
border-radius: 8px;
|
|
margin: 20px auto;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.team-actions {
|
|
display: flex;
|
|
gap: 10px;
|
|
padding: 10px 20px;
|
|
position: fixed;
|
|
bottom: 80px;
|
|
left: 0;
|
|
right: 0;
|
|
z-index: 5;
|
|
width: 80%;
|
|
max-width: 450px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.action-button {
|
|
flex: 1;
|
|
padding: 15px;
|
|
border: none;
|
|
border-radius: 8px;
|
|
font-weight: bold;
|
|
cursor: pointer;
|
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
.leave-button {
|
|
background-color: #f6bd60;
|
|
color: #333;
|
|
}
|
|
|
|
.start-button {
|
|
background-color: #588157;
|
|
color: white;
|
|
}
|
|
|
|
@media (max-height: 500px) {
|
|
.content-wrapper {
|
|
padding-top: 60px;
|
|
padding-bottom: 80px;
|
|
}
|
|
|
|
.scroll-background {
|
|
padding: 40px 15px 60px 15px;
|
|
min-height: 60vh;
|
|
}
|
|
|
|
.members-title {
|
|
margin-top: -15px;
|
|
font-size: 18px;
|
|
}
|
|
|
|
.team-actions {
|
|
bottom: 75px;
|
|
}
|
|
}
|
|
|
|
@media (min-height: 1000px) {
|
|
.scroll-background {
|
|
min-height: 65vh;
|
|
}
|
|
}
|
|
`}</style>
|
|
</div>
|
|
);
|
|
}
|