SCANNER WORKS!!! JUST ADD THE MORE CLUE BUTTONS !!! AND NEXT CLUE ERROR CHECKING

This commit is contained in:
bobbert committed 2025-03-17 12:41:17 +00:00
1 parent 07099ba6b4
commit 02a0088d28
11 files changed
+861 -217

No files matched your search

+191 -27
View File
@@ -1,5 +1,5 @@
"use client";
import { useState, useEffect } from "react";
import { useState, useEffect, useRef } from "react";
import { useRouter } from "next/navigation";
import { useSession } from "next-auth/react";
import {
@@ -10,9 +10,8 @@ import {
import Link from "next/link";
import dynamic from "next/dynamic";
import { use } from "react";
import QRCodeHelper from "@/components/QRCodeHelper";
// Dynamically import the new QR scanner component with no SSR
// Dynamically import the QR scanner component with no SSR
const EnhancedQRScanner = dynamic(
() => import("@/components/EnhancedQRScanner"),
{
@@ -35,10 +34,13 @@ export default function CluePage({ params }) {
const [clueFound, setClueFound] = useState(false);
const [allClues, setAllClues] = useState([]);
const [nextClueId, setNextClueId] = useState(null);
const [huntActive, setHuntActive] = useState(true); // Track hunt active status
const [previousHuntState, setPreviousHuntState] = useState(true); // To detect changes
const [huntActive, setHuntActive] = useState(true);
const [previousHuntState, setPreviousHuntState] = useState(true);
const [showSuccessNotification, setShowSuccessNotification] = useState(false);
const [teamData, setTeamData] = useState(null);
const router = useRouter();
const { data: session, status } = useSession();
const notificationTimeoutRef = useRef(null);
// Check if user is admin
const isAdmin = session?.user?.role === "admin";
@@ -219,9 +221,41 @@ export default function CluePage({ params }) {
};
}, [router]);
// Fetch team data on component mount
useEffect(() => {
if (status === "authenticated") {
fetchTeamData();
}
}, [status]);
// Add function to fetch team data
const fetchTeamData = async () => {
if (!session) return;
try {
const response = await fetch("/api/user/team", {
headers: {
"Content-Type": "application/json",
"Cache-Control": "no-cache, no-store, must-revalidate",
},
credentials: "include",
});
if (response.ok) {
const data = await response.json();
setTeamData(data);
}
} catch (err) {
console.error("Failed to fetch team data:", err);
}
};
// Handle QR scan result
const handleScan = (data) => {
setScanMessage(`QR Code scanned: ${data}`);
// Only update message if we have data
if (data) {
setScanMessage(`QR Code scanned: ${data}`);
}
try {
// Handle multiple QR code formats: "QR123", "123", or even "Clue 123"
@@ -284,6 +318,19 @@ export default function CluePage({ params }) {
setScanMessage(
"✅ Clue verified! You can now proceed to the next clue."
);
// Show success notification
setShowSuccessNotification(true);
// Clear previous timeout if it exists
if (notificationTimeoutRef.current) {
clearTimeout(notificationTimeoutRef.current);
}
// Auto-hide the notification after 5 seconds
notificationTimeoutRef.current = setTimeout(() => {
setShowSuccessNotification(false);
}, 5000);
} else {
setScanMessage("Error verifying clue. Please try again.");
}
@@ -293,6 +340,15 @@ export default function CluePage({ params }) {
}
};
// Clear notification timeout on component unmount
useEffect(() => {
return () => {
if (notificationTimeoutRef.current) {
clearTimeout(notificationTimeoutRef.current);
}
};
}, []);
// Navigate to the next clue
const handleNextClue = () => {
if (nextClueId) {
@@ -381,7 +437,27 @@ export default function CluePage({ params }) {
return (
<div className="clue-container">
<div className="timer-display">{elapsedTime}</div>
{/* Map Banner with team info and timer */}
<div className="map-banner">
<div className="team-name">{teamData?.team?.name || "Loading..."}</div>
<div className="elapsed-time">
<span className="time-value">{elapsedTime}</span>
</div>
<div className="team-code">{teamData?.team?.code || ""}</div>
</div>
{showSuccessNotification && (
<div className="success-notification">
<div className="success-notification-content">
<div className="success-icon">🎉</div>
<div className="success-text">
<h3>Clue Found!</h3>
<p>Great work! You've found this clue.</p>
</div>
</div>
</div>
)}
<div className="clue-content-wrapper">
{/* Admin tools section - only visible to admins */}
{isAdmin && (
@@ -396,7 +472,7 @@ export default function CluePage({ params }) {
</div>
)}
{/* QR Scanner Section - Removed manual entry button */}
{/* QR Scanner Section */}
<div className="scanner-section">
<h2>Scan QR Code</h2>
<EnhancedQRScanner onScan={handleScan} width={280} height={280} />
@@ -405,8 +481,7 @@ export default function CluePage({ params }) {
{scanMessage}
</div>
)}
{/* Add the QR Code helper component */}
<QRCodeHelper clueId={clueId} />
{/* QR Code helper removed as requested */}
</div>
{/* Clue Information Section */}
@@ -455,9 +530,9 @@ export default function CluePage({ params }) {
<div className="nav-icon">👥</div>
<div className="nav-label">Team</div>
</Link>
<Link href="/" className="nav-item">
<div className="nav-icon">🏠</div>
<div className="nav-label">Home</div>
<Link href="/leaderboard" className="nav-item">
<div className="nav-icon">🏆</div>
<div className="nav-label">Leaderboard</div>
</Link>
</div>
@@ -467,11 +542,57 @@ export default function CluePage({ params }) {
flex-direction: column;
min-height: 100vh;
background-color: #f4f1ea;
padding: 20px;
padding-bottom: 70px; /* Space for bottom navbar */
position: relative;
}
.success-notification {
position: fixed;
top: 0;
left: 0;
width: 100%;
padding: 20px;
background-color: rgba(0, 0, 0, 0.7);
z-index: 1000;
display: flex;
justify-content: center;
animation: slide-down 0.5s ease-out;
}
.success-notification-content {
display: flex;
align-items: center;
background-color: #4caf50;
color: white;
padding: 15px 25px;
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
max-width: 400px;
}
.success-icon {
font-size: 2rem;
margin-right: 15px;
}
.success-text h3 {
margin: 0 0 5px 0;
font-size: 1.2rem;
}
.success-text p {
margin: 0;
}
@keyframes slide-down {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(0);
}
}
.hunt-ended {
text-align: center;
padding: 30px;
@@ -497,6 +618,7 @@ export default function CluePage({ params }) {
}
.clue-content-wrapper {
padding: 0 20px;
flex: 1;
display: flex;
flex-direction: column;
@@ -506,19 +628,6 @@ export default function CluePage({ params }) {
margin: 0 auto;
}
.timer-display {
position: absolute;
top: 15px;
right: 15px;
background-color: rgba(0, 0, 0, 0.1);
padding: 5px 10px;
border-radius: 5px;
font-family: monospace;
font-weight: bold;
font-size: 1rem;
z-index: 10;
}
.scanner-section {
width: 100%;
display: flex;
@@ -724,6 +833,61 @@ export default function CluePage({ params }) {
.admin-refresh-button:hover {
background-color: #c9302c;
}
/* Map Banner Styles */
.map-banner {
background-color: #68543c;
color: white;
padding: 15px;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 2px solid #4a3c2a;
z-index: 10;
flex-shrink: 0;
margin-bottom: 20px;
}
.team-name {
font-size: 18px;
font-weight: bold;
flex: 1;
text-align: left;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-right: 5px;
}
.elapsed-time {
text-align: center;
padding: 5px 10px;
background-color: rgba(0, 0, 0, 0.2);
border-radius: 8px;
flex: 0 0 auto;
margin: 0 10px;
}
.time-value {
font-size: 18px;
font-weight: bold;
}
.team-code {
font-size: 16px;
font-weight: bold;
flex: 1;
text-align: right;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-left: 5px;
}
/* Remove the standalone timer display as it's now in the banner */
.timer-display {
display: none;
}
`}</style>
</div>
);