diff --git a/src/components/PracticeModal.tsx b/src/components/PracticeModal.tsx new file mode 100644 index 00000000..82ebc779 --- /dev/null +++ b/src/components/PracticeModal.tsx @@ -0,0 +1,26 @@ +import { useState } from "react"; +import Button from "./Low/Button"; +import Modal from "./Modal"; + +interface Props { + open?: boolean +} + +export default function PracticeModal({ open }: Props) { + const [isOpen, setIsOpen] = useState(open || false) + + return ( + setIsOpen(false)}> +
+ + To acquaint yourself with the question types in this section, please respond to the practice questions provided. +
+ Do note that these questions are for practice purposes only and are not graded. +
+ You may choose to skip them if you prefer. +
+ +
+
+ ) +} diff --git a/src/exams/Listening.tsx b/src/exams/Listening.tsx index 86721643..a7b4a277 100644 --- a/src/exams/Listening.tsx +++ b/src/exams/Listening.tsx @@ -14,6 +14,7 @@ import RenderAudioInstructionsPlayer from "./components/RenderAudioInstructionsP import RenderAudioPlayer from "./components/RenderAudioPlayer"; import SectionNavbar from "./Navigation/SectionNavbar"; import ProgressButtons from "./components/ProgressButtons"; +import PracticeModal from "@/components/PracticeModal"; const Listening: React.FC> = ({ exam, showSolutions = false, preview = false }) => { @@ -83,8 +84,11 @@ const Listening: React.FC> = ({ exam, showSolutions = f userSolutions: userSolutions.find((x) => x.exercise === exercise.id)?.solutions || [], })) + const hasPractice = exercises.some(e => e.isPractice) + return (
+ {formattedExercises.map(e => showSolutions ? renderSolution(e) : (!startNow && !showPartDivider && !isBetweenParts && !showSolutions) && renderExercise(e, exam.id, registerSolution, preview))} @@ -93,8 +97,6 @@ const Listening: React.FC> = ({ exam, showSolutions = f // eslint-disable-next-line react-hooks/exhaustive-deps }, [partIndex, startNow, showPartDivider, isBetweenParts, showSolutions]); - - const confirmFinishModule = (keepGoing?: boolean) => { if (!keepGoing) { setShowBlankModal(false); @@ -129,12 +131,12 @@ const Listening: React.FC> = ({ exam, showSolutions = f timesListened={timesListened} setShowTextModal={setShowTextModal} setTimesListened={setTimesListened} - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps />, [partIndex, assignment, timesListened, setShowTextModal, setTimesListened]) - const memoizedInstructions = useMemo(()=> + const memoizedInstructions = useMemo(() => - , []) + , []) return ( <> @@ -180,9 +182,9 @@ const Listening: React.FC> = ({ exam, showSolutions = f {/* Exercise renderer */} <> - {(!startNow && !showPartDivider) && progressButtons} - {renderPartExercises} - {(!startNow && !showPartDivider && !isBetweenParts) && progressButtons} + {(!startNow && !showPartDivider) && progressButtons} + {renderPartExercises} + {(!startNow && !showPartDivider && !isBetweenParts) && progressButtons}
diff --git a/src/exams/Reading.tsx b/src/exams/Reading.tsx index e9d7f094..95f1b28c 100644 --- a/src/exams/Reading.tsx +++ b/src/exams/Reading.tsx @@ -10,13 +10,14 @@ import { countExercises } from "@/utils/moduleUtils"; import PartDivider from "./Navigation/SectionDivider"; import ReadingPassage from "./components/ReadingPassage"; //import ReadingPassageModal from "./components/ReadingPassageModal"; -import {calculateExerciseIndex} from "./utils/calculateExerciseIndex"; +import { calculateExerciseIndex } from "./utils/calculateExerciseIndex"; import useExamNavigation from "./Navigation/useExamNavigation"; import { ExamProps } from "./types"; import useExamStore, { usePersistentExamStore } from "@/stores/exam"; import useExamTimer from "@/hooks/useExamTimer"; import SectionNavbar from "./Navigation/SectionNavbar"; import ProgressButtons from "./components/ProgressButtons"; +import PracticeModal from "@/components/PracticeModal"; const Reading: React.FC> = ({ exam, showSolutions = false, preview = false }) => { const updateTimers = useExamTimer(exam.module, preview || showSolutions); @@ -50,6 +51,13 @@ const Reading: React.FC> = ({ exam, showSolutions = false startNow } = useExamNavigation({ exam, module: "reading", showBlankModal, setShowBlankModal, showSolutions, preview, disableBetweenParts: showSolutions }); + const hasPractice = useMemo(() => { + if (partIndex > -1 && partIndex < exam.parts.length) { + return exam.parts[partIndex].exercises.some(e => e.isPractice) + } + return false + }, [partIndex, exam.parts]) + useEffect(() => { if (finalizeModule || timeIsUp) { updateTimers(); @@ -130,7 +138,7 @@ const Reading: React.FC> = ({ exam, showSolutions = false const progressButtons = useMemo(() => // Do not remove the ()=> in handle next nextExercise()} /> - , [nextExercise, previousExercise]); + , [nextExercise, previousExercise]); return ( <> @@ -146,6 +154,7 @@ const Reading: React.FC> = ({ exam, showSolutions = false /> : ( <> +
{/* setShowTextModal(false)} />*/} diff --git a/src/exams/Speaking.tsx b/src/exams/Speaking.tsx index 4205ee87..4801a931 100644 --- a/src/exams/Speaking.tsx +++ b/src/exams/Speaking.tsx @@ -13,6 +13,7 @@ import useExamNavigation from "./Navigation/useExamNavigation"; import ProgressButtons from "./components/ProgressButtons"; import { calculateExerciseIndexSpeaking } from "./utils/calculateExerciseIndex"; import SectionNavbar from "./Navigation/SectionNavbar"; +import PracticeModal from "@/components/PracticeModal"; const Speaking: React.FC> = ({ exam, showSolutions = false, preview = false }) => { @@ -33,6 +34,7 @@ const Speaking: React.FC> = ({ exam, showSolutions = fal const { finalizeModule, timeIsUp } = flags; const timer = useRef(exam.minTimer - timeSpentCurrentModule / 60); + const hasPractice = useMemo(() => exam.exercises.some(e => e.isPractice), [exam.exercises]) const { nextExercise, previousExercise, @@ -110,6 +112,7 @@ const Speaking: React.FC> = ({ exam, showSolutions = fal onNext={handlePartDividerClick} /> : ( <> + {exam.exercises.length > 1 && > = ({ exam, showSolutions = false, preview = false }) => { const updateTimers = useExamTimer(exam.module, preview || showSolutions); @@ -27,6 +28,7 @@ const Writing: React.FC> = ({ exam, showSolutions = false } = !preview ? examState : persistentExamState; const timer = useRef(exam.minTimer - timeSpentCurrentModule / 60); + const hasPractice = useMemo(() => exam.exercises.some(e => e.isPractice), [exam.exercises]) const { finalizeModule, timeIsUp } = flags; const { nextDisabled } = navigation; @@ -80,8 +82,8 @@ const Writing: React.FC> = ({ exam, showSolutions = false const progressButtons = useMemo(() => // Do not remove the ()=> in handle next - nextExercise()} nextDisabled={nextDisabled}/> - , [nextExercise, previousExercise, nextDisabled]); + nextExercise()} nextDisabled={nextDisabled} /> + , [nextExercise, previousExercise, nextDisabled]); return ( @@ -96,6 +98,7 @@ const Writing: React.FC> = ({ exam, showSolutions = false onNext={handlePartDividerClick} /> : (
+ {exam.exercises.length > 1 && ([]); @@ -19,5 +19,5 @@ export default function useSessions(user?: string) { useEffect(getData, [user]); - return {sessions, isLoading, isError, reload: getData}; + return { sessions, isLoading, isError, reload: getData }; } diff --git a/src/pages/(exam)/ExamPage.tsx b/src/pages/(exam)/ExamPage.tsx index 5cb5829f..922e7c1b 100644 --- a/src/pages/(exam)/ExamPage.tsx +++ b/src/pages/(exam)/ExamPage.tsx @@ -22,6 +22,7 @@ import ShortUniqueId from "short-unique-id"; import { ExamProps } from "@/exams/types"; import useExamStore from "@/stores/exam"; import useEvaluationPolling from "@/hooks/useEvaluationPolling"; +import PracticeModal from "@/components/PracticeModal"; interface Props { page: "exams" | "exercises"; @@ -128,7 +129,7 @@ export default function ExamPage({ page, user, destination = "/", hideSidebar = if (exercise.type === "writing") await evaluateWritingAnswer(user.id, sessionId, exercise, index + 1, userSolutions.find((x) => x.exercise === exercise.id)!); - if (exercise.type === "interactiveSpeaking" || exercise.type === "speaking"){ + if (exercise.type === "interactiveSpeaking" || exercise.type === "speaking") { await evaluateSpeakingAnswer( user.id, sessionId, @@ -144,7 +145,7 @@ export default function ExamPage({ page, user, destination = "/", hideSidebar = } }, [exam, showSolutions, userSolutions, sessionId, user?.id, flags]); - useEvaluationPolling({pendingExercises, setPendingExercises}); + useEvaluationPolling({ pendingExercises, setPendingExercises }); useEffect(() => { if (flags.finalizeExam && moduleIndex !== -1) { diff --git a/src/pages/api/sessions/index.ts b/src/pages/api/sessions/index.ts index 267ae57b..a352ea78 100644 --- a/src/pages/api/sessions/index.ts +++ b/src/pages/api/sessions/index.ts @@ -1,9 +1,9 @@ // Next.js API route support: https://nextjs.org/docs/api-routes/introduction -import type {NextApiRequest, NextApiResponse} from "next"; +import type { NextApiRequest, NextApiResponse } from "next"; import client from "@/lib/mongodb"; -import {withIronSessionApiRoute} from "iron-session/next"; -import {sessionOptions} from "@/lib/session"; -import {Session} from "@/hooks/useSessions"; +import { withIronSessionApiRoute } from "iron-session/next"; +import { sessionOptions } from "@/lib/session"; +import { Session } from "@/hooks/useSessions"; import moment from "moment"; const db = client.db(process.env.MONGODB_DB); @@ -17,14 +17,17 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { async function get(req: NextApiRequest, res: NextApiResponse) { if (!req.session.user) { - res.status(401).json({ok: false}); + res.status(401).json({ ok: false }); return; } - const {user} = req.query as {user?: string}; + const { user } = req.query as { user?: string }; - const q = user ? {user: user} : {}; - const sessions = await db.collection("sessions").find(q).limit(10).toArray(); + const q = user ? { user: user } : {}; + const sessions = await db.collection("sessions").find({ + ...q, + }).limit(12).toArray(); + console.log(sessions) res.status(200).json( sessions.filter((x) => { @@ -37,12 +40,12 @@ async function get(req: NextApiRequest, res: NextApiResponse) { async function post(req: NextApiRequest, res: NextApiResponse) { if (!req.session.user) { - res.status(401).json({ok: false}); + res.status(401).json({ ok: false }); return; } const session = req.body; - await db.collection("sessions").updateOne({id: session.id}, {$set: session}, {upsert: true}); + await db.collection("sessions").updateOne({ id: session.id }, { $set: session }, { upsert: true }); - res.status(200).json({ok: true}); + res.status(200).json({ ok: true }); }