mirror of
https://github.com/mudabbir-ahmad/peworkshop.git
synced 2026-10-07 19:50:20 +00:00
fixed a bit of stuff the current but API is broken again for logging in so will be fixing that with the next push
This commit is contained in:
1 parent
6ad943a868
commit
ea38775a82
20 files changed
+1643
-1027
No files matched your search
@@ -0,0 +1,44 @@
|
||||
/* Login page specific styles */
|
||||
.loginForm {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
margin-bottom: 8px; /* Reduced spacing (was 15px) */
|
||||
}
|
||||
|
||||
.input-container {
|
||||
margin-bottom: 10px; /* Add consistent spacing between input fields */
|
||||
}
|
||||
|
||||
.errorMessage {
|
||||
color: #e53935;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px; /* Add space after error message */
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.register-button-container {
|
||||
margin-top: 5px; /* Reduced spacing (was 10px) */
|
||||
}
|
||||
|
||||
.auth-button {
|
||||
padding: 10px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background-color: #68543c; /* Changed back to dark brown */
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.auth-button:hover {
|
||||
background-color: #53402d; /* Darker brown on hover */
|
||||
}
|
||||
|
||||
.auth-button:disabled {
|
||||
background-color: #cccccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* You can also add other component-specific styles here */
|
||||
+58
-105
@@ -1,4 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { signIn } from "next-auth/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
@@ -19,84 +20,39 @@ export default function Login() {
|
||||
try {
|
||||
console.log("Attempting to sign in with:", username);
|
||||
|
||||
// First try using the NextAuth signIn method
|
||||
try {
|
||||
const result = await signIn("credentials", {
|
||||
redirect: false,
|
||||
username,
|
||||
password,
|
||||
callbackUrl: "/team-selection",
|
||||
});
|
||||
const result = await signIn("credentials", {
|
||||
redirect: false,
|
||||
username,
|
||||
password,
|
||||
callbackUrl: `${window.location.origin}/team-selection`,
|
||||
});
|
||||
|
||||
if (result?.error) {
|
||||
throw new Error(result.error);
|
||||
}
|
||||
console.log("Sign in result:", result);
|
||||
|
||||
// If the user has a team, redirect directly to the team page
|
||||
if (result?.url) {
|
||||
// Extract the teamId from the URL or session data if available
|
||||
// This part depends on how your auth system returns the result
|
||||
router.push(`/${session?.user?.teamId || ""}`);
|
||||
} else {
|
||||
// Otherwise go to team selection
|
||||
router.push("/team-selection");
|
||||
}
|
||||
return;
|
||||
} catch (signInError) {
|
||||
console.error("Error with signIn method:", signInError);
|
||||
console.log("Falling back to direct API call");
|
||||
|
||||
// Fall back to direct fetch approach with explicit JSON content-type
|
||||
const loginResponse = await fetch("/api/auth/callback/credentials", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username,
|
||||
password,
|
||||
callbackUrl: "/team-selection",
|
||||
}),
|
||||
credentials: "include", // Important for cookie handling
|
||||
});
|
||||
|
||||
if (!loginResponse.ok) {
|
||||
const errorData = await loginResponse
|
||||
.json()
|
||||
.catch(() => ({ error: "Failed to parse error response" }));
|
||||
throw new Error(
|
||||
errorData.error ||
|
||||
`Login failed with status ${loginResponse.status}`
|
||||
);
|
||||
}
|
||||
|
||||
// On successful login
|
||||
const userData = await loginResponse
|
||||
.json()
|
||||
.catch(() => ({ url: "/team-selection" }));
|
||||
|
||||
// Allow time for cookie to be set
|
||||
setTimeout(() => {
|
||||
router.push(userData.url || "/team-selection");
|
||||
}, 100);
|
||||
if (result?.error) {
|
||||
throw new Error(result.error);
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err.message || "An unexpected error occurred");
|
||||
console.error("Login error:", err);
|
||||
|
||||
if (result?.ok) {
|
||||
console.log("Sign in successful, redirecting...");
|
||||
// Use router.push with { replace: true } to avoid history issues
|
||||
router.push("/team-selection", { replace: true });
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Login error:", error);
|
||||
setError(error.message || "Failed to login. Please try again.");
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="auth-page">
|
||||
<div className="page-header">
|
||||
<div className="page-title">Login</div>
|
||||
<div className="placeholder"></div>
|
||||
</div>
|
||||
|
||||
<div className="auth-container">
|
||||
<div className="auth-card">
|
||||
<form onSubmit={handleLogin}>
|
||||
<h2>Login to Treasure Hunt</h2>
|
||||
<form onSubmit={handleLogin} className="loginForm">
|
||||
<div className="input-container">
|
||||
<input
|
||||
type="text"
|
||||
@@ -119,64 +75,61 @@ export default function Login() {
|
||||
</div>
|
||||
{error && <div className="error-message">{error}</div>}
|
||||
<button type="submit" className="auth-button" disabled={isLoading}>
|
||||
{isLoading ? "Logging in..." : "Log In"}
|
||||
{isLoading ? "Logging in..." : "Login"}
|
||||
</button>
|
||||
</form>
|
||||
<Link href="/register">
|
||||
<button className="auth-button">Register</button>
|
||||
</Link>
|
||||
|
||||
{/* Add separate div for register button with margin-top */}
|
||||
<div className="register-button-container">
|
||||
<Link href="/register">
|
||||
<button className="auth-button register-button">Register</button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Update styles to include spacing between buttons */}
|
||||
<style jsx>{`
|
||||
.auth-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
.auth-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.auth-card {
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8px;
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.loginForm {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
margin-bottom: 8px; /* Reduced margin below the form (was 15px) */
|
||||
}
|
||||
.input-container {
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 10px; /* Add consistent spacing between input fields */
|
||||
}
|
||||
.auth-input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
.register-button-container {
|
||||
margin-top: 5px; /* Reduced margin above register button (was 10px) */
|
||||
}
|
||||
.auth-button {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
background-color: #68543c; /* Updated brown color */
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background-color: #68543c; /* Dark brown */
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
.auth-button:hover {
|
||||
background-color: #53402d; /* Darker brown on hover */
|
||||
}
|
||||
.auth-button:disabled {
|
||||
background-color: #cccccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.error-message {
|
||||
color: red;
|
||||
color: #e53935;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
|
||||
+25
-69
@@ -194,8 +194,6 @@ export default function TeamPage({ params }) {
|
||||
) : (
|
||||
<div className="empty-state">No team members found</div>
|
||||
)}
|
||||
|
||||
{/* Remove the add member button - it was here before */}
|
||||
</div>
|
||||
|
||||
<div className="team-actions">
|
||||
@@ -243,61 +241,31 @@ export default function TeamPage({ params }) {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
if (!huntActive) {
|
||||
// If hunt is not active, redirect to the "not started" page
|
||||
// Use the dedicated start hunt API endpoint
|
||||
const response = await fetch("/api/hunt/start", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Cache-Control": "no-cache, no-store, must-revalidate",
|
||||
},
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success && data.active) {
|
||||
// Redirect to map page using window.location for a clean reload
|
||||
window.location.href = data.redirectUrl || "/map";
|
||||
} else if (!data.active) {
|
||||
// Hunt is not active
|
||||
router.push(`/hunt-not-started?teamId=${teamId}`);
|
||||
} else {
|
||||
// If hunt is active, try to start the hunt
|
||||
const response = await fetch(
|
||||
`/api/teams/${teamId}/start-hunt`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
credentials: "include",
|
||||
}
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
fetchTeamData();
|
||||
} else {
|
||||
const error = await response.json();
|
||||
throw new Error(error.message || "Failed to start hunt");
|
||||
}
|
||||
// Some other error
|
||||
throw new Error(data.error || "Failed to start hunt journey");
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
setLoading(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
Start Hunt
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* Keep the admin start button which directly starts the hunt */}
|
||||
{session?.user?.role === "admin" && (
|
||||
<button
|
||||
className="action-button start-button"
|
||||
onClick={async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await fetch(
|
||||
`/api/teams/${teamId}/start-hunt`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
credentials: "include",
|
||||
}
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
fetchTeamData();
|
||||
} else {
|
||||
const error = await response.json();
|
||||
throw new Error(error.message || "Failed to start hunt");
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
console.error("Error starting hunt:", err);
|
||||
setError(`Failed to start hunt: ${err.message}`);
|
||||
setLoading(false);
|
||||
}
|
||||
}}
|
||||
@@ -352,7 +320,7 @@ export default function TeamPage({ params }) {
|
||||
.team-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
justify-content: space-between; /* This spreads items to opposite ends */
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@@ -360,13 +328,14 @@ export default function TeamPage({ params }) {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
color: white; /* Explicitly setting color to white */
|
||||
}
|
||||
|
||||
.team-code {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-left: auto;
|
||||
color: white; /* Ensuring team code is also white */
|
||||
margin-left: auto; /* Pushes the code to the right */
|
||||
}
|
||||
|
||||
.members-container {
|
||||
@@ -411,19 +380,6 @@ export default function TeamPage({ params }) {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.add-member-button {
|
||||
background-color: #68543c;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
margin-top: 20px;
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.team-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
|
||||
@@ -1,68 +1,74 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useHuntStatusCheck } from "@/hooks/useHuntStatusCheck";
|
||||
import Link from "next/link";
|
||||
import { use } from "react"; // Add this import for React.use()
|
||||
|
||||
export default function CluePage({ params }) {
|
||||
const { clueId } = params;
|
||||
const { data: session, status } = useSession();
|
||||
// Unwrap the params Promise using React.use()
|
||||
const unwrappedParams = use(params);
|
||||
const { clueId } = unwrappedParams;
|
||||
|
||||
const [clue, setClue] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const router = useRouter();
|
||||
|
||||
// Add this line to use the hunt status hook
|
||||
const huntActive = useHuntStatusCheck();
|
||||
|
||||
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;
|
||||
// Try to check hunt status first with robust error handling
|
||||
try {
|
||||
const statusResponse = await fetch("/api/hunt-status", {
|
||||
credentials: "include",
|
||||
headers: { Accept: "application/json" },
|
||||
});
|
||||
|
||||
// Fetch the specific clue from the API with improved error handling
|
||||
const response = await fetch(`/api/clues/${clueId}`, {
|
||||
credentials: "include",
|
||||
});
|
||||
// Read as text first
|
||||
const statusText = await statusResponse.text();
|
||||
|
||||
// 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}`
|
||||
);
|
||||
try {
|
||||
const statusData = JSON.parse(statusText);
|
||||
if (!statusData.active && statusData.authenticated) {
|
||||
router.push("/");
|
||||
return;
|
||||
}
|
||||
} catch (parseError) {
|
||||
console.error("Failed to parse hunt status response:", parseError);
|
||||
// Continue anyway to try fetching clue details
|
||||
}
|
||||
} catch (statusErr) {
|
||||
console.error("Error checking hunt status:", statusErr);
|
||||
// Continue anyway to try fetching clue details
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
// Now fetch clue details with robust error handling
|
||||
const response = await fetch(`/api/clues/${clueId}`, {
|
||||
credentials: "include",
|
||||
headers: { Accept: "application/json" },
|
||||
});
|
||||
|
||||
// If we got back an error field, show it as an error
|
||||
// Read as text first
|
||||
const responseText = await response.text();
|
||||
|
||||
// Try to parse as JSON
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(responseText);
|
||||
} catch (parseError) {
|
||||
console.error("Failed to parse clue details response:", parseError);
|
||||
console.log("Response received:", responseText.slice(0, 200));
|
||||
throw new Error("Invalid response from server. Please try again.");
|
||||
}
|
||||
|
||||
// Check for error in the response
|
||||
if (data.error) {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
"use client";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
// ...other imports
|
||||
|
||||
export default function LeaderboardPage() {
|
||||
const [teams, setTeams] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const searchParams = useSearchParams();
|
||||
const huntJustEnded = searchParams.get("huntEnded") === "true";
|
||||
|
||||
// ...existing fetch logic
|
||||
|
||||
return (
|
||||
<div className="leaderboard-page">
|
||||
<div className="page-header">
|
||||
<div className="back-button"></div>
|
||||
<div className="page-title">Leaderboard</div>
|
||||
</div>
|
||||
|
||||
{huntJustEnded && (
|
||||
<div className="hunt-ended-alert">
|
||||
<div className="hunt-ended-icon">🎯</div>
|
||||
<h2>The Hunt Has Ended!</h2>
|
||||
<p>
|
||||
The timer has been stopped by an administrator. Here are the final
|
||||
results.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Rest of the leaderboard content */}
|
||||
{/* ...existing code */}
|
||||
|
||||
<style jsx>{`
|
||||
/* ...existing styles */
|
||||
|
||||
.hunt-ended-alert {
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
margin: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hunt-ended-icon {
|
||||
font-size: 36px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
.map-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100vh; /* Full viewport height */
|
||||
overflow: hidden; /* Hide overflow at page level */
|
||||
position: relative;
|
||||
background-color: #69b5a8;
|
||||
}
|
||||
|
||||
.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; /* Prevent banner from shrinking */
|
||||
}
|
||||
|
||||
.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; /* Don't grow or shrink */
|
||||
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;
|
||||
}
|
||||
|
||||
/* Map container takes all available space */
|
||||
.map-container {
|
||||
flex: 1; /* Take all available space */
|
||||
overflow: auto; /* Enable scrolling */
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.treasure-map {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.map-svg {
|
||||
width: 100% !important; /* Override Next.js Image default width */
|
||||
height: auto !important; /* Override Next.js Image default height */
|
||||
max-width: none; /* Allow image to grow beyond container */
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
border: 4px solid #f3f3f3;
|
||||
border-top: 4px solid #68543c;
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.map-error {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Navigation bar - now sticky at the bottom */
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
background-color: white;
|
||||
border-top: 1px solid #ddd;
|
||||
padding: 10px 0;
|
||||
position: sticky; /* Sticky positioning */
|
||||
bottom: 0; /* Stick to bottom */
|
||||
left: 0;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
z-index: 100; /* Ensure it stays on top */
|
||||
flex-shrink: 0; /* Prevent navbar from shrinking */
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
color: #666;
|
||||
flex: 1;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
font-size: 22px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.nav-label {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
color: #68543c;
|
||||
}
|
||||
|
||||
/* Make sure the map is responsive */
|
||||
@media (max-width: 480px) {
|
||||
.team-name {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.team-code {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.time-value {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.nav-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
+181
-316
@@ -1,358 +1,223 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import styles from "./map.module.css";
|
||||
import { useHuntStatusCheck } from "@/hooks/useHuntStatusCheck";
|
||||
|
||||
export default function MapPage() {
|
||||
const [clues, setClues] = useState([]);
|
||||
const { data: session, status } = useSession();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [mapError, setMapError] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const [huntActive, setHuntActive] = useState(false);
|
||||
const [teamData, setTeamData] = useState(null);
|
||||
const [elapsedTime, setElapsedTime] = useState("00:00:00");
|
||||
const mapContainerRef = useRef(null);
|
||||
const router = useRouter();
|
||||
|
||||
// Use the custom hook to check if hunt is still active
|
||||
const huntActive = useHuntStatusCheck();
|
||||
|
||||
// Debug log for hunt status
|
||||
useEffect(() => {
|
||||
async function checkHuntStatus() {
|
||||
try {
|
||||
const response = await fetch("/api/hunt-status", {
|
||||
credentials: "include",
|
||||
});
|
||||
console.log("Hunt active status in map page:", huntActive);
|
||||
}, [huntActive]);
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
setHuntActive(data.active || false);
|
||||
|
||||
// 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;
|
||||
}
|
||||
useEffect(() => {
|
||||
// Only redirect if explicitly not authenticated
|
||||
if (status === "unauthenticated") {
|
||||
router.push("/login");
|
||||
return;
|
||||
}
|
||||
|
||||
async function fetchClues() {
|
||||
try {
|
||||
// First verify the hunt is active
|
||||
const huntIsActive = await checkHuntStatus();
|
||||
if (!huntIsActive) return;
|
||||
|
||||
// Fetch the clues from the API with proper error handling
|
||||
const response = await fetch("/api/clues", {
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
// Handle non-OK responses without throwing
|
||||
if (!response.ok) {
|
||||
const contentType = response.headers.get("content-type");
|
||||
if (contentType && contentType.includes("application/json")) {
|
||||
// If it's JSON, parse it for the error message
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.error || "Failed to fetch clues");
|
||||
} else {
|
||||
// If it's not JSON, use the status text
|
||||
throw new Error(
|
||||
`Failed to fetch clues: ${response.status} ${response.statusText}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the JSON response
|
||||
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);
|
||||
}
|
||||
|
||||
setClues(data.clues || []);
|
||||
} catch (err) {
|
||||
console.error("Error fetching clues:", err);
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
// If hunt is not active, redirect to waiting page
|
||||
if (huntActive === false) {
|
||||
console.log("Hunt is not active, redirecting to waiting page");
|
||||
router.push("/waiting");
|
||||
return;
|
||||
}
|
||||
|
||||
fetchClues();
|
||||
}, [router]);
|
||||
// Only fetch team data if authenticated and hunt is active or status unknown
|
||||
if (status === "authenticated" && huntActive !== false) {
|
||||
fetchTeamData();
|
||||
}
|
||||
}, [status, huntActive, router]);
|
||||
|
||||
// Generate random x positions and sort by descending y positions
|
||||
const generatePositions = (clues) => {
|
||||
const positions = [];
|
||||
const maxWidth = 800;
|
||||
const maxHeight = 500;
|
||||
const padding = 50;
|
||||
// Update elapsed time every second
|
||||
useEffect(() => {
|
||||
if (!teamData?.team) return;
|
||||
|
||||
for (let i = 0; i < clues.length; i++) {
|
||||
positions.push({
|
||||
id: clues[i].id,
|
||||
title: clues[i].title || `Clue ${i + 1}`,
|
||||
x: Math.random() * (maxWidth - 2 * padding) + padding,
|
||||
y: (i + 1) * (maxHeight / (clues.length + 1)),
|
||||
const startTime = new Date(teamData?.huntStartTime || Date.now()).getTime();
|
||||
|
||||
const timer = setInterval(() => {
|
||||
const now = Date.now();
|
||||
const elapsed = Math.floor((now - startTime) / 1000);
|
||||
|
||||
const hours = Math.floor(elapsed / 3600)
|
||||
.toString()
|
||||
.padStart(2, "0");
|
||||
const minutes = Math.floor((elapsed % 3600) / 60)
|
||||
.toString()
|
||||
.padStart(2, "0");
|
||||
const seconds = Math.floor(elapsed % 60)
|
||||
.toString()
|
||||
.padStart(2, "0");
|
||||
|
||||
setElapsedTime(`${hours}:${minutes}:${seconds}`);
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(timer);
|
||||
}, [teamData]);
|
||||
|
||||
const fetchTeamData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const response = await fetch("/api/user/team", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Cache-Control": "no-cache, no-store, must-revalidate",
|
||||
},
|
||||
credentials: "include",
|
||||
});
|
||||
}
|
||||
|
||||
return positions;
|
||||
if (!response.ok) {
|
||||
// Handle specific error cases without immediate redirect
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
|
||||
if (response.status === 401) {
|
||||
// Set error but don't redirect immediately - let the session handling take care of it
|
||||
setError(
|
||||
"Authentication error: " + (errorData.error || "Not logged in")
|
||||
);
|
||||
setLoading(false);
|
||||
return;
|
||||
} else if (
|
||||
response.status === 404 ||
|
||||
errorData.error?.includes("not part of a team")
|
||||
) {
|
||||
setError("You need to join a team before viewing the map");
|
||||
setLoading(false);
|
||||
// Give user time to read the error message before redirect
|
||||
setTimeout(() => router.push("/team-selection"), 2000);
|
||||
return;
|
||||
} else {
|
||||
throw new Error(errorData.error || "Failed to fetch team data");
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
setTeamData(data);
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
console.error("Error fetching team data:", err);
|
||||
setError(err.message);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const cluePositions = clues.length > 0 ? generatePositions(clues) : [];
|
||||
|
||||
if (loading) {
|
||||
if (status === "loading" || loading) {
|
||||
return (
|
||||
<div className="map-container">
|
||||
<div className="loading">Loading clues...</div>
|
||||
<div className={styles["loading-container"]}>
|
||||
<div className={styles["loading-spinner"]}></div>
|
||||
<div>Loading map...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// If hunt status is known to be inactive, show message
|
||||
if (huntActive === false) {
|
||||
return (
|
||||
<div className={styles["map-error"]}>
|
||||
<h2>Hunt Not Active</h2>
|
||||
<p>The treasure hunt has not started yet.</p>
|
||||
<button onClick={() => router.push("/waiting")}>
|
||||
Go to Waiting Room
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="map-container">
|
||||
<div className="error">Error: {error}</div>
|
||||
<button onClick={() => router.push("/")} className="back-button">
|
||||
Back to Home
|
||||
</button>
|
||||
<div className={styles["map-error"]}>
|
||||
<h2>Error</h2>
|
||||
<p>{error}</p>
|
||||
<button onClick={fetchTeamData}>Retry</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (clues.length === 0) {
|
||||
if (mapError) {
|
||||
return (
|
||||
<div className="map-container">
|
||||
<div className="no-clues">No clues available yet.</div>
|
||||
<button onClick={() => router.push("/")} className="back-button">
|
||||
Back to Home
|
||||
</button>
|
||||
<div className={styles["map-error"]}>
|
||||
<h2>Error Loading Map</h2>
|
||||
<p>We couldn't load the treasure map. Please try again.</p>
|
||||
<button onClick={fetchTeamData}>Retry</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="map-container">
|
||||
<h1>Treasure Map</h1>
|
||||
<div className="map-wrapper">
|
||||
<svg
|
||||
className="map-svg"
|
||||
viewBox="0 0 800 500"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
<div className={styles["map-page"]}>
|
||||
{/* Map Banner with team info and timer */}
|
||||
<div className={styles["map-banner"]}>
|
||||
<div className={styles["team-name"]}>
|
||||
{teamData?.team?.name || "Your Team"}
|
||||
</div>
|
||||
<div className={styles["elapsed-time"]}>
|
||||
<span className={styles["time-value"]}>{elapsedTime}</span>
|
||||
</div>
|
||||
<div className={styles["team-code"]}>{teamData?.team?.code || ""}</div>
|
||||
</div>
|
||||
|
||||
{/* Main map content area */}
|
||||
<div className={styles["map-container"]} ref={mapContainerRef}>
|
||||
<div className={styles["treasure-map"]}>
|
||||
{/* Use Image component with fill layout for better SVG handling */}
|
||||
<Image
|
||||
src="/images/treasure-map.svg"
|
||||
alt="Treasure Map"
|
||||
className={styles["map-svg"]}
|
||||
width={1000}
|
||||
height={1000}
|
||||
priority
|
||||
onError={() => {
|
||||
// Fallback to a static image if SVG fails to load
|
||||
setMapError(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Navigation bar - now sticky */}
|
||||
<nav className={styles["nav-bar"]}>
|
||||
<Link
|
||||
href={teamData?.teamId ? `/${teamData.teamId}` : "/"}
|
||||
className={styles["nav-item"]}
|
||||
>
|
||||
{/* Map background with a treasure map style */}
|
||||
<rect width="800" height="500" fill="#f5e6ca" />
|
||||
<path
|
||||
d="M0,0 L800,0 L800,500 L0,500 Z"
|
||||
fill="none"
|
||||
stroke="#9c6b30"
|
||||
strokeWidth="10"
|
||||
/>
|
||||
|
||||
{/* Decorative map elements */}
|
||||
<path
|
||||
d="M100,100 Q400,50 700,150"
|
||||
stroke="#9c6b30"
|
||||
strokeWidth="2"
|
||||
fill="none"
|
||||
strokeDasharray="5,5"
|
||||
/>
|
||||
<path
|
||||
d="M150,400 Q400,450 650,350"
|
||||
stroke="#9c6b30"
|
||||
strokeWidth="2"
|
||||
fill="none"
|
||||
strokeDasharray="5,5"
|
||||
/>
|
||||
<path
|
||||
d="M300,100 Q350,250 300,400"
|
||||
stroke="#9c6b30"
|
||||
strokeWidth="2"
|
||||
fill="none"
|
||||
strokeDasharray="5,5"
|
||||
/>
|
||||
<path
|
||||
d="M500,100 Q450,250 500,400"
|
||||
stroke="#9c6b30"
|
||||
strokeWidth="2"
|
||||
fill="none"
|
||||
strokeDasharray="5,5"
|
||||
/>
|
||||
|
||||
{/* Connection lines between clue points */}
|
||||
{cluePositions.slice(0, -1).map((clue, index) => (
|
||||
<line
|
||||
key={`line-${index}`}
|
||||
x1={clue.x}
|
||||
y1={clue.y}
|
||||
x2={cluePositions[index + 1].x}
|
||||
y2={cluePositions[index + 1].y}
|
||||
stroke="#9c6b30"
|
||||
strokeWidth="2"
|
||||
strokeDasharray="5,5"
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* Clue points */}
|
||||
{cluePositions.map((clue, index) => (
|
||||
<g key={`clue-${index}`} className="clue-point">
|
||||
<circle
|
||||
cx={clue.x}
|
||||
cy={clue.y}
|
||||
r="15"
|
||||
fill="#d4a76a"
|
||||
stroke="#9c6b30"
|
||||
strokeWidth="2"
|
||||
onClick={() => router.push(`/clue/${clue.id}`)}
|
||||
style={{ cursor: "pointer" }}
|
||||
/>
|
||||
<text
|
||||
x={clue.x}
|
||||
y={clue.y}
|
||||
textAnchor="middle"
|
||||
dominantBaseline="central"
|
||||
fill="#5d4037"
|
||||
fontSize="12"
|
||||
fontWeight="bold"
|
||||
>
|
||||
{index + 1}
|
||||
</text>
|
||||
</g>
|
||||
))}
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{/* Clue list */}
|
||||
<div className="clue-list">
|
||||
<h2>Clues</h2>
|
||||
<ul>
|
||||
{cluePositions.map((clue, index) => (
|
||||
<li key={`clue-list-${index}`}>
|
||||
<Link href={`/clue/${clue.id}`}>
|
||||
{index + 1}. {clue.title}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<button onClick={() => router.push("/")} className="back-button">
|
||||
Back to Home
|
||||
</button>
|
||||
|
||||
<style jsx>{`
|
||||
.map-container {
|
||||
padding: 20px;
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #5d4037;
|
||||
margin-bottom: 20px;
|
||||
font-family: "Pirates", cursive, serif;
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.map-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
border: 5px solid #9c6b30;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
||||
background: #f5e6ca;
|
||||
}
|
||||
|
||||
.map-svg {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.clue-list {
|
||||
margin-top: 30px;
|
||||
background: #f5e6ca;
|
||||
border: 2px solid #9c6b30;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.clue-list h2 {
|
||||
color: #5d4037;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 10px 0;
|
||||
padding: 8px;
|
||||
border-bottom: 1px dashed #9c6b30;
|
||||
}
|
||||
|
||||
li:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #5d4037;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
padding: 5px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background-color: rgba(156, 107, 48, 0.2);
|
||||
transform: translateX(5px);
|
||||
}
|
||||
|
||||
.back-button {
|
||||
margin-top: 20px;
|
||||
padding: 10px 20px;
|
||||
background-color: #9c6b30;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.back-button:hover {
|
||||
background-color: #7d562a;
|
||||
}
|
||||
|
||||
.loading,
|
||||
.error,
|
||||
.no-clues {
|
||||
padding: 50px;
|
||||
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 className={styles["nav-icon"]}>🏠</div>
|
||||
<div className={styles["nav-label"]}>Home</div>
|
||||
</Link>
|
||||
<Link href="/leaderboard" className={styles["nav-item"]}>
|
||||
<div className={styles["nav-icon"]}>📊</div>
|
||||
<div className={styles["nav-label"]}>Stats</div>
|
||||
</Link>
|
||||
<Link href="/team-selection" className={styles["nav-item"]}>
|
||||
<div className={styles["nav-icon"]}>👥</div>
|
||||
<div className={styles["nav-label"]}>Team</div>
|
||||
</Link>
|
||||
<Link
|
||||
href="/map"
|
||||
className={`${styles["nav-item"]} ${styles["active"]}`}
|
||||
>
|
||||
<div className={styles["nav-icon"]}>🗺️</div>
|
||||
<div className={styles["nav-label"]}>Map</div>
|
||||
</Link>
|
||||
</nav>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -234,27 +234,39 @@ export default function TeamPage({ params }) {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
// Check hunt status first
|
||||
// Use try-catch with robust error handling
|
||||
const response = await fetch("/api/hunt-status", {
|
||||
method: "GET",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
if (data.active) {
|
||||
// If hunt is active, navigate to map page
|
||||
router.push("/map");
|
||||
} else {
|
||||
// If hunt is not active, show not started page
|
||||
router.push(`/hunt-not-started?teamId=${teamId}`);
|
||||
}
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error("Hunt status error response:", errorText);
|
||||
throw new Error(
|
||||
`Failed to check hunt status: ${response.status}`
|
||||
);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log("Hunt status response:", data);
|
||||
|
||||
// If hunt is active, go directly to the map page using window.location
|
||||
if (data.active) {
|
||||
// Use window.location.href for a full page reload to avoid caching issues
|
||||
window.location.href = "/map";
|
||||
} else {
|
||||
throw new Error("Failed to check hunt status");
|
||||
// If hunt is not active, show the not started page
|
||||
router.push(`/hunt-not-started?teamId=${teamId}`);
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
console.error("Error checking hunt status:", err);
|
||||
setError(`Failed to check hunt status: ${err.message}`);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}}
|
||||
@@ -483,6 +495,7 @@ export default function TeamPage({ params }) {
|
||||
.error-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 20px;
|
||||
color: #d32f2f;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
|
||||
@@ -1,378 +1,83 @@
|
||||
export const runtime = "nodejs";
|
||||
|
||||
import NextAuth from "next-auth"; // <-- Changed from "next-auth/next"
|
||||
import CredentialsProvider from "next-auth/providers/credentials";
|
||||
import { validateCredentials } from "@/actions/auth";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { cookies } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
// Define hardcoded values for NextAuth
|
||||
const NEXTAUTH_SECRET = "banana";
|
||||
const NEXTAUTH_URL = "http://localhost:3000";
|
||||
// Use a consistent secret for JWT signing and verification
|
||||
const JWT_SECRET = process.env.NEXTAUTH_SECRET || "banana";
|
||||
console.log("Using NextAuth Secret:", JWT_SECRET);
|
||||
|
||||
// Use hardcoded values instead of environment variables
|
||||
console.log("Using NextAuth Secret: banana");
|
||||
console.log(`Using NextAuth URL: ${NEXTAUTH_URL}`);
|
||||
|
||||
// Function to create a signed token
|
||||
function createToken(payload) {
|
||||
const encodedData = Buffer.from(JSON.stringify(payload)).toString("base64");
|
||||
const signature = Buffer.from(`${encodedData}.${NEXTAUTH_SECRET}`).toString(
|
||||
"base64"
|
||||
);
|
||||
return `${encodedData}.${signature}`;
|
||||
}
|
||||
|
||||
// Function to verify a token
|
||||
function verifyToken(token) {
|
||||
try {
|
||||
if (!token) return null;
|
||||
|
||||
const [encodedData, signature] = token.split(".");
|
||||
const expectedSignature = Buffer.from(
|
||||
`${encodedData}.${NEXTAUTH_SECRET}`
|
||||
).toString("base64");
|
||||
|
||||
if (signature !== expectedSignature) return null;
|
||||
|
||||
const data = JSON.parse(
|
||||
Buffer.from(encodedData, "base64").toString("utf-8")
|
||||
);
|
||||
|
||||
// Check if token is expired
|
||||
if (data.exp && data.exp < Math.floor(Date.now() / 1000)) return null;
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Token verification error:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the handler functions directly without calling NextAuth function
|
||||
export async function GET(request) {
|
||||
try {
|
||||
// Safely parse URL with error handling
|
||||
let pathname = "/";
|
||||
let searchParams = new URLSearchParams();
|
||||
|
||||
try {
|
||||
if (request && request.url) {
|
||||
const url = new URL(request.url);
|
||||
pathname = url.pathname;
|
||||
searchParams = url.searchParams;
|
||||
} else {
|
||||
console.warn("Request or request.url is undefined");
|
||||
}
|
||||
} catch (urlError) {
|
||||
console.error("Failed to parse URL:", urlError);
|
||||
pathname = request.nextUrl?.pathname || "/api/auth/session";
|
||||
}
|
||||
|
||||
console.log("Auth GET request path:", pathname);
|
||||
|
||||
// Handle session requests
|
||||
if (pathname.includes("/session")) {
|
||||
try {
|
||||
const cookieStore = await cookies();
|
||||
const sessionToken = cookieStore.get("next-auth.session-token")?.value;
|
||||
|
||||
if (!sessionToken) {
|
||||
return Response.json({ user: null });
|
||||
}
|
||||
|
||||
const userData = verifyToken(sessionToken);
|
||||
|
||||
return Response.json({
|
||||
user: userData
|
||||
? {
|
||||
id: userData.id,
|
||||
name: userData.username,
|
||||
email: `${userData.username}@example.com`, // Next-auth expects an email
|
||||
image: null,
|
||||
role: userData.role,
|
||||
teamId: userData.teamId,
|
||||
}
|
||||
: null,
|
||||
});
|
||||
} catch (cookieError) {
|
||||
console.error("Error accessing cookies:", cookieError);
|
||||
return Response.json({ user: null });
|
||||
}
|
||||
}
|
||||
|
||||
// For CSRF token requests - IMPORTANT: Return JSON, don't redirect
|
||||
if (pathname.includes("/csrf")) {
|
||||
const csrfToken = Buffer.from(
|
||||
`csrf-${Date.now()}-${NEXTAUTH_SECRET}`
|
||||
).toString("base64");
|
||||
return new Response(JSON.stringify({ csrfToken }), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
// Check if this is a signin request
|
||||
if (pathname.includes("/signin")) {
|
||||
// Redirect to the login page with callbackUrl
|
||||
const callbackUrl = searchParams.get("callbackUrl") || "/";
|
||||
return Response.redirect(
|
||||
`${NEXTAUTH_URL}/login?callbackUrl=${encodeURIComponent(callbackUrl)}`
|
||||
);
|
||||
}
|
||||
|
||||
// Handle providers request
|
||||
if (pathname.includes("/providers")) {
|
||||
return Response.json({
|
||||
credentials: {
|
||||
id: "credentials",
|
||||
name: "Credentials",
|
||||
type: "credentials",
|
||||
signinUrl: `${NEXTAUTH_URL}/api/auth/signin/credentials`,
|
||||
callbackUrl: `${NEXTAUTH_URL}/api/auth/callback/credentials`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Default: redirect to login
|
||||
return Response.redirect(`${NEXTAUTH_URL}/login`);
|
||||
} catch (error) {
|
||||
console.error("Auth GET error:", error);
|
||||
return Response.json({ error: "Internal server error" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request) {
|
||||
try {
|
||||
// Debug the actual URL value before trying to parse it
|
||||
console.log("Raw request URL:", request.url);
|
||||
|
||||
// More robust URL parsing
|
||||
let pathname = "";
|
||||
let requestUrl = null;
|
||||
|
||||
try {
|
||||
// Add protocol and host if the URL doesn't have them
|
||||
const urlString = request.url;
|
||||
|
||||
// Only try to parse if we have a string
|
||||
if (typeof urlString === "string" && urlString) {
|
||||
// Check if URL has proper protocol
|
||||
const hasProtocol =
|
||||
urlString.startsWith("http://") || urlString.startsWith("https://");
|
||||
|
||||
if (hasProtocol) {
|
||||
requestUrl = new URL(urlString);
|
||||
} else {
|
||||
// Add protocol and host to handle relative URLs
|
||||
requestUrl = new URL(urlString, "http://localhost:3000");
|
||||
}
|
||||
pathname = requestUrl.pathname;
|
||||
console.log("Successfully parsed URL:", pathname);
|
||||
} else {
|
||||
console.log("URL is not valid, using fallback methods");
|
||||
}
|
||||
} catch (urlError) {
|
||||
console.error("Invalid URL in request:", urlError);
|
||||
}
|
||||
|
||||
// Use nextUrl as fallback if available
|
||||
if (!pathname && request.nextUrl?.pathname) {
|
||||
pathname = request.nextUrl.pathname;
|
||||
console.log("Using nextUrl pathname:", pathname);
|
||||
}
|
||||
|
||||
const contentType = request.headers.get("content-type") || "";
|
||||
console.log("Content-Type:", contentType);
|
||||
|
||||
// Check for callback/credentials without relying on URL parsing
|
||||
const isCallbackRequest =
|
||||
pathname.includes("/callback/credentials") ||
|
||||
(request.url && request.url.includes("/callback/credentials"));
|
||||
|
||||
if (isCallbackRequest) {
|
||||
// Extract credentials from the appropriate source based on content type
|
||||
let username, password, callbackUrl;
|
||||
|
||||
if (contentType.includes("application/json")) {
|
||||
// Handle JSON request
|
||||
const jsonData = await request.json();
|
||||
username = jsonData.username;
|
||||
password = jsonData.password;
|
||||
callbackUrl = jsonData.callbackUrl || "/team-selection";
|
||||
console.log("Parsed JSON credentials from callback request");
|
||||
} else if (
|
||||
contentType.includes("application/x-www-form-urlencoded") ||
|
||||
contentType.includes("multipart/form-data")
|
||||
) {
|
||||
// Handle form data
|
||||
const formData = await request.formData();
|
||||
username = formData.get("username");
|
||||
password = formData.get("password");
|
||||
callbackUrl = formData.get("callbackUrl") || "/team-selection";
|
||||
console.log("Parsed form credentials from callback request");
|
||||
} else {
|
||||
return Response.json(
|
||||
{ error: "Unsupported content type for authentication" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
console.log(`Credentials callback for user: ${username || "(unknown)"}`);
|
||||
|
||||
// Validate credentials
|
||||
const user = await validateCredentials(username, password);
|
||||
|
||||
if (!user) {
|
||||
return Response.json({ error: "Invalid credentials" }, { status: 401 });
|
||||
}
|
||||
|
||||
// Create token with user data
|
||||
const token = {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
role: user.role,
|
||||
teamId: user.teamId,
|
||||
iat: Math.floor(Date.now() / 1000),
|
||||
exp: Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60, // 30 days
|
||||
};
|
||||
|
||||
// Create JWT-like token
|
||||
const jwtToken = createToken(token);
|
||||
|
||||
// Calculate expiration date for cookie
|
||||
const expiryDate = new Date();
|
||||
expiryDate.setDate(expiryDate.getDate() + 30);
|
||||
|
||||
// Return success with cookie and redirect URL
|
||||
return new Response(JSON.stringify({ url: callbackUrl }), {
|
||||
status: 200,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Set-Cookie": `next-auth.session-token=${jwtToken}; Path=/; HttpOnly; SameSite=Lax; Expires=${expiryDate.toUTCString()}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Handle regular form submission
|
||||
let username, password, callbackUrl;
|
||||
|
||||
// Handle form data (application/x-www-form-urlencoded)
|
||||
if (contentType.includes("application/x-www-form-urlencoded")) {
|
||||
// Clone request to read the body
|
||||
const clonedRequest = request.clone();
|
||||
const formText = await clonedRequest.text();
|
||||
console.log("Form data:", formText);
|
||||
|
||||
// Parse form data manually
|
||||
const params = new URLSearchParams(formText);
|
||||
username = params.get("username");
|
||||
password = params.get("password");
|
||||
callbackUrl = params.get("callbackUrl") || "/team-selection";
|
||||
|
||||
console.log("Parsed credentials:", {
|
||||
username,
|
||||
password: password ? "[REDACTED]" : undefined,
|
||||
});
|
||||
}
|
||||
// Handle JSON
|
||||
else if (contentType.includes("application/json")) {
|
||||
const jsonData = await request.json();
|
||||
username = jsonData.username;
|
||||
password = jsonData.password;
|
||||
callbackUrl = jsonData.callbackUrl || "/team-selection";
|
||||
}
|
||||
// Try form data as a last resort
|
||||
else {
|
||||
try {
|
||||
const formData = await request.formData();
|
||||
username = formData.get("username");
|
||||
password = formData.get("password");
|
||||
callbackUrl = formData.get("callbackUrl") || "/team-selection";
|
||||
} catch (formError) {
|
||||
console.error("Form data parsing error:", formError);
|
||||
// Instead of returning an error, log and continue - the request might be something else
|
||||
}
|
||||
}
|
||||
|
||||
if (!username || !password) {
|
||||
// If no credentials were found, this might be a different kind of request
|
||||
// Log it and return an appropriate error
|
||||
console.error("Missing credentials in request");
|
||||
return Response.json(
|
||||
{ error: "Username and password required" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Validate credentials
|
||||
const user = await validateCredentials(username, password);
|
||||
|
||||
if (!user) {
|
||||
return Response.json({ error: "Invalid credentials" }, { status: 401 });
|
||||
}
|
||||
|
||||
// Create token with user data
|
||||
const token = {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
role: user.role,
|
||||
teamId: user.teamId,
|
||||
iat: Math.floor(Date.now() / 1000),
|
||||
exp: Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60, // 30 days
|
||||
};
|
||||
|
||||
// Create JWT-like token
|
||||
const jwtToken = createToken(token);
|
||||
|
||||
// Calculate expiration date for cookie
|
||||
const expiryDate = new Date();
|
||||
expiryDate.setDate(expiryDate.getDate() + 30);
|
||||
|
||||
// Return success with cookie and redirect URL
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
ok: true,
|
||||
url: callbackUrl,
|
||||
}),
|
||||
{
|
||||
status: 200,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Set-Cookie": `next-auth.session-token=${jwtToken}; Path=/; HttpOnly; SameSite=Lax; Expires=${expiryDate.toUTCString()}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Auth POST error:", error);
|
||||
return Response.json({ error: "Internal server error" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
// Export authOptions for compatibility with other code using getServerSession
|
||||
export const authOptions = {
|
||||
providers: [
|
||||
// Correct syntax: no function call, just reference the imported object
|
||||
{
|
||||
id: "credentials",
|
||||
name: "Credentials",
|
||||
type: "credentials",
|
||||
authorize: async (credentials) => {
|
||||
if (!credentials?.username || !credentials?.password) return null;
|
||||
return await validateCredentials(
|
||||
credentials.username,
|
||||
credentials.password
|
||||
);
|
||||
credentials: {
|
||||
username: { label: "Username", type: "text" },
|
||||
password: { label: "Password", type: "password" },
|
||||
},
|
||||
async authorize(credentials) {
|
||||
try {
|
||||
if (!credentials?.username || !credentials?.password) {
|
||||
console.log("Missing credentials");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Validate credentials against our database
|
||||
const user = await validateCredentials(
|
||||
credentials.username,
|
||||
credentials.password
|
||||
);
|
||||
|
||||
if (user) {
|
||||
console.log("User authenticated:", user.username);
|
||||
return user;
|
||||
}
|
||||
|
||||
console.log("Invalid credentials for:", credentials.username);
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error("Authentication error:", error);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
session: {
|
||||
strategy: "jwt",
|
||||
maxAge: 30 * 24 * 60 * 60, // 30 days
|
||||
},
|
||||
|
||||
jwt: {
|
||||
secret: JWT_SECRET,
|
||||
encode: async ({ secret, token }) => {
|
||||
return jwt.sign(token, secret);
|
||||
},
|
||||
decode: async ({ secret, token }) => {
|
||||
try {
|
||||
return jwt.verify(token, secret);
|
||||
} catch (error) {
|
||||
console.error("JWT decode error:", error);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
callbacks: {
|
||||
async jwt({ token, user }) {
|
||||
if (user) {
|
||||
token.id = user.id;
|
||||
token.role = user.role;
|
||||
token.username = user.username;
|
||||
token.teamId = user.teamId;
|
||||
token.teamId = user.team_id || user.teamId;
|
||||
}
|
||||
return token;
|
||||
},
|
||||
|
||||
async session({ session, token }) {
|
||||
if (token) {
|
||||
session.user = session.user || {};
|
||||
@@ -384,9 +89,46 @@ export const authOptions = {
|
||||
return session;
|
||||
},
|
||||
},
|
||||
session: { strategy: "jwt", maxAge: 30 * 24 * 60 * 60 },
|
||||
secret: NEXTAUTH_SECRET,
|
||||
|
||||
pages: {
|
||||
signIn: "/login",
|
||||
error: "/login?error=true",
|
||||
},
|
||||
|
||||
// Add debugging to help identify issues with API responses
|
||||
debug: process.env.NODE_ENV === "development",
|
||||
|
||||
// Add logger to catch issues
|
||||
logger: {
|
||||
error(code, metadata) {
|
||||
console.error(`NextAuth error: ${code}`, metadata);
|
||||
},
|
||||
warn(code) {
|
||||
console.warn(`NextAuth warning: ${code}`);
|
||||
},
|
||||
debug(code, metadata) {
|
||||
console.log(`NextAuth debug: ${code}`, metadata);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Export the token functions directly for use in other files
|
||||
export { createToken, verifyToken };
|
||||
// Token helpers
|
||||
export function createToken(payload) {
|
||||
return jwt.sign(payload, JWT_SECRET, { expiresIn: "30d" });
|
||||
}
|
||||
|
||||
export function verifyToken(token) {
|
||||
try {
|
||||
return jwt.verify(token, JWT_SECRET);
|
||||
} catch (error) {
|
||||
console.error("Token verification error:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Set session runtime to nodejs (not edge for better compatibility)
|
||||
export const runtime = "nodejs";
|
||||
|
||||
// Create the handler with correct NextAuth instantiation
|
||||
const handler = NextAuth(authOptions);
|
||||
export { handler as GET, handler as POST };
|
||||
@@ -9,38 +9,41 @@ export async function GET(request, { params }) {
|
||||
const { clueId } = params;
|
||||
|
||||
try {
|
||||
// Verify the user is authenticated
|
||||
const cookieStore = await cookies();
|
||||
const sessionToken = cookieStore.get("next-auth.session-token")?.value;
|
||||
|
||||
if (!sessionToken) {
|
||||
return Response.json(
|
||||
{ error: "Unauthorized", clue: null },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
const userData = verifyToken(sessionToken);
|
||||
if (!userData) {
|
||||
return Response.json(
|
||||
{ error: "Invalid session", clue: null },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
// Check if the hunt is active
|
||||
// Check if the hunt is active first
|
||||
const huntActiveSetting = await queryOne(
|
||||
"SELECT value FROM settings WHERE key = 'hunt_active'"
|
||||
);
|
||||
|
||||
const huntActive = huntActiveSetting && huntActiveSetting.value === "true";
|
||||
|
||||
// Only verify authentication when hunt is not active
|
||||
if (!huntActive) {
|
||||
// Return a valid JSON response with clue set to null
|
||||
return Response.json({
|
||||
error: "Hunt is not active",
|
||||
clue: null,
|
||||
});
|
||||
const cookieStore = await cookies();
|
||||
const sessionToken =
|
||||
cookieStore.get("next-auth.session-token")?.value ||
|
||||
cookieStore.get("__Secure-next-auth.session-token")?.value;
|
||||
|
||||
if (!sessionToken) {
|
||||
return Response.json(
|
||||
{ error: "Unauthorized", clue: null },
|
||||
{ status: 401, headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
|
||||
const userData = verifyToken(sessionToken);
|
||||
if (!userData) {
|
||||
return Response.json(
|
||||
{ error: "Invalid session", clue: null },
|
||||
{ status: 401, headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
|
||||
// Admin can see clues even if hunt is not active
|
||||
if (userData.role !== "admin") {
|
||||
return Response.json(
|
||||
{ error: "Hunt is not active", clue: null },
|
||||
{ headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch the specific clue
|
||||
@@ -49,17 +52,19 @@ export async function GET(request, { params }) {
|
||||
if (!clue) {
|
||||
return Response.json(
|
||||
{ error: "Clue not found", clue: null },
|
||||
{ status: 404 }
|
||||
{ status: 404, headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
|
||||
return Response.json({ clue });
|
||||
return Response.json(
|
||||
{ clue },
|
||||
{ headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error fetching clue details:", error);
|
||||
// Always return a valid JSON response even on error
|
||||
return Response.json(
|
||||
{ error: "Failed to fetch clue details: " + error.message, clue: null },
|
||||
{ status: 500 }
|
||||
{ status: 500, headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
}
|
||||
+71
-48
@@ -4,21 +4,18 @@ import { verifyToken } from "@/app/api/auth/[...nextauth]/route";
|
||||
|
||||
export const dynamic = "force-dynamic"; // Disable caching
|
||||
|
||||
// Helper function to ensure the clues table exists (if not already created elsewhere)
|
||||
// Helper function to ensure the clues table exists
|
||||
async function ensureCluesTableExists() {
|
||||
try {
|
||||
// Create the clues table if it doesn't exist
|
||||
await run(`
|
||||
CREATE TABLE IF NOT EXISTS clues (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
location TEXT,
|
||||
order_number INTEGER,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error ensuring clues table exists:", error);
|
||||
@@ -26,79 +23,105 @@ async function ensureCluesTableExists() {
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to extract numeric value from clue title
|
||||
function extractClueNumber(title) {
|
||||
// Look for patterns like "Clue 1", "Clue 10", etc.
|
||||
const match = title.match(/Clue\s+(\d+)/i);
|
||||
// If we find a match, return the number, otherwise return Infinity (to sort to the end)
|
||||
return match ? parseInt(match[1], 10) : Infinity;
|
||||
}
|
||||
|
||||
// GET endpoint to retrieve all clues
|
||||
export async function GET(request) {
|
||||
try {
|
||||
// Verify the user is authenticated
|
||||
const cookieStore = await cookies();
|
||||
const sessionToken = cookieStore.get("next-auth.session-token")?.value;
|
||||
// First check if the hunt is active
|
||||
const huntActiveSetting = await queryOne(
|
||||
"SELECT value FROM settings WHERE key = 'hunt_active'"
|
||||
);
|
||||
const huntActive = huntActiveSetting && huntActiveSetting.value === "true";
|
||||
|
||||
if (!sessionToken) {
|
||||
return Response.json(
|
||||
{ error: "Unauthorized", clues: [] },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
// If hunt is active, allow access without authentication
|
||||
if (!huntActive) {
|
||||
// Only verify authentication when hunt is not active
|
||||
const cookieStore = await cookies();
|
||||
const sessionToken =
|
||||
cookieStore.get("next-auth.session-token")?.value ||
|
||||
cookieStore.get("__Secure-next-auth.session-token")?.value;
|
||||
|
||||
const userData = verifyToken(sessionToken);
|
||||
if (!userData) {
|
||||
return Response.json(
|
||||
{ error: "Invalid session", clues: [] },
|
||||
{ status: 401 }
|
||||
);
|
||||
if (!sessionToken) {
|
||||
return Response.json(
|
||||
{ error: "Unauthorized", clues: [] },
|
||||
{ status: 401, headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
|
||||
const userData = verifyToken(sessionToken);
|
||||
if (!userData) {
|
||||
return Response.json(
|
||||
{ error: "Invalid session", clues: [] },
|
||||
{ status: 401, headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
|
||||
// Admin can see clues even if hunt is not active
|
||||
if (userData.role !== "admin") {
|
||||
return Response.json(
|
||||
{ error: "Hunt is not active", clues: [] },
|
||||
{ headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the clues table exists
|
||||
await ensureCluesTableExists();
|
||||
|
||||
// Check if the hunt is active
|
||||
const huntActiveSetting = await queryOne(
|
||||
"SELECT value FROM settings WHERE key = 'hunt_active'"
|
||||
);
|
||||
|
||||
const huntActive = huntActiveSetting && huntActiveSetting.value === "true";
|
||||
|
||||
if (!huntActive) {
|
||||
// Return empty clues array instead of error status to avoid redirect issues
|
||||
return Response.json({
|
||||
error: "Hunt is not active",
|
||||
clues: [],
|
||||
});
|
||||
}
|
||||
|
||||
// Fetch all clues
|
||||
const clues = await query("SELECT * FROM clues ORDER BY order_number ASC");
|
||||
// Fetch all clues without sorting in SQL
|
||||
const clues = await query("SELECT * FROM clues");
|
||||
|
||||
// If no clues are found, insert sample clues (for testing purposes)
|
||||
if (!clues || clues.length === 0) {
|
||||
// Insert sample clues
|
||||
await run(
|
||||
"INSERT INTO clues (title, description, location, order_number) VALUES (?, ?, ?, ?)",
|
||||
["First Clue", "This is the first clue description", "Location 1", 1]
|
||||
"INSERT INTO clues (title, description, location) VALUES (?, ?, ?)",
|
||||
["Clue 1", "This is the first clue description", "Location 1"]
|
||||
);
|
||||
await run(
|
||||
"INSERT INTO clues (title, description, location, order_number) VALUES (?, ?, ?, ?)",
|
||||
["Second Clue", "This is the second clue description", "Location 2", 2]
|
||||
"INSERT INTO clues (title, description, location) VALUES (?, ?, ?)",
|
||||
["Clue 2", "This is the second clue description", "Location 2"]
|
||||
);
|
||||
await run(
|
||||
"INSERT INTO clues (title, description, location, order_number) VALUES (?, ?, ?, ?)",
|
||||
["Final Clue", "This is the final clue description", "Location 3", 3]
|
||||
"INSERT INTO clues (title, description, location) VALUES (?, ?, ?)",
|
||||
["Clue 3", "This is the final clue description", "Location 3"]
|
||||
);
|
||||
|
||||
// Fetch the inserted clues
|
||||
const sampleClues = await query(
|
||||
"SELECT * FROM clues ORDER BY order_number ASC"
|
||||
const sampleClues = await query("SELECT * FROM clues");
|
||||
|
||||
// Sort clues based on numbering in title
|
||||
sampleClues.sort(
|
||||
(a, b) => extractClueNumber(a.title) - extractClueNumber(b.title)
|
||||
);
|
||||
|
||||
return Response.json(
|
||||
{ clues: sampleClues },
|
||||
{ headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
return Response.json({ clues: sampleClues });
|
||||
}
|
||||
|
||||
return Response.json({ clues });
|
||||
// Sort clues based on numbering in title
|
||||
clues.sort(
|
||||
(a, b) => extractClueNumber(a.title) - extractClueNumber(b.title)
|
||||
);
|
||||
|
||||
return Response.json(
|
||||
{ clues },
|
||||
{ headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error fetching clues:", error);
|
||||
// Always return a valid JSON response even on error
|
||||
return Response.json(
|
||||
{ error: "Failed to fetch clues: " + error.message, clues: [] },
|
||||
{ status: 500 }
|
||||
{ status: 500, headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,59 +4,99 @@ import { verifyToken } from "@/app/api/auth/[...nextauth]/route";
|
||||
|
||||
export const dynamic = "force-dynamic"; // Disable caching
|
||||
|
||||
// Helper function to ensure the hunt_status table exists
|
||||
async function ensureHuntStatusTableExists() {
|
||||
try {
|
||||
// Create hunt_status table if it doesn't exist
|
||||
await run(`
|
||||
CREATE TABLE IF NOT EXISTS hunt_status (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
is_active BOOLEAN DEFAULT 0,
|
||||
start_time TEXT,
|
||||
end_time TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`);
|
||||
|
||||
// Check if we need to insert the initial record
|
||||
const status = await queryOne("SELECT * FROM hunt_status LIMIT 1");
|
||||
if (!status) {
|
||||
await run(
|
||||
"INSERT INTO hunt_status (id, is_active, start_time) VALUES (1, 0, NULL)"
|
||||
);
|
||||
console.log("Initialized hunt_status table with default record");
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error ensuring hunt_status table:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// GET endpoint to check if the hunt is active
|
||||
export async function GET(request) {
|
||||
try {
|
||||
// Verify the user is authenticated
|
||||
// First ensure the table exists
|
||||
await ensureHuntStatusTableExists();
|
||||
|
||||
// Get the token from cookies - properly awaited
|
||||
const cookieStore = await cookies();
|
||||
const sessionToken = cookieStore.get("next-auth.session-token")?.value;
|
||||
|
||||
if (!sessionToken) {
|
||||
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
||||
// Check hunt status even if user is not authenticated
|
||||
// to prevent auth errors from interfering with status checks
|
||||
|
||||
// First check the hunt_status table for direct status
|
||||
const huntStatus = await queryOne(
|
||||
"SELECT is_active, start_time FROM hunt_status LIMIT 1"
|
||||
).catch((err) => {
|
||||
console.warn("Error querying hunt_status:", err.message);
|
||||
return null;
|
||||
});
|
||||
|
||||
// Then check the settings table as a backup
|
||||
const huntActiveSetting = await queryOne(
|
||||
"SELECT value FROM settings WHERE key = 'hunt_active'"
|
||||
).catch((err) => {
|
||||
console.warn("Error querying settings:", err.message);
|
||||
return null;
|
||||
});
|
||||
|
||||
// Also check the hunts table if it exists (for backward compatibility)
|
||||
const oldHuntRecord = await queryOne(
|
||||
"SELECT active, start_time FROM hunts ORDER BY id DESC LIMIT 1"
|
||||
).catch((err) => {
|
||||
// Ignore errors if table doesn't exist
|
||||
return null;
|
||||
});
|
||||
|
||||
// Determine active status from all available sources
|
||||
let isActive = false;
|
||||
let startTime = null;
|
||||
|
||||
// First priority: hunt_status.is_active
|
||||
if (huntStatus && huntStatus.is_active !== null) {
|
||||
isActive = Boolean(huntStatus.is_active);
|
||||
startTime = huntStatus.start_time;
|
||||
}
|
||||
// Second priority: settings table
|
||||
else if (huntActiveSetting) {
|
||||
isActive = huntActiveSetting.value === "true";
|
||||
}
|
||||
// Third priority: old hunts table
|
||||
else if (oldHuntRecord) {
|
||||
isActive = Boolean(oldHuntRecord.active);
|
||||
startTime = oldHuntRecord.start_time;
|
||||
}
|
||||
|
||||
const userData = verifyToken(sessionToken);
|
||||
if (!userData) {
|
||||
return Response.json({ error: "Invalid session" }, { status: 401 });
|
||||
}
|
||||
console.log("Hunt status check:", { isActive, startTime });
|
||||
|
||||
let huntActive = false;
|
||||
|
||||
try {
|
||||
// First try to create the settings table if it doesn't exist
|
||||
await run(`
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL
|
||||
)
|
||||
`);
|
||||
|
||||
// Check if hunt_active setting exists
|
||||
const huntSetting = await queryOne(
|
||||
"SELECT value FROM settings WHERE key = 'hunt_active'"
|
||||
);
|
||||
|
||||
// If the setting exists, use its value
|
||||
if (huntSetting) {
|
||||
huntActive = huntSetting.value === "true";
|
||||
} else {
|
||||
// If setting doesn't exist, create it with default value "false"
|
||||
await run(
|
||||
"INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)",
|
||||
["hunt_active", "false"]
|
||||
);
|
||||
}
|
||||
} catch (dbError) {
|
||||
console.error("Database error in hunt-status:", dbError);
|
||||
// Continue with default value if there's a DB error
|
||||
}
|
||||
|
||||
// Return the active status
|
||||
return Response.json({
|
||||
active: huntActive,
|
||||
timestamp: new Date().toISOString(),
|
||||
active: isActive,
|
||||
startTime: startTime || null,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Hunt status check error:", error);
|
||||
console.error("Error checking hunt status:", error);
|
||||
return Response.json(
|
||||
{ error: "Failed to check hunt status", active: false },
|
||||
{ status: 500 }
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
import { getServerSession } from "next-auth/next";
|
||||
import { authOptions, verifyToken } from "../../auth/[...nextauth]/route";
|
||||
import { query, queryOne, run } from "../../../../lib/db";
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
export const dynamic = "force-dynamic"; // Disable caching
|
||||
|
||||
// Helper function to ensure the hunt_status table exists
|
||||
async function ensureHuntStatusTableExists() {
|
||||
try {
|
||||
// Create hunt_status table if it doesn't exist
|
||||
await run(`
|
||||
CREATE TABLE IF NOT EXISTS hunt_status (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
is_active BOOLEAN DEFAULT 0,
|
||||
start_time TEXT,
|
||||
end_time TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`);
|
||||
|
||||
// Check if we need to insert the initial record
|
||||
const status = await queryOne("SELECT * FROM hunt_status LIMIT 1");
|
||||
if (!status) {
|
||||
await run(
|
||||
"INSERT INTO hunt_status (id, is_active, start_time) VALUES (1, 0, NULL)"
|
||||
);
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error ensuring hunt_status table:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback function to get user data when session fails
|
||||
async function getUserFromToken() {
|
||||
try {
|
||||
const cookieStore = await cookies();
|
||||
const sessionToken = cookieStore.get("next-auth.session-token")?.value;
|
||||
|
||||
if (!sessionToken) return null;
|
||||
|
||||
const userData = verifyToken(sessionToken);
|
||||
if (!userData) return null;
|
||||
|
||||
// Verify that the user exists and get their role
|
||||
const user = await queryOne("SELECT id, role FROM users WHERE id = ?", [
|
||||
userData.id || userData.sub,
|
||||
]);
|
||||
|
||||
return user;
|
||||
} catch (error) {
|
||||
console.error("Error getting user from token:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request) {
|
||||
try {
|
||||
// First ensure the table exists
|
||||
await ensureHuntStatusTableExists();
|
||||
|
||||
// Try to get session, with fallback to token-based auth
|
||||
let user = null;
|
||||
let session = null;
|
||||
|
||||
try {
|
||||
session = await getServerSession(authOptions);
|
||||
user = session?.user;
|
||||
} catch (sessionError) {
|
||||
console.warn(
|
||||
"Session error, trying fallback auth:",
|
||||
sessionError.message
|
||||
);
|
||||
user = await getUserFromToken();
|
||||
}
|
||||
|
||||
// Verify the user is authenticated
|
||||
if (!user) {
|
||||
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
// Check if user is admin
|
||||
if (user.role !== "admin") {
|
||||
return Response.json(
|
||||
{ error: "Only admins can start the hunt" },
|
||||
{ status: 403 }
|
||||
);
|
||||
}
|
||||
|
||||
// Check if hunt is already in progress
|
||||
const huntStatus = await queryOne("SELECT * FROM hunt_status LIMIT 1");
|
||||
|
||||
if (huntStatus && huntStatus.is_active) {
|
||||
return Response.json(
|
||||
{ error: "Hunt is already in progress" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Start the hunt by updating the status
|
||||
const now = new Date().toISOString();
|
||||
|
||||
// Update the hunt_status table
|
||||
await run(
|
||||
`UPDATE hunt_status SET is_active = ?, start_time = ? WHERE id = 1`,
|
||||
[true, now]
|
||||
);
|
||||
|
||||
// Also update settings table for compatibility
|
||||
try {
|
||||
await run("INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)", [
|
||||
"hunt_active",
|
||||
"true",
|
||||
]);
|
||||
} catch (err) {
|
||||
console.log(
|
||||
"Could not update settings table (might not exist yet):",
|
||||
err.message
|
||||
);
|
||||
}
|
||||
|
||||
return Response.json(
|
||||
{ success: true, message: "Hunt started successfully", startTime: now },
|
||||
{ status: 200 }
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Start hunt error:", error);
|
||||
return Response.json({ error: "Failed to start hunt" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import { getServerSession } from "next-auth/next";
|
||||
import { authOptions } from "../../auth/[...nextauth]/route";
|
||||
import { query, queryOne } from "../../../../lib/db";
|
||||
|
||||
export async function GET(request) {
|
||||
try {
|
||||
// Get the user's session with better error handling
|
||||
let session;
|
||||
try {
|
||||
session = await getServerSession(authOptions);
|
||||
} catch (sessionError) {
|
||||
console.error("Session fetch error:", sessionError);
|
||||
return Response.json(
|
||||
{ error: "Authentication error", details: sessionError.message },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
// Check if user is authenticated
|
||||
if (!session || !session.user) {
|
||||
return Response.json({ error: "You must be logged in" }, { status: 401 });
|
||||
}
|
||||
|
||||
// Check if user is part of a team
|
||||
if (!session.user.teamId) {
|
||||
return Response.json(
|
||||
{ error: "You are not part of a team" },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
// Fetch team data
|
||||
const team = await queryOne("SELECT * FROM teams WHERE id = ?", [
|
||||
session.user.teamId,
|
||||
]);
|
||||
|
||||
if (!team) {
|
||||
return Response.json({ error: "Team not found" }, { status: 404 });
|
||||
}
|
||||
|
||||
// Fetch team members
|
||||
const members = await query(
|
||||
"SELECT id, username FROM users WHERE team_id = ? ORDER BY username ASC",
|
||||
[session.user.teamId]
|
||||
);
|
||||
|
||||
// Fetch hunt status to provide start time - safely check multiple places
|
||||
let huntStartTime = null;
|
||||
|
||||
try {
|
||||
// Try hunt_status table first
|
||||
const huntStatus = await queryOne("SELECT * FROM hunt_status LIMIT 1");
|
||||
if (huntStatus) {
|
||||
huntStartTime = huntStatus.start_time;
|
||||
} else {
|
||||
// Fallback to hunts table if it exists
|
||||
const oldHuntRecord = await queryOne(
|
||||
"SELECT start_time FROM hunts ORDER BY id DESC LIMIT 1"
|
||||
);
|
||||
if (oldHuntRecord) {
|
||||
huntStartTime = oldHuntRecord.start_time;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Error fetching hunt status (non-critical):", err.message);
|
||||
// Continue even if hunt status check fails
|
||||
}
|
||||
|
||||
// Add cache-busting headers
|
||||
return Response.json(
|
||||
{
|
||||
success: true,
|
||||
team,
|
||||
members,
|
||||
huntStartTime: huntStartTime,
|
||||
refreshed: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
status: 200,
|
||||
headers: {
|
||||
"Cache-Control": "no-store, must-revalidate, max-age=0",
|
||||
Pragma: "no-cache",
|
||||
Expires: "0",
|
||||
},
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Team fetch error:", error);
|
||||
return Response.json(
|
||||
{ error: "Failed to fetch team data" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,15 @@
|
||||
'use client';
|
||||
"use client";
|
||||
|
||||
import { SessionProvider } from "next-auth/react";
|
||||
|
||||
export function NextAuthProvider({ children }) {
|
||||
return <SessionProvider>{children}</SessionProvider>;
|
||||
}
|
||||
// Add options to help debug the issue
|
||||
return (
|
||||
<SessionProvider
|
||||
refetchInterval={0} // Disable background refetching during debug
|
||||
refetchOnWindowFocus={false} // Disable refetching when window gets focus
|
||||
>
|
||||
{children}
|
||||
</SessionProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
"use client";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useSession } from "next-auth/react";
|
||||
import styles from "./waiting.module.css";
|
||||
|
||||
export default function WaitingPage() {
|
||||
const router = useRouter();
|
||||
const { status } = useSession();
|
||||
const [huntActive, setHuntActive] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
// Check hunt status
|
||||
useEffect(() => {
|
||||
// Function to check hunt status
|
||||
async function checkHuntStatus() {
|
||||
try {
|
||||
const response = await fetch("/api/hunt-status", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Cache-Control": "no-store, no-store, must-revalidate",
|
||||
},
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
console.log("Hunt status from waiting page:", data);
|
||||
|
||||
// If hunt is active, redirect to map page
|
||||
if (data.active) {
|
||||
console.log("Hunt is active! Redirecting to map page");
|
||||
router.push("/map");
|
||||
}
|
||||
|
||||
// Update local state
|
||||
setHuntActive(data.active);
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
console.error("Error checking hunt status:", err);
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Check on load and every 5 seconds
|
||||
checkHuntStatus();
|
||||
const intervalId = setInterval(checkHuntStatus, 5000);
|
||||
|
||||
return () => clearInterval(intervalId);
|
||||
}, [router]);
|
||||
|
||||
// Redirect to login if not authenticated
|
||||
useEffect(() => {
|
||||
if (status === "unauthenticated") {
|
||||
router.push("/login");
|
||||
}
|
||||
}, [status, router]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.spinner}></div>
|
||||
<h2>Loading...</h2>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<h1>Waiting for Hunt to Start</h1>
|
||||
<p>
|
||||
The treasure hunt has not started yet. Please wait for the administrator
|
||||
to start the hunt.
|
||||
</p>
|
||||
<div className={styles.waitingAnimation}>⏳</div>
|
||||
{huntActive && (
|
||||
<div className={styles.startedMessage}>
|
||||
<p>The hunt has started!</p>
|
||||
<button onClick={() => router.push("/map")}>Go to Map</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,58 @@
|
||||
"use client";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
export function useHuntStatusCheck(checkIntervalMs = 5000) {
|
||||
const [huntActive, setHuntActive] = useState(null); // Start with null to indicate unknown status
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
// Initial check
|
||||
checkHuntStatus();
|
||||
|
||||
// Set up interval for periodic checks
|
||||
const intervalId = setInterval(checkHuntStatus, checkIntervalMs);
|
||||
|
||||
// Clean up on unmount
|
||||
return () => clearInterval(intervalId);
|
||||
|
||||
// Function to check hunt status
|
||||
async function checkHuntStatus() {
|
||||
try {
|
||||
const response = await fetch("/api/hunt-status", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Cache-Control": "no-cache, no-store, must-revalidate",
|
||||
},
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
console.log("Hunt status check:", data); // Debug log
|
||||
|
||||
// If hunt was active but is now inactive, redirect to leaderboard
|
||||
if (huntActive === true && data.active === false) {
|
||||
console.log("Hunt has ended! Redirecting to leaderboard...");
|
||||
window.location.href = "/leaderboard?huntEnded=true";
|
||||
return;
|
||||
}
|
||||
|
||||
// If hunt was not active but is now active, let user know they can go to map page
|
||||
if (huntActive === false && data.active === true) {
|
||||
console.log("Hunt has started! Access to map is now available");
|
||||
// Can show a notification here if desired
|
||||
}
|
||||
|
||||
// Update hunt status state
|
||||
setHuntActive(data.active);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to check hunt status:", err);
|
||||
}
|
||||
}
|
||||
}, [huntActive, checkIntervalMs, router]);
|
||||
|
||||
return huntActive;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
// This would be a new image file you need to add to your project
|
||||
@@ -0,0 +1,356 @@
|
||||
<svg width="430" height="2155" viewBox="0 0 430 2155" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<mask id="mask0_37_192" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="430" height="2155">
|
||||
<rect width="430" height="2155" fill="#D9D9D9"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_37_192)">
|
||||
<path d="M430.503 1L39.3446 1C104.063 63.7386 204.551 197.004 88.7568 228.157C-27.0374 259.31 69.7905 379.595 132.679 435.843C184.586 486.863 272.83 592.473 210.54 606.752C132.679 624.6 11.7469 796.59 32.7097 904.76C32.7097 904.76 75.6357 1088.65 171.962 1195.74C268.289 1302.83 130.075 1334.86 65.6512 1413.16C28.7168 1458.05 -16.374 1862.94 54.5001 1877C111.199 1888.25 47.5001 1973 73.5001 2016C73.5001 2016 142.883 2048.24 163 2056C248.5 2089 270.291 2128.5 307 2128.5C364.212 2128.5 368.788 2157.5 426 2157.5C450.603 2157.5 432 1987 432 1987C428.506 1878.83 430.503 1 430.503 1Z" fill="#D6B23D" stroke="black" stroke-width="3" stroke-linecap="round"/>
|
||||
<path d="M431.489 1L74.3623 1C142.53 63.7386 248.374 197.004 126.408 228.157C4.44207 259.31 106.431 379.595 172.671 435.843C227.346 486.863 320.292 592.473 254.683 606.752C172.671 624.6 92.6494 729.137 80.1452 775.497C52.2822 878.799 434.854 950.215 434.854 1027.51L431.489 1Z" fill="#614723"/>
|
||||
<path d="M435.528 1232.93C435.528 1378.95 71.8716 1581.04 55.9623 1625.71C32.0496 1692.85 3.39064 1852.87 78.0423 1866.93C137.764 1878.18 124.305 1951.66 110.111 1987C95.9167 2022.34 170.5 2024.5 231 2032.5C291.5 2040.5 361.985 2162.28 416.5 2106.5C449.643 2072.59 438 1987 438 1987L435.528 1232.93Z" fill="#614723"/>
|
||||
<path d="M431.489 1L74.3623 1C142.53 63.7386 248.374 197.004 126.408 228.157C4.44207 259.31 106.431 379.595 172.671 435.843C227.346 486.863 320.292 592.473 254.683 606.752C172.671 624.6 92.6494 729.137 80.1452 775.497C52.2822 878.799 434.854 950.215 434.854 1027.51L431.489 1ZM431.489 1L438 1987M438 1987L435.528 1232.93C435.528 1378.95 71.8716 1581.04 55.9623 1625.71C32.0496 1692.85 3.39064 1852.87 78.0423 1866.93C137.764 1878.18 124.305 1951.66 110.111 1987C95.9167 2022.34 170.5 2024.5 231 2032.5C291.5 2040.5 361.985 2162.28 416.5 2106.5C449.643 2072.59 438 1987 438 1987Z" stroke="black" stroke-width="3" stroke-linecap="round"/>
|
||||
<path d="M92.6168 1H428V964.253V1366.65V1987C428 1987 432.5 2043 410.5 2059.5C388.5 2076 355 1991 318 1982.5C281 1974 197.5 1956.5 197.5 1956.5C138.5 1945 152.986 1878.18 96.1208 1866.93C25.0396 1852.87 46.498 1687.74 81.6042 1627.88C113.641 1573.25 360.923 1487.8 428 1366.65V964.253C332.724 903.678 71.5928 878.799 98.1231 775.497C110.029 729.137 186.224 624.6 264.313 606.752C326.784 592.473 238.283 486.863 186.224 435.843C123.152 379.595 26.0407 259.31 142.173 228.157C258.306 197.004 157.524 63.7386 92.6168 1Z" fill="#519D48"/>
|
||||
<path d="M428 964.253C332.724 903.678 71.5928 878.799 98.1231 775.497C110.029 729.137 186.224 624.6 264.313 606.752C326.784 592.473 238.283 486.863 186.224 435.843C123.152 379.595 26.0407 259.31 142.173 228.157C258.306 197.004 157.524 63.7386 92.6168 1H428V1987C428 1987 432.5 2043 410.5 2059.5C388.5 2076 355 1991 318 1982.5C281 1974 197.5 1956.5 197.5 1956.5C138.5 1945 152.986 1878.18 96.1208 1866.93C25.0396 1852.87 46.498 1687.74 81.6042 1627.88C113.641 1573.25 360.923 1487.8 428 1366.65" stroke="black" stroke-width="3" stroke-linecap="round"/>
|
||||
<path d="M232 212C259.667 248 347.7 280.3 322.5 291.5C291 305.5 248.5 388 265 400C281.5 412 360 514 342.5 522C325 530 326.5 647.5 315.5 654C304.5 660.5 301.47 760.917 294.5 777C281.5 807 231.5 852.5 231.5 880.5C231.5 894.037 380.326 961.248 361.5 966C310 979 269.5 1017.5 259.5 1042C236.116 1099.29 300.5 1163 281 1174C261.5 1185 322 1286.5 338.5 1305C346.591 1314.07 299.759 1348.96 252 1388C202.369 1428.57 151.539 1473.46 149.5 1489C145.62 1518.59 304.498 1509.34 313.5 1534C325 1565.5 349.095 1642.86 345 1649C317 1691 282 1702.5 228 1725.5C203.98 1735.73 204.261 1818.88 238.5 1860.5C273.981 1903.63 334.805 1912.57 365 1923C395.195 1933.43 304 2045 304 2045" stroke="white" stroke-width="3" stroke-linecap="round" stroke-dasharray="10 10"/>
|
||||
<rect x="329" y="767" width="88" height="88" fill="url(#pattern0_37_192)"/>
|
||||
<rect x="45" y="934" width="88" height="88" fill="url(#pattern1_37_192)"/>
|
||||
<rect x="147" y="1949" width="88" height="88" fill="url(#pattern2_37_192)"/>
|
||||
<rect x="153" y="334" width="88" height="88" fill="url(#pattern3_37_192)"/>
|
||||
<rect width="85.7722" height="88" transform="matrix(-1 0 0 1 417 1189)" fill="url(#pattern4_37_192)"/>
|
||||
<rect x="49" y="1520" width="88" height="88" fill="url(#pattern5_37_192)"/>
|
||||
<rect x="57" y="1778" width="88" height="88" fill="url(#pattern6_37_192)"/>
|
||||
<rect x="137" y="1115" width="88" height="88" fill="url(#pattern7_37_192)"/>
|
||||
<rect x="362" y="284" width="88" height="88" fill="url(#pattern8_37_192)"/>
|
||||
<rect x="5" y="1245" width="88" height="88" fill="url(#pattern9_37_192)"/>
|
||||
<rect x="181" y="1974" width="88" height="88" fill="url(#pattern10_37_192)"/>
|
||||
<rect x="373" y="1512" width="88" height="88" fill="url(#pattern11_37_192)"/>
|
||||
<g filter="url(#filter0_d_37_192)">
|
||||
<circle cx="230" cy="211" r="25" fill="#A5D6A7"/>
|
||||
<circle cx="230" cy="211" r="25" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
<g filter="url(#filter1_d_37_192)">
|
||||
<circle cx="320" cy="284" r="25" fill="#A5D6A7"/>
|
||||
<circle cx="320" cy="284" r="25" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
<g filter="url(#filter2_d_37_192)">
|
||||
<circle cx="264" cy="397" r="25" fill="#A5D6A7"/>
|
||||
<circle cx="264" cy="397" r="25" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
<g filter="url(#filter3_d_37_192)">
|
||||
<circle cx="342" cy="517" r="25" fill="#A5D6A7"/>
|
||||
<circle cx="342" cy="517" r="25" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
<g filter="url(#filter4_d_37_192)">
|
||||
<circle cx="320" cy="645" r="25" fill="#A5D6A7"/>
|
||||
<circle cx="320" cy="645" r="25" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
<g filter="url(#filter5_d_37_192)">
|
||||
<circle cx="295" cy="773" r="25" fill="#E57373"/>
|
||||
<circle cx="295" cy="773" r="25" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
<g filter="url(#filter6_d_37_192)">
|
||||
<circle cx="239" cy="880" r="25" fill="#38809E"/>
|
||||
<circle cx="239" cy="880" r="25" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
<g filter="url(#filter7_d_37_192)">
|
||||
<circle cx="354" cy="959" r="25" fill="#D9D9D9"/>
|
||||
<circle cx="354" cy="959" r="25" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
<g filter="url(#filter8_d_37_192)">
|
||||
<circle cx="270" cy="1036" r="25" fill="#D9D9D9"/>
|
||||
<circle cx="270" cy="1036" r="25" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
<g filter="url(#filter9_d_37_192)">
|
||||
<circle cx="279" cy="1174" r="25" fill="#D9D9D9"/>
|
||||
<circle cx="279" cy="1174" r="25" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
<g filter="url(#filter10_d_37_192)">
|
||||
<circle cx="337" cy="1302" r="25" fill="#D9D9D9"/>
|
||||
<circle cx="337" cy="1302" r="25" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
<g filter="url(#filter11_d_37_192)">
|
||||
<circle cx="250" cy="1388" r="25" fill="#D9D9D9"/>
|
||||
<circle cx="250" cy="1388" r="25" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
<g filter="url(#filter12_d_37_192)">
|
||||
<circle cx="162" cy="1487" r="25" fill="#D9D9D9"/>
|
||||
<circle cx="162" cy="1487" r="25" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
<g filter="url(#filter13_d_37_192)">
|
||||
<circle cx="306" cy="1531" r="25" fill="#D9D9D9"/>
|
||||
<circle cx="306" cy="1531" r="25" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
<g filter="url(#filter14_d_37_192)">
|
||||
<circle cx="342" cy="1649" r="25" fill="#D9D9D9"/>
|
||||
<circle cx="342" cy="1649" r="25" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
<g filter="url(#filter15_d_37_192)">
|
||||
<circle cx="225" cy="1724" r="25" fill="#D9D9D9"/>
|
||||
<circle cx="225" cy="1724" r="25" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
<g filter="url(#filter16_d_37_192)">
|
||||
<circle cx="216" cy="1822" r="25" fill="#D9D9D9"/>
|
||||
<circle cx="216" cy="1822" r="25" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
<g filter="url(#filter17_d_37_192)">
|
||||
<circle cx="275" cy="1883" r="25" fill="#D9D9D9"/>
|
||||
<circle cx="275" cy="1883" r="25" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
<g filter="url(#filter18_d_37_192)">
|
||||
<circle cx="362" cy="1917" r="25" fill="#D9D9D9"/>
|
||||
<circle cx="362" cy="1917" r="25" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
<g filter="url(#filter19_d_37_192)">
|
||||
<circle cx="300" cy="2037" r="25" fill="#D9D9D9"/>
|
||||
<circle cx="300" cy="2037" r="25" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<pattern id="pattern0_37_192" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||
<use xlink:href="#image0_37_192" transform="scale(0.00195312)"/>
|
||||
</pattern>
|
||||
<pattern id="pattern1_37_192" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||
<use xlink:href="#image1_37_192" transform="scale(0.00195312)"/>
|
||||
</pattern>
|
||||
<pattern id="pattern2_37_192" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||
<use xlink:href="#image2_37_192" transform="scale(0.00195312)"/>
|
||||
</pattern>
|
||||
<pattern id="pattern3_37_192" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||
<use xlink:href="#image3_37_192" transform="scale(0.00195312)"/>
|
||||
</pattern>
|
||||
<pattern id="pattern4_37_192" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||
<use xlink:href="#image4_37_192" transform="scale(0.00177557 0.00173062)"/>
|
||||
</pattern>
|
||||
<pattern id="pattern5_37_192" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||
<use xlink:href="#image5_37_192" transform="scale(0.00195312)"/>
|
||||
</pattern>
|
||||
<pattern id="pattern6_37_192" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||
<use xlink:href="#image6_37_192" transform="scale(0.00195312)"/>
|
||||
</pattern>
|
||||
<pattern id="pattern7_37_192" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||
<use xlink:href="#image7_37_192" transform="scale(0.00195312)"/>
|
||||
</pattern>
|
||||
<pattern id="pattern8_37_192" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||
<use xlink:href="#image8_37_192" transform="scale(0.00195312)"/>
|
||||
</pattern>
|
||||
<pattern id="pattern9_37_192" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||
<use xlink:href="#image9_37_192" transform="scale(0.00195312)"/>
|
||||
</pattern>
|
||||
<pattern id="pattern10_37_192" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||
<use xlink:href="#image10_37_192" transform="scale(0.00195312)"/>
|
||||
</pattern>
|
||||
<pattern id="pattern11_37_192" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||
<use xlink:href="#image11_37_192" transform="scale(0.00195312)"/>
|
||||
</pattern>
|
||||
<filter id="filter0_d_37_192" x="200" y="185" width="60" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_37_192"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_37_192" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter1_d_37_192" x="290" y="258" width="60" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_37_192"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_37_192" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter2_d_37_192" x="234" y="371" width="60" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_37_192"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_37_192" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter3_d_37_192" x="312" y="491" width="60" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_37_192"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_37_192" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter4_d_37_192" x="290" y="619" width="60" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_37_192"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_37_192" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter5_d_37_192" x="265" y="747" width="60" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_37_192"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_37_192" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter6_d_37_192" x="209" y="854" width="60" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_37_192"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_37_192" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter7_d_37_192" x="324" y="933" width="60" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_37_192"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_37_192" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter8_d_37_192" x="240" y="1010" width="60" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_37_192"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_37_192" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter9_d_37_192" x="249" y="1148" width="60" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_37_192"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_37_192" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter10_d_37_192" x="307" y="1276" width="60" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_37_192"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_37_192" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter11_d_37_192" x="220" y="1362" width="60" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_37_192"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_37_192" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter12_d_37_192" x="132" y="1461" width="60" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_37_192"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_37_192" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter13_d_37_192" x="276" y="1505" width="60" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_37_192"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_37_192" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter14_d_37_192" x="312" y="1623" width="60" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_37_192"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_37_192" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter15_d_37_192" x="195" y="1698" width="60" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_37_192"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_37_192" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter16_d_37_192" x="186" y="1796" width="60" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_37_192"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_37_192" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter17_d_37_192" x="245" y="1857" width="60" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_37_192"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_37_192" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter18_d_37_192" x="332" y="1891" width="60" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_37_192"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_37_192" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter19_d_37_192" x="270" y="2011" width="60" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_37_192"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_37_192" result="shape"/>
|
||||
</filter>
|
||||
<image id="image0_37_192" width="512" height="512" preserveAspectRatio="none" xlink:href="data:image/png;base64,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 truncated
|
||||
<image id="image1_37_192" width="512" height="512" preserveAspectRatio="none" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAIABJREFUeJzs3Xd4VFX+P/D3nZbeQ0ISEkLovUnvgrpgw766FrCX1dXdVVd3XeuuFRsqKqBiRbEAdmog1NBrQggQAiGF9J4p9/7+GPmuP0RybjJ37szc9+t55tln5Zx7PklmzvnMvacARERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERGpNA7ACQO0vrxUApuoaEREREWnqCQDK77we1y8sIiIi0sot+P3B/9Trbt2iIyIiIo87H4ADrScATgAX6RQjEREReVBvAFVoffA/9aoF0F+XSImIiMgj4gHkQ3zwP/U6AiBRh3iJiIionYIBbID6wf/UawuAUK9HTURERG0mAfgEbR/8T70WATB5OXYiIiJqo/+g/YP/qdczXo6diIiI2uAmeG7wP/W63as/AREREakyDkAzPJ8A2AGc68Wfg4iIiAR1BVAGzw/+p14VAHp67achIiKiVsUByIOKAT0xLqwsMS5MbcKQ90tbREREpDMrgFVQMZAH2SxNuV/9eU/e1/ftDwmyNqipCyALQJDXfjoi0gSX9xD5NwnAPACThCtIUJa9cf32yLCgfuGh1t4r5ty42yRJsoo2xwL44Je2ichPmfUOgIja5TEA96up8OFT09cM65My7tT/j4kI7tSvW8KaxZm56Sou0w/ucwPWqmmbiHwHEwAi/3UVgDeh4pv4X68bue76aQPHn/7fu3aKTbdZzVnrdhZ2VtH+JLi3Gd6jog4R+QgmAET+aRiAxQBsohUmD8/Y/dL95w8AYDnTv4/o1yk550j57oPHKpMELykBmAZgNYBjonEQkW/gMzwi/5MOYBNUHNbTrVNsYebcGeEmSYo9WzlFQdXkuz6szi042UVFPOUARsF9N4CI/AQnARL5l0gAS6Fi8I+NCq1c8faNcmuDPwBIEmJ+nn29pUN0aIWKmOJ/iSlGRR0i0hkTACL/YQHw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 truncated
|
||||
<image id="image2_37_192" width="512" height="512" preserveAspectRatio="none" xlink:href="data:image/png;base64,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 truncated
|
||||
<image id="image3_37_192" width="512" height="512" preserveAspectRatio="none" xlink:href="data:image/png;base64,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 truncated
|
||||
<image id="image4_37_192" width="512" height="512" preserveAspectRatio="none" xlink:href="data:image/png;base64,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 truncated
|
||||
<image id="image5_37_192" width="512" height="512" preserveAspectRatio="none" xlink:href="data:image/png;base64,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 truncated
|
||||
<image id="image6_37_192" width="512" height="512" preserveAspectRatio="none" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAIABJREFUeJzs3Xd4VNW6BvB3TzLpvZIESAghEELvHVQQFRQ5iHpU5ACCKKIoCiIdBWygYkFBARF7FxQQ6V2Q3jvSCUlII2WSmfvHgnuQA8zaM7Nn9k7e3/Pkuffo2rNXxsysb6/yfQAREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREZUPtwJYAODs5Z8FAG7xaI+IiIhIU8MAWAHYrvmxXv53REREVM7cj+sP/lcHAb081jsiIiJyuVYAinDjwf/KTzGA9h7qIxEREblQAoBzsD/4X/k5CyDeIz0lIiIilzAB+APyg/+VnxUAvNzfXSIiInKF8VA/+F/5GeuB/hIREZGTWgMog+MBQCmAlm7vNRERETnMF8AeOD74X/nZB8DfzX0nIiIiB70J5wf/Kz+T3dx3IiIickBjiOl7VwUAFgAN3PobEBERkSoKgFVw3eB/5WfN5dcmIiIiHeoNyUFdURSroig3ywx47c8jbv1NiIiISEoIgDOQHNCfePHBVQOHPaBmtuA0gGA3/j5EREQkYQIkB/OEqjEn1xz/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 truncated
|
||||
<image id="image7_37_192" width="512" height="512" preserveAspectRatio="none" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAIABJREFUeJzs3Wd4HOW5N/D/7Kr3Xm1JVi+2mi13y3LFdEINBAghtCTUQCBvzkmi5Jzk5ByKaaGkQCABUgihG7DBvduSLNmyrG7L6r233Z33w9AD2meknd3Z1f93Xfsh5N57bsmS5t55GkBERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERENFOSowsgsjE/AKsBFAFYBCAFQBQAI4BeAE0AKgDsAPA2gDZHFElEREQz4wZgGYCfAtgJYAKALPiaBPAalEaBiIiIdC4VwA8A/AtAH8Rv+F/3sgB4CUC4Pb8IIiIimloYgCsAPAugATO/4X/dqwPAWjt9TURERPQl3gDWA/gNgCMAzNDupv/l1ziAS7X/EomIiMgAZRz+xwC2ARiF/W74X/WagNKAEBERkY3NA3ALgL8D6IJjb/hf9eoFEKfZV09ERDRLhAC4DMAzAGrh+Bu8yGubJt8JIiIiF+YJYA2AXwE4BMAEx9/Qp/O6xNbfGCIiIlciAcgGcC+ALQCG4fibtwxA9vJwG40K8+uc5vsP2/bbRESkD9wJkGZiDpTJchsArAMQ6dhyFBHBvj3XnJt96tpzs00LUiISPNyMcwFAhjx07FRb2beLX48pr2lPVJGyAMpqBCIil8EGgNQIgLLF7ic3/XSHVvMxb0/30QtWppz8zsX5g6vy4iL9vD1Soaws+Dqjdz+8peyxVw4uE7zEbwD8v5lXSkSkH2wAaCpuAJZCueGvB7Dk4//mUAaDZFm2YG71dy/Obz9/VYp/RLBvJgAvlWkm133vxeqPDtdnCcQeA5CrvlIiIv1iA0BflgHl0/16KJ/2/R1azceS54ac/fYFuQ1XbchyS5oTkmYwSCEzzdk7OFoRsuZ/FwiEygCiAbTP9JpERHrh8E9z5HBRUMbvP7npxzq2HEVIoE/fVRuzqq47L3tiYXpMvIe7MR7KnAObCfb3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 truncated
|
||||
<image id="image8_37_192" width="512" height="512" preserveAspectRatio="none" xlink:href="data:image/png;base64,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 truncated
|
||||
<image id="image9_37_192" width="512" height="512" preserveAspectRatio="none" xlink:href="data:image/png;base64,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 truncated
|
||||
<image id="image10_37_192" width="512" height="512" preserveAspectRatio="none" xlink:href="data:image/png;base64,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 truncated
|
||||
<image id="image11_37_192" width="512" height="512" preserveAspectRatio="none" xlink:href="data:image/png;base64,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 truncated
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 493 KiB |
Reference in new issue
Block a user