From 06de06fbf6d108a387e0c34ebba285573e4a19b0 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Fri, 28 Apr 2023 18:19:29 +0100 Subject: [PATCH] Created a really basic history page --- package.json | 1 + src/components/Navbar.tsx | 1 + src/hooks/useUser.tsx | 3 ++ src/interfaces/user.ts | 1 + src/pages/exam/index.tsx | 1 + src/pages/history.tsx | 106 ++++++++++++++++++++++++++++++++++++++ src/utils/stats.ts | 5 +- yarn.lock | 5 ++ 8 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 src/pages/history.tsx 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 ( +
+ {formatTimestamp(timestamp)} +
+ + Modules:{" "} + {formatModuleTotalStats(dateStats) + .filter((x) => x.value > 0) + .map((x) => x.label) + .join(", ")} + + + Score: {correct}/{total} | {((correct / total) * 100).toFixed(2)}% + +
+
+ ); + }; + + return ( + <> + + IELTS GPT | Muscat Training Institute + + + + + {user && ( +
+ +
+ {groupedStats && !isLoading && ( +
+ +
+ )} +
+
+ )} + + ); +} diff --git a/src/utils/stats.ts b/src/utils/stats.ts index a1c3b3aa..dca0dcbf 100644 --- a/src/utils/stats.ts +++ b/src/utils/stats.ts @@ -1,5 +1,5 @@ import {Stat} from "@/interfaces/user"; -import {capitalize} from "lodash"; +import {capitalize, groupBy} from "lodash"; import {convertCamelCaseToReadable} from "@/utils/string"; export const totalExams = (stats: Stat[]): number => { @@ -90,3 +90,6 @@ export const formatExerciseAverageScoreStats = (stats: Stat[]): {label: string; }; }); }; + +export const groupBySession = (stats: Stat[]) => groupBy(stats, "session"); +export const groupByDate = (stats: Stat[]) => groupBy(stats, "date"); diff --git a/yarn.lock b/yarn.lock index cf43cba3..9317b77d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2679,6 +2679,11 @@ minimist@^1.2.0, minimist@^1.2.6: resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== +moment@^2.29.4: + version "2.29.4" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" + integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== + ms@2.1.2, ms@^2.1.1: version "2.1.2" resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"