mirror of
https://github.com/mudabbir-ahmad/peworkshop.git
synced 2026-10-08 03:50:21 +00:00
SCANNER WORKS!!! JUST ADD THE MORE CLUE BUTTONS !!! AND NEXT CLUE ERROR CHECKING
This commit is contained in:
1 parent
07099ba6b4
commit
02a0088d28
11 files changed
+861
-217
No files matched your search
@@ -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>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user