diff --git a/src/pages/exam/index.tsx b/src/pages/exam/index.tsx index 1e171e4d..b66bb1c4 100644 --- a/src/pages/exam/index.tsx +++ b/src/pages/exam/index.tsx @@ -40,16 +40,16 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => { }, sessionOptions); export default function Page() { - const [userSolutions, setUserSolutions] = useState([]); - const [selectedModules, setSelectedModules] = useState([]); const [hasBeenUploaded, setHasBeenUploaded] = useState(false); const [moduleIndex, setModuleIndex] = useState(0); const [sessionId, setSessionId] = useState(""); const [exam, setExam] = useState(); const [timer, setTimer] = useState(-1); - const [showSolutions, setShowSolutions] = useExamStore((state) => [state.showSolutions, state.setShowSolutions]); const [exams, setExams] = useExamStore((state) => [state.exams, state.setExams]); + const [userSolutions, setUserSolutions] = useExamStore((state) => [state.userSolutions, state.setUserSolutions]); + const [showSolutions, setShowSolutions] = useExamStore((state) => [state.showSolutions, state.setShowSolutions]); + const [selectedModules, setSelectedModules] = useExamStore((state) => [state.selectedModules, state.setSelectedModules]); const {user} = useUser({redirectTo: "/login"}); @@ -66,15 +66,16 @@ export default function Page() { }, [selectedModules, moduleIndex, exams]); useEffect(() => { - async () => { + (async () => { if (selectedModules.length > 0) { const examPromises = selectedModules.map(getExam); Promise.all(examPromises).then((values) => { if (values.every((x) => !!x)) { + setExams(values.map((x) => x!)); } }); } - }; + })(); }, [selectedModules, setExams]); useEffect(() => { @@ -141,7 +142,7 @@ export default function Page() { const onFinish = (solutions: UserSolution[]) => { const solutionIds = solutions.map((x) => x.exercise); - setUserSolutions((prev) => [...prev.filter((x) => !solutionIds.includes(x.exercise)), ...solutions]); + setUserSolutions([...userSolutions.filter((x) => !solutionIds.includes(x.exercise)), ...solutions]); setModuleIndex((prev) => prev + 1); }; @@ -158,6 +159,7 @@ export default function Page() { onViewResults={() => { setShowSolutions(true); setModuleIndex(0); + setExam(exams[0]); }} scores={userSolutions.map((x) => ({...x.score, module: x.module}))} /> diff --git a/src/pages/history.tsx b/src/pages/history.tsx index c4048d23..fa1e4249 100644 --- a/src/pages/history.tsx +++ b/src/pages/history.tsx @@ -8,7 +8,7 @@ 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 {averageScore, convertToUserSolutions, formatModuleTotalStats, groupByDate, groupBySession, totalExams} from "@/utils/stats"; import {Divider} from "primereact/divider"; import useUser from "@/hooks/useUser"; import {Timeline} from "primereact/timeline"; @@ -16,6 +16,12 @@ import moment from "moment"; import {AutoComplete} from "primereact/autocomplete"; import useUsers from "@/hooks/useUsers"; import {Dropdown} from "primereact/dropdown"; +import useExamStore from "@/stores/examStore"; +import {Exam, ListeningExam, ReadingExam, SpeakingExam, WritingExam} from "@/interfaces/exam"; +import {Module} from "@/interfaces"; +import axios from "axios"; +import {toast} from "react-toastify"; +import {useRouter} from "next/router"; export const getServerSideProps = withIronSessionSsr(({req, res}) => { const user = req.session.user; @@ -43,12 +49,40 @@ export default function History({user}: {user: User}) { const {users, isLoading: isUsersLoading} = useUsers(); const {stats, isLoading: isStatsLoading} = useStats(selectedUser?.id); + const setExams = useExamStore((state) => state.setExams); + const setShowSolutions = useExamStore((state) => state.setShowSolutions); + const setUserSolutions = useExamStore((state) => state.setUserSolutions); + const setSelectedModules = useExamStore((state) => state.setSelectedModules); + + const router = useRouter(); + useEffect(() => { if (stats && !isStatsLoading) { setGroupedStats(groupByDate(stats)); } }, [stats, isStatsLoading]); + const getExam = async (module: Module): Promise => { + const examRequest = await axios(`/api/exam/${module}`); + if (examRequest.status !== 200) { + toast.error("Something went wrong!"); + return undefined; + } + + const newExam = examRequest.data; + + switch (module) { + case "reading": + return newExam.shift() as ReadingExam; + case "listening": + return newExam.shift() as ListeningExam; + case "writing": + return newExam.shift() as WritingExam; + case "speaking": + return newExam.shift() as SpeakingExam; + } + }; + const formatTimestamp = (timestamp: string) => { const date = moment(parseInt(timestamp)); const formatter = "YYYY/MM/DD - HH:mm"; @@ -63,6 +97,22 @@ export default function History({user}: {user: User}) { const correct = dateStats.reduce((accumulator, current) => accumulator + current.score.correct, 0); const total = dateStats.reduce((accumulator, current) => accumulator + current.score.total, 0); + const selectExam = () => { + const examPromises = formatModuleTotalStats(dateStats) + .filter((x) => x.value > 0) + .map((module) => getExam(module.label.toLowerCase() as Module)); + + Promise.all(examPromises).then((exams) => { + if (exams.every((x) => !!x)) { + setUserSolutions(convertToUserSolutions(dateStats)); + setShowSolutions(true); + setExams(exams.map((x) => x!)); + setSelectedModules(exams.map((x) => x!.module)); + router.push("/exam"); + } + }); + }; + return (
{formatTimestamp(timestamp)} @@ -77,6 +127,9 @@ export default function History({user}: {user: User}) { Score: {correct}/{total} | {((correct / total) * 100).toFixed(2)}% +
); diff --git a/src/stores/examStore.ts b/src/stores/examStore.ts index 4383c860..e074fe2c 100644 --- a/src/stores/examStore.ts +++ b/src/stores/examStore.ts @@ -1,25 +1,28 @@ import {Module} from "@/interfaces"; -import {Exam} from "@/interfaces/exam"; +import {Exam, UserSolution} from "@/interfaces/exam"; import {Stat} from "@/interfaces/user"; -import {getExamsBySession} from "@/utils/stats"; import {create} from "zustand"; export interface ExamState { exams: Exam[]; - stats: Stat[]; + userSolutions: UserSolution[]; showSolutions: boolean; - setStats: (stats: Stat[]) => void; + selectedModules: Module[]; + setUserSolutions: (userSolutions: UserSolution[]) => void; setExams: (exams: Exam[]) => void; setShowSolutions: (showSolutions: boolean) => void; + setSelectedModules: (modules: Module[]) => void; } const useExamStore = create((set) => ({ exams: [], - stats: [], + userSolutions: [], showSolutions: false, - setStats: (stats: Stat[]) => set(() => ({stats})), + selectedModules: [], + setUserSolutions: (userSolutions: UserSolution[]) => set(() => ({userSolutions})), setExams: (exams: Exam[]) => set(() => ({exams})), setShowSolutions: (showSolutions: boolean) => set(() => ({showSolutions})), + setSelectedModules: (modules: Module[]) => set(() => ({selectedModules: modules})), })); export default useExamStore; diff --git a/src/utils/stats.ts b/src/utils/stats.ts index 3f8b445c..8d730958 100644 --- a/src/utils/stats.ts +++ b/src/utils/stats.ts @@ -1,6 +1,7 @@ import {Stat} from "@/interfaces/user"; import {capitalize, groupBy} from "lodash"; import {convertCamelCaseToReadable} from "@/utils/string"; +import {UserSolution} from "@/interfaces/exam"; export const totalExams = (stats: Stat[]): number => { const moduleStats = formatModuleTotalStats(stats); @@ -98,3 +99,14 @@ export const getExamsBySession = (stats: Stat[], session: string) => { export const groupBySession = (stats: Stat[]) => groupBy(stats, "session"); export const groupByDate = (stats: Stat[]) => groupBy(stats, "date"); + +export const convertToUserSolutions = (stats: Stat[]): UserSolution[] => { + return stats.map((stat) => ({ + exercise: stat.exercise, + exam: stat.exam, + score: stat.score, + solutions: stat.solutions, + type: stat.type, + module: stat.module, + })); +};