diff --git a/package.json b/package.json index 90b6da33..ac6ba615 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "framer-motion": "^9.0.2", "iron-session": "^6.3.1", "lodash": "^4.17.21", + "moment": "^2.29.4", "next": "13.1.6", "primeicons": "^6.0.1", "primereact": "^9.2.3", diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 10b866bb..3f9672a7 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -43,6 +43,7 @@ export default function Navbar({profilePicture, timer, showExamEnd = false}: Pro items: [ {label: "List", icon: "pi pi-fw pi-users", url: "/users"}, {label: "Stats", icon: "pi pi-fw pi-chart-pie", url: "/stats"}, + {label: "History", icon: "pi pi-fw pi-chart-pie", url: "/history"}, ], }, { diff --git a/src/hooks/useUser.tsx b/src/hooks/useUser.tsx index 7a64c02b..f6a27519 100644 --- a/src/hooks/useUser.tsx +++ b/src/hooks/useUser.tsx @@ -13,6 +13,9 @@ export default function useUser({redirectTo = "", redirectIfFound = false} = {}) // if no redirect needed, just return (example: already on /dashboard) // if user data not yet there (fetch in progress, logged in or not) then don't do anything yet if (!redirectTo || !user) return; + if (redirectTo && !user) { + Router.push(redirectTo); + } if ( // If redirectTo is set, redirect if the user was not found. diff --git a/src/interfaces/user.ts b/src/interfaces/user.ts index c7f68f43..5bec2c57 100644 --- a/src/interfaces/user.ts +++ b/src/interfaces/user.ts @@ -14,6 +14,7 @@ export interface Stat { exam: string; exercise: string; session: string; + date: number; module: Module; solutions: any[]; type: string; diff --git a/src/pages/exam/index.tsx b/src/pages/exam/index.tsx index f01f19fe..28e3459f 100644 --- a/src/pages/exam/index.tsx +++ b/src/pages/exam/index.tsx @@ -71,6 +71,7 @@ export default function Page() { exam: solution.exam!, module: solution.module!, user: user?.id || "", + date: new Date().getTime(), })); axios diff --git a/src/pages/history.tsx b/src/pages/history.tsx new file mode 100644 index 00000000..d819d035 --- /dev/null +++ b/src/pages/history.tsx @@ -0,0 +1,106 @@ +/* eslint-disable @next/next/no-img-element */ +import Head from "next/head"; +import SingleDatasetChart from "@/components/UserResultChart"; +import Navbar from "@/components/Navbar"; +import ProfileCard from "@/components/ProfileCard"; +import {withIronSessionSsr} from "iron-session/next"; +import {sessionOptions} from "@/lib/session"; +import {Stat, User} from "@/interfaces/user"; +import {useEffect, useState} from "react"; +import useStats from "@/hooks/useStats"; +import {averageScore, formatModuleTotalStats, groupByDate, groupBySession, totalExams} from "@/utils/stats"; +import {Divider} from "primereact/divider"; +import useUser from "@/hooks/useUser"; +import {Timeline} from "primereact/timeline"; +import moment from "moment"; + +export const getServerSideProps = withIronSessionSsr(({req, res}) => { + const user = req.session.user; + + if (!user) { + res.setHeader("location", "/login"); + res.statusCode = 302; + res.end(); + return { + props: { + user: null, + }, + }; + } + + return { + props: {user: req.session.user}, + }; +}, sessionOptions); + +export default function History() { + const [groupedStats, setGroupedStats] = useState<{[key: string]: Stat[]}>(); + + const {stats, isLoading} = useStats(); + const {user} = useUser({redirectTo: "/login"}); + + useEffect(() => { + if (stats && !isLoading) { + setGroupedStats(groupByDate(stats)); + } + }, [stats, isLoading]); + + const formatTimestamp = (timestamp: string) => { + const date = moment(parseInt(timestamp)); + const formatter = "YYYY/MM/DD - HH:mm"; + + return date.format(formatter); + }; + + const customContent = (timestamp: string) => { + if (!groupedStats) return <>>; + + const dateStats = groupedStats[timestamp]; + const correct = dateStats.reduce((accumulator, current) => accumulator + current.score.correct, 0); + const total = dateStats.reduce((accumulator, current) => accumulator + current.score.total, 0); + + return ( +