From c8e0ea8697641f3cc6b5b7ead635d8da29990eae Mon Sep 17 00:00:00 2001 From: Ashu Date: Sat, 25 May 2024 18:43:04 +0530 Subject: [PATCH] feat-#12: Added Statistics Section on home page --- client/src/App.jsx | 6 +- client/src/components/Navbar/Navbar.jsx | 9 +- .../src/components/Statistics/Statistics.jsx | 247 ++++++++++++++++++ server/controllers/user.controller.js | 22 +- server/package-lock.json | 16 +- 5 files changed, 295 insertions(+), 5 deletions(-) create mode 100644 client/src/components/Statistics/Statistics.jsx diff --git a/client/src/App.jsx b/client/src/App.jsx index 0373726..80e692e 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -12,7 +12,8 @@ import { Analytics } from "@vercel/analytics/react"; import { SpeedInsights } from "@vercel/speed-insights/react"; import Notifcation from "./components/NotifcationPage/Notifcation"; import Footer from "./components/Footer"; -import Settings from "./components/Settings/Setting"; +import Settings from "./components/Settings/Setting";import Statistics from "./components/Statistics/Statistics"; + function App() { return ( @@ -30,7 +31,8 @@ function App() { } /> } /> } /> - + } /> + diff --git a/client/src/components/Navbar/Navbar.jsx b/client/src/components/Navbar/Navbar.jsx index 2ac5e24..9ce9ebc 100644 --- a/client/src/components/Navbar/Navbar.jsx +++ b/client/src/components/Navbar/Navbar.jsx @@ -7,7 +7,7 @@ import axios from "axios"; import { useLocation, useNavigate } from "react-router"; import Login from "../Login/Login.jsx"; import { Link } from "react-router-dom"; - +import { IoIosStats } from "react-icons/io"; function classNames(...classes) { return classes.filter(Boolean).join(" "); } @@ -18,12 +18,14 @@ const Navbar = () => { const [uploadNav, setUploadNav] = useState(false); const [requestNav, setRequestNav] = useState(false); const [leaderBoardNav, setleaderBoardNav] = useState(false); + const [statisticsNav, setStatisticsNav] = useState(false); const [searchInput, setSearchInput] = useState(""); const navigation = [ { name: "Upload Notes", href: "/upload", current: uploadNav }, { name: "Request Notes", href: "/request", current: requestNav }, { name: "Leaderboard", href: "/leaderboard", current: leaderBoardNav }, + {name : "Statistics", href: "/statistics", current:statisticsNav} ]; const navigate = useNavigate(); @@ -80,6 +82,11 @@ const Navbar = () => { setRequestNav(false); setUploadNav(false); } + else if (loc === "/statistics"){ + setStatisticsNav(true); + setRequestNav(false); + setUploadNav(false); + } }, []); return ( diff --git a/client/src/components/Statistics/Statistics.jsx b/client/src/components/Statistics/Statistics.jsx new file mode 100644 index 0000000..58d3b85 --- /dev/null +++ b/client/src/components/Statistics/Statistics.jsx @@ -0,0 +1,247 @@ +Statistics.jsx + +import React, { useContext, useState, useEffect, Fragment } from 'react' +import Navbar from '../Navbar/Navbar' +import { useNavigate } from "react-router"; +import { UserContext } from "../../Context/UserContext"; +import storage from "../../firebase/firebase"; +import { Loader } from "../Loader/Loader"; +import axios from "axios"; + +const Statistics = () => { + const { user, setUser } = useContext(UserContext); + const [subjects, setSubjects] = useState([]); + const [notes, setNotes] = useState([]); + const [users, setUsers] = useState([]); + const [loading, setLoading] = useState(true); + const [isSmallScreen, setIsSmallScreen] = useState(false); + +useEffect(() => { + const handleResize = () => { + setIsSmallScreen(window.innerWidth < 768); + }; + + handleResize(); + + window.addEventListener("resize", handleResize); + + return () => { + window.removeEventListener("resize", handleResize); + }; +}, []); +useEffect(() => { + const fetchAllData = async () => { + try { + const fetchSubjects = async () => { + const token = localStorage.getItem("token"); + const config = { + headers: { + Authorization: `Bearer ${token}`, + }, + withCredentials: true, + }; + const res = await axios.get(`${import.meta.env.VITE_BASE_URL}/subject`, config); + setSubjects(res.data); + }; + + const fetchNotes = async () => { + const token = localStorage.getItem("token"); + const config = { + headers: { + Authorization: `Bearer ${token}`, + }, + withCredentials: true, + }; + const res = await axios.get(`${import.meta.env.VITE_BASE_URL}/note`, config); + setNotes(res.data); + }; + + const fetchUsers = async () => { + const response = await axios.get(`${import.meta.env.VITE_BASE_URL}/user/leaderboard`); + if (response.status === 200) { + setUsers(response.data); + console.log(response.data); + } + }; + + await Promise.all([fetchSubjects(), fetchNotes(), fetchUsers()]); + setLoading(false); + } catch (error) { + console.error("Error fetching data", error); + setLoading(false); + } + }; + + fetchAllData(); +}, []); + + return ( +
+ {loading ? ( +

+ ) : ( +
+ +
+
+
+
+ Avatar +
+ Subjects +
+
+
+

{subjects.length}

+
+
+
+
+
+
+
+
+
+ Avatar +
+ Students +
+
+
+

{users.length}

+
+
+
+
+
+
+
+
+
+ Avatar +
+ Notes +
+
+
+

{notes.length}

+
+
+
+
+
+
+ +
+ )} + +
+ ) +} + +const smallScreenStyles = { + uidesign: { + flexDirection: "column", + }, +}; +const styles = { + cardBox: { + display: "flex", + flexWrap: "wrap", + justifyContent: "center", + }, + subject: { + "> div": { + background: "#f5f7cd" + } + }, + + student: { + "> div": { + background: "#F0F4FF" + } + }, + notes: { + "> div": { + background: "#EFF8FF" + } + }, + card: { + flex: "1 1 300px", + boxShadow: "0 4px 8px 4px rgba(0, 0, 0, 0.4)", + borderRadius: "15px", + maxWidth: "300px", + margin: "20px 10px" + }, + minicard: { + position: "relative", + bottom: "30px", + boxShadow: "0 4px 8px 0 rgba(0, 0, 0, 0.4)", + borderRadius: "15px", + paddingTop: "10px", + paddingRight: "20px", + paddingBottom: "10px", + paddingLeft: "20px", + maxWidth: "170px", + backgroundColor: "white", + margin: "auto", + textAlign: "center", + font: '17px "Fira Sans", sans-serif', + color: "grey" + }, + rectangle: { + position: "relative", + width: "300px", + height: "350px", + background: "white" + }, + rectangle2: { + display: "block", + textAlign: "center", + position: "relative", + width: "50px", + height: "50px", + background: "black" + }, + avatar: { + position: "relative", + top: "30px", + borderRadius: "50%", + width: "120px", + height: "120px", + display: "block", + marginLeft: "auto", + marginRight: "auto", + marginBottom: "50px", + boxShadow: "0 4px 8px 0 rgba(0, 0, 0, 0.2)" + }, + pseudo: { + position: "relative", + top: "10px", + textAlign: "center", + color: "#100B51", + fontSize: "25px", + fontFamily: '"Helvetica Neue", Roboto, "Segoe UI", Calibri, sans-serif' + }, + cardExcerpt: { + font: '30px "Fira Sans", sans-serif', + color: "grey" + } + }; + +export default Statistics diff --git a/server/controllers/user.controller.js b/server/controllers/user.controller.js index 80691fc..2599f6a 100644 --- a/server/controllers/user.controller.js +++ b/server/controllers/user.controller.js @@ -236,7 +236,26 @@ const getLeaderBoard = async (req, res) => { error: error.message, }); } -}; +} + +const getUsers=async(req,res)=>{ + try { + const users=await User.find().select('-password') + if(!users){ + return res.status(404).json({ + message:"No users found" + }) + } + res.status(200).json(users) + } catch (error) { + res.status(500).json({ + message:"Internal server error", + error:error.message + }) + } +} + + module.exports = { checkUsername, @@ -247,4 +266,5 @@ module.exports = { verifyOtp, getLeaderBoard, sendOTPcon, + getUsers }; diff --git a/server/package-lock.json b/server/package-lock.json index 7b366f6..8615823 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -25,6 +25,7 @@ "method-override": "^3.0.0", "mongoose": "^8.0.3", "multer": "^1.4.5-lts.1", + "nodemailer": "^6.9.13", "passport": "^0.7.0", "passport-local-mongoose": "^8.0.0", "sharp": "^0.33.1", @@ -3491,6 +3492,14 @@ "node": ">= 6.13.0" } }, + "node_modules/nodemailer": { + "version": "6.9.13", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.13.tgz", + "integrity": "sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/nodemon": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.0.tgz", @@ -7191,6 +7200,11 @@ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" }, + "nodemailer": { + "version": "6.9.13", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.13.tgz", + "integrity": "sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==" + }, "nodemon": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.0.tgz", @@ -8054,4 +8068,4 @@ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" } } -} \ No newline at end of file +}