Jamjar/src/components/themes/theme-vote.tsx
2025-02-09 02:39:02 +02:00

234 lines
7.1 KiB
TypeScript

"use client";
import React, { useState, useEffect } from "react";
import { getCookie } from "@/helpers/cookie";
import {
getCurrentJam,
hasJoinedCurrentJam,
ActiveJamResponse,
} from "@/helpers/jam";
import { ThemeType } from "@/types/ThemeType";
import { joinJam } from "@/helpers/jam";
import { getThemeVotes, getTopThemes, postThemeSuggestionVote } from "@/requests/theme";
interface VoteType {
themeSuggestionId: number;
votingScore: number;
}
export default function VotingPage() {
const [themes, setThemes] = useState<ThemeType[]>([]);
const [loading, setLoading] = useState(false);
const [activeJamResponse, setActiveJamResponse] = useState<ActiveJamResponse | null>(null);
const [hasJoined, setHasJoined] = useState<boolean>(false);
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 themes only when phase is "Voting"
useEffect(() => {
// Fetch top N themes with voting scores
async function fetchThemes() {
if (!token || !activeJamResponse) return;
try {
const response = await getTopThemes();
if (response.ok) {
const themes = await response.json();
// Fetch user's votes for these themes
const votesResponse = await getThemeVotes();
if (votesResponse.ok) {
const votes = await votesResponse.json();
// Merge themes with user's votes
const themesWithVotes = themes.map((theme: ThemeType) => {
const vote = votes.find((v: VoteType) => v.themeSuggestionId === theme.id);
return {
...theme,
votingScore: vote ? vote.votingScore : null,
};
});
setThemes(themesWithVotes);
}
} else {
console.error("Failed to fetch themes.");
}
} catch (error) {
console.error("Error fetching themes:", error);
}
}
if (activeJamResponse?.phase === "Voting") {
fetchThemes();
}
}, [activeJamResponse, token]);
// Handle voting
const handleVote = async (themeId: number, votingScore: number) => {
setThemes((prevThemes) =>
prevThemes.map((theme) =>
theme.id === themeId ? { ...theme, loading: true } : theme
)
);
try {
const response = await postThemeSuggestionVote(themeId, votingScore);
if (response.ok) {
setThemes((prevThemes) =>
prevThemes.map((theme) =>
theme.id === themeId
? { ...theme, votingScore, loading: false }
: theme
)
);
} else {
console.error("Failed to submit vote.");
setThemes((prevThemes) =>
prevThemes.map((theme) =>
theme.id === themeId ? { ...theme, loading: false } : theme
)
);
}
} catch (error) {
console.error("Error submitting vote:", error);
setThemes((prevThemes) =>
prevThemes.map((theme) =>
theme.id === themeId ? { ...theme, loading: false } : theme
)
);
}
};
useEffect(() => {
const init = async () => {
const joined = await hasJoinedCurrentJam();
setHasJoined(joined);
setLoading(false);
};
init();
}, []);
if (phaseLoading || loading) {
return <div>Loading...</div>;
}
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>;
}
if (!hasJoined) {
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">
Join the Jam First
</h1>
<p className="text-gray-600 dark:text-gray-400">
You need to join the current jam before you can vote themes.
</p>
<button
onClick={() => {
if (activeJamResponse?.jam?.id !== undefined) {
joinJam(activeJamResponse.jam.id);
}
}}
className="mt-4 px-6 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600"
>
Join Jam
</button>
</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>
);
}