From 78efa373299b90f9294dfb77502ac0c965d108ff Mon Sep 17 00:00:00 2001 From: Jaswanth Kumar Date: Wed, 17 Jun 2026 21:11:25 +0530 Subject: [PATCH 1/2] Modified POTD Section --- src/pages/potd.tsx | 766 +++++++++++++++++++++++---------------------- 1 file changed, 391 insertions(+), 375 deletions(-) diff --git a/src/pages/potd.tsx b/src/pages/potd.tsx index 1da4dc6..3e190e3 100644 --- a/src/pages/potd.tsx +++ b/src/pages/potd.tsx @@ -1,407 +1,423 @@ -import React, { useEffect, useState, useCallback } from "react"; -import { getPOTD } from "../services/potd_fetch"; -import { doc, getDoc, setDoc, onSnapshot, collection, query, where, runTransaction } from "firebase/firestore"; -import { onAuthStateChanged } from "firebase/auth"; // MUST IMPORT THIS -import { db, auth } from "../lib/firebase"; -import styles from "../styles/POTDpage.module.css"; -import Image from "next/image"; -import { formatDescription } from "../utils/formatDescription"; -import { toast } from "sonner"; -import { checkCodeforcesSubmission } from "../services/codeforces_api"; -import { FiCheck, FiAward, FiExternalLink, FiZap, FiTarget, FiTrendingUp } from "react-icons/fi"; -import { BsLightningCharge } from "react-icons/bs"; - -interface Solver { - uid: string; - username: string; - solvedAt: string | Date; - codeforcesUsername?: string; - isFallback?: boolean; -} - -interface Problem { - id: string; - title: string; - description?: string; - rating?: number; - tags?: string[]; - problemUrl?: string; -} - -interface UserData { - uid?: string; - coins: number; - totalCoins?: number; - streak: number; - weeklyPotdSolves?: number; - lastWeeklyResetDate?: string; - lastSolvedDate?: string; - username?: string; - isFallback?: boolean; -} - -const getLocalDateString = (date: Date = new Date()): string => - `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`; - -const getStartOfWeek = (d: Date) => { - const date = new Date(d); - const day = date.getDay(); - const diff = date.getDate() - day + (day === 0 ? -6 : 1); - date.setDate(diff); - return getLocalDateString(date); -}; - -const POTDPage: React.FC = () => { - const [problem, setProblem] = useState(null); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - const [topWeeklySolvers, setTopWeeklySolvers] = useState([]); - const [dailySolvers, setDailySolvers] = useState([]); - const [userSolved, setUserSolved] = useState(false); - const [userData, setUserData] = useState({ coins: 0, streak: 0 }); - const [isCheckingCF, setIsCheckingCF] = useState(false); - const [cfUsername, setCfUsername] = useState(""); - - // 1. REACT STATE FOR AUTH (Fixes the stale auth bug) - const [currentUser, setCurrentUser] = useState(auth.currentUser); - - const todayDate = getLocalDateString(); - const displayDate = new Date().toLocaleDateString("en-US", { - weekday: "long", month: "long", day: "numeric", - }); - - // 2. FIREBASE AUTH LISTENER - useEffect(() => { - const unsubscribe = onAuthStateChanged(auth, (user) => { - setCurrentUser(user); - - if (!user) { - setUserData({ coins: 0, streak: 0 }); - setCfUsername(""); - setUserSolved(false); - } - }); - return () => unsubscribe(); - }, []); + import React, { useEffect, useState, useCallback } from "react"; + import { getPOTD } from "../services/potd_fetch"; + import { doc, getDoc, setDoc, onSnapshot, collection, query, where, runTransaction } from "firebase/firestore"; + import { onAuthStateChanged } from "firebase/auth"; // MUST IMPORT THIS + import { db, auth } from "../lib/firebase"; + import styles from "../styles/POTDpage.module.css"; + import Image from "next/image"; + import { formatDescription } from "../utils/formatDescription"; + import { toast } from "sonner"; + import { checkCodeforcesSubmission } from "../services/codeforces_api"; + import { FiCheck, FiAward, FiExternalLink, FiZap, FiTarget, FiTrendingUp } from "react-icons/fi"; + import { BsLightningCharge } from "react-icons/bs"; + + interface Solver { + uid: string; + username: string; + solvedAt: string | Date; + codeforcesUsername?: string; + isFallback?: boolean; + } + + interface Problem { + id: string; + title: string; + description?: string; + rating?: number; + tags?: string[]; + problemUrl?: string; + } + + interface UserData { + uid?: string; + coins: number; + totalCoins?: number; + streak: number; + weeklyPotdSolves?: number; + lastWeeklyResetDate?: string; + lastSolvedDate?: string; + username?: string; + isFallback?: boolean; + } + + const getLocalDateString = (date: Date = new Date()): string => + `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`; + + const getStartOfWeek = (d: Date) => { + const date = new Date(d); + const day = date.getDay(); + const diff = date.getDate() - day + (day === 0 ? -6 : 1); + date.setDate(diff); + return getLocalDateString(date); + }; - useEffect(() => { - async function fetchPOTD() { - try { - const id = await getPOTD(); - if (!id || typeof id !== "string") throw new Error("Invalid POTD ID"); - const ref = doc(db, "problems", id); - const snapshot = await getDoc(ref); - if (!snapshot.exists()) throw new Error("Problem not found"); - const data = snapshot.data(); - setProblem({ - id, title: data.title, description: data.description, rating: data.rating, tags: data.tags, problemUrl: data.problemUrl, - }); - } catch (err: any) { - setError(err.message || "Error fetching POTD"); - } finally { - setLoading(false); - } - } - fetchPOTD(); + const POTDPage: React.FC = () => { + const [problem, setProblem] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [topWeeklySolvers, setTopWeeklySolvers] = useState([]); + const [dailySolvers, setDailySolvers] = useState([]); + const [userSolved, setUserSolved] = useState(false); + const [userData, setUserData] = useState({ coins: 0, streak: 0 }); + const [isCheckingCF, setIsCheckingCF] = useState(false); + const [cfUsername, setCfUsername] = useState(""); + + // 1. REACT STATE FOR AUTH (Fixes the stale auth bug) + const [currentUser, setCurrentUser] = useState(auth.currentUser); + + const todayDate = getLocalDateString(); + const displayDate = new Date().toLocaleDateString("en-US", { + weekday: "long", month: "long", day: "numeric", + }); - const currentStartOfWeek = getStartOfWeek(new Date()); - const weeklyQ = query( - collection(db, "users"), - where("lastWeeklyResetDate", "==", currentStartOfWeek) - ); - const unsubWeekly = onSnapshot( - weeklyQ, - (snap: any) => { - let solvers = snap.docs.map((d: any) => ({ ...d.data(), uid: d.id } as UserData)); - solvers.sort((a: UserData, b: UserData) => (b.weeklyPotdSolves || 0) - (a.weeklyPotdSolves || 0)); - setTopWeeklySolvers(solvers); - }, - (err: any) => console.error("Error watching weekly solvers", err) - ); + // 2. FIREBASE AUTH LISTENER + useEffect(() => { + const unsubscribe = onAuthStateChanged(auth, (user) => { + setCurrentUser(user); - return () => unsubWeekly(); - }, []); - - useEffect(() => { - if (!problem?.id) return; - const submissionRef = doc(db, "potd_submissions", todayDate); - const unsubscribe = onSnapshot(submissionRef, async (docSnap) => { - let solversArray: Solver[] = []; - if (docSnap.exists()) { - const data = docSnap.data(); - if (data?.problemId === problem?.id) { - solversArray = Object.entries(data?.solvers || {}).map( - ([uid, value]: [string, any]) => ({ - uid, username: value.username, solvedAt: value.solvedAt, codeforcesUsername: value.codeforcesUsername, - }) - ); + if (!user) { + setUserData({ coins: 0, streak: 0 }); + setCfUsername(""); + setUserSolved(false); + } + }); + return () => unsubscribe(); + }, []); + + useEffect(() => { + async function fetchPOTD() { + try { + const id = await getPOTD(); + if (!id || typeof id !== "string") throw new Error("Invalid POTD ID"); + const ref = doc(db, "problems", id); + const snapshot = await getDoc(ref); + if (!snapshot.exists()) throw new Error("Problem not found"); + const data = snapshot.data(); + setProblem({ + id, title: data.title, description: data.description, rating: data.rating, tags: data.tags, problemUrl: data.problemUrl, + }); + } catch (err: any) { + setError(err.message || "Error fetching POTD"); + } finally { + setLoading(false); } - } else { - setDoc(submissionRef, { problemId: problem?.id || "", solvers: {} }, { merge: true }); } + fetchPOTD(); - solversArray.sort((a, b) => new Date(a.solvedAt as string).getTime() - new Date(b.solvedAt as string).getTime()); - setDailySolvers(solversArray); - setUserSolved(currentUser ? solversArray.some((s) => s.uid === currentUser.uid) : false); - }); - return () => unsubscribe(); - }, [todayDate, problem?.id, currentUser]); - - useEffect(() => { - if (!currentUser) return; - const userRef = doc(db, "users", currentUser.uid); - const unsubscribe = onSnapshot(userRef, (docSnap) => { - if (docSnap.exists()) { - const data = docSnap.data() as UserData; - setUserData({ - coins: data.coins || 0, streak: data.streak || 0, lastSolvedDate: data.lastSolvedDate, username: data.username, - }); - setCfUsername(data.username || ""); - } - }); - return () => unsubscribe(); - }, [currentUser]); - - // 3. THROW ERROR INSTEAD OF SILENT RETURN (Fixes Fake Success) - const markAsSolved = useCallback(async () => { - if (!currentUser || !problem || userSolved) { - throw new Error("Unable to save: Session expired or already verified."); - } - - const submissionRef = doc(db, "potd_submissions", todayDate); - const userRef = doc(db, "users", currentUser.uid); - - await runTransaction(db, async (transaction) => { - const userDoc = await transaction.get(userRef); - const currentUserData = userDoc.exists() - ? (userDoc.data() as UserData) - : { coins: 0, totalCoins: 0, streak: 0 }; - - if (currentUserData.lastSolvedDate === todayDate) { - throw new Error("You have already verified today's POTD."); - } + const currentStartOfWeek = getStartOfWeek(new Date()); - const yesterday = new Date(); - yesterday.setDate(yesterday.getDate() - 1); - const yesterdayStr = getLocalDateString(yesterday); + // const weeklyQ = query( + // collection(db, "users"), + // where("lastWeeklyResetDate", "==", currentStartOfWeek) + // ); - const newStreak = currentUserData.lastSolvedDate === yesterdayStr ? (currentUserData.streak || 0) + 1 : 1; + // Changes for Weekly POTD Listing Based on the WeeklyPOTD count > 0 - const currentStartOfWeek = getStartOfWeek(new Date()); - const newWeeklySolves = currentUserData.lastWeeklyResetDate === currentStartOfWeek ? (currentUserData.weeklyPotdSolves || 0) + 1 : 1; - - const resolvedUsername = - userData.username || - currentUser.displayName || - (currentUser.email ? currentUser.email.split("@")[0] : "User"); - - transaction.set( - userRef, - { - coins: (currentUserData.coins || 0) + 50, totalCoins: (currentUserData.totalCoins || 0) + 50, - streak: newStreak, lastSolvedDate: todayDate, weeklyPotdSolves: newWeeklySolves, lastWeeklyResetDate: currentStartOfWeek, + const weeklyQ = query( + collection(db, "users"), + where("weeklyPotdSolves", ">=", 1) + ); + + const unsubWeekly = onSnapshot( + weeklyQ, + (snap: any) => { + let solvers = snap.docs.map((d: any) => ({ ...d.data(), uid: d.id } as UserData)); + solvers.sort((a: UserData, b: UserData) => (b.weeklyPotdSolves || 0) - (a.weeklyPotdSolves || 0)); + setTopWeeklySolvers(solvers); }, - { merge: true } + (err: any) => console.error("Error watching weekly solvers", err) ); - transaction.set( - submissionRef, - { - problemId: problem.id, - solvers: { - [currentUser.uid]: { - uid: currentUser.uid, username: resolvedUsername, solvedAt: new Date().toISOString(), + return () => unsubWeekly(); + }, []); + + useEffect(() => { + if (!problem?.id) return; + const submissionRef = doc(db, "potd_submissions", todayDate); + const unsubscribe = onSnapshot(submissionRef, async (docSnap) => { + let solversArray: Solver[] = []; + if (docSnap.exists()) { + const data = docSnap.data(); + if (data?.problemId === problem?.id) { + solversArray = Object.entries(data?.solvers || {}).map( + ([uid, value]: [string, any]) => ({ + uid, username: value.username, solvedAt: value.solvedAt, codeforcesUsername: value.codeforcesUsername, + }) + ); + } + } else { + setDoc(submissionRef, { problemId: problem?.id || "", solvers: {} }, { merge: true }); + } + + solversArray.sort((a, b) => new Date(a.solvedAt as string).getTime() - new Date(b.solvedAt as string).getTime()); + setDailySolvers(solversArray); + setUserSolved(currentUser ? solversArray.some((s) => s.uid === currentUser.uid) : false); + }); + return () => unsubscribe(); + }, [todayDate, problem?.id, currentUser]); + + useEffect(() => { + if (!currentUser) return; + const userRef = doc(db, "users", currentUser.uid); + const unsubscribe = onSnapshot(userRef, (docSnap) => { + if (docSnap.exists()) { + const data = docSnap.data() as UserData; + setUserData({ + coins: data.coins || 0, streak: data.streak || 0, lastSolvedDate: data.lastSolvedDate, username: data.username, + }); + setCfUsername(data.username || ""); + } + }); + return () => unsubscribe(); + }, [currentUser]); + + // 3. THROW ERROR INSTEAD OF SILENT RETURN (Fixes Fake Success) + const markAsSolved = useCallback(async () => { + if (!currentUser || !problem || userSolved) { + throw new Error("Unable to save: Session expired or already verified."); + } + + const submissionRef = doc(db, "potd_submissions", todayDate); + const userRef = doc(db, "users", currentUser.uid); + + await runTransaction(db, async (transaction) => { + const userDoc = await transaction.get(userRef); + const currentUserData = userDoc.exists() + ? (userDoc.data() as UserData) + : { coins: 0, totalCoins: 0, streak: 0 }; + + if (currentUserData.lastSolvedDate === todayDate) { + throw new Error("You have already verified today's POTD."); + } + + const yesterday = new Date(); + yesterday.setDate(yesterday.getDate() - 1); + const yesterdayStr = getLocalDateString(yesterday); + + const newStreak = currentUserData.lastSolvedDate === yesterdayStr ? (currentUserData.streak || 0) + 1 : 1; + + const currentStartOfWeek = getStartOfWeek(new Date()); + + // const newWeeklySolves = currentUserData.lastWeeklyResetDate === currentStartOfWeek ? (currentUserData.weeklyPotdSolves || 0) + 1 : 1; + + // Changes for the Weekly Count Addition + + const newWeeklySolves = (currentUserData.weeklyPotdSolves || 0) + 1; + + const resolvedUsername = + userData.username || + currentUser.displayName || + (currentUser.email ? currentUser.email.split("@")[0] : "User"); + + transaction.set( + userRef, + { + coins: (currentUserData.coins || 0) + 50, totalCoins: (currentUserData.totalCoins || 0) + 50, + streak: newStreak, lastSolvedDate: todayDate, weeklyPotdSolves: newWeeklySolves, lastWeeklyResetDate: currentStartOfWeek, + }, + { merge: true } + ); + + transaction.set( + submissionRef, + { + problemId: problem.id, + solvers: { + [currentUser.uid]: { + uid: currentUser.uid, username: resolvedUsername, solvedAt: new Date().toISOString(), + }, }, }, - }, - { merge: true } - ); - }); - }, [currentUser, problem, userSolved, todayDate, userData.username]); - - const handleCheckCFSubmission = useCallback(async () => { - if (!currentUser || !problem || userSolved || isCheckingCF) return; - - const usernameToCheck = userData.username || cfUsername; - if (!usernameToCheck) { - toast.error("Please set your Codeforces username in settings"); - return; - } - - setIsCheckingCF(true); - try { - const urlMatch = problem.problemUrl?.match(/codeforces\.com\/problemset\/problem\/(\d+)\/([A-Z]\d?)/); - if (!urlMatch) { - toast.error("Invalid Codeforces problem URL"); + { merge: true } + ); + }); + }, [currentUser, problem, userSolved, todayDate, userData.username]); + + const handleCheckCFSubmission = useCallback(async () => { + if (!currentUser || !problem || userSolved || isCheckingCF) return; + + const usernameToCheck = userData.username || cfUsername; + if (!usernameToCheck) { + toast.error("Please set your Codeforces username in settings"); return; } - const [, contestId, problemIndex] = urlMatch; - const hasSolved = await checkCodeforcesSubmission(usernameToCheck, contestId, problemIndex); - - if (hasSolved) { - await markAsSolved(); // If this fails, it jumps to the catch block and WON'T show success - toast.success("🎉 Verified! Solution found and progress saved!"); - } else { - toast.error("No accepted submission found today."); + + setIsCheckingCF(true); + try { + const urlMatch = problem.problemUrl?.match(/codeforces\.com\/problemset\/problem\/(\d+)\/([A-Z]\d?)/); + if (!urlMatch) { + toast.error("Invalid Codeforces problem URL"); + return; + } + const [, contestId, problemIndex] = urlMatch; + const hasSolved = await checkCodeforcesSubmission(usernameToCheck, contestId, problemIndex); + + if (hasSolved) { + await markAsSolved(); // If this fails, it jumps to the catch block and WON'T show success + toast.success("🎉 Verified! Solution found and progress saved!"); + } else { + toast.error("No accepted submission found today."); + } + } catch (error: any) { + toast.error(error.message || "Failed to save progress"); + } finally { + setIsCheckingCF(false); } - } catch (error: any) { - toast.error(error.message || "Failed to save progress"); - } finally { - setIsCheckingCF(false); - } - }, [currentUser, problem, userData.username, cfUsername, userSolved, isCheckingCF, markAsSolved]); - - const getDifficulty = (rating?: number) => { - if (!rating) return "Unknown"; - if (rating < 1200) return "Easy"; - if (rating < 1600) return "Medium"; - return "Hard"; - }; + }, [currentUser, problem, userData.username, cfUsername, userSolved, isCheckingCF, markAsSolved]); + + const getDifficulty = (rating?: number) => { + if (!rating) return "Unknown"; + if (rating < 1200) return "Easy"; + if (rating < 1600) return "Medium"; + return "Hard"; + }; + + if (loading) return
Loading mission...
; + if (error || !problem) return
Error: {error || "Problem not found"}
; + + const difficulty = getDifficulty(problem.rating); + const fullDesc = problem.description || ""; + + return ( +
+
+
+ +
+
+

+ 🎯 Problem of the Day +

+

{displayDate} — Complete your daily trial.

+

+ Master a new algorithmic challenge every 24 hours. Boost your rating, earn coins, + and maintain your streak to climb the elite leaderboard. +

+
+
+ POTD Mascot +
+
+ +
+
+
+

{problem.title}

+
+ {difficulty} + {problem.rating && ( + Rating: {problem.rating} + )} +
+
- if (loading) return
Loading mission...
; - if (error || !problem) return
Error: {error || "Problem not found"}
; - - const difficulty = getDifficulty(problem.rating); - const fullDesc = problem.description || ""; - - return ( -
-
-
- -
-
-

- 🎯 Problem of the Day -

-

{displayDate} — Complete your daily trial.

-

- Master a new algorithmic challenge every 24 hours. Boost your rating, earn coins, - and maintain your streak to climb the elite leaderboard. -

-
-
- POTD Mascot -
-
- -
-
-
-

{problem.title}

-
- {difficulty} - {problem.rating && ( - Rating: {problem.rating} +
+
+ {problem.tags && ( +
+ {problem.tags.slice(0, 6).map((tag) => ( + # {tag} + ))} +
)}
-
-
-
- {problem.tags && ( -
- {problem.tags.slice(0, 6).map((tag) => ( - # {tag} - ))} -
- )} -
- -
-
-
- 💰 -
- Coins - {userData.coins} +
+
+
+ 💰 +
+ Coins + {userData.coins} +
-
-
- 🔥 -
- Streak - {userData.streak} Days +
+ 🔥 +
+ Streak + {userData.streak} Days +
-
-
- Solve Challenge - {userSolved ? ( -
Verification Complete
- ) : ( - - )} - {problem.problemUrl && ( - - View on Codeforces - - )} +
+ Solve Challenge + {userSolved ? ( +
Verification Complete
+ ) : ( + + )} + {problem.problemUrl && ( + + View on Codeforces + + )} +
-
-
+
- + )} + +
+ +
- - - - ); -}; + + + ); + }; -export default POTDPage; \ No newline at end of file + export default POTDPage; \ No newline at end of file From e594afeea13fe13fd76650a8af7fedee0b3cea5d Mon Sep 17 00:00:00 2001 From: Jaswanth Kumar Date: Wed, 17 Jun 2026 21:23:45 +0530 Subject: [PATCH 2/2] Remove Comment inside JSX --- src/pages/potd.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/potd.tsx b/src/pages/potd.tsx index 3e190e3..527f494 100644 --- a/src/pages/potd.tsx +++ b/src/pages/potd.tsx @@ -385,7 +385,6 @@ - // Changed the Name of the Leaderboard Weekly Top to Leaderboard Top 🎉