Auto stash before merge of "main" and "origin/main"

This commit is contained in:
boragenel 2025-01-21 11:53:14 +03:00
parent 440b9d685d
commit e2a436bda3
6 changed files with 164 additions and 26 deletions

View file

@ -1,15 +1,56 @@
"use client"
import { Calendar } from "lucide-react";
import { useEffect, useState } from "react";
import { getCurrentJam, ActiveJamResponse } from "../../helpers/jam"
import ThemeSuggestions from "../themes";
export default function JamHeader() {
const [activeJamResponse, setActiveJam] = useState<ActiveJamResponse | null>(null);
useEffect(() => {
const fetchActiveJam = async () => {
const jamData = await getCurrentJam();
setActiveJam(jamData);
};
fetchActiveJam();
}, []);
return (
<div className="bg-[#7090b9] dark:bg-[#124a88] flex rounded-2xl overflow-hidden text-white transition-color duration-250">
<div className="bg-[#85bdd2] dark:bg-[#1892b3] p-4 px-6 flex items-center gap-2 font-bold transition-color duration-250">
<Calendar />
<p>Down2Jam 1</p>
</div>
<div className="p-4 px-6 font-bold">
<p>April 4th - 7th</p>
<div className="bg-[#7090b9] dark:bg-[#124a88] flex flex-col rounded-2xl overflow-hidden text-white transition-color duration-250">
{/* Jam Header */}
<div className="flex">
<div className="bg-[#85bdd2] dark:bg-[#1892b3] p-4 px-6 flex items-center gap-2 font-bold transition-color duration-250">
<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>April 4th - 7th</p>
</div>
</div>
{/* Conditional Link for Suggestion Phase */}
{activeJamResponse?.phase === "Suggestion" && (
<div className="bg-gray-100 dark:bg-gray-800 p-4 text-center rounded-b-2x transition-color duration-250">
<a
href="/theme-suggestion"
className="text-blue-300 dark:text-blue-500 hover:underline font-semibold transition-color duration-250"
>
Go to Theme Suggestion Page
</a>
<ThemeSuggestions />
</div>
)}
</div>
);
}

View file

@ -27,7 +27,8 @@ export default function MobileNavbar() {
useEffect(() => {
loadUser();
async function loadUser() {
const currentJam = await getCurrentJam();
const currentJamResponse = await getCurrentJam();
const currentJam = currentJamResponse?.jam;
setJam(currentJam);
if (!hasCookie("token")) {

View file

@ -24,7 +24,7 @@ import NextImage from "next/image";
import { useEffect, useState } from "react";
import { usePathname } from "next/navigation";
import { getCookie, hasCookie } from "@/helpers/cookie";
import { getCurrentJam, joinJam } from "@/helpers/jam";
import { getCurrentJam, joinJam, ActiveJamResponse } from "@/helpers/jam";
import { JamType } from "@/types/JamType";
import { UserType } from "@/types/UserType";
import NavbarUser from "./PCNavbarUser";
@ -57,7 +57,8 @@ export default function PCNavbar() {
useEffect(() => {
loadUser();
async function loadUser() {
const currentJam = await getCurrentJam();
const jamResponse = await getCurrentJam();
const currentJam = jamResponse?.jam;
setJam(currentJam);
if (!hasCookie("token")) {
@ -141,7 +142,8 @@ export default function PCNavbar() {
icon={<CalendarPlus />}
name="Join jam"
onPress={async () => {
const currentJam = await getCurrentJam();
const currentJamResponse = await getCurrentJam();
const currentJam = currentJamResponse?.jam;
if (!currentJam) {
toast.error("There is no jam to join");

View file

@ -0,0 +1,81 @@
"use client";
import React, { useState } from "react";
export default function ThemeSuggestions() {
const [suggestion, setSuggestion] = useState("");
const [loading, setLoading] = useState(false);
const [successMessage, setSuccessMessage] = useState("");
const [errorMessage, setErrorMessage] = useState("");
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 userId = localStorage.getItem("userId");
const response = await fetch("http://localhost:3005/api/v1/themes/suggestion", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${localStorage.getItem("accessToken")}`,
},
credentials: "include",
body: JSON.stringify({ suggestionText: suggestion, userId }),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || "Failed to submit suggestion.");
}
setSuccessMessage("Suggestion added successfully!");
setSuggestion(""); // Clear the input field
} catch (error: any) {
console.error("Error submitting suggestion:", error.message);
setErrorMessage(error.message || "An unexpected error occurred.");
} finally {
setLoading(false);
}
};
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>
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
<textarea
className="w-full p-3 border rounded-lg focus:outline-none focus:ring focus:ring-blue-300 dark:bg-gray-700 dark:text-white"
placeholder="Enter your theme suggestion..."
value={suggestion}
onChange={(e) => setSuggestion(e.target.value)}
rows={4}
></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>
</div>
);
}

View file

@ -2,6 +2,11 @@ import { JamType } from "@/types/JamType";
import { getCookie } from "./cookie";
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[]> {
const response = await fetch(
process.env.NEXT_PUBLIC_MODE === "PROD"
@ -12,24 +17,29 @@ export async function getJams(): Promise<JamType[]> {
return response.json();
}
export async function getCurrentJam(): Promise<JamType | null> {
const jams = await getJams();
const now = new Date();
export async function getCurrentJam(): Promise<ActiveJamResponse | null> {
try {
const response = await fetch(
process.env.NEXT_PUBLIC_MODE === "PROD"
? "https://d2jam.com/api/v1/jams"
: "http://localhost:3005/api/v1/jams/active"
);
// Get only jams that happen in the future
const futureJams = jams.filter((jam) => new Date(jam.startTime) > now);
// Parse JSON response
const data = await response.json();
// If theres no jams happening returns null
if (futureJams.length === 0) {
return null;
}
// Return the phase and jam details
return {
phase: data.phase,
jam: data.jam,
};
// Sort future jams by startTime (earliest first)
futureJams.sort(
(a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime()
);
} catch (error) {
console.error("Error fetching active jam:", error);
return null;
}
return futureJams[0];
}
export async function joinJam(jamId: number) {

View file

@ -1,8 +1,11 @@
export interface JamType {
id: number;
name: string;
ratingHours: number;
suggestionHours:number;
slaughterHours: number;
votingHours:number;
jammingHours:number;
ratingHours: number;
startTime: Date;
createdAt: Date;
updatedAt: Date;