mirror of
https://github.com/Ategon/Jamjar.git
synced 2025-02-12 06:16:21 +00:00
Compare commits
No commits in common. "1237e8a008cdf527ee2c1bc48608d41302fd2719" and "613704951853fc91bc345c7dbbea061cc18592a2" have entirely different histories.
1237e8a008
...
6137049518
13 changed files with 36 additions and 985 deletions
src
app
components
jam-header
navbar
themes
timers
helpers
types
|
@ -1,18 +0,0 @@
|
||||||
import Timers from "@/components/timers";
|
|
||||||
import Streams from "@/components/streams";
|
|
||||||
import JamHeader from "@/components/jam-header";
|
|
||||||
import ThemeSlaughter from "@/components/themes/theme-slaughter";
|
|
||||||
|
|
||||||
export default async function Home() {
|
|
||||||
return (
|
|
||||||
<div className="flex justify-between flex-wrap">
|
|
||||||
<div className="md:w-2/3">
|
|
||||||
<ThemeSlaughter />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Timers />
|
|
||||||
<Streams />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
import Timers from "@/components/timers";
|
|
||||||
import Streams from "@/components/streams";
|
|
||||||
import JamHeader from "@/components/jam-header";
|
|
||||||
import ThemeSuggestions from "@/components/themes/theme-suggest";
|
|
||||||
|
|
||||||
export default async function Home() {
|
|
||||||
return (
|
|
||||||
<div className="flex justify-between flex-wrap">
|
|
||||||
<div className="md:w-2/3">
|
|
||||||
<ThemeSuggestions />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Timers />
|
|
||||||
<Streams />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
import Timers from "@/components/timers";
|
|
||||||
import Streams from "@/components/streams";
|
|
||||||
import JamHeader from "@/components/jam-header";
|
|
||||||
import ThemeVoting from "@/components/themes/theme-vote";
|
|
||||||
|
|
||||||
export default async function Home() {
|
|
||||||
return (
|
|
||||||
<div className="flex justify-between flex-wrap">
|
|
||||||
<div className="md:w-2/3">
|
|
||||||
<ThemeVoting />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Timers />
|
|
||||||
<Streams />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,150 +1,15 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import { Calendar } from "lucide-react";
|
import { Calendar } from "lucide-react";
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { getCurrentJam, ActiveJamResponse } from "../../helpers/jam";
|
|
||||||
|
|
||||||
export default function JamHeader() {
|
export default function JamHeader() {
|
||||||
const [activeJamResponse, setActiveJamResponse] = useState<ActiveJamResponse | null>(null);
|
|
||||||
const [topTheme, setTopTheme] = useState<string | null>(null);
|
|
||||||
|
|
||||||
// Fetch active jam details
|
|
||||||
useEffect(() => {
|
|
||||||
const fetchData = async () => {
|
|
||||||
const jamData = await getCurrentJam();
|
|
||||||
setActiveJamResponse(jamData);
|
|
||||||
|
|
||||||
// If we're in Jamming phase, fetch top themes and pick the first one
|
|
||||||
if (jamData?.phase === "Jamming" && jamData.jam) {
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
process.env.NEXT_PUBLIC_MODE === "PROD"
|
|
||||||
? "https://d2jam.com/api/v1/themes/top-themes"
|
|
||||||
: "http://localhost:3005/api/v1/themes/top-themes"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
const themes = await response.json();
|
|
||||||
if (themes.length > 0) {
|
|
||||||
setTopTheme(themes[0].suggestion);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.error("Failed to fetch top themes.", response.status);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching top themes:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchData();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
|
|
||||||
// Helper function to get ordinal suffix
|
|
||||||
const getOrdinalSuffix = (day: number): string => {
|
|
||||||
if (day > 3 && day < 21) return "th";
|
|
||||||
switch (day % 10) {
|
|
||||||
case 1: return "st";
|
|
||||||
case 2: return "nd";
|
|
||||||
case 3: return "rd";
|
|
||||||
default: return "th";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-[#7090b9] dark:bg-[#124a88] flex flex-col rounded-2xl overflow-hidden text-white transition-color duration-250">
|
<div className="bg-[#7090b9] dark:bg-[#124a88] flex rounded-2xl overflow-hidden text-white transition-color duration-250">
|
||||||
{/* Jam Header */}
|
<div className="bg-[#85bdd2] dark:bg-[#1892b3] p-4 px-6 flex items-center gap-2 font-bold transition-color duration-250">
|
||||||
<div className="flex">
|
<Calendar />
|
||||||
<div className="bg-[#85bdd2] dark:bg-[#1892b3] p-4 px-6 flex items-center gap-2 font-bold transition-color duration-250">
|
<p>Down2Jam 1</p>
|
||||||
<Calendar />
|
|
||||||
<p>
|
|
||||||
{activeJamResponse?.jam && activeJamResponse.phase ? (
|
|
||||||
<span className="text-sm font-normal">
|
|
||||||
{activeJamResponse.jam.name} - {activeJamResponse.phase} Phase
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<span className="text-sm font-normal">(No Active Jams)</span>
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="p-4 px-6 font-bold">
|
|
||||||
<p>
|
|
||||||
{activeJamResponse?.jam ? (
|
|
||||||
<>
|
|
||||||
{new Date(activeJamResponse.jam.startTime).toLocaleDateString('en-US', {
|
|
||||||
month: 'long',
|
|
||||||
})} {new Date(activeJamResponse.jam.startTime).getDate()}
|
|
||||||
{getOrdinalSuffix(new Date(activeJamResponse.jam.startTime).getDate())}
|
|
||||||
{" - "}
|
|
||||||
{new Date(new Date(activeJamResponse.jam.startTime).getTime() +
|
|
||||||
(activeJamResponse.jam.jammingHours * 60 * 60 * 1000)).toLocaleDateString('en-US', {
|
|
||||||
month: 'long',
|
|
||||||
})} {new Date(new Date(activeJamResponse.jam.startTime).getTime() +
|
|
||||||
(activeJamResponse.jam.jammingHours * 60 * 60 * 1000)).getDate()}
|
|
||||||
{getOrdinalSuffix(new Date(new Date(activeJamResponse.jam.startTime).getTime() +
|
|
||||||
(activeJamResponse.jam.jammingHours * 60 * 60 * 1000)).getDate())}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
"Dates TBA"
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="p-4 px-6 font-bold">
|
||||||
|
<p>April 4th - 7th</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Phase-Specific Display */}
|
|
||||||
{activeJamResponse?.phase === "Suggestion" && (
|
|
||||||
<div className="bg-gray-100 dark:bg-gray-800 p-4 text-center rounded-b-2x">
|
|
||||||
<a
|
|
||||||
href="/theme-suggestions"
|
|
||||||
className="text-blue-300 dark:text-blue-500 hover:underline font-semibold"
|
|
||||||
>
|
|
||||||
Go to Theme Suggestion
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{activeJamResponse?.phase === "Survival" && (
|
|
||||||
<div className="bg-gray-100 dark:bg-gray-800 p-4 text-center rounded-b-2x">
|
|
||||||
<a
|
|
||||||
href="/theme-slaughter"
|
|
||||||
className="text-blue-300 dark:text-blue-500 hover:underline font-semibold"
|
|
||||||
>
|
|
||||||
Go to Theme Survival
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{activeJamResponse?.phase === "Voting" && (
|
|
||||||
<div className="bg-gray-100 dark:bg-gray-800 p-4 text-center rounded-b-2x">
|
|
||||||
<a
|
|
||||||
href="/theme-voting"
|
|
||||||
className="text-blue-300 dark:text-blue-500 hover:underline font-semibold"
|
|
||||||
>
|
|
||||||
Go to Theme Voting
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{activeJamResponse?.phase === "Jamming" && (
|
|
||||||
<div className="bg-gray-100 dark:bg-gray-800 p-4 text-center rounded-b-2x">
|
|
||||||
{topTheme ? (
|
|
||||||
<p className="text-xl font-bold text-blue-500">THEME: {topTheme}</p>
|
|
||||||
) : (
|
|
||||||
<p>No top-scoring theme available.</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{activeJamResponse?.phase === "Rating" && (
|
|
||||||
<div className="bg-gray-100 dark:bg-gray-800 p-4 text-center rounded-b-2x">
|
|
||||||
{topTheme ? (
|
|
||||||
<p className="text-xl font-bold text-blue-500">THEME: {topTheme} RESULTS</p>
|
|
||||||
) : (
|
|
||||||
<p>No top-scoring theme available.</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,8 +27,7 @@ export default function MobileNavbar() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadUser();
|
loadUser();
|
||||||
async function loadUser() {
|
async function loadUser() {
|
||||||
const currentJamResponse = await getCurrentJam();
|
const currentJam = await getCurrentJam();
|
||||||
const currentJam = currentJamResponse?.jam;
|
|
||||||
setJam(currentJam);
|
setJam(currentJam);
|
||||||
|
|
||||||
if (!hasCookie("token")) {
|
if (!hasCookie("token")) {
|
||||||
|
|
|
@ -24,7 +24,7 @@ import NextImage from "next/image";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
import { getCookie, hasCookie } from "@/helpers/cookie";
|
import { getCookie, hasCookie } from "@/helpers/cookie";
|
||||||
import { getCurrentJam, joinJam, ActiveJamResponse } from "@/helpers/jam";
|
import { getCurrentJam, joinJam } from "@/helpers/jam";
|
||||||
import { JamType } from "@/types/JamType";
|
import { JamType } from "@/types/JamType";
|
||||||
import { UserType } from "@/types/UserType";
|
import { UserType } from "@/types/UserType";
|
||||||
import NavbarUser from "./PCNavbarUser";
|
import NavbarUser from "./PCNavbarUser";
|
||||||
|
@ -57,8 +57,7 @@ export default function PCNavbar() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadUser();
|
loadUser();
|
||||||
async function loadUser() {
|
async function loadUser() {
|
||||||
const jamResponse = await getCurrentJam();
|
const currentJam = await getCurrentJam();
|
||||||
const currentJam = jamResponse?.jam;
|
|
||||||
setJam(currentJam);
|
setJam(currentJam);
|
||||||
|
|
||||||
if (!hasCookie("token")) {
|
if (!hasCookie("token")) {
|
||||||
|
@ -142,8 +141,7 @@ export default function PCNavbar() {
|
||||||
icon={<CalendarPlus />}
|
icon={<CalendarPlus />}
|
||||||
name="Join jam"
|
name="Join jam"
|
||||||
onPress={async () => {
|
onPress={async () => {
|
||||||
const currentJamResponse = await getCurrentJam();
|
const currentJam = await getCurrentJam();
|
||||||
const currentJam = currentJamResponse?.jam;
|
|
||||||
|
|
||||||
if (!currentJam) {
|
if (!currentJam) {
|
||||||
toast.error("There is no jam to join");
|
toast.error("There is no jam to join");
|
||||||
|
|
|
@ -1,249 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import React, { useState, useEffect } from "react";
|
|
||||||
import { getCookie } from "@/helpers/cookie";
|
|
||||||
import { getCurrentJam,ActiveJamResponse } from "@/helpers/jam";
|
|
||||||
|
|
||||||
export default function ThemeSlaughter() {
|
|
||||||
|
|
||||||
const [randomTheme, setRandomTheme] = useState(null);
|
|
||||||
const [votedThemes, setVotedThemes] = useState([]);
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [token, setToken] = useState(null); // Store token after fetching it on the client
|
|
||||||
const [activeJamResponse, setActiveJam] = useState<ActiveJamResponse | null>(null);
|
|
||||||
const [phaseLoading, setPhaseLoading] = useState(true); // Loading state for fetching phase
|
|
||||||
|
|
||||||
// Fetch token on the client side
|
|
||||||
useEffect(() => {
|
|
||||||
const fetchedToken = getCookie("token");
|
|
||||||
setToken(fetchedToken);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Fetch the current jam phase using helpers/jam
|
|
||||||
useEffect(() => {
|
|
||||||
const fetchCurrentJamPhase = async () => {
|
|
||||||
try {
|
|
||||||
const activeJam = await getCurrentJam();
|
|
||||||
setActiveJam(activeJam); // Set active jam details
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching current jam:", error);
|
|
||||||
} finally {
|
|
||||||
setPhaseLoading(false); // Stop loading when phase is fetched
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchCurrentJamPhase();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
|
|
||||||
// Fetch a random theme
|
|
||||||
const fetchRandomTheme = async () => {
|
|
||||||
if (!token) return; // Wait until token is available
|
|
||||||
if( !activeJamResponse) return;
|
|
||||||
if(activeJamResponse && activeJamResponse.jam && activeJamResponse.phase != "Survival")
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h1>It's not Theme Survival phase.</h1>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
process.env.NEXT_PUBLIC_MODE === "PROD"
|
|
||||||
? "https://d2jam.com/api/v1/themes/random"
|
|
||||||
: "http://localhost:3005/api/v1/themes/random",
|
|
||||||
{
|
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
|
||||||
credentials: "include",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (response.ok) {
|
|
||||||
const data = await response.json();
|
|
||||||
setRandomTheme(data);
|
|
||||||
} else {
|
|
||||||
console.error("Failed to fetch random theme.");
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching random theme:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Fetch voted themes
|
|
||||||
const fetchVotedThemes = async () => {
|
|
||||||
if (!token) return; // Wait until token is available
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
process.env.NEXT_PUBLIC_MODE === "PROD"
|
|
||||||
? "https://d2jam.com/api/v1/themes/voteSlaughter"
|
|
||||||
: "http://localhost:3005/api/v1/themes/voteSlaughter",
|
|
||||||
{
|
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
|
||||||
credentials: "include",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (response.ok) {
|
|
||||||
const data = await response.json();
|
|
||||||
setVotedThemes(data);
|
|
||||||
} else {
|
|
||||||
console.error("Failed to fetch voted themes.");
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching voted themes:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle voting
|
|
||||||
const handleVote = async (voteType) => {
|
|
||||||
if (!randomTheme) return;
|
|
||||||
|
|
||||||
setLoading(true);
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
process.env.NEXT_PUBLIC_MODE === "PROD"
|
|
||||||
? "https://d2jam.com/api/v1/themes/voteSlaughter"
|
|
||||||
: "http://localhost:3005/api/v1/themes/voteSlaughter",
|
|
||||||
{
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
credentials: "include",
|
|
||||||
body: JSON.stringify({
|
|
||||||
suggestionId: randomTheme.id,
|
|
||||||
voteType,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
// Refresh data after voting
|
|
||||||
fetchRandomTheme();
|
|
||||||
fetchVotedThemes();
|
|
||||||
} else {
|
|
||||||
console.error("Failed to submit vote.");
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error submitting vote:", error);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle resetting a vote from the grid
|
|
||||||
const handleResetVote = async (themeId) => {
|
|
||||||
try {
|
|
||||||
setRandomTheme(votedThemes.find((theme) => theme.id === themeId));
|
|
||||||
setVotedThemes((prev) =>
|
|
||||||
prev.map((theme) =>
|
|
||||||
theme.id === themeId ? { ...theme, slaughterScore: 0 } : theme
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error resetting vote:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (token && activeJamResponse?.phase === "Survival") {
|
|
||||||
fetchRandomTheme();
|
|
||||||
fetchVotedThemes();
|
|
||||||
}
|
|
||||||
}, [token, activeJamResponse]);
|
|
||||||
|
|
||||||
// Render loading state while fetching phase
|
|
||||||
if (phaseLoading) {
|
|
||||||
return <div>Loading...</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render message if not in Theme Slaughter phase
|
|
||||||
if (activeJamResponse?.phase !== "Survival") {
|
|
||||||
return (
|
|
||||||
<div className="p-6 bg-gray-100 dark:bg-gray-800 min-h-screen">
|
|
||||||
<h1 className="text-2xl font-bold text-gray-800 dark:text-white mb-6">
|
|
||||||
Not in Theme Slaughter Phase
|
|
||||||
</h1>
|
|
||||||
<p className="text-gray-600 dark:text-gray-400">
|
|
||||||
The current phase is <strong>{activeJamResponse?.phase || "Unknown"}</strong>. Please come back during the Theme Slaughter phase.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const loggedIn = getCookie("token");
|
|
||||||
|
|
||||||
if (!loggedIn) {
|
|
||||||
return (
|
|
||||||
<div>Sign in to be able to join the Theme Survival</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex h-screen">
|
|
||||||
{/* Left Side */}
|
|
||||||
<div className="w-1/2 p-6 bg-gray-100 dark:bg-gray-800 flex flex-col justify-start items-center">
|
|
||||||
{randomTheme ? (
|
|
||||||
<>
|
|
||||||
<h2 className="text-2xl font-bold text-gray-800 dark:text-white mb-4">
|
|
||||||
{randomTheme.suggestion}
|
|
||||||
</h2>
|
|
||||||
<div className="flex gap-4">
|
|
||||||
<button
|
|
||||||
onClick={() => handleVote("YES")}
|
|
||||||
className="px-6 py-3 bg-green-500 text-white font-bold rounded-lg hover:bg-green-600"
|
|
||||||
disabled={loading}
|
|
||||||
>
|
|
||||||
YES
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => handleVote("NO")}
|
|
||||||
className="px-6 py-3 bg-red-500 text-white font-bold rounded-lg hover:bg-red-600"
|
|
||||||
disabled={loading}
|
|
||||||
>
|
|
||||||
NO
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => handleVote("SKIP")}
|
|
||||||
className="px-6 py-3 bg-gray-500 text-white font-bold rounded-lg hover:bg-gray-600"
|
|
||||||
disabled={loading}
|
|
||||||
>
|
|
||||||
SKIP
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<p className="text-gray-600 dark:text-gray-400">No themes available.</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Right Side */}
|
|
||||||
<div className="w-1/2 p-6 bg-white dark:bg-gray-900 overflow-y-auto">
|
|
||||||
<h3 className="text-xl font-bold text-gray-800 dark:text-white mb-4">
|
|
||||||
Your Votes
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-4 gap-4">
|
|
||||||
{votedThemes.map((theme) => (
|
|
||||||
<div
|
|
||||||
key={theme.id}
|
|
||||||
onClick={() => handleResetVote(theme.id)}
|
|
||||||
className={`p-4 rounded-lg cursor-pointer ${
|
|
||||||
theme.slaughterScore > 0
|
|
||||||
? "bg-green-500 text-white"
|
|
||||||
: theme.slaughterScore < 0
|
|
||||||
? "bg-red-500 text-white"
|
|
||||||
: "bg-gray-300 text-black"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{theme.suggestion}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,241 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import React, { useState, useEffect } from "react";
|
|
||||||
import { getCookie } from "@/helpers/cookie";
|
|
||||||
import { getCurrentJam, ActiveJamResponse } from "@/helpers/jam";
|
|
||||||
|
|
||||||
export default function ThemeSuggestions() {
|
|
||||||
const [suggestion, setSuggestion] = useState("");
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [successMessage, setSuccessMessage] = useState("");
|
|
||||||
const [errorMessage, setErrorMessage] = useState("");
|
|
||||||
const [userSuggestions, setUserSuggestions] = useState([]);
|
|
||||||
const [themeLimit, setThemeLimit] = useState(0);
|
|
||||||
const [activeJamResponse, setActiveJamResponse] = useState<ActiveJamResponse | null>(null);
|
|
||||||
const [phaseLoading, setPhaseLoading] = useState(true); // Loading state for fetching phase
|
|
||||||
|
|
||||||
// Fetch the current jam phase using helpers/jam
|
|
||||||
useEffect(() => {
|
|
||||||
const fetchCurrentJamPhase = async () => {
|
|
||||||
try {
|
|
||||||
const activeJam = await getCurrentJam();
|
|
||||||
setActiveJamResponse(activeJam); // Set active jam details
|
|
||||||
if (activeJam?.jam) {
|
|
||||||
setThemeLimit(activeJam.jam.themePerUser || Infinity); // Set theme limit
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching current jam:", error);
|
|
||||||
} finally {
|
|
||||||
setPhaseLoading(false); // Stop loading when phase is fetched
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchCurrentJamPhase();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Fetch all suggestions for the logged-in user
|
|
||||||
const fetchSuggestions = async () => {
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
process.env.NEXT_PUBLIC_MODE === "PROD"
|
|
||||||
? "https://d2jam.com/api/v1/themes/suggestion"
|
|
||||||
: "http://localhost:3005/api/v1/themes/suggestion",
|
|
||||||
{
|
|
||||||
headers: { Authorization: `Bearer ${getCookie("token")}` },
|
|
||||||
credentials: "include",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (response.ok) {
|
|
||||||
const data = await response.json();
|
|
||||||
setUserSuggestions(data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching suggestions:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Fetch suggestions only when phase is "Suggestion"
|
|
||||||
useEffect(() => {
|
|
||||||
if (activeJamResponse?.phase === "Suggestion") {
|
|
||||||
fetchSuggestions();
|
|
||||||
}
|
|
||||||
}, [activeJamResponse]);
|
|
||||||
|
|
||||||
// Handle form submission to add a new suggestion
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
setLoading(true);
|
|
||||||
setSuccessMessage("");
|
|
||||||
setErrorMessage("");
|
|
||||||
|
|
||||||
if (!suggestion.trim()) {
|
|
||||||
setErrorMessage("Suggestion cannot be empty.");
|
|
||||||
setLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const token = getCookie("token");
|
|
||||||
|
|
||||||
if (!token) {
|
|
||||||
throw new Error("User is not authenticated. Please log in.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await fetch(
|
|
||||||
process.env.NEXT_PUBLIC_MODE === "PROD"
|
|
||||||
? "https://d2jam.com/api/v1/themes/suggestion"
|
|
||||||
: "http://localhost:3005/api/v1/themes/suggestion",
|
|
||||||
{
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
credentials: "include",
|
|
||||||
body: JSON.stringify({ suggestionText: suggestion }),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const errorData = await response.json();
|
|
||||||
throw new Error(errorData.error || "Failed to submit suggestion.");
|
|
||||||
}
|
|
||||||
|
|
||||||
setSuccessMessage("Suggestion added successfully!");
|
|
||||||
setSuggestion(""); // Clear input field
|
|
||||||
fetchSuggestions(); // Refresh suggestions list
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error("Error submitting suggestion:", error.message);
|
|
||||||
setErrorMessage(error.message || "An unexpected error occurred.");
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle deleting a suggestion
|
|
||||||
const handleDelete = async (id: number) => {
|
|
||||||
try {
|
|
||||||
const token = getCookie("token");
|
|
||||||
|
|
||||||
const response = await fetch(
|
|
||||||
process.env.NEXT_PUBLIC_MODE === "PROD"
|
|
||||||
? `https://d2jam.com/api/v1/themes/suggestion/${id}`
|
|
||||||
: `http://localhost:3005/api/v1/themes/suggestion/${id}`,
|
|
||||||
{
|
|
||||||
method: "DELETE",
|
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
|
||||||
credentials: "include",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error("Failed to delete suggestion.");
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchSuggestions(); // Refresh suggestions list
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error deleting suggestion:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Render loading state while fetching phase
|
|
||||||
if (phaseLoading) {
|
|
||||||
return <div>Loading...</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const token = getCookie("token");
|
|
||||||
|
|
||||||
if (!token) {
|
|
||||||
return (
|
|
||||||
<div>Sign in to be able to suggest themes</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render message if not in Suggestion phase
|
|
||||||
if (activeJamResponse?.phase !== "Suggestion") {
|
|
||||||
return (
|
|
||||||
<div className="p-6 bg-gray-100 dark:bg-gray-800 min-h-screen">
|
|
||||||
<h1 className="text-2xl font-bold text-gray-800 dark:text-white mb-6">
|
|
||||||
Not in Suggestion Phase
|
|
||||||
</h1>
|
|
||||||
<p className="text-gray-600 dark:text-gray-400">
|
|
||||||
The current phase is <strong>{activeJamResponse?.phase || "Unknown"}</strong>. Please come back during the Suggestion phase.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="max-w-md mx-auto mt-8 p-6 bg-white dark:bg-gray-800 rounded-lg shadow-md">
|
|
||||||
<h2 className="text-xl font-bold text-gray-800 dark:text-white mb-4">
|
|
||||||
Submit Your Theme Suggestion
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
{/* Hide form if user has reached their limit */}
|
|
||||||
{userSuggestions.length < themeLimit ? (
|
|
||||||
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
|
|
||||||
<textarea
|
|
||||||
className="w-full p-3 border rounded-lg focus:outline-none text-gray-600 focus:ring focus:ring-blue-300 dark:bg-gray-700 dark:text-white"
|
|
||||||
placeholder="Enter your theme suggestion..."
|
|
||||||
value={suggestion}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (e.target.value.length <= 32) {
|
|
||||||
setSuggestion(e.target.value);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
rows={1}
|
|
||||||
maxLength={32}
|
|
||||||
></textarea>
|
|
||||||
{errorMessage && (
|
|
||||||
<p className="text-red-500 text-sm">{errorMessage}</p>
|
|
||||||
)}
|
|
||||||
{successMessage && (
|
|
||||||
<p className="text-green-500 text-sm">{successMessage}</p>
|
|
||||||
)}
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className={`w-full py-2 px-4 bg-blue-500 text-white font-semibold rounded-lg shadow-md hover:bg-blue-600 focus:outline-none focus:ring focus:ring-blue-300 ${
|
|
||||||
loading ? "opacity-50 cursor-not-allowed" : ""
|
|
||||||
}`}
|
|
||||||
disabled={loading}
|
|
||||||
>
|
|
||||||
{loading ? "Submitting..." : "Submit Suggestion"}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
) : (
|
|
||||||
<p className="text-yellow-500 text-sm">
|
|
||||||
You've reached your theme suggestion limit for this jam!
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* List of user's suggestions */}
|
|
||||||
<div className="mt-6">
|
|
||||||
<h3 className="text-lg font-semibold text-gray-800 dark:text-white mb-4">
|
|
||||||
Your Suggestions
|
|
||||||
</h3>
|
|
||||||
{userSuggestions.length > 0 ? (
|
|
||||||
<ul className="space-y-4">
|
|
||||||
{userSuggestions.map((suggestion) => (
|
|
||||||
<li
|
|
||||||
key={suggestion.id}
|
|
||||||
className="flex justify-between items-center text-gray-400 bg-gray-100 dark:bg-gray-700 p-3 rounded-lg shadow-sm"
|
|
||||||
>
|
|
||||||
<span>{suggestion.suggestion}</span>
|
|
||||||
<button
|
|
||||||
onClick={() => handleDelete(suggestion.id)}
|
|
||||||
className="text-red-500 hover:text-red-700 font-semibold"
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
) : (
|
|
||||||
<p className="text-gray-600 dark:text-gray-400">
|
|
||||||
You haven't submitted any suggestions yet.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,212 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import React, { useState, useEffect } from "react";
|
|
||||||
import { getCookie } from "@/helpers/cookie";
|
|
||||||
import { getCurrentJam, ActiveJamResponse } from "@/helpers/jam";
|
|
||||||
|
|
||||||
export default function VotingPage() {
|
|
||||||
const [themes, setThemes] = useState([]);
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [activeJamResponse, setActiveJamResponse] = useState<ActiveJamResponse | null>(null);
|
|
||||||
const [phaseLoading, setPhaseLoading] = useState(true); // Loading state for fetching phase
|
|
||||||
const token = getCookie("token");
|
|
||||||
|
|
||||||
// Fetch the current jam phase using helpers/jam
|
|
||||||
useEffect(() => {
|
|
||||||
const fetchCurrentJamPhase = async () => {
|
|
||||||
try {
|
|
||||||
const activeJam = await getCurrentJam();
|
|
||||||
setActiveJamResponse(activeJam); // Set active jam details
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching current jam:", error);
|
|
||||||
} finally {
|
|
||||||
setPhaseLoading(false); // Stop loading when phase is fetched
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchCurrentJamPhase();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Fetch top N themes with voting scores
|
|
||||||
const fetchThemes = async () => {
|
|
||||||
if (!token || !activeJamResponse) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
process.env.NEXT_PUBLIC_MODE === "PROD"
|
|
||||||
? "https://d2jam.com/api/v1/themes/top-themes"
|
|
||||||
: "http://localhost:3005/api/v1/themes/top-themes",
|
|
||||||
{
|
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
|
||||||
credentials: "include",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (response.ok) {
|
|
||||||
const themes = await response.json();
|
|
||||||
console.log("Fetched themes:", themes); // Debug log
|
|
||||||
|
|
||||||
// Fetch user's votes for these themes
|
|
||||||
const votesResponse = await fetch(
|
|
||||||
process.env.NEXT_PUBLIC_MODE === "PROD"
|
|
||||||
? "https://d2jam.com/api/v1/themes/votes"
|
|
||||||
: "http://localhost:3005/api/v1/themes/votes",
|
|
||||||
{
|
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
|
||||||
credentials: "include",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (votesResponse.ok) {
|
|
||||||
const votes = await votesResponse.json();
|
|
||||||
console.log("Fetched votes:", votes); // Debug log
|
|
||||||
|
|
||||||
// Merge themes with user's votes
|
|
||||||
const themesWithVotes = themes.map(theme => {
|
|
||||||
const vote = votes.find(v => v.themeSuggestionId === theme.id);
|
|
||||||
console.log(`Theme ${theme.id} vote:`, vote); // Debug log
|
|
||||||
return {
|
|
||||||
...theme,
|
|
||||||
votingScore: vote ? vote.votingScore : null
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("Themes with votes:", themesWithVotes); // Debug log
|
|
||||||
setThemes(themesWithVotes);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.error("Failed to fetch themes.");
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching themes:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Fetch themes only when phase is "Voting"
|
|
||||||
useEffect(() => {
|
|
||||||
if (activeJamResponse?.phase === "Voting") {
|
|
||||||
fetchThemes();
|
|
||||||
}
|
|
||||||
}, [activeJamResponse]);
|
|
||||||
|
|
||||||
// Handle voting
|
|
||||||
const handleVote = async (themeId, votingScore) => {
|
|
||||||
setLoading(true);
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
process.env.NEXT_PUBLIC_MODE === "PROD"
|
|
||||||
? "https://d2jam.com/api/v1/themes/vote"
|
|
||||||
: "http://localhost:3005/api/v1/themes/vote",
|
|
||||||
{
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
credentials: "include",
|
|
||||||
body: JSON.stringify({ suggestionId: themeId, votingScore }),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
// Just update the local state instead of re-fetching all themes
|
|
||||||
setThemes((prevThemes) =>
|
|
||||||
prevThemes.map((theme) =>
|
|
||||||
theme.id === themeId
|
|
||||||
? { ...theme, votingScore }
|
|
||||||
: theme
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
console.error("Failed to submit vote.");
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error submitting vote:", error);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Render loading state while fetching phase
|
|
||||||
if (phaseLoading) {
|
|
||||||
return <div>Loading...</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render message if not in Voting phase
|
|
||||||
if (activeJamResponse?.phase !== "Voting") {
|
|
||||||
return (
|
|
||||||
<div className="p-4 bg-gray-100 dark:bg-gray-800 min-h-screen">
|
|
||||||
<h1 className="text-2xl font-bold text-gray-800 dark:text-white mb-4">
|
|
||||||
Not in Voting Phase
|
|
||||||
</h1>
|
|
||||||
<p className="text-gray-600 dark:text-gray-400">
|
|
||||||
The current phase is <strong>{activeJamResponse?.phase || "Unknown"}</strong>. Please come back during the Voting phase.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const loggedIn = getCookie("token");
|
|
||||||
|
|
||||||
if (!loggedIn) {
|
|
||||||
return (
|
|
||||||
<div>Sign in to be able to vote</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-3 bg-gray-100 dark:bg-gray-800 min-h-screen">
|
|
||||||
<h1 className="text-2xl font-bold text-gray-800 dark:text-white mb-4">
|
|
||||||
Voting Phase
|
|
||||||
</h1>
|
|
||||||
<div className="space-y-2">
|
|
||||||
{themes.map((theme) => (
|
|
||||||
<div
|
|
||||||
key={theme.id}
|
|
||||||
className="p-3 bg-white dark:bg-gray-900 rounded-lg shadow-md flex items-center"
|
|
||||||
>
|
|
||||||
{/* Voting Buttons */}
|
|
||||||
<div className="flex gap-1 mr-4">
|
|
||||||
<button
|
|
||||||
onClick={() => handleVote(theme.id, -1)}
|
|
||||||
className={`px-3 py-2 rounded-lg ${
|
|
||||||
theme.votingScore === -1
|
|
||||||
? "bg-red-500 text-white"
|
|
||||||
: "bg-gray-300 text-black hover:bg-red-500 hover:text-white"
|
|
||||||
}`}
|
|
||||||
disabled={loading}
|
|
||||||
>
|
|
||||||
-1
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => handleVote(theme.id, 0)}
|
|
||||||
className={`px-3 py-2 rounded-lg ${
|
|
||||||
theme.votingScore === 0
|
|
||||||
? "bg-gray-500 text-white"
|
|
||||||
: "bg-gray-300 text-black hover:bg-gray-500 hover:text-white"
|
|
||||||
}`}
|
|
||||||
disabled={loading}
|
|
||||||
>
|
|
||||||
0
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => handleVote(theme.id, +1)}
|
|
||||||
className={`px-3 py-2 rounded-lg ${
|
|
||||||
theme.votingScore === +1
|
|
||||||
? "bg-green-500 text-white"
|
|
||||||
: "bg-gray-300 text-black hover:bg-green-500 hover:text-white"
|
|
||||||
}`}
|
|
||||||
disabled={loading}
|
|
||||||
>
|
|
||||||
+1
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Theme Suggestion */}
|
|
||||||
<div className="text-gray-800 dark:text-white">{theme.suggestion}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,54 +1,15 @@
|
||||||
"use client"
|
|
||||||
import { Spacer } from "@nextui-org/react";
|
import { Spacer } from "@nextui-org/react";
|
||||||
import React, { useState, useEffect } from "react";
|
|
||||||
import Timer from "./Timer";
|
import Timer from "./Timer";
|
||||||
import { getCurrentJam, ActiveJamResponse } from "@/helpers/jam";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default function Timers() {
|
export default function Timers() {
|
||||||
|
return (
|
||||||
const [activeJamResponse, setActiveJamResponse] = useState<ActiveJamResponse | null>(null);
|
<div className="text-[#333] dark:text-white transition-color duration-250">
|
||||||
const [phaseLoading, setPhaseLoading] = useState(true); // Loading state for fetching phase
|
<Timer
|
||||||
|
name="Jam Start"
|
||||||
// Fetch the current jam phase using helpers/jam
|
targetDate={new Date("2025-04-04T18:00:00-05:00")}
|
||||||
useEffect(() => {
|
/>
|
||||||
const fetchCurrentJamPhase = async () => {
|
<Spacer y={8} />
|
||||||
try {
|
<p>Site under construction</p>
|
||||||
const activeJam = await getCurrentJam();
|
</div>
|
||||||
setActiveJamResponse(activeJam); // Set active jam details
|
);
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching current jam:", error);
|
|
||||||
} finally {
|
|
||||||
setPhaseLoading(false); // Stop loading when phase is fetched
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchCurrentJamPhase();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if(activeJamResponse && activeJamResponse.jam)
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
<div className="text-[#333] dark:text-white transition-color duration-250">
|
|
||||||
<Timer
|
|
||||||
name="Jam Start"
|
|
||||||
targetDate={new Date(activeJamResponse.jam.startTime)}
|
|
||||||
/>
|
|
||||||
<Spacer y={8} />
|
|
||||||
<p>Site under construction</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
<div className="text-[#333] dark:text-white transition-color duration-250">
|
|
||||||
No upcoming jams
|
|
||||||
<Spacer y={8} />
|
|
||||||
<p>Site under construction</p>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,6 @@ export function getCookies() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCookie(cookie: string) {
|
export function getCookie(cookie: string) {
|
||||||
if (typeof document === "undefined") {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const pairs = document.cookie.split(";");
|
const pairs = document.cookie.split(";");
|
||||||
for (let i = 0; i < pairs.length; i++) {
|
for (let i = 0; i < pairs.length; i++) {
|
||||||
const pair = pairs[i].trim().split("=");
|
const pair = pairs[i].trim().split("=");
|
||||||
|
|
|
@ -2,11 +2,6 @@ import { JamType } from "@/types/JamType";
|
||||||
import { getCookie } from "./cookie";
|
import { getCookie } from "./cookie";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
|
|
||||||
export interface ActiveJamResponse {
|
|
||||||
phase: string;
|
|
||||||
jam: JamType | null; // Jam will be null if no active jam is found
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getJams(): Promise<JamType[]> {
|
export async function getJams(): Promise<JamType[]> {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
process.env.NEXT_PUBLIC_MODE === "PROD"
|
process.env.NEXT_PUBLIC_MODE === "PROD"
|
||||||
|
@ -17,29 +12,24 @@ export async function getJams(): Promise<JamType[]> {
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCurrentJam(): Promise<ActiveJamResponse | null> {
|
export async function getCurrentJam(): Promise<JamType | null> {
|
||||||
|
const jams = await getJams();
|
||||||
try {
|
const now = new Date();
|
||||||
const response = await fetch(
|
|
||||||
process.env.NEXT_PUBLIC_MODE === "PROD"
|
|
||||||
? "https://d2jam.com/api/v1/jams"
|
|
||||||
: "http://localhost:3005/api/v1/jams/active"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Parse JSON response
|
// Get only jams that happen in the future
|
||||||
const data = await response.json();
|
const futureJams = jams.filter((jam) => new Date(jam.startTime) > now);
|
||||||
|
|
||||||
// Return the phase and jam details
|
// If theres no jams happening returns null
|
||||||
return {
|
if (futureJams.length === 0) {
|
||||||
phase: data.phase,
|
return null;
|
||||||
jam: data.jam,
|
}
|
||||||
};
|
|
||||||
|
|
||||||
} catch (error) {
|
// Sort future jams by startTime (earliest first)
|
||||||
console.error("Error fetching active jam:", error);
|
futureJams.sort(
|
||||||
return null;
|
(a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime()
|
||||||
}
|
);
|
||||||
|
|
||||||
|
return futureJams[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function joinJam(jamId: number) {
|
export async function joinJam(jamId: number) {
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
export interface JamType {
|
export interface JamType {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
suggestionHours:number;
|
|
||||||
slaughterHours: number;
|
|
||||||
votingHours:number;
|
|
||||||
jammingHours:number;
|
|
||||||
ratingHours: number;
|
ratingHours: number;
|
||||||
|
slaughterHours: number;
|
||||||
startTime: Date;
|
startTime: Date;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
|
|
Loading…
Reference in a new issue