diff --git a/api/routes/user.ts b/api/routes/user.ts index 0ed2ea5..be39c30 100644 --- a/api/routes/user.ts +++ b/api/routes/user.ts @@ -1,70 +1,73 @@ -import { Request, Response } from "express" -import { Router } from "express" -import User from "../db/User" -import Prize from "../db/Prize" -import totalStampsCalc from "../pipelines/totalStamps" +import { Request, Response } from "express"; +import { Router } from "express"; +import User from "../db/User"; +import Prize from "../db/Prize"; +import totalStampsCalc from "../pipelines/totalStamps"; -const userRoutes = Router() +const userRoutes = Router(); -userRoutes.get("/total-stamps/:accessToken", async (req: Request, res: Response) => { - const accessToken = req.params.accessToken - try { - const result = await totalStampsCalc(accessToken) - res.json(result) - } catch (error: any) { - res.status(500).json({ message: error.message }) +userRoutes.get( + "/total-stamps/:accessToken", + async (req: Request, res: Response) => { + const accessToken = req.params.accessToken; + try { + const result = await totalStampsCalc(accessToken); + res.json(result); + } catch (error: any) { + res.status(500).json({ message: error.message }); + } } -}) +); // GET /api/users userRoutes.get("/", async (req: Request, res: Response) => { // Logic to fetch all users from the database try { - const results = await User.find() - const users = results.map((user) => user.toObject()) - return res.json(users) + const results = await User.find(); + const users = results.map((user) => user.toObject()); + return res.json(users); } catch (error: any) { - res.status(500).json({ message: error.message }) + res.status(500).json({ message: error.message }); } -}) +}); userRoutes.post("/check-user", async (req: Request, res: Response) => { - const accessToken = req.body.accessToken + const accessToken = req.body.accessToken; try { - const response = await User.findOne({ accessToken: accessToken }).exec() - console.log(response) + const response = await User.findOne({ accessToken: accessToken }).exec(); + console.log(response); if (response != undefined && response !== null) { - res.status(200).json({ user: response, success: true }) + res.status(200).json({ user: response, success: true }); } else { res.status(200).json({ success: false, error: "User not found", - }) + }); } } catch (error) { - res.status(400).json({ success: false, errorMessage: error }) + res.status(400).json({ success: false, errorMessage: error }); } -}) +}); // GET /api/user/:upi userRoutes.get("/:upi", async (req: Request, res: Response) => { - const userUpi = req.params.upi + const userUpi = req.params.upi; try { - const user = await User.findOne({ upi: userUpi }).exec() // Await the result or use exec() + const user = await User.findOne({ upi: userUpi }).exec(); // Await the result or use exec() if (user) { - res.json(user) + res.json(user); } else { - res.status(404).json({ message: "User not found" }) + res.status(404).json({ message: "User not found" }); } } catch (error: any) { - res.status(500).json({ message: error.message }) + res.status(500).json({ message: error.message }); } -}) +}); // POST /api/user userRoutes.post("/", (req: Request, res: Response) => { - const userData = req.body + const userData = req.body; // Logic to create a new user in the database const newUser = new User({ @@ -74,15 +77,15 @@ userRoutes.post("/", (req: Request, res: Response) => { accessToken: req.body.accessToken, upi: req.body.upi, eventList: [], - }) + }); try { - const savedUser = newUser.save() - res.status(201).json(savedUser) + const savedUser = newUser.save(); + res.status(201).json(savedUser); } catch (error: any) { - res.status(400).json({ message: error.message }) + res.status(400).json({ message: error.message }); } -}) +}); // PUT /api/user/:upi userRoutes.put("/:upi", async (req: Request, res: Response) => { @@ -91,50 +94,72 @@ userRoutes.put("/:upi", async (req: Request, res: Response) => { { upi: req.params.upi }, // Filter criteria req.body, // Updated data { new: true } // Return the updated document - ).exec() // Execute the query + ).exec(); // Execute the query if (updatedUser) { - res.json(updatedUser) + res.json(updatedUser); } else { - res.status(404).json({ message: "User not found" }) + res.status(404).json({ message: "User not found" }); } } catch (error: any) { - res.status(400).json({ message: error.message }) + res.status(400).json({ message: error.message }); } -}) +}); //Redeem Prize Endpoint // POST /api/user/redeem-prize/:accessToken userRoutes.post( "/redeem-prize/:accessToken", async (req: Request, res: Response) => { - const accessToken = req.params.accessToken + const accessToken = req.params.accessToken; try { - const user = await User.findOne({ accessToken: accessToken }).exec() + const user = await User.findOne({ accessToken: accessToken }).exec(); if (user) { //Logic check if user has enough stamps - const userStamps = user.eventList.length - const userPrizes = await Prize.find({ userId: user._id }).exec() + const userStamps = user.eventList.length; + const userPrizes = await Prize.find({ userId: user._id }).exec(); if (userStamps / 5 <= userPrizes.length) { - res.status(400).json({ message: "User has already redeemed prize" }) - return + res.status(400).json({ message: "User has already redeemed prize" }); + return; } const prize = new Prize({ userId: user._id, redeemed: false, redeemedTime: Date.now(), - }) - const savedPrize = await prize.save() - res.status(201).json(savedPrize) + }); + const savedPrize = await prize.save(); + res.status(201).json(savedPrize); + } else { + res.status(404).json({ message: "User not found" }); + } + } catch (error: any) { + res.status(500).json({ message: error.message }); + } + } +); + +userRoutes.get( + "/redeemed-prizes/:accessToken", + async (req: Request, res: Response) => { + const accessToken = req.params.accessToken; + try { + const user = await User.findOne({ accessToken: accessToken }).exec(); + + if (user) { + const userPrizesRedeemed = await Prize.find({ + $and: [{ userId: user._id }, { redeemed: true }], + }).exec(); + console.log("Successfully obtained data"); + res.json(userPrizesRedeemed.length); } else { - res.status(404).json({ message: "User not found" }) + res.status(404).json({ message: "User not found" }); } } catch (error: any) { - res.status(500).json({ message: error.message }) + console.error("Unable to find any matching user", error); } } -) +); -export default userRoutes +export default userRoutes; diff --git a/web/src/components/CheckLoggedIn.tsx b/web/src/components/CheckLoggedIn.tsx index 0a36320..c69e53c 100644 --- a/web/src/components/CheckLoggedIn.tsx +++ b/web/src/components/CheckLoggedIn.tsx @@ -1,8 +1,10 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState } from "react"; import { useNavigate, useLocation } from "react-router-dom"; -import axios from 'axios'; +import axios from "axios"; -const CheckLoggedIn: React.FC<{ children: React.ReactNode }> = ({ children }) => { +const CheckLoggedIn: React.FC<{ children: React.ReactNode }> = ({ + children, +}) => { const navigate = useNavigate(); const location = useLocation(); const [isLoading, setIsLoading] = useState(true); @@ -11,17 +13,19 @@ const CheckLoggedIn: React.FC<{ children: React.ReactNode }> = ({ children }) => useEffect(() => { const fetchUserData = async () => { try { - const accessToken = localStorage.getItem('accessToken'); + const accessToken = localStorage.getItem("accessToken"); - const response = await axios.post(`${import.meta.env.VITE_SERVER_URL}/api/user/check-user`, { accessToken }); + const response = await axios.post( + `${import.meta.env.VITE_SERVER_URL}/api/user/check-user`, + { accessToken } + ); if (response.data.success && accessToken) { console.log("User is logged in"); setIsLoggedIn(true); - } else { - if (location.pathname!== "/sign-in") { - localStorage.setItem('prevLocation', location.pathname); - } + if (location.pathname !== "/sign-in") { + localStorage.setItem("prevLocation", location.pathname); + } console.log("User is not logged in"); navigate("/sign-in"); } @@ -37,6 +41,6 @@ const CheckLoggedIn: React.FC<{ children: React.ReactNode }> = ({ children }) => }, [navigate]); return isLoading ? null : isLoggedIn ? <>{children} : null; -} +}; export default CheckLoggedIn; diff --git a/web/src/components/LeaderboardRedeemedPrizes.tsx b/web/src/components/LeaderboardRedeemedPrizes.tsx new file mode 100644 index 0000000..73686cc --- /dev/null +++ b/web/src/components/LeaderboardRedeemedPrizes.tsx @@ -0,0 +1,26 @@ +import { useState, useEffect } from "react"; + +export default function GetRedeemedPrizes() { + const [data, setData] = useState(); + const accessToken = localStorage.getItem("accessToken"); + + useEffect(() => { + const fetchData = async () => { + try { + const response = await fetch( + `${import.meta.env.VITE_SERVER_URL}/api/user/redeemed-prizes/` + + accessToken + ); + if (!response.ok) { + throw new Error("Error retrieving data"); + } + const result = await response.json(); + setData(result); + } catch (err) { + console.log(err); + } + }; + fetchData(); + }, [accessToken]); + return data; +} diff --git a/web/src/components/LeaderboardStats.tsx b/web/src/components/LeaderboardStats.tsx index abc3a97..bd49455 100644 --- a/web/src/components/LeaderboardStats.tsx +++ b/web/src/components/LeaderboardStats.tsx @@ -1,41 +1,40 @@ -import { useEffect, useState } from 'react'; -//import PrizesAchieved from './PrizesAchieved'; +// LeaderboardStats.js +import { useEffect, useState } from "react"; -const GetLeaderboardStats = () => { - const [data, setData] = useState({ - accessToken : "null", - email : "null", - eventList: [], - firstName : "null", - lastName : "null", - totalStamps : 0, - stampsLeft : 0, - prizesAchieved : 0, - upi : "null", - __v : 0, - _id : "0" +export default function LeaderboardStats() { + const [data, setData] = useState({ + accessToken: "null", + email: "null", + eventList: [], + firstName: "null", + lastName: "null", + totalStamps: 0, + stampsLeft: 0, + prizesAchieved: 0, + upi: "null", + __v: 0, + _id: "0", + }); - }); + const upi = localStorage.getItem("userUpi"); - const upi = localStorage.getItem('userUpi'); - - useEffect(() => { + useEffect(() => { const fetchData = async () => { - try { - const response = await fetch(`${import.meta.env.VITE_SERVER_URL}/api/user/` + upi); + try { + const response = await fetch( + `${import.meta.env.VITE_SERVER_URL}/api/user/` + upi + ); if (!response.ok) { - throw new Error('Error retrieving data'); + throw new Error("Error retrieving data"); } const result = await response.json(); setData(result); - } catch (error) { - console.error('Error fetching data'); - } + } catch (error) { + console.error("Error fetching data"); + } }; fetchData(); - }, [upi]); - return data; -}; - -export default GetLeaderboardStats; \ No newline at end of file + }, [upi]); + return data; +} diff --git a/web/src/components/RedeemPrizeButton.tsx b/web/src/components/RedeemPrizeButton.tsx index 2654f02..5356673 100644 --- a/web/src/components/RedeemPrizeButton.tsx +++ b/web/src/components/RedeemPrizeButton.tsx @@ -1,32 +1,37 @@ -const RedeemPrizeButton = () => ( -
- -
-) +import { useNavigate } from "react-router"; -export default RedeemPrizeButton +export default function RedeemPrizeButton() { + const navigate = useNavigate(); + + return ( +
+ +
+ ); +} diff --git a/web/src/pages/Leaderboard.tsx b/web/src/pages/Leaderboard.tsx index ca90b3c..aadcd3d 100644 --- a/web/src/pages/Leaderboard.tsx +++ b/web/src/pages/Leaderboard.tsx @@ -1,17 +1,42 @@ -import StampsAwayCount from "@components/StampsAwayCount" -import ProgressBar from "@components/ProgressBar" -import RedeemPrizeButton from "@components/RedeemPrizeButton" -import "../styles/page styles/Leaderboard.css" -import PrizesAchieved from "@components/PrizesAchieved" -import HamburgerMenu from "@components/HamburgerMenu" -import CheckLoggedIn from "@components/CheckLoggedIn" -import GetLeaderboardStats from "@components/LeaderboardStats" +import StampsAwayCount from "@components/StampsAwayCount"; +import ProgressBar from "@components/ProgressBar"; +import RedeemPrizeButton from "@components/RedeemPrizeButton"; +import "../styles/page styles/Leaderboard.css"; +import PrizesAchieved from "@components/PrizesAchieved"; +import HamburgerMenu from "@components/HamburgerMenu"; +import CheckLoggedIn from "@components/CheckLoggedIn"; +import LeaderboardStats from "@components/LeaderboardStats"; +import GetRedeemedPrizes from "@components/LeaderboardRedeemedPrizes"; +import { useEffect } from "react"; export default function Leaderboard() { - const userData = GetLeaderboardStats() - const stampsLeft = userData.stampsLeft - const prizes = userData.prizesAchieved - const height = 5 - stampsLeft + const userData = LeaderboardStats(); + const redeemedPrizes = GetRedeemedPrizes() ?? 0; + const redeemed = userData.eventList.length / 5 == redeemedPrizes; + + let stampsLeft = 5 - (userData.eventList.length % 5); + let height = userData.eventList.length % 5; + + if (!redeemed && userData.eventList.length % 5 == 0) { + height += 5; + stampsLeft -= 5; + } + + useEffect(() => { + if ( + redeemedPrizes == undefined || + redeemedPrizes == null || + redeemedPrizes == 0 || + userData == undefined || + userData == null + ) { + return; + } + if (!redeemed && userData.eventList.length % 5 == 0) { + console.log("Redirect to leaderboards"); + window.location.href = "/leaderboard-prize"; + } + }, [redeemedPrizes]); return ( @@ -27,10 +52,10 @@ export default function Leaderboard() { ) : (
- +
)}
- ) + ); }