diff --git a/app/(auth)/register/page.js b/app/(auth)/register/page.js
index cbf9ffe..b3709c4 100644
--- a/app/(auth)/register/page.js
+++ b/app/(auth)/register/page.js
@@ -64,7 +64,6 @@ export default function Register() {
return (
diff --git a/app/(main)/[teamId]/page.js b/app/(main)/[teamId]/page.js
index b3362b0..f9b6e6e 100644
--- a/app/(main)/[teamId]/page.js
+++ b/app/(main)/[teamId]/page.js
@@ -334,6 +334,10 @@ export default function TeamPage({ params }) {
min-height: 100vh;
background-color: #f4f4f4;
font-family: Arial, sans-serif;
+ position: relative;
+ overflow-x: hidden; /* Prevent horizontal scroll */
+ width: 100%;
+ padding-bottom: 70px; /* Add padding to accommodate fixed navbar */
}
.team-banner {
@@ -449,6 +453,12 @@ export default function TeamPage({ params }) {
background-color: white;
border-top: 1px solid #ddd;
padding: 10px 0;
+ position: fixed; /* Fix the navbar at the bottom */
+ bottom: 0;
+ left: 0;
+ width: 100%; /* Use 100% instead of 100vw to prevent overflow issues */
+ box-sizing: border-box; /* Include padding in width calculation */
+ z-index: 100; /* Ensure navbar is above other content */
}
.nav-item {
@@ -458,7 +468,8 @@ export default function TeamPage({ params }) {
text-decoration: none;
color: #666;
flex: 1;
- padding: 10px 0;
+ padding: 8px 0;
+ max-width: 25%; /* Ensure each item takes up equal space */
}
.nav-item.active {
@@ -466,12 +477,27 @@ export default function TeamPage({ params }) {
}
.nav-icon {
- font-size: 24px;
- margin-bottom: 5px;
+ font-size: 22px;
+ margin-bottom: 3px;
}
.nav-label {
font-size: 12px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ width: 100%;
+ text-align: center;
+ }
+
+ /* Responsive adjustments for smaller screens */
+ @media (max-width: 375px) {
+ .nav-icon {
+ font-size: 20px;
+ }
+ .nav-label {
+ font-size: 10px;
+ }
}
.loading-container,
diff --git a/app/(main)/admin/page.js b/app/(main)/admin/page.js
index 44dbc42..3f6d17e 100644
--- a/app/(main)/admin/page.js
+++ b/app/(main)/admin/page.js
@@ -1,124 +1,444 @@
-'use client'; // Mark as a Client Component
-import { useState, useEffect } from 'react';
-import { useSession } from 'next-auth/react';
-import { useRouter } from 'next/navigation';
-import { startHuntTimer, getHuntStatus } from '../actions/auth';
+"use client";
+import { useState, useEffect, useRef } from "react";
+import { useSession } from "next-auth/react";
+import { useRouter } from "next/navigation";
-export default function Admin() {
- const { data: session, status } = useSession();
- const [timerActive, setTimerActive] = useState(false);
- const [loading, setLoading] = useState(true);
- const [error, setError] = useState('');
- const router = useRouter();
+export default function AdminDashboard() {
+ const { data: session, status } = useSession();
+ const [timerActive, setTimerActive] = useState(false);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState("");
+ const [elapsedTime, setElapsedTime] = useState(0); // in seconds
+ const [timerDisplay, setTimerDisplay] = useState("--:--:--");
+ const [presetHours, setPresetHours] = useState("");
+ const [presetMinutes, setPresetMinutes] = useState("");
+ const [presetSeconds, setPresetSeconds] = useState("");
+ const [timerEndTime, setTimerEndTime] = useState(null);
+ const timerRef = useRef(null);
+ const router = useRouter();
- // Load timer status on page load
- useEffect(() => {
- async function loadTimerStatus() {
- try {
- const status = await getHuntStatus();
- setTimerActive(status.active);
- } catch (err) {
- console.error('Failed to load timer status:', err);
- setError('Failed to load timer status');
- } finally {
- setLoading(false);
- }
+ // Load timer status on page load
+ useEffect(() => {
+ async function loadTimerStatus() {
+ try {
+ const response = await fetch("/api/hunt/timer", {
+ credentials: "include",
+ headers: {
+ "Cache-Control": "no-cache",
+ },
+ });
+ const data = await response.json();
+
+ if (data.active) {
+ setTimerActive(true);
+
+ // Calculate elapsed time
+ if (data.startTime) {
+ const startTime = new Date(data.startTime).getTime();
+ const currentTime = Date.now();
+ const elapsed = Math.floor((currentTime - startTime) / 1000);
+ setElapsedTime(elapsed);
+ }
+
+ // If there's an end time, set it
+ if (data.endTime) {
+ setTimerEndTime(new Date(data.endTime).getTime());
+ }
}
- if (status === 'authenticated') {
- loadTimerStatus();
- }
- }, [status]);
-
- // Check if user is admin
- useEffect(() => {
- if (status === 'authenticated' && session?.user?.role !== 'admin') {
- router.push('/');
- }
- }, [session, status, router]);
-
- // Handle access denied and loading states
- if (status === 'loading' || loading) {
- return
Loading...
;
+ setLoading(false);
+ } catch (err) {
+ console.error("Failed to load timer status:", err);
+ setError("Failed to load timer status");
+ setLoading(false);
+ }
}
- if (status === 'unauthenticated') {
- router.push('/login');
- return null;
+ if (status === "authenticated") {
+ loadTimerStatus();
+ }
+ }, [status]);
+
+ // Check if user is admin
+ useEffect(() => {
+ if (status === "authenticated") {
+ if (session?.user?.role !== "admin") {
+ router.push("/team-selection");
+ }
+ } else if (status === "unauthenticated") {
+ router.push("/login");
+ }
+ }, [session, status, router]);
+
+ // Timer update effect
+ useEffect(() => {
+ if (timerActive) {
+ timerRef.current = setInterval(() => {
+ setElapsedTime((prev) => {
+ const newElapsed = prev + 1;
+
+ // If we have an end time and we've reached it, stop the timer
+ if (timerEndTime && Date.now() >= timerEndTime) {
+ stopTimer();
+ return prev;
+ }
+
+ // Update the display
+ formatTimeDisplay(newElapsed);
+ return newElapsed;
+ });
+ }, 1000);
+ } else {
+ if (timerRef.current) {
+ clearInterval(timerRef.current);
+ }
}
- if (session?.user?.role !== 'admin') {
- return null; // Already redirecting via useEffect
- }
-
- const toggleTimer = async () => {
- setLoading(true);
- try {
- const newStatus = !timerActive;
- const result = await startHuntTimer(newStatus);
-
- if (result.success) {
- setTimerActive(newStatus);
- } else {
- setError('Failed to update timer status');
- }
- } catch (err) {
- console.error('Failed to toggle timer:', err);
- setError('Failed to toggle timer');
- } finally {
- setLoading(false);
- }
+ return () => {
+ if (timerRef.current) {
+ clearInterval(timerRef.current);
+ }
};
+ }, [timerActive, timerEndTime]);
- return (
-
-
+ // Format time for display whenever elapsed time changes
+ useEffect(() => {
+ formatTimeDisplay(elapsedTime);
+ }, [elapsedTime]);
-
-
-
-
-
+ // Format seconds into HH:MM:SS
+ const formatTimeDisplay = (totalSeconds) => {
+ if (!timerActive) {
+ setTimerDisplay("--:--:--");
+ return;
+ }
-
-
-
-
+ const hours = Math.floor(totalSeconds / 3600);
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
+ const seconds = totalSeconds % 60;
-
-
-
-
+ const display = [
+ hours.toString().padStart(2, "0"),
+ minutes.toString().padStart(2, "0"),
+ seconds.toString().padStart(2, "0"),
+ ].join(":");
-
+ setTimerDisplay(display);
+ };
- {error &&
{error}
}
-
+ const startTimer = async (preset = false) => {
+ setLoading(true);
+ try {
+ let endTime = null;
-
+ // If using preset, calculate the end time
+ if (preset) {
+ const hours = parseInt(presetHours || "0");
+ const minutes = parseInt(presetMinutes || "0");
+ const seconds = parseInt(presetSeconds || "0");
+
+ // Validate input
+ if (isNaN(hours) || isNaN(minutes) || isNaN(seconds)) {
+ setError(
+ "Please enter valid numbers for hours, minutes, and seconds"
+ );
+ setLoading(false);
+ return;
+ }
+
+ // Calculate total duration in milliseconds
+ const durationMs = (hours * 3600 + minutes * 60 + seconds) * 1000;
+ if (durationMs <= 0) {
+ setError("Please enter a duration greater than zero");
+ setLoading(false);
+ return;
+ }
+
+ // Set end time
+ endTime = new Date(Date.now() + durationMs).toISOString();
+ }
+
+ // API call to start timer
+ const response = await fetch("/api/hunt/timer", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ action: "start",
+ endTime: endTime,
+ }),
+ });
+
+ const data = await response.json();
+
+ if (data.success) {
+ setTimerActive(true);
+ setElapsedTime(0);
+
+ if (endTime) {
+ setTimerEndTime(new Date(endTime).getTime());
+ } else {
+ setTimerEndTime(null);
+ }
+
+ setError("");
+ } else {
+ setError(data.error || "Failed to start timer");
+ }
+ } catch (err) {
+ console.error("Failed to start timer:", err);
+ setError("Failed to start timer");
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const stopTimer = async () => {
+ setLoading(true);
+ try {
+ const response = await fetch("/api/hunt/timer", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({ action: "stop" }),
+ });
+
+ const data = await response.json();
+
+ if (data.success) {
+ setTimerActive(false);
+ setTimerEndTime(null);
+ setError("");
+ } else {
+ setError(data.error || "Failed to stop timer");
+ }
+ } catch (err) {
+ console.error("Failed to stop timer:", err);
+ setError("Failed to stop timer");
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const startPresetTimer = () => {
+ startTimer(true);
+ };
+
+ // Handle access denied and loading states
+ if (status === "loading" || loading) {
+ return
Loading...
;
+ }
+
+ if (status === "unauthenticated") {
+ return null; // Will redirect via useEffect
+ }
+
+ if (session?.user?.role !== "admin") {
+ return null; // Will redirect via useEffect
+ }
+
+ return (
+
+
+
router.push("/team-selection")}
+ >
+ ←
- );
-}
\ No newline at end of file
+
Admin Dashboard
+
+
+
+
+
+
Hunt Timer
+
{timerDisplay}
+
+
+
+
+
+
+
+
Set Timer Duration
+
+
+
+
+
+ {error &&
{error}
}
+
+
+
+
+
+ );
+}
diff --git a/app/(main)/create-team/page.js b/app/(main)/create-team/page.js
index df50595..0d8b408 100644
--- a/app/(main)/create-team/page.js
+++ b/app/(main)/create-team/page.js
@@ -4,17 +4,7 @@ import { useSession } from "next-auth/react";
import { useRouter } from "next/navigation";
import Link from "next/link";
-const styles = {
- navbar: {
- position: "fixed",
- bottom: 0,
- width: "100%",
- backgroundColor: "#fff",
- borderTop: "1px solid #ccc",
- padding: "10px 0",
- textAlign: "center",
- },
-};
+// Removed the styles object for navbar
export default function CreateTeam() {
const { data: session, status } = useSession();
@@ -106,20 +96,7 @@ export default function CreateTeam() {