From 065497dfa334e42132519d277a54a12453459a8a Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Thu, 7 Nov 2024 22:51:45 +0000 Subject: [PATCH] Updated the code to return to official-exam if they came from that page --- src/exams/Finish.tsx | 39 +++++++++++------------ src/pages/(exam)/ExamPage.tsx | 58 ++++++++++++++++++----------------- src/pages/exam.tsx | 38 ++++++++++++----------- src/pages/official-exam.tsx | 6 ++-- 4 files changed, 74 insertions(+), 67 deletions(-) diff --git a/src/exams/Finish.tsx b/src/exams/Finish.tsx index 9d89242d..8c3f9a3b 100644 --- a/src/exams/Finish.tsx +++ b/src/exams/Finish.tsx @@ -1,14 +1,14 @@ import Button from "@/components/Low/Button"; import ModuleTitle from "@/components/Medium/ModuleTitle"; -import {moduleResultText} from "@/constants/ielts"; -import {Module} from "@/interfaces"; -import {User} from "@/interfaces/user"; +import { moduleResultText } from "@/constants/ielts"; +import { Module } from "@/interfaces"; +import { User } from "@/interfaces/user"; import useExamStore from "@/stores/examStore"; -import {calculateBandScore, getGradingLabel} from "@/utils/score"; +import { calculateBandScore, getGradingLabel } from "@/utils/score"; import clsx from "clsx"; import Link from "next/link"; -import {useRouter} from "next/router"; -import {Fragment, useEffect, useState} from "react"; +import { useRouter } from "next/router"; +import { Fragment, useEffect, useState } from "react"; import { BsArrowCounterclockwise, BsBan, @@ -21,14 +21,14 @@ import { BsPen, BsShareFill, } from "react-icons/bs"; -import {LevelScore} from "@/constants/ielts"; -import {getLevelScore} from "@/utils/score"; -import {capitalize} from "lodash"; +import { LevelScore } from "@/constants/ielts"; +import { getLevelScore } from "@/utils/score"; +import { capitalize } from "lodash"; import Modal from "@/components/Modal"; -import {UserSolution} from "@/interfaces/exam"; +import { UserSolution } from "@/interfaces/exam"; import ai_usage from "@/utils/ai.detection"; import useGradingSystem from "@/hooks/useGrading"; -import {Assignment} from "@/interfaces/results"; +import { Assignment } from "@/interfaces/results"; interface Score { module: Module; @@ -49,20 +49,23 @@ interface Props { isLoading: boolean; assignment?: Assignment; onViewResults: (moduleIndex?: number) => void; + destination?: string } -export default function Finish({user, scores, modules, information, solutions, isLoading, assignment, onViewResults}: Props) { +export default function Finish({ user, scores, modules, information, solutions, isLoading, assignment, onViewResults, destination }: Props) { const [selectedModule, setSelectedModule] = useState(modules[0]); const [selectedScore, setSelectedScore] = useState(scores.find((x) => x.module === modules[0])!); const [isExtraInformationOpen, setIsExtraInformationOpen] = useState(false); const aiUsage = Math.round(ai_usage(solutions) * 100); const exams = useExamStore((state) => state.exams); - const {gradingSystem} = useGradingSystem(); + const { gradingSystem } = useGradingSystem(); + + const router = useRouter() useEffect(() => setSelectedScore(scores.find((x) => x.module === selectedModule)!), [scores, selectedModule]); - const moduleColors: {[key in Module]: {progress: string; inner: string}} = { + const moduleColors: { [key in Module]: { progress: string; inner: string } } = { reading: { progress: "text-ielts-reading", inner: "bg-ielts-reading-light", @@ -286,10 +289,8 @@ export default function Finish({user, scores, modules, information, solutions, i
@@ -325,7 +326,7 @@ export default function Finish({user, scores, modules, information, solutions, i )}
- + diff --git a/src/pages/(exam)/ExamPage.tsx b/src/pages/(exam)/ExamPage.tsx index 79069e30..480310ac 100644 --- a/src/pages/(exam)/ExamPage.tsx +++ b/src/pages/(exam)/ExamPage.tsx @@ -1,6 +1,6 @@ /* eslint-disable @next/next/no-img-element */ -import {Module} from "@/interfaces"; -import {useEffect, useState} from "react"; +import { Module } from "@/interfaces"; +import { useEffect, useState } from "react"; import AbandonPopup from "@/components/AbandonPopup"; import Layout from "@/components/High/Layout"; @@ -12,15 +12,15 @@ import Selection from "@/exams/Selection"; import Speaking from "@/exams/Speaking"; import Writing from "@/exams/Writing"; import useUser from "@/hooks/useUser"; -import {Exam, LevelExam, UserSolution, Variant} from "@/interfaces/exam"; -import {Stat, User} from "@/interfaces/user"; +import { Exam, LevelExam, UserSolution, Variant } from "@/interfaces/exam"; +import { Stat, User } from "@/interfaces/user"; import useExamStore from "@/stores/examStore"; -import {evaluateSpeakingAnswer, evaluateWritingAnswer} from "@/utils/evaluation"; -import {defaultExamUserSolutions, getExam} from "@/utils/exams"; +import { evaluateSpeakingAnswer, evaluateWritingAnswer } from "@/utils/evaluation"; +import { defaultExamUserSolutions, getExam } from "@/utils/exams"; import axios from "axios"; -import {useRouter} from "next/router"; -import {toast, ToastContainer} from "react-toastify"; -import {v4 as uuidv4} from "uuid"; +import { useRouter } from "next/router"; +import { toast, ToastContainer } from "react-toastify"; +import { v4 as uuidv4 } from "uuid"; import useSessions from "@/hooks/useSessions"; import ShortUniqueId from "short-unique-id"; import clsx from "clsx"; @@ -31,10 +31,11 @@ import { mapBy } from "@/utils"; interface Props { page: "exams" | "exercises"; user: User; + destination?: string hideSidebar?: boolean } -export default function ExamPage({page, user, hideSidebar = false}: Props) { +export default function ExamPage({ page, user, destination = "/exam", hideSidebar = false }: Props) { const [variant, setVariant] = useState("full"); const [avoidRepeated, setAvoidRepeated] = useState(false); const [hasBeenUploaded, setHasBeenUploaded] = useState(false); @@ -49,18 +50,18 @@ export default function ExamPage({page, user, hideSidebar = false}: Props) { const assignment = useExamStore((state) => state.assignment); const initialTimeSpent = useExamStore((state) => state.timeSpent); - const {exam, setExam} = useExamStore((state) => state); - const {exams, setExams} = useExamStore((state) => state); - const {sessionId, setSessionId} = useExamStore((state) => state); - const {partIndex, setPartIndex} = useExamStore((state) => state); - const {moduleIndex, setModuleIndex} = useExamStore((state) => state); - const {questionIndex, setQuestionIndex} = useExamStore((state) => state); - const {exerciseIndex, setExerciseIndex} = useExamStore((state) => state); - const {userSolutions, setUserSolutions} = useExamStore((state) => state); - const {showSolutions, setShowSolutions} = useExamStore((state) => state); - const {selectedModules, setSelectedModules} = useExamStore((state) => state); - const {inactivity, setInactivity} = useExamStore((state) => state); - const {bgColor, setBgColor} = useExamStore((state) => state); + const { exam, setExam } = useExamStore((state) => state); + const { exams, setExams } = useExamStore((state) => state); + const { sessionId, setSessionId } = useExamStore((state) => state); + const { partIndex, setPartIndex } = useExamStore((state) => state); + const { moduleIndex, setModuleIndex } = useExamStore((state) => state); + const { questionIndex, setQuestionIndex } = useExamStore((state) => state); + const { exerciseIndex, setExerciseIndex } = useExamStore((state) => state); + const { userSolutions, setUserSolutions } = useExamStore((state) => state); + const { showSolutions, setShowSolutions } = useExamStore((state) => state); + const { selectedModules, setSelectedModules } = useExamStore((state) => state); + const { inactivity, setInactivity } = useExamStore((state) => state); + const { bgColor, setBgColor } = useExamStore((state) => state); const setShuffleMaps = useExamStore((state) => state.setShuffles); const router = useRouter(); @@ -262,11 +263,11 @@ export default function ExamPage({page, user, hideSidebar = false}: Props) { date: new Date().getTime(), isDisabled: solution.isDisabled, shuffleMaps: solution.shuffleMaps, - ...(assignment ? {assignment: assignment.id} : {}), + ...(assignment ? { assignment: assignment.id } : {}), })); axios - .post<{ok: boolean}>("/api/stats", newStats) + .post<{ ok: boolean }>("/api/stats", newStats) .then((response) => setHasBeenUploaded(response.data.ok)) .catch(() => setHasBeenUploaded(false)); } @@ -329,7 +330,7 @@ export default function ExamPage({page, user, hideSidebar = false}: Props) { ), }), ); - return Object.assign(exam, {parts}); + return Object.assign(exam, { parts }); } const exercises = exam.exercises.map((x) => @@ -337,7 +338,7 @@ export default function ExamPage({page, user, hideSidebar = false}: Props) { userSolutions: userSolutions.find((y) => x.id === y.exercise)?.solutions, }), ); - return Object.assign(exam, {exercises}); + return Object.assign(exam, { exercises }); }; const onFinish = async (solutions: UserSolution[]) => { @@ -392,7 +393,7 @@ export default function ExamPage({page, user, hideSidebar = false}: Props) { correct: number; }[] => { const scores: { - [key in Module]: {total: number; missing: number; correct: number}; + [key in Module]: { total: number; missing: number; correct: number }; } = { reading: { total: 0, @@ -434,7 +435,7 @@ export default function ExamPage({page, user, hideSidebar = false}: Props) { return Object.keys(scores) .filter((x) => scores[x as Module].total > 0) - .map((x) => ({module: x as Module, ...scores[x as Module]})); + .map((x) => ({ module: x as Module, ...scores[x as Module] })); }; const renderScreen = () => { @@ -465,6 +466,7 @@ export default function ExamPage({page, user, hideSidebar = false}: Props) { timeSpent, inactivity: totalInactivity, }} + destination={destination} onViewResults={(index?: number) => { if (exams[0].module === "level") { const levelExam = exams[0] as LevelExam; diff --git a/src/pages/exam.tsx b/src/pages/exam.tsx index f3b3c526..e77f90c3 100644 --- a/src/pages/exam.tsx +++ b/src/pages/exam.tsx @@ -1,11 +1,11 @@ /* eslint-disable @next/next/no-img-element */ -import {withIronSessionSsr} from "iron-session/next"; -import {sessionOptions} from "@/lib/session"; -import {shouldRedirectHome} from "@/utils/navigation.disabled"; +import { withIronSessionSsr } from "iron-session/next"; +import { sessionOptions } from "@/lib/session"; +import { shouldRedirectHome } from "@/utils/navigation.disabled"; import ExamPage from "./(exam)/ExamPage"; import Head from "next/head"; -import {User} from "@/interfaces/user"; +import { User } from "@/interfaces/user"; import { filterBy, findBy, redirect, serialize } from "@/utils"; import { requestUser } from "@/utils/api"; import { getAssignment, getAssignments, getAssignmentsByAssignee } from "@/utils/assignments.be"; @@ -21,35 +21,36 @@ import { getSessionByAssignment, getSessionsByUser } from "@/utils/sessions.be"; import { Session } from "@/hooks/useSessions"; import moment from "moment"; -export const getServerSideProps = withIronSessionSsr(async ({req, res, query}) => { +export const getServerSideProps = withIronSessionSsr(async ({ req, res, query }) => { const user = await requestUser(req, res) - const destination = Buffer.from(req.url || "/").toString("base64") - if (!user) return redirect(`/login?destination=${destination}`) + const loginDestination = Buffer.from(req.url || "/").toString("base64") + if (!user) return redirect(`/login?destination=${loginDestination}`) if (shouldRedirectHome(user)) return redirect("/") - const {assignment: assignmentID} = query as {assignment?: string} + const { assignment: assignmentID, destination } = query as { assignment?: string, destination?: string } + const destinationURL = !!destination ? Buffer.from(destination, 'base64').toString() : undefined if (assignmentID) { const assignment = await getAssignment(assignmentID) - if (!assignment) return redirect("/exam") + if (!assignment) return redirect(destinationURL || "/exam") if (!assignment.assignees.includes(user.id) && !["admin", "developer"].includes(user.type)) - return redirect("/exam") + return redirect(destinationURL || "/exam") if (filterBy(assignment.results, 'user', user.id).length > 0) - return redirect("/exam") + return redirect(destinationURL || "/exam") const exams = await getExamsByIds(uniqBy(assignment.exams, "id")) const session = await getSessionByAssignment(assignmentID) return { - props: serialize({user, assignment, exams, session: session ?? undefined}) + props: serialize({ user, assignment, exams, destinationURL, session: session ?? undefined }) } } return { - props: serialize({user}), + props: serialize({ user, destinationURL }), }; }, sessionOptions); @@ -58,9 +59,10 @@ interface Props { assignment?: Assignment exams?: Exam[] session?: Session + destinationURL?: string } -export default function Page({user, assignment, exams = [], session}: Props) { +export default function Page({ user, assignment, exams = [], destinationURL = "/exam", session }: Props) { const router = useRouter() const state = useExamStore((state) => state) @@ -82,12 +84,12 @@ export default function Page({user, assignment, exams = [], session}: Props) { router.replace(router.asPath) } - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [assignment, exams, session]) useEffect(() => { if (assignment && exams.length > 0 && !state.assignment && !!session) { - state.setShuffles(session.userSolutions.map((x) => ({exerciseID: x.exercise, shuffles: x.shuffleMaps ? x.shuffleMaps : []}))); + state.setShuffles(session.userSolutions.map((x) => ({ exerciseID: x.exercise, shuffles: x.shuffleMaps ? x.shuffleMaps : [] }))); state.setSelectedModules(session.selectedModules); state.setExam(session.exam); state.setExams(session.exams); @@ -103,7 +105,7 @@ export default function Page({user, assignment, exams = [], session}: Props) { router.replace(router.asPath) } - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [assignment, exams, session]) return ( @@ -117,7 +119,7 @@ export default function Page({user, assignment, exams = [], session}: Props) { - + ); } diff --git a/src/pages/official-exam.tsx b/src/pages/official-exam.tsx index 241742a8..bd069c4a 100644 --- a/src/pages/official-exam.tsx +++ b/src/pages/official-exam.tsx @@ -69,6 +69,8 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => { return { props: serialize({ user, entities, assignments, exams, sessions }) }; }, sessionOptions); +const destination = Buffer.from("/official-exam").toString("base64") + export default function OfficialExam({ user, entities, assignments, sessions, exams }: Props) { const [isLoading, setIsLoading] = useState(false) @@ -93,7 +95,7 @@ export default function OfficialExam({ user, entities, assignments, sessions, ex state.setSelectedModules(mapBy(assignmentExams.sort(sortByModule), 'module')); state.setAssignment(assignment); - router.push(`/exam?assignment=${assignment.id}`); + router.push(`/exam?assignment=${assignment.id}&destination=${destination}`); } }; @@ -112,7 +114,7 @@ export default function OfficialExam({ user, entities, assignments, sessions, ex state.setShowSolutions(false); state.setQuestionIndex(session.questionIndex); - router.push(`/exam?assignment=${session.assignment?.id}`); + router.push(`/exam?assignment=${session.assignment?.id}&destination=${destination}`); }; const logout = async () => {