mirror of
https://github.com/mudabbir-ahmad/peworkshop.git
synced 2026-10-08 03:50:21 +00:00
added map page files and fixed code so that when an admin starts the timer the user can move on and isnt accidentally being redirected to the page to show that the timer isn't active even though it is
This commit is contained in:
1 parent
6b233c90e7
commit
6ad943a868
7 files changed
+965
-39
No files matched your search
@@ -0,0 +1,298 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function CluePage({ params }) {
|
||||
const { clueId } = params;
|
||||
const [clue, setClue] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
async function checkHuntStatus() {
|
||||
try {
|
||||
const response = await fetch("/api/hunt-status", {
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
// If hunt is not active, redirect to the home page
|
||||
if (!data.active) {
|
||||
router.push("/");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
console.error("Failed to check hunt status");
|
||||
return false;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error checking hunt status:", err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchClueDetails() {
|
||||
try {
|
||||
// First verify the hunt is active
|
||||
const huntIsActive = await checkHuntStatus();
|
||||
if (!huntIsActive) return;
|
||||
|
||||
// Fetch the specific clue from the API with improved error handling
|
||||
const response = await fetch(`/api/clues/${clueId}`, {
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
// Better error handling for non-JSON responses
|
||||
if (!response.ok) {
|
||||
const contentType = response.headers.get("content-type");
|
||||
if (contentType && contentType.includes("application/json")) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.error || "Failed to fetch clue details");
|
||||
} else {
|
||||
throw new Error(
|
||||
`Failed to fetch clue details: ${response.status} ${response.statusText}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// If we got back an error field, show it as an error
|
||||
if (data.error) {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
|
||||
setClue(data.clue);
|
||||
} catch (err) {
|
||||
console.error("Error fetching clue details:", err);
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (clueId) {
|
||||
fetchClueDetails();
|
||||
}
|
||||
}, [clueId, router]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="clue-container">
|
||||
<div className="loading">Loading clue details...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="clue-container">
|
||||
<div className="error">Error: {error}</div>
|
||||
<div className="nav-buttons">
|
||||
<button onClick={() => router.push("/map")} className="map-button">
|
||||
Back to Map
|
||||
</button>
|
||||
<button onClick={() => router.push("/")} className="home-button">
|
||||
Back to Home
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!clue) {
|
||||
return (
|
||||
<div className="clue-container">
|
||||
<div className="not-found">Clue not found</div>
|
||||
<div className="nav-buttons">
|
||||
<button onClick={() => router.push("/map")} className="map-button">
|
||||
Back to Map
|
||||
</button>
|
||||
<button onClick={() => router.push("/")} className="home-button">
|
||||
Back to Home
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="clue-container">
|
||||
<div className="clue-scroll">
|
||||
<h1 className="clue-title">{clue.title}</h1>
|
||||
|
||||
<div className="clue-content">
|
||||
<h2>Description</h2>
|
||||
<p className="clue-description">{clue.description}</p>
|
||||
|
||||
{clue.location && (
|
||||
<div className="clue-location">
|
||||
<h2>Location Hint</h2>
|
||||
<p>{clue.location}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="nav-buttons">
|
||||
<button onClick={() => router.push("/map")} className="map-button">
|
||||
Back to Map
|
||||
</button>
|
||||
<button onClick={() => router.push("/")} className="home-button">
|
||||
Back to Home
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style jsx>{`
|
||||
.clue-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
background-color: #f4f1ea;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.clue-scroll {
|
||||
background-color: #f5e6ca;
|
||||
border: 2px solid #9c6b30;
|
||||
border-radius: 10px;
|
||||
padding: 30px;
|
||||
max-width: 800px;
|
||||
width: 100%;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.clue-scroll::before,
|
||||
.clue-scroll::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
height: 30px;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.clue-scroll::before {
|
||||
top: 0;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(156, 107, 48, 0.2) 0%,
|
||||
rgba(156, 107, 48, 0) 100%
|
||||
);
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
|
||||
.clue-scroll::after {
|
||||
bottom: 0;
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
rgba(156, 107, 48, 0.2) 0%,
|
||||
rgba(156, 107, 48, 0) 100%
|
||||
);
|
||||
border-radius: 0 0 8px 8px;
|
||||
}
|
||||
|
||||
.clue-title {
|
||||
color: #5d4037;
|
||||
font-size: 2rem;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
font-family: "Pirates", cursive, serif;
|
||||
border-bottom: 2px dashed #9c6b30;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.clue-content {
|
||||
padding: 20px;
|
||||
background-color: #f9f3e3;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px dashed #9c6b30;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #9c6b30;
|
||||
font-size: 1.5rem;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.clue-description {
|
||||
font-size: 1.2rem;
|
||||
line-height: 1.6;
|
||||
color: #5d4037;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.clue-location {
|
||||
margin-top: 20px;
|
||||
padding-top: 15px;
|
||||
border-top: 1px dashed #9c6b30;
|
||||
}
|
||||
|
||||
.nav-buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.map-button,
|
||||
.home-button {
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s, transform 0.2s;
|
||||
}
|
||||
|
||||
.map-button {
|
||||
background-color: #9c6b30;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.home-button {
|
||||
background-color: #5d4037;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.map-button:hover,
|
||||
.home-button:hover {
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.map-button:hover {
|
||||
background-color: #7d562a;
|
||||
}
|
||||
|
||||
.home-button:hover {
|
||||
background-color: #4a332e;
|
||||
}
|
||||
|
||||
.loading,
|
||||
.error,
|
||||
.not-found {
|
||||
padding: 50px;
|
||||
text-align: center;
|
||||
border: 2px dashed #9c6b30;
|
||||
border-radius: 8px;
|
||||
margin: 30px 0;
|
||||
color: #5d4037;
|
||||
background: #f5e6ca;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.error {
|
||||
border-color: #d32f2f;
|
||||
color: #d32f2f;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user