ENCOA-233: Added the option for certain exercises to not count towards scores
This commit is contained in:
@@ -11,6 +11,7 @@ import MCDropdown from "./MCDropdown";
|
||||
const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
|
||||
id,
|
||||
type,
|
||||
isPractice = false,
|
||||
prompt,
|
||||
solutions,
|
||||
text,
|
||||
@@ -40,6 +41,11 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
|
||||
const shuffleMaps = shuffles.find((x) => x.exerciseID == id)?.shuffles;
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (disableProgressButtons) onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [answers, disableProgressButtons])
|
||||
|
||||
const excludeWordMCType = (x: any) => {
|
||||
return typeof x === "string" ? x : (x as { letter: string; word: string });
|
||||
};
|
||||
@@ -169,7 +175,7 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
|
||||
<Button
|
||||
color="purple"
|
||||
variant="outline"
|
||||
onClick={() => onBack({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps })}
|
||||
onClick={() => onBack({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps, isPractice })}
|
||||
className="max-w-[200px] w-full"
|
||||
disabled={exam && exam.module === "level" && partIndex === 0 && questionIndex === 0}>
|
||||
Previous Page
|
||||
@@ -178,7 +184,7 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
|
||||
<Button
|
||||
color="purple"
|
||||
onClick={() => {
|
||||
onNext({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps });
|
||||
onNext({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps, isPractice });
|
||||
}}
|
||||
className="max-w-[200px] self-end w-full">
|
||||
Next Page
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import {InteractiveSpeakingExercise} from "@/interfaces/exam";
|
||||
import {CommonProps} from ".";
|
||||
import {useEffect, useState} from "react";
|
||||
import {BsCheckCircleFill, BsMicFill, BsPauseCircle, BsPlayCircle, BsTrashFill} from "react-icons/bs";
|
||||
import { InteractiveSpeakingExercise } from "@/interfaces/exam";
|
||||
import { CommonProps } from ".";
|
||||
import { useEffect, useState } from "react";
|
||||
import { BsCheckCircleFill, BsMicFill, BsPauseCircle, BsPlayCircle, BsTrashFill } from "react-icons/bs";
|
||||
import dynamic from "next/dynamic";
|
||||
import Button from "../Low/Button";
|
||||
import useExamStore from "@/stores/examStore";
|
||||
import {downloadBlob} from "@/utils/evaluation";
|
||||
import { downloadBlob } from "@/utils/evaluation";
|
||||
import axios from "axios";
|
||||
|
||||
const Waveform = dynamic(() => import("../Waveform"), {ssr: false});
|
||||
const Waveform = dynamic(() => import("../Waveform"), { ssr: false });
|
||||
const ReactMediaRecorder = dynamic(() => import("react-media-recorder").then((mod) => mod.ReactMediaRecorder), {
|
||||
ssr: false,
|
||||
});
|
||||
@@ -24,16 +24,17 @@ export default function InteractiveSpeaking({
|
||||
userSolutions,
|
||||
onNext,
|
||||
onBack,
|
||||
isPractice = false,
|
||||
preview = false
|
||||
}: InteractiveSpeakingExercise & CommonProps) {
|
||||
const [recordingDuration, setRecordingDuration] = useState(0);
|
||||
const [isRecording, setIsRecording] = useState(false);
|
||||
const [mediaBlob, setMediaBlob] = useState<string>();
|
||||
const [answers, setAnswers] = useState<{prompt: string; blob: string; questionIndex: number}[]>([]);
|
||||
const [answers, setAnswers] = useState<{ prompt: string; blob: string; questionIndex: number }[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const {questionIndex, setQuestionIndex} = useExamStore((state) => state);
|
||||
const {userSolutions: storeUserSolutions, setUserSolutions} = useExamStore((state) => state);
|
||||
const { questionIndex, setQuestionIndex } = useExamStore((state) => state);
|
||||
const { userSolutions: storeUserSolutions, setUserSolutions } = useExamStore((state) => state);
|
||||
|
||||
const hasExamEnded = useExamStore((state) => state.hasExamEnded);
|
||||
|
||||
@@ -52,8 +53,9 @@ export default function InteractiveSpeaking({
|
||||
onBack({
|
||||
exercise: id,
|
||||
solutions: [...answers.filter((x) => x.questionIndex !== questionIndex), answer],
|
||||
score: {correct: 100, total: 100, missing: 0},
|
||||
score: { correct: 100, total: 100, missing: 0 },
|
||||
type,
|
||||
isPractice
|
||||
});
|
||||
};
|
||||
|
||||
@@ -74,8 +76,9 @@ export default function InteractiveSpeaking({
|
||||
onNext({
|
||||
exercise: id,
|
||||
solutions: [...answers.filter((x) => x.questionIndex !== questionIndex), answer],
|
||||
score: {correct: 100, total: 100, missing: 0},
|
||||
score: { correct: 100, total: 100, missing: 0 },
|
||||
type,
|
||||
isPractice
|
||||
});
|
||||
};
|
||||
|
||||
@@ -100,7 +103,7 @@ export default function InteractiveSpeaking({
|
||||
onNext({
|
||||
exercise: id,
|
||||
solutions: [...answers.filter((x) => x.questionIndex !== questionIndex), answer],
|
||||
score: {correct: 100, total: 100, missing: 0},
|
||||
score: { correct: 100, total: 100, missing: 0 },
|
||||
type,
|
||||
});
|
||||
}
|
||||
@@ -142,10 +145,11 @@ export default function InteractiveSpeaking({
|
||||
{
|
||||
exercise: id,
|
||||
solutions: [...answers.filter((x) => x.questionIndex !== questionIndex), answer],
|
||||
score: {correct: 100, total: 100, missing: 0},
|
||||
score: { correct: 100, total: 100, missing: 0 },
|
||||
module: "speaking",
|
||||
exam: examID,
|
||||
type,
|
||||
isPractice
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -181,7 +185,7 @@ export default function InteractiveSpeaking({
|
||||
audio
|
||||
key={questionIndex}
|
||||
onStop={(blob) => setMediaBlob(blob)}
|
||||
render={({status, startRecording, stopRecording, pauseRecording, resumeRecording, clearBlobUrl, mediaBlobUrl}) => (
|
||||
render={({ status, startRecording, stopRecording, pauseRecording, resumeRecording, clearBlobUrl, mediaBlobUrl }) => (
|
||||
<div className="w-full p-4 px-8 bg-transparent border-2 border-mti-gray-platinum rounded-2xl flex-col gap-8 items-center">
|
||||
<p className="text-base font-normal">Record your answer:</p>
|
||||
<div className="flex gap-8 items-center justify-center py-8">
|
||||
@@ -301,12 +305,12 @@ export default function InteractiveSpeaking({
|
||||
</Button>
|
||||
{preview ? (
|
||||
<Button color="purple" isLoading={isLoading} onClick={next} className="max-w-[200px] self-end w-full">
|
||||
{questionIndex + 1 < prompts.length ? "Next Prompt" : "Submit"}
|
||||
</Button>
|
||||
{questionIndex + 1 < prompts.length ? "Next Prompt" : "Submit"}
|
||||
</Button>
|
||||
) : (
|
||||
<Button color="purple" disabled={!mediaBlob} isLoading={isLoading} onClick={next} className="max-w-[200px] self-end w-full">
|
||||
{questionIndex + 1 < prompts.length ? "Next Prompt" : "Submit"}
|
||||
</Button>
|
||||
{questionIndex + 1 < prompts.length ? "Next Prompt" : "Submit"}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -72,6 +72,7 @@ export default function MatchSentences({
|
||||
userSolutions,
|
||||
onNext,
|
||||
onBack,
|
||||
isPractice = false,
|
||||
disableProgressButtons = false
|
||||
}: MatchSentencesExercise & CommonProps) {
|
||||
const [answers, setAnswers] = useState<{ question: string; option: string }[]>(userSolutions);
|
||||
@@ -81,7 +82,7 @@ export default function MatchSentences({
|
||||
const setCurrentSolution = useExamStore((state) => state.setCurrentSolution);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type });
|
||||
setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [answers, setAnswers]);
|
||||
|
||||
@@ -105,12 +106,12 @@ export default function MatchSentences({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (disableProgressButtons) onNext({ exercise: id, solutions: answers, score: calculateScore(), type });
|
||||
if (disableProgressButtons) onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [answers, disableProgressButtons])
|
||||
|
||||
useEffect(() => {
|
||||
if (hasExamEnded) onNext({ exercise: id, solutions: answers, score: calculateScore(), type });
|
||||
if (hasExamEnded) onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [hasExamEnded]);
|
||||
|
||||
@@ -119,14 +120,14 @@ export default function MatchSentences({
|
||||
<Button
|
||||
color="purple"
|
||||
variant="outline"
|
||||
onClick={() => onBack({ exercise: id, solutions: answers, score: calculateScore(), type })}
|
||||
onClick={() => onBack({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice })}
|
||||
className="max-w-[200px] w-full">
|
||||
Back
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
color="purple"
|
||||
onClick={() => onNext({ exercise: id, solutions: answers, score: calculateScore(), type })}
|
||||
onClick={() => onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice })}
|
||||
className="max-w-[200px] self-end w-full">
|
||||
Next
|
||||
</Button>
|
||||
|
||||
@@ -78,6 +78,7 @@ export default function MultipleChoice({
|
||||
type,
|
||||
questions,
|
||||
userSolutions,
|
||||
isPractice = false,
|
||||
onNext,
|
||||
onBack,
|
||||
disableProgressButtons = false
|
||||
@@ -93,7 +94,7 @@ export default function MultipleChoice({
|
||||
const scrollToTop = () => Array.from(document.getElementsByTagName("body")).forEach((body) => body.scrollTo(0, 0));
|
||||
|
||||
useEffect(() => {
|
||||
if (hasExamEnded) onNext({ exercise: id, solutions: answers, score: calculateScore(), type });
|
||||
if (hasExamEnded) onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [hasExamEnded]);
|
||||
|
||||
@@ -102,7 +103,7 @@ export default function MultipleChoice({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps });
|
||||
setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps, isPractice });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [answers, setAnswers]);
|
||||
|
||||
@@ -140,13 +141,13 @@ export default function MultipleChoice({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (disableProgressButtons) onNext({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps });
|
||||
if (disableProgressButtons) onNext({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps, isPractice });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [answers, disableProgressButtons])
|
||||
|
||||
const next = () => {
|
||||
if (questionIndex + 1 >= questions.length - 1) {
|
||||
onNext({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps });
|
||||
onNext({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps, isPractice });
|
||||
} else {
|
||||
setQuestionIndex(questionIndex + 2);
|
||||
}
|
||||
@@ -155,7 +156,7 @@ export default function MultipleChoice({
|
||||
|
||||
const back = () => {
|
||||
if (questionIndex === 0) {
|
||||
onBack({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps });
|
||||
onBack({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps, isPractice });
|
||||
} else {
|
||||
if (exam?.module === "level" && typeof exam.parts[0].intro !== "undefined" && questionIndex === 0) return;
|
||||
setQuestionIndex(questionIndex - 2);
|
||||
|
||||
@@ -14,7 +14,7 @@ const ReactMediaRecorder = dynamic(() => import("react-media-recorder").then((mo
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
export default function Speaking({ id, title, text, video_url, type, prompts, suffix, userSolutions, onNext, onBack, preview = false }: SpeakingExercise & CommonProps) {
|
||||
export default function Speaking({ id, title, text, video_url, type, prompts, suffix, userSolutions, isPractice = false, onNext, onBack, preview = false }: SpeakingExercise & CommonProps) {
|
||||
const [recordingDuration, setRecordingDuration] = useState(0);
|
||||
const [isRecording, setIsRecording] = useState(false);
|
||||
const [mediaBlob, setMediaBlob] = useState<string>();
|
||||
@@ -81,7 +81,7 @@ export default function Speaking({ id, title, text, video_url, type, prompts, su
|
||||
exercise: id,
|
||||
solutions: mediaBlob ? [{ id, solution: mediaBlob }] : [],
|
||||
score: { correct: 0, total: 100, missing: 0 },
|
||||
type,
|
||||
type, isPractice
|
||||
});
|
||||
};
|
||||
|
||||
@@ -90,7 +90,7 @@ export default function Speaking({ id, title, text, video_url, type, prompts, su
|
||||
exercise: id,
|
||||
solutions: mediaBlob ? [{ id, solution: mediaBlob }] : [],
|
||||
score: { correct: 0, total: 100, missing: 0 },
|
||||
type,
|
||||
type, isPractice
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ export default function TrueFalse({
|
||||
prompt,
|
||||
questions,
|
||||
userSolutions,
|
||||
isPractice = false,
|
||||
onNext,
|
||||
onBack,
|
||||
disableProgressButtons = false
|
||||
@@ -21,7 +22,7 @@ export default function TrueFalse({
|
||||
const setCurrentSolution = useExamStore((state) => state.setCurrentSolution);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasExamEnded) onNext({ exercise: id, solutions: answers, score: calculateScore(), type });
|
||||
if (hasExamEnded) onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [hasExamEnded]);
|
||||
|
||||
@@ -40,7 +41,7 @@ export default function TrueFalse({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type });
|
||||
setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [answers, setAnswers]);
|
||||
|
||||
@@ -55,7 +56,7 @@ export default function TrueFalse({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (disableProgressButtons) onNext({ exercise: id, solutions: answers, score: calculateScore(), type });
|
||||
if (disableProgressButtons) onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [answers, disableProgressButtons])
|
||||
|
||||
@@ -64,14 +65,14 @@ export default function TrueFalse({
|
||||
<Button
|
||||
color="purple"
|
||||
variant="outline"
|
||||
onClick={() => onBack({ exercise: id, solutions: answers, score: calculateScore(), type })}
|
||||
onClick={() => onBack({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice })}
|
||||
className="max-w-[200px] w-full">
|
||||
Back
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
color="purple"
|
||||
onClick={() => onNext({ exercise: id, solutions: answers, score: calculateScore(), type })}
|
||||
onClick={() => onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice })}
|
||||
className="max-w-[200px] self-end w-full">
|
||||
Next
|
||||
</Button>
|
||||
|
||||
@@ -53,6 +53,7 @@ export default function WriteBlanks({
|
||||
maxWords,
|
||||
solutions,
|
||||
userSolutions,
|
||||
isPractice = false,
|
||||
text,
|
||||
onNext,
|
||||
onBack,
|
||||
@@ -63,7 +64,7 @@ export default function WriteBlanks({
|
||||
const { hasExamEnded, setCurrentSolution } = useExamStore((state) => state);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasExamEnded) onNext({ exercise: id, solutions: answers, score: calculateScore(), type });
|
||||
if (hasExamEnded) onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [hasExamEnded]);
|
||||
|
||||
@@ -82,12 +83,12 @@ export default function WriteBlanks({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type });
|
||||
setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [answers, setAnswers]);
|
||||
|
||||
useEffect(() => {
|
||||
if (disableProgressButtons) onNext({ exercise: id, solutions: answers, score: calculateScore(), type });
|
||||
if (disableProgressButtons) onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [answers, disableProgressButtons])
|
||||
|
||||
@@ -112,14 +113,14 @@ export default function WriteBlanks({
|
||||
<Button
|
||||
color="purple"
|
||||
variant="outline"
|
||||
onClick={() => onBack({ exercise: id, solutions: answers, score: calculateScore(), type })}
|
||||
onClick={() => onBack({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice })}
|
||||
className="max-w-[200px] w-full">
|
||||
Back
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
color="purple"
|
||||
onClick={() => onNext({ exercise: id, solutions: answers, score: calculateScore(), type })}
|
||||
onClick={() => onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice })}
|
||||
className="max-w-[200px] self-end w-full">
|
||||
Next
|
||||
</Button>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
import {WritingExercise} from "@/interfaces/exam";
|
||||
import {CommonProps} from ".";
|
||||
import React, {Fragment, useEffect, useRef, useState} from "react";
|
||||
import {toast} from "react-toastify";
|
||||
import { WritingExercise } from "@/interfaces/exam";
|
||||
import { CommonProps } from ".";
|
||||
import React, { Fragment, useEffect, useRef, useState } from "react";
|
||||
import { toast } from "react-toastify";
|
||||
import Button from "../Low/Button";
|
||||
import {Dialog, Transition} from "@headlessui/react";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import useExamStore from "@/stores/examStore";
|
||||
|
||||
export default function Writing({
|
||||
@@ -16,6 +16,7 @@ export default function Writing({
|
||||
wordCounter,
|
||||
attachment,
|
||||
userSolutions,
|
||||
isPractice = false,
|
||||
onNext,
|
||||
onBack,
|
||||
enableNavigation = false
|
||||
@@ -25,7 +26,7 @@ export default function Writing({
|
||||
const [isSubmitEnabled, setIsSubmitEnabled] = useState(false);
|
||||
const [saveTimer, setSaveTimer] = useState(0);
|
||||
|
||||
const {userSolutions: storeUserSolutions, setUserSolutions} = useExamStore((state) => state);
|
||||
const { userSolutions: storeUserSolutions, setUserSolutions } = useExamStore((state) => state);
|
||||
const hasExamEnded = useExamStore((state) => state.hasExamEnded);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -42,7 +43,7 @@ export default function Writing({
|
||||
if (inputText.length > 0 && saveTimer % 10 === 0) {
|
||||
setUserSolutions([
|
||||
...storeUserSolutions.filter((x) => x.exercise !== id),
|
||||
{exercise: id, solutions: [{id, solution: inputText}], score: {correct: 100, total: 100, missing: 0}, type, module: "writing"},
|
||||
{ exercise: id, solutions: [{ id, solution: inputText }], score: { correct: 100, total: 100, missing: 0 }, type, module: "writing" },
|
||||
]);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -66,7 +67,7 @@ export default function Writing({
|
||||
|
||||
useEffect(() => {
|
||||
if (hasExamEnded)
|
||||
onNext({exercise: id, solutions: [{id, solution: inputText}], score: {correct: 100, total: 100, missing: 0}, type, module: "writing"});
|
||||
onNext({ exercise: id, solutions: [{ id, solution: inputText }], score: { correct: 100, total: 100, missing: 0 }, type, module: "writing", isPractice });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [hasExamEnded]);
|
||||
|
||||
@@ -78,7 +79,7 @@ export default function Writing({
|
||||
} else {
|
||||
setIsSubmitEnabled(true);
|
||||
if (wordCounter.limit < words.length) {
|
||||
toast.warning(`You have reached your word limit of ${wordCounter.limit} words!`, {toastId: "word-limit"});
|
||||
toast.warning(`You have reached your word limit of ${wordCounter.limit} words!`, { toastId: "word-limit" });
|
||||
setInputText(words.slice(0, words.length - 1).join(" "));
|
||||
}
|
||||
}
|
||||
@@ -91,7 +92,7 @@ export default function Writing({
|
||||
color="purple"
|
||||
variant="outline"
|
||||
onClick={() =>
|
||||
onBack({exercise: id, solutions: [{id, solution: inputText}], score: {correct: 100, total: 100, missing: 0}, type})
|
||||
onBack({ exercise: id, solutions: [{ id, solution: inputText }], score: { correct: 100, total: 100, missing: 0 }, type, isPractice })
|
||||
}
|
||||
className="max-w-[200px] self-end w-full">
|
||||
Back
|
||||
@@ -102,10 +103,10 @@ export default function Writing({
|
||||
onClick={() =>
|
||||
onNext({
|
||||
exercise: id,
|
||||
solutions: [{id, solution: inputText.replaceAll(/\s{2,}/g, " ")}],
|
||||
score: {correct: 100, total: 100, missing: 0},
|
||||
solutions: [{ id, solution: inputText.replaceAll(/\s{2,}/g, " ") }],
|
||||
score: { correct: 100, total: 100, missing: 0 },
|
||||
type,
|
||||
module: "writing",
|
||||
module: "writing", isPractice
|
||||
})
|
||||
}
|
||||
className="max-w-[200px] self-end w-full">
|
||||
@@ -177,7 +178,7 @@ export default function Writing({
|
||||
color="purple"
|
||||
variant="outline"
|
||||
onClick={() =>
|
||||
onBack({exercise: id, solutions: [{id, solution: inputText}], score: {correct: 100, total: 100, missing: 0}, type})
|
||||
onBack({ exercise: id, solutions: [{ id, solution: inputText }], score: { correct: 100, total: 100, missing: 0 }, type, isPractice })
|
||||
}
|
||||
className="max-w-[200px] self-end w-full">
|
||||
Back
|
||||
@@ -188,10 +189,10 @@ export default function Writing({
|
||||
onClick={() =>
|
||||
onNext({
|
||||
exercise: id,
|
||||
solutions: [{id, solution: inputText.replaceAll(/\s{2,}/g, " ")}],
|
||||
score: {correct: 100, total: 100, missing: 0},
|
||||
solutions: [{ id, solution: inputText.replaceAll(/\s{2,}/g, " ") }],
|
||||
score: { correct: 100, total: 100, missing: 0 },
|
||||
type,
|
||||
module: "writing",
|
||||
module: "writing", isPractice
|
||||
})
|
||||
}
|
||||
className="max-w-[200px] self-end w-full">
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import React from "react";
|
||||
import {BsClock, BsXCircle} from "react-icons/bs";
|
||||
import { BsClock, BsXCircle } from "react-icons/bs";
|
||||
import clsx from "clsx";
|
||||
import {Stat, User} from "@/interfaces/user";
|
||||
import {Module, Step} from "@/interfaces";
|
||||
import { Stat, User } from "@/interfaces/user";
|
||||
import { Module, Step } from "@/interfaces";
|
||||
import ai_usage from "@/utils/ai.detection";
|
||||
import {calculateBandScore} from "@/utils/score";
|
||||
import { calculateBandScore } from "@/utils/score";
|
||||
import moment from "moment";
|
||||
import {Assignment} from "@/interfaces/results";
|
||||
import {uuidv4} from "@firebase/util";
|
||||
import {useRouter} from "next/router";
|
||||
import {uniqBy} from "lodash";
|
||||
import {sortByModule} from "@/utils/moduleUtils";
|
||||
import {convertToUserSolutions} from "@/utils/stats";
|
||||
import {getExamById} from "@/utils/exams";
|
||||
import {Exam, UserSolution} from "@/interfaces/exam";
|
||||
import { Assignment } from "@/interfaces/results";
|
||||
import { uuidv4 } from "@firebase/util";
|
||||
import { useRouter } from "next/router";
|
||||
import { uniqBy } from "lodash";
|
||||
import { sortByModule } from "@/utils/moduleUtils";
|
||||
import { convertToUserSolutions } from "@/utils/stats";
|
||||
import { getExamById } from "@/utils/exams";
|
||||
import { Exam, UserSolution } from "@/interfaces/exam";
|
||||
import ModuleBadge from "../ModuleBadge";
|
||||
|
||||
const formatTimestamp = (timestamp: string | number) => {
|
||||
@@ -23,9 +23,9 @@ const formatTimestamp = (timestamp: string | number) => {
|
||||
return date.format(formatter);
|
||||
};
|
||||
|
||||
const aggregateScoresByModule = (stats: Stat[]): {module: Module; total: number; missing: number; correct: number}[] => {
|
||||
const aggregateScoresByModule = (stats: Stat[]): { module: Module; total: number; missing: number; 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,
|
||||
@@ -54,7 +54,7 @@ const aggregateScoresByModule = (stats: Stat[]): {module: Module; total: number;
|
||||
},
|
||||
};
|
||||
|
||||
stats.forEach((x) => {
|
||||
stats.filter(x => !x.isPractice).forEach((x) => {
|
||||
scores[x.module!] = {
|
||||
total: scores[x.module!].total + x.score.total,
|
||||
correct: scores[x.module!].correct + x.score.correct,
|
||||
@@ -64,7 +64,7 @@ const aggregateScoresByModule = (stats: Stat[]): {module: Module; total: number;
|
||||
|
||||
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] }));
|
||||
};
|
||||
|
||||
interface StatsGridItemProps {
|
||||
@@ -133,7 +133,7 @@ const StatsGridItem: React.FC<StatsGridItemProps> = ({
|
||||
correct / total < 0.3 && "text-mti-rose",
|
||||
);
|
||||
|
||||
const {timeSpent, inactivity, session} = stats[0];
|
||||
const { timeSpent, inactivity, session } = stats[0];
|
||||
|
||||
const selectExam = () => {
|
||||
if (
|
||||
@@ -247,7 +247,7 @@ const StatsGridItem: React.FC<StatsGridItemProps> = ({
|
||||
<div className={clsx("grid grid-cols-4 gap-2 place-items-start w-full -md:mt-2", examNumber !== undefined && "pr-10")}>
|
||||
{!!assignment &&
|
||||
(assignment.released || assignment.released === undefined) &&
|
||||
aggregatedLevels.map(({module, level}) => <ModuleBadge key={module} module={module} level={level} />)}
|
||||
aggregatedLevels.map(({ module, level }) => <ModuleBadge key={module} module={module} level={level} />)}
|
||||
</div>
|
||||
|
||||
{assignment && (
|
||||
@@ -270,9 +270,9 @@ const StatsGridItem: React.FC<StatsGridItemProps> = ({
|
||||
correct / total >= 0.3 && correct / total < 0.7 && "hover:border-mti-red",
|
||||
correct / total < 0.3 && "hover:border-mti-rose",
|
||||
typeof selectedTrainingExams !== "undefined" &&
|
||||
typeof timestamp === "string" &&
|
||||
selectedTrainingExams.some((exam) => exam.includes(timestamp)) &&
|
||||
"border-2 border-slate-600",
|
||||
typeof timestamp === "string" &&
|
||||
selectedTrainingExams.some((exam) => exam.includes(timestamp)) &&
|
||||
"border-2 border-slate-600",
|
||||
)}
|
||||
onClick={() => {
|
||||
if (!!assignment && !assignment.released) return;
|
||||
@@ -280,8 +280,8 @@ const StatsGridItem: React.FC<StatsGridItemProps> = ({
|
||||
return;
|
||||
}}
|
||||
style={{
|
||||
...(width !== undefined && {width}),
|
||||
...(height !== undefined && {height}),
|
||||
...(width !== undefined && { width }),
|
||||
...(height !== undefined && { height }),
|
||||
}}
|
||||
data-tip={isDisabled ? "This exam is still being evaluated..." : "This exam is still locked by its assigner..."}
|
||||
role="button">
|
||||
@@ -297,8 +297,8 @@ const StatsGridItem: React.FC<StatsGridItemProps> = ({
|
||||
)}
|
||||
data-tip="Your screen size is too small to view previous exams."
|
||||
style={{
|
||||
...(width !== undefined && {width}),
|
||||
...(height !== undefined && {height}),
|
||||
...(width !== undefined && { width }),
|
||||
...(height !== undefined && { height }),
|
||||
}}
|
||||
role="button">
|
||||
{content}
|
||||
|
||||
Reference in New Issue
Block a user