Shuffles fixed
This commit is contained in:
@@ -20,9 +20,10 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
|
|||||||
onNext,
|
onNext,
|
||||||
onBack,
|
onBack,
|
||||||
}) => {
|
}) => {
|
||||||
const { shuffleMaps, exam, partIndex, questionIndex, exerciseIndex } = useExamStore((state) => state);
|
const { shuffles, exam, partIndex, questionIndex, exerciseIndex } = useExamStore((state) => state);
|
||||||
const [answers, setAnswers] = useState<{ id: string; solution: string }[]>(userSolutions);
|
const [answers, setAnswers] = useState<{ id: string; solution: string }[]>(userSolutions);
|
||||||
const hasExamEnded = useExamStore((state) => state.hasExamEnded);
|
const hasExamEnded = useExamStore((state) => state.hasExamEnded);
|
||||||
|
const shuffleMaps = shuffles.find((x) => x.exerciseID == id)?.shuffles;
|
||||||
|
|
||||||
const [currentMCSelection, setCurrentMCSelection] = useState<{ id: string, selection: FillBlanksMCOption }>();
|
const [currentMCSelection, setCurrentMCSelection] = useState<{ id: string, selection: FillBlanksMCOption }>();
|
||||||
|
|
||||||
@@ -77,7 +78,7 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
|
|||||||
};
|
};
|
||||||
const renderLines = (line: string) => {
|
const renderLines = (line: string) => {
|
||||||
return (
|
return (
|
||||||
<div className="text-base leading-5">
|
<div className="text-base leading-5" key={v4()}>
|
||||||
{reactStringReplace(line, /({{\d+}})/g, (match) => {
|
{reactStringReplace(line, /({{\d+}})/g, (match) => {
|
||||||
const id = match.replaceAll(/[\{\}]/g, "");
|
const id = match.replaceAll(/[\{\}]/g, "");
|
||||||
const userSolution = answers.find((x) => x.id === id);
|
const userSolution = answers.find((x) => x.id === id);
|
||||||
@@ -127,10 +128,10 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
|
|||||||
|
|
||||||
const getShuffles = () => {
|
const getShuffles = () => {
|
||||||
let shuffle = {};
|
let shuffle = {};
|
||||||
if (shuffleMaps.length !== 0) {
|
if (shuffleMaps) {
|
||||||
shuffle = {
|
shuffle = {
|
||||||
shuffleMaps: shuffleMaps.filter((map) =>
|
shuffleMaps: shuffleMaps.filter((map) =>
|
||||||
answers.some(answer => answer.id === map.id)
|
answers.some(answer => answer.id === map.questionID)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable @next/next/no-img-element */
|
/* eslint-disable @next/next/no-img-element */
|
||||||
import { MultipleChoiceExercise, MultipleChoiceQuestion } from "@/interfaces/exam";
|
import { MultipleChoiceExercise, MultipleChoiceQuestion, ShuffleMap } from "@/interfaces/exam";
|
||||||
import useExamStore from "@/stores/examStore";
|
import useExamStore from "@/stores/examStore";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
@@ -77,13 +77,15 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
|
|||||||
const {
|
const {
|
||||||
questionIndex,
|
questionIndex,
|
||||||
exam,
|
exam,
|
||||||
shuffleMaps,
|
shuffles,
|
||||||
hasExamEnded,
|
hasExamEnded,
|
||||||
userSolutions: storeUserSolutions,
|
userSolutions: storeUserSolutions,
|
||||||
setQuestionIndex,
|
setQuestionIndex,
|
||||||
setUserSolutions
|
setUserSolutions
|
||||||
} = useExamStore((state) => state);
|
} = useExamStore((state) => state);
|
||||||
|
|
||||||
|
const shuffleMaps = shuffles.find((x) => x.exerciseID == id)?.shuffles;
|
||||||
|
|
||||||
const scrollToTop = () => Array.from(document.getElementsByTagName("body")).forEach((body) => body.scrollTo(0, 0));
|
const scrollToTop = () => Array.from(document.getElementsByTagName("body")).forEach((body) => body.scrollTo(0, 0));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -104,6 +106,16 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
|
|||||||
setAnswers((prev) => [...prev.filter((x) => x.question !== question.id), { option, question: question.id }]);
|
setAnswers((prev) => [...prev.filter((x) => x.question !== question.id), { option, question: question.id }]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getShuffledSolution = (originalSolution: string, questionShuffleMap: ShuffleMap) => {
|
||||||
|
for (const [newPosition, originalPosition] of Object.entries(questionShuffleMap.map)) {
|
||||||
|
if (originalPosition === originalSolution) {
|
||||||
|
return newPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return originalSolution;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const calculateScore = () => {
|
const calculateScore = () => {
|
||||||
const total = questions.length;
|
const total = questions.length;
|
||||||
const correct = answers.filter((x) => {
|
const correct = answers.filter((x) => {
|
||||||
@@ -112,34 +124,25 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
|
|||||||
});
|
});
|
||||||
|
|
||||||
let isSolutionCorrect;
|
let isSolutionCorrect;
|
||||||
if (shuffleMaps.length == 0) {
|
if (!shuffleMaps) {
|
||||||
isSolutionCorrect = matchingQuestion?.solution === x.option;
|
isSolutionCorrect = matchingQuestion?.solution === x.option;
|
||||||
} else {
|
} else {
|
||||||
const shuffleMap = shuffleMaps.find((map) => map.id == x.question)
|
const shuffleMap = shuffleMaps.find((map) => map.questionID == x.question);
|
||||||
isSolutionCorrect = shuffleMap?.map[x.option] == matchingQuestion?.solution;
|
if (shuffleMap) {
|
||||||
|
isSolutionCorrect = getShuffledSolution(x.option, shuffleMap) == matchingQuestion?.solution;
|
||||||
|
}else {
|
||||||
|
isSolutionCorrect = matchingQuestion?.solution === x.option;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return isSolutionCorrect || false;
|
return isSolutionCorrect || false;
|
||||||
}).length;
|
}).length;
|
||||||
const missing = total - correct;
|
const missing = total - correct;
|
||||||
|
|
||||||
return { total, correct, missing };
|
return { total, correct, missing };
|
||||||
};
|
};
|
||||||
|
|
||||||
const getShuffles = () => {
|
|
||||||
let shuffle = {};
|
|
||||||
if (shuffleMaps.length !== 0) {
|
|
||||||
shuffle = {
|
|
||||||
shuffleMaps: shuffleMaps.filter((map) =>
|
|
||||||
answers.some(answer => answer.question === map.id)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return shuffle;
|
|
||||||
}
|
|
||||||
|
|
||||||
const next = () => {
|
const next = () => {
|
||||||
if (questionIndex === questions.length - 1) {
|
if (questionIndex === questions.length - 1) {
|
||||||
onNext({ exercise: id, solutions: answers, score: calculateScore(), type, ...getShuffles() });
|
onNext({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps });
|
||||||
} else {
|
} else {
|
||||||
setQuestionIndex(questionIndex + 1);
|
setQuestionIndex(questionIndex + 1);
|
||||||
}
|
}
|
||||||
@@ -148,7 +151,7 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
|
|||||||
|
|
||||||
const back = () => {
|
const back = () => {
|
||||||
if (questionIndex === 0) {
|
if (questionIndex === 0) {
|
||||||
onBack({ exercise: id, solutions: answers, score: calculateScore(), type, ...getShuffles() });
|
onBack({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps });
|
||||||
} else {
|
} else {
|
||||||
setQuestionIndex(questionIndex - 1);
|
setQuestionIndex(questionIndex - 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ export default function FillBlanksSolutions({
|
|||||||
const total = text.match(/({{\d+}})/g)?.length || 0;
|
const total = text.match(/({{\d+}})/g)?.length || 0;
|
||||||
const correct = correctUserSolutions!.filter((x) => {
|
const correct = correctUserSolutions!.filter((x) => {
|
||||||
const solution = solutions.find((y) => x.id.toString() === y.id.toString())?.solution;
|
const solution = solutions.find((y) => x.id.toString() === y.id.toString())?.solution;
|
||||||
console.log(solution);
|
|
||||||
if (!solution) return false;
|
if (!solution) return false;
|
||||||
|
|
||||||
const option = words.find((w) => {
|
const option = words.find((w) => {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { useEffect, useState } from "react";
|
|||||||
import reactStringReplace from "react-string-replace";
|
import reactStringReplace from "react-string-replace";
|
||||||
import { CommonProps } from ".";
|
import { CommonProps } from ".";
|
||||||
import Button from "../Low/Button";
|
import Button from "../Low/Button";
|
||||||
|
import { v4 } from "uuid";
|
||||||
|
|
||||||
function Question({
|
function Question({
|
||||||
id,
|
id,
|
||||||
@@ -17,34 +18,12 @@ function Question({
|
|||||||
}: MultipleChoiceQuestion & { userSolution: string | undefined; onSelectOption?: (option: string) => void; showSolution?: boolean }) {
|
}: MultipleChoiceQuestion & { userSolution: string | undefined; onSelectOption?: (option: string) => void; showSolution?: boolean }) {
|
||||||
const { userSolutions } = useExamStore((state) => state);
|
const { userSolutions } = useExamStore((state) => state);
|
||||||
|
|
||||||
const getShuffledOptions = (options: { id: string, text: string }[], questionShuffleMap: ShuffleMap) => {
|
|
||||||
const shuffledOptions = ['A', 'B', 'C', 'D'].map(newId => {
|
|
||||||
const originalId = questionShuffleMap.map[newId];
|
|
||||||
const originalOption = options.find(option => option.id === originalId);
|
|
||||||
return {
|
|
||||||
id: newId,
|
|
||||||
text: originalOption!.text
|
|
||||||
};
|
|
||||||
});
|
|
||||||
return shuffledOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getShuffledSolution = (originalSolution: string, questionShuffleMap: ShuffleMap) => {
|
|
||||||
for (const [newPosition, originalPosition] of Object.entries(questionShuffleMap.map)) {
|
|
||||||
if (originalPosition === originalSolution) {
|
|
||||||
return newPosition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return originalSolution;
|
|
||||||
}
|
|
||||||
|
|
||||||
const questionShuffleMap = userSolutions.reduce((foundMap, userSolution) => {
|
const questionShuffleMap = userSolutions.reduce((foundMap, userSolution) => {
|
||||||
if (foundMap) return foundMap;
|
if (foundMap) return foundMap;
|
||||||
return userSolution.shuffleMaps?.find(map => map.id === id) || null;
|
return userSolution.shuffleMaps?.find(map => map.questionID === id) || null;
|
||||||
}, null as ShuffleMap | null);
|
}, null as ShuffleMap | null);
|
||||||
|
|
||||||
const questionOptions = questionShuffleMap ? getShuffledOptions(options as { id: string, text: string }[], questionShuffleMap) : options;
|
const newSolution = questionShuffleMap ? questionShuffleMap?.map[solution] : solution;
|
||||||
const newSolution = questionShuffleMap ? getShuffledSolution(solution, questionShuffleMap) : solution;
|
|
||||||
|
|
||||||
const renderPrompt = (prompt: string) => {
|
const renderPrompt = (prompt: string) => {
|
||||||
return reactStringReplace(prompt, /(<u>.*?<\/u>)/g, (match) => {
|
return reactStringReplace(prompt, /(<u>.*?<\/u>)/g, (match) => {
|
||||||
@@ -70,15 +49,15 @@ function Question({
|
|||||||
{isNaN(Number(id)) ? (
|
{isNaN(Number(id)) ? (
|
||||||
<span>{renderPrompt(prompt).filter((x) => x?.toString() !== "<u>")} </span>
|
<span>{renderPrompt(prompt).filter((x) => x?.toString() !== "<u>")} </span>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-lg">
|
<span className="text-lg" key={v4()}>
|
||||||
<>
|
<>
|
||||||
{id} - <span className="text-lg">{renderPrompt(prompt).filter((x) => x?.toString() !== "<u>")} </span>
|
{id} - <span className="text-lg" key={v4()}>{renderPrompt(prompt).filter((x) => x?.toString() !== "<u>")} </span>
|
||||||
</>
|
</>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<div className="flex flex-wrap gap-4 justify-between">
|
<div className="flex flex-wrap gap-4 justify-between">
|
||||||
{variant === "image" &&
|
{variant === "image" &&
|
||||||
questionOptions.map((option) => (
|
options.map((option) => (
|
||||||
<div
|
<div
|
||||||
key={option?.id}
|
key={option?.id}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
@@ -90,7 +69,7 @@ function Question({
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{variant === "text" &&
|
{variant === "text" &&
|
||||||
questionOptions.map((option) => (
|
options.map((option) => (
|
||||||
<div
|
<div
|
||||||
key={option?.id}
|
key={option?.id}
|
||||||
className={clsx("flex border p-4 rounded-xl gap-2 cursor-pointer bg-white text-base select-none", optionColor(option!.id))}>
|
className={clsx("flex border p-4 rounded-xl gap-2 cursor-pointer bg-white text-base select-none", optionColor(option!.id))}>
|
||||||
@@ -106,14 +85,23 @@ function Question({
|
|||||||
export default function MultipleChoice({ id, type, prompt, questions, userSolutions, onNext, onBack }: MultipleChoiceExercise & CommonProps) {
|
export default function MultipleChoice({ id, type, prompt, questions, userSolutions, onNext, onBack }: MultipleChoiceExercise & CommonProps) {
|
||||||
const { questionIndex, setQuestionIndex, partIndex, exam } = useExamStore((state) => state);
|
const { questionIndex, setQuestionIndex, partIndex, exam } = useExamStore((state) => state);
|
||||||
|
|
||||||
|
const stats = useExamStore((state) => state.userSolutions);
|
||||||
|
|
||||||
const calculateScore = () => {
|
const calculateScore = () => {
|
||||||
const total = questions.length;
|
const total = questions.length;
|
||||||
|
const questionShuffleMap = stats.find((x) => x.exercise == id)?.shuffleMaps;
|
||||||
const correct = userSolutions.filter(
|
const correct = userSolutions.filter(
|
||||||
(x) => questions.find((y) => y.id.toString() === x.question.toString())?.solution === x.option || false,
|
(x) => {
|
||||||
|
if (questionShuffleMap) {
|
||||||
|
const shuffleMap = questionShuffleMap.find((y) => y.questionID === x.question)
|
||||||
|
const originalSol = questions.find((y) => y.id.toString() === x.question.toString())?.solution!;
|
||||||
|
return x.option == shuffleMap?.map[originalSol]
|
||||||
|
} else {
|
||||||
|
return questions.find((y) => y.id.toString() === x.question.toString())?.solution === x.option || false
|
||||||
|
}
|
||||||
|
},
|
||||||
).length;
|
).length;
|
||||||
const missing = total - userSolutions.filter((x) => questions.find((y) => y.id.toString() === x.question.toString())).length;
|
const missing = total - userSolutions.filter((x) => questions.find((y) => y.id.toString() === x.question.toString())).length;
|
||||||
|
|
||||||
return { total, correct, missing };
|
return { total, correct, missing };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
155
src/exams/Level/Shuffle.ts
Normal file
155
src/exams/Level/Shuffle.ts
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
import { Exercise, FillBlanksExercise, FillBlanksMCOption, MultipleChoiceExercise, MultipleChoiceQuestion, ShuffleMap, Shuffles, UserSolution } from "@/interfaces/exam";
|
||||||
|
|
||||||
|
export default function shuffleExamExercise(
|
||||||
|
shuffle: boolean | undefined,
|
||||||
|
exercise: Exercise,
|
||||||
|
showSolutions: boolean,
|
||||||
|
userSolutions: UserSolution[],
|
||||||
|
shuffles: Shuffles[],
|
||||||
|
setShuffles: (maps: Shuffles[]) => void
|
||||||
|
): Exercise {
|
||||||
|
if (!shuffle) {
|
||||||
|
return exercise;
|
||||||
|
}
|
||||||
|
const userSolution = userSolutions.find((x) => x.exercise === exercise.id)!;
|
||||||
|
|
||||||
|
if (exercise.type === "multipleChoice") {
|
||||||
|
return shuffleMultipleChoice(exercise, userSolution, shuffles, setShuffles, showSolutions);
|
||||||
|
} else if (exercise.type === "fillBlanks") {
|
||||||
|
return shuffleFillBlanks(exercise, userSolution, shuffles, setShuffles, showSolutions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return exercise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function shuffleMultipleChoice(
|
||||||
|
exercise: MultipleChoiceExercise,
|
||||||
|
userSolution: UserSolution,
|
||||||
|
shuffles: Shuffles[],
|
||||||
|
setShuffles: (maps: Shuffles[]) => void,
|
||||||
|
showSolutions: boolean,
|
||||||
|
): MultipleChoiceExercise {
|
||||||
|
|
||||||
|
if (typeof userSolution.shuffleMaps === "undefined" || (userSolution.shuffleMaps && userSolution.shuffleMaps.length === 0) && !showSolutions) {
|
||||||
|
const newShuffleMaps: ShuffleMap[] = [];
|
||||||
|
exercise.questions = exercise.questions.map(shuffleQuestion(newShuffleMaps));
|
||||||
|
userSolution!.shuffleMaps = newShuffleMaps;
|
||||||
|
setShuffles([...shuffles.filter((x) => x.exerciseID !== exercise.id), {exerciseID: exercise.id, shuffles: newShuffleMaps}]);
|
||||||
|
} else {
|
||||||
|
exercise.questions = exercise.questions.map(retrieveShuffledQuestion(userSolution.shuffleMaps));
|
||||||
|
}
|
||||||
|
|
||||||
|
return exercise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function shuffleQuestion(newShuffleMaps: ShuffleMap[]) {
|
||||||
|
return (question: MultipleChoiceQuestion): MultipleChoiceQuestion => {
|
||||||
|
const options = [...question.options];
|
||||||
|
const shuffledOptions = fisherYatesShuffle(options);
|
||||||
|
|
||||||
|
const optionMapping: Record<string, string> = {};
|
||||||
|
const newOptions = shuffledOptions.map((option, index) => {
|
||||||
|
const newId = String.fromCharCode(65 + index);
|
||||||
|
optionMapping[option.id] = newId;
|
||||||
|
return { ...option, id: newId };
|
||||||
|
});
|
||||||
|
|
||||||
|
newShuffleMaps.push({ questionID: question.id, map: optionMapping });
|
||||||
|
|
||||||
|
return { ...question, options: newOptions, shuffleMap: optionMapping };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function retrieveShuffledQuestion(shuffleMaps: ShuffleMap[]) {
|
||||||
|
return (question: MultipleChoiceQuestion): MultipleChoiceQuestion => {
|
||||||
|
const questionShuffleMap = shuffleMaps.find(map => map.questionID === question.id);
|
||||||
|
if (questionShuffleMap) {
|
||||||
|
const shuffledOptions = Object.entries(questionShuffleMap.map)
|
||||||
|
.sort(([, a], [, b]) => a.localeCompare(b))
|
||||||
|
.map(([originalId, newId]) => {
|
||||||
|
const originalOption = question.options.find(opt => opt.id === originalId);
|
||||||
|
return { ...originalOption, id: newId };
|
||||||
|
});
|
||||||
|
|
||||||
|
return { ...question, options: shuffledOptions, shuffleMap: questionShuffleMap.map };
|
||||||
|
}
|
||||||
|
return question;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function shuffleFillBlanks(
|
||||||
|
exercise: FillBlanksExercise,
|
||||||
|
userSolution: UserSolution,
|
||||||
|
shuffles: Shuffles[],
|
||||||
|
setShuffles: (maps: Shuffles[]) => void,
|
||||||
|
showSolutions: boolean
|
||||||
|
): FillBlanksExercise {
|
||||||
|
if (typeof userSolution.shuffleMaps === "undefined" || (userSolution.shuffleMaps && userSolution.shuffleMaps.length === 0) && !showSolutions) {
|
||||||
|
const newShuffleMaps: ShuffleMap[] = [];
|
||||||
|
exercise.words = exercise.words.map(shuffleWord(newShuffleMaps));
|
||||||
|
userSolution.shuffleMaps = newShuffleMaps;
|
||||||
|
setShuffles([...shuffles.filter((x) => x.exerciseID !== exercise.id), {exerciseID: exercise.id, shuffles: newShuffleMaps}]);
|
||||||
|
} else {
|
||||||
|
exercise.words = exercise.words.map(retrieveShuffledWord(userSolution.shuffleMaps!));
|
||||||
|
}
|
||||||
|
|
||||||
|
return exercise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function shuffleWord(newShuffleMaps: ShuffleMap[]) {
|
||||||
|
return (word: string | { letter: string; word: string } | FillBlanksMCOption): typeof word => {
|
||||||
|
if (typeof word === 'object' && 'options' in word) {
|
||||||
|
const options = word.options;
|
||||||
|
const originalKeys = Object.keys(options);
|
||||||
|
const shuffledKeys = fisherYatesShuffle(originalKeys);
|
||||||
|
|
||||||
|
const newOptions = shuffledKeys.reduce<typeof options>((acc, key, index) => {
|
||||||
|
acc[key as keyof typeof options] = options[originalKeys[index] as keyof typeof options];
|
||||||
|
return acc;
|
||||||
|
}, {} as typeof options);
|
||||||
|
|
||||||
|
const optionMapping = originalKeys.reduce<Record<string, string>>((acc, key, index) => {
|
||||||
|
acc[key] = shuffledKeys[index];
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
newShuffleMaps.push({ questionID: word.id, map: optionMapping });
|
||||||
|
|
||||||
|
return { ...word, options: newOptions };
|
||||||
|
}
|
||||||
|
return word;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function retrieveShuffledWord(shuffleMaps: ShuffleMap[]) {
|
||||||
|
return (word: string | { letter: string; word: string } | FillBlanksMCOption): typeof word => {
|
||||||
|
if (typeof word === 'object' && 'options' in word) {
|
||||||
|
const shuffleMap = shuffleMaps.find(map => map.questionID === word.id);
|
||||||
|
if (shuffleMap) {
|
||||||
|
const options = word.options;
|
||||||
|
const shuffledOptions = Object.keys(options).reduce<typeof options>((acc, key) => {
|
||||||
|
const shuffledKey = shuffleMap.map[key as keyof typeof options];
|
||||||
|
acc[shuffledKey as keyof typeof options] = options[key as keyof typeof options];
|
||||||
|
return acc;
|
||||||
|
}, {} as typeof options);
|
||||||
|
|
||||||
|
return { ...word, options: shuffledOptions };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return word;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function fisherYatesShuffle<T>(array: T[]): T[] {
|
||||||
|
const shuffled = [...array];
|
||||||
|
for (let i = shuffled.length - 1; i > 0; i--) {
|
||||||
|
const j = Math.floor(Math.random() * (i + 1));
|
||||||
|
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
||||||
|
}
|
||||||
|
return shuffled;
|
||||||
|
}
|
||||||
|
|
||||||
|
const typeCheckWordsMC = (words: any[]): words is FillBlanksMCOption[] => {
|
||||||
|
return Array.isArray(words) && words.every(
|
||||||
|
word => word && typeof word === 'object' && 'id' in word && 'options' in word
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ import { use, useEffect, useState } from "react";
|
|||||||
import TextComponent from "./TextComponent";
|
import TextComponent from "./TextComponent";
|
||||||
import PartDivider from "./PartDivider";
|
import PartDivider from "./PartDivider";
|
||||||
import Timer from "@/components/Medium/Timer";
|
import Timer from "@/components/Medium/Timer";
|
||||||
import { Stat } from "@/interfaces/user";
|
import shuffleExamExercise from "./Shuffle";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
exam: LevelExam;
|
exam: LevelExam;
|
||||||
@@ -40,7 +40,7 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
|
|||||||
const { partIndex, setPartIndex } = useExamStore((state) => state);
|
const { partIndex, setPartIndex } = useExamStore((state) => state);
|
||||||
const { exerciseIndex, setExerciseIndex } = useExamStore((state) => state);
|
const { exerciseIndex, setExerciseIndex } = useExamStore((state) => state);
|
||||||
const [storeQuestionIndex, setStoreQuestionIndex] = useExamStore((state) => [state.questionIndex, state.setQuestionIndex]);
|
const [storeQuestionIndex, setStoreQuestionIndex] = useExamStore((state) => [state.questionIndex, state.setQuestionIndex]);
|
||||||
const [shuffleMaps, setShuffleMaps] = useExamStore((state) => [state.shuffleMaps, state.setShuffleMaps])
|
const [shuffles, setShuffles] = useExamStore((state) => [state.shuffles, state.setShuffles])
|
||||||
const [currentExercise, setCurrentExercise] = useState<Exercise>();
|
const [currentExercise, setCurrentExercise] = useState<Exercise>();
|
||||||
const [showPartDivider, setShowPartDivider] = useState<boolean>(typeof exam.parts[0].intro === "string" && !showSolutions);
|
const [showPartDivider, setShowPartDivider] = useState<boolean>(typeof exam.parts[0].intro === "string" && !showSolutions);
|
||||||
|
|
||||||
@@ -49,11 +49,6 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
|
|||||||
const [contextWord, setContextWord] = useState<string | undefined>(undefined);
|
const [contextWord, setContextWord] = useState<string | undefined>(undefined);
|
||||||
const [contextWordLine, setContextWordLine] = useState<number | undefined>(undefined);
|
const [contextWordLine, setContextWordLine] = useState<number | undefined>(undefined);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (showSolutions && exerciseIndex && exam.shuffle && userSolutions[exerciseIndex].shuffleMaps) {
|
|
||||||
setShuffleMaps(userSolutions[exerciseIndex].shuffleMaps as ShuffleMap[])
|
|
||||||
}
|
|
||||||
}, [showSolutions, exerciseIndex, setShuffleMaps, userSolutions, exam.shuffle])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (hasExamEnded && exerciseIndex === -1) {
|
if (hasExamEnded && exerciseIndex === -1) {
|
||||||
@@ -69,109 +64,21 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
|
|||||||
...exercise,
|
...exercise,
|
||||||
userSolutions: userSolutions.find((x) => x.exercise === exercise.id)?.solutions || [],
|
userSolutions: userSolutions.find((x) => x.exercise === exercise.id)?.solutions || [],
|
||||||
};
|
};
|
||||||
|
if (showSolutions) {
|
||||||
if (exam.shuffle && exercise.type === "multipleChoice" && !showSolutions) {
|
setShuffles([...shuffles.filter((x) => x.exerciseID !== exercise.id), { exerciseID: exercise.id, shuffles: userSolutions.find((x) => x.exercise === exercise.id)!.shuffleMaps!}]);
|
||||||
console.log("Shuffling MC ");
|
|
||||||
const exerciseShuffles = userSolutions[exerciseIndex].shuffleMaps;
|
|
||||||
if (exerciseShuffles && exerciseShuffles.length == 0) {
|
|
||||||
const newShuffleMaps: ShuffleMap[] = [];
|
|
||||||
|
|
||||||
exercise.questions = exercise.questions.map(question => {
|
|
||||||
const options = [...question.options];
|
|
||||||
let shuffledOptions = [...options].sort(() => Math.random() - 0.5);
|
|
||||||
|
|
||||||
const newOptions = options.map((option, index) => ({
|
|
||||||
id: option.id,
|
|
||||||
text: shuffledOptions[index].text
|
|
||||||
}));
|
|
||||||
|
|
||||||
const optionMapping = options.reduce<{ [key: string]: string }>((acc, originalOption) => {
|
|
||||||
const shuffledPosition = newOptions.find(newOpt => newOpt.text === originalOption.text)?.id;
|
|
||||||
if (shuffledPosition) {
|
|
||||||
acc[shuffledPosition] = originalOption.id;
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
newShuffleMaps.push({ id: question.id, map: optionMapping });
|
|
||||||
|
|
||||||
return { ...question, options: newOptions };
|
|
||||||
});
|
|
||||||
|
|
||||||
setShuffleMaps(newShuffleMaps);
|
|
||||||
} else {
|
} else {
|
||||||
console.log("retrieving MC shuffles");
|
exercise = shuffleExamExercise(exam.shuffle, exercise, showSolutions, userSolutions, shuffles, setShuffles);
|
||||||
exercise.questions = exercise.questions.map(question => {
|
|
||||||
const questionShuffleMap = shuffleMaps.find(map => map.id === question.id);
|
|
||||||
if (questionShuffleMap) {
|
|
||||||
const newOptions = question.options.map(option => ({
|
|
||||||
id: option.id,
|
|
||||||
text: question.options.find(o => questionShuffleMap.map[o.id] === option.id)?.text || option.text
|
|
||||||
}));
|
|
||||||
return { ...question, options: newOptions };
|
|
||||||
}
|
}
|
||||||
return question;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (exam.shuffle && exercise.type === "fillBlanks" && typeCheckWordsMC(exercise.words) && !showSolutions) {
|
|
||||||
if (shuffleMaps.length === 0 && !showSolutions) {
|
|
||||||
const newShuffleMaps: ShuffleMap[] = [];
|
|
||||||
console.log("Shuffling Words");
|
|
||||||
exercise.words = exercise.words.map(word => {
|
|
||||||
if ('options' in word) {
|
|
||||||
const options = { ...word.options };
|
|
||||||
const originalKeys = Object.keys(options);
|
|
||||||
const shuffledKeys = [...originalKeys].sort(() => Math.random() - 0.5);
|
|
||||||
|
|
||||||
const newOptions = shuffledKeys.reduce((acc, key, index) => {
|
|
||||||
acc[key as keyof typeof options] = options[originalKeys[index] as keyof typeof options];
|
|
||||||
return acc;
|
|
||||||
}, {} as { [key in keyof typeof options]: string });
|
|
||||||
|
|
||||||
const optionMapping = originalKeys.reduce((acc, key, index) => {
|
|
||||||
acc[key as keyof typeof options] = shuffledKeys[index];
|
|
||||||
return acc;
|
|
||||||
}, {} as { [key in keyof typeof options]: string });
|
|
||||||
|
|
||||||
newShuffleMaps.push({ id: word.id, map: optionMapping });
|
|
||||||
|
|
||||||
return { ...word, options: newOptions };
|
|
||||||
}
|
|
||||||
return word;
|
|
||||||
});
|
|
||||||
|
|
||||||
setShuffleMaps(newShuffleMaps);
|
|
||||||
} else {
|
|
||||||
console.log("Retrieving Words shuffle");
|
|
||||||
exercise.words = exercise.words.map(word => {
|
|
||||||
if ('options' in word) {
|
|
||||||
const shuffleMap = shuffleMaps.find(map => map.id === word.id);
|
|
||||||
if (shuffleMap) {
|
|
||||||
const options = { ...word.options };
|
|
||||||
const shuffledOptions = Object.keys(options).reduce((acc, key) => {
|
|
||||||
const shuffledKey = shuffleMap.map[key as keyof typeof options];
|
|
||||||
acc[shuffledKey as keyof typeof options] = options[key as keyof typeof options];
|
|
||||||
return acc;
|
|
||||||
}, {} as { [key in keyof typeof options]: string });
|
|
||||||
|
|
||||||
return { ...word, options: shuffledOptions };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return word;
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log(exercise);
|
|
||||||
return exercise;
|
return exercise;
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
if (exerciseIndex !== -1) {
|
if (exerciseIndex !== -1) {
|
||||||
setCurrentExercise(getExercise());
|
setCurrentExercise(getExercise());
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [partIndex, exerciseIndex, shuffleMaps, exam.parts[partIndex].context]);
|
}, [partIndex, exerciseIndex, exam.parts[partIndex].context]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -202,7 +109,7 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
|
|||||||
const nextExercise = (solution?: UserSolution) => {
|
const nextExercise = (solution?: UserSolution) => {
|
||||||
scrollToTop();
|
scrollToTop();
|
||||||
if (solution) {
|
if (solution) {
|
||||||
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), { ...solution, module: "level", exam: exam.id }]);
|
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), { ...solution, module: "level" as Module, exam: exam.id, shuffleMaps: exam.shuffle ? [...shuffles.find((x) => x.exerciseID == currentExercise?.id)?.shuffles!] : [] }]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*if (storeQuestionIndex > 0 || currentExercise?.type == "fillBlanks") {
|
/*if (storeQuestionIndex > 0 || currentExercise?.type == "fillBlanks") {
|
||||||
@@ -247,11 +154,7 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
|
|||||||
setHasExamEnded(false);
|
setHasExamEnded(false);
|
||||||
|
|
||||||
if (solution) {
|
if (solution) {
|
||||||
let stat = { ...solution, module: "level" as Module, exam: exam.id }
|
onFinish([...userSolutions.filter((x) => x.exercise !== solution.exercise), { ...solution, module: "level" as Module, exam: exam.id, shuffleMaps: exam.shuffle ? [...shuffles.find((x) => x.exerciseID == currentExercise?.id)?.shuffles!] : [] }]);
|
||||||
if (exam.shuffle) {
|
|
||||||
stat.shuffleMaps = shuffleMaps
|
|
||||||
}
|
|
||||||
onFinish([...userSolutions.filter((x) => x.exercise !== solution.exercise), { ...stat }]);
|
|
||||||
} else {
|
} else {
|
||||||
onFinish(userSolutions);
|
onFinish(userSolutions);
|
||||||
}
|
}
|
||||||
@@ -260,7 +163,7 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
|
|||||||
const previousExercise = (solution?: UserSolution) => {
|
const previousExercise = (solution?: UserSolution) => {
|
||||||
scrollToTop();
|
scrollToTop();
|
||||||
if (solution) {
|
if (solution) {
|
||||||
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), { ...solution, module: "level", exam: exam.id }]);
|
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), { ...solution, module: "level" as Module, exam: exam.id, shuffleMaps: exam.shuffle ? [...shuffles.find((x) => x.exerciseID == currentExercise?.id)?.shuffles!] : [] }]);
|
||||||
}
|
}
|
||||||
|
|
||||||
setExerciseIndex(exerciseIndex - 1);
|
setExerciseIndex(exerciseIndex - 1);
|
||||||
@@ -383,7 +286,7 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
|
|||||||
module="level"
|
module="level"
|
||||||
totalExercises={countExercises(exam.parts.flatMap((x) => x.exercises))}
|
totalExercises={countExercises(exam.parts.flatMap((x) => x.exercises))}
|
||||||
disableTimer={showSolutions || editing}
|
disableTimer={showSolutions || editing}
|
||||||
showTimer={typeof exam.parts[0].intro === "undefined"}
|
showTimer={false}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
|||||||
@@ -303,8 +303,13 @@ export interface MultipleChoiceQuestion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ShuffleMap {
|
export interface ShuffleMap {
|
||||||
id: string;
|
questionID: string;
|
||||||
map: {
|
map: {
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Shuffles {
|
||||||
|
exerciseID: string;
|
||||||
|
shuffles: ShuffleMap[]
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable @next/next/no-img-element */
|
/* eslint-disable @next/next/no-img-element */
|
||||||
import {Module} from "@/interfaces";
|
import { Module } from "@/interfaces";
|
||||||
import {useEffect, useState} from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import AbandonPopup from "@/components/AbandonPopup";
|
import AbandonPopup from "@/components/AbandonPopup";
|
||||||
import Layout from "@/components/High/Layout";
|
import Layout from "@/components/High/Layout";
|
||||||
@@ -12,15 +12,15 @@ import Selection from "@/exams/Selection";
|
|||||||
import Speaking from "@/exams/Speaking";
|
import Speaking from "@/exams/Speaking";
|
||||||
import Writing from "@/exams/Writing";
|
import Writing from "@/exams/Writing";
|
||||||
import useUser from "@/hooks/useUser";
|
import useUser from "@/hooks/useUser";
|
||||||
import {Exam, LevelExam, UserSolution, Variant} from "@/interfaces/exam";
|
import { Exam, LevelExam, UserSolution, Variant } from "@/interfaces/exam";
|
||||||
import {Stat} from "@/interfaces/user";
|
import { Stat } from "@/interfaces/user";
|
||||||
import useExamStore from "@/stores/examStore";
|
import useExamStore from "@/stores/examStore";
|
||||||
import {evaluateSpeakingAnswer, evaluateWritingAnswer} from "@/utils/evaluation";
|
import { evaluateSpeakingAnswer, evaluateWritingAnswer } from "@/utils/evaluation";
|
||||||
import {defaultExamUserSolutions, getExam} from "@/utils/exams";
|
import { defaultExamUserSolutions, getExam } from "@/utils/exams";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import {useRouter} from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import {toast, ToastContainer} from "react-toastify";
|
import { toast, ToastContainer } from "react-toastify";
|
||||||
import {v4 as uuidv4} from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import useSessions from "@/hooks/useSessions";
|
import useSessions from "@/hooks/useSessions";
|
||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from "short-unique-id";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
@@ -29,7 +29,7 @@ interface Props {
|
|||||||
page: "exams" | "exercises";
|
page: "exams" | "exercises";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ExamPage({page}: Props) {
|
export default function ExamPage({ page }: Props) {
|
||||||
const [variant, setVariant] = useState<Variant>("full");
|
const [variant, setVariant] = useState<Variant>("full");
|
||||||
const [avoidRepeated, setAvoidRepeated] = useState(false);
|
const [avoidRepeated, setAvoidRepeated] = useState(false);
|
||||||
const [hasBeenUploaded, setHasBeenUploaded] = useState(false);
|
const [hasBeenUploaded, setHasBeenUploaded] = useState(false);
|
||||||
@@ -44,20 +44,21 @@ export default function ExamPage({page}: Props) {
|
|||||||
const assignment = useExamStore((state) => state.assignment);
|
const assignment = useExamStore((state) => state.assignment);
|
||||||
const initialTimeSpent = useExamStore((state) => state.timeSpent);
|
const initialTimeSpent = useExamStore((state) => state.timeSpent);
|
||||||
|
|
||||||
const {exam, setExam} = useExamStore((state) => state);
|
const { exam, setExam } = useExamStore((state) => state);
|
||||||
const {exams, setExams} = useExamStore((state) => state);
|
const { exams, setExams } = useExamStore((state) => state);
|
||||||
const {sessionId, setSessionId} = useExamStore((state) => state);
|
const { sessionId, setSessionId } = useExamStore((state) => state);
|
||||||
const {partIndex, setPartIndex} = useExamStore((state) => state);
|
const { partIndex, setPartIndex } = useExamStore((state) => state);
|
||||||
const {moduleIndex, setModuleIndex} = useExamStore((state) => state);
|
const { moduleIndex, setModuleIndex } = useExamStore((state) => state);
|
||||||
const {questionIndex, setQuestionIndex} = useExamStore((state) => state);
|
const { questionIndex, setQuestionIndex } = useExamStore((state) => state);
|
||||||
const {exerciseIndex, setExerciseIndex} = useExamStore((state) => state);
|
const { exerciseIndex, setExerciseIndex } = useExamStore((state) => state);
|
||||||
const {userSolutions, setUserSolutions} = useExamStore((state) => state);
|
const { userSolutions, setUserSolutions } = useExamStore((state) => state);
|
||||||
const {showSolutions, setShowSolutions} = useExamStore((state) => state);
|
const { showSolutions, setShowSolutions } = useExamStore((state) => state);
|
||||||
const {selectedModules, setSelectedModules} = useExamStore((state) => state);
|
const { selectedModules, setSelectedModules } = useExamStore((state) => state);
|
||||||
const {inactivity, setInactivity} = useExamStore((state) => state);
|
const { inactivity, setInactivity } = useExamStore((state) => state);
|
||||||
const {bgColor, setBgColor} = useExamStore((state) => state);
|
const { bgColor, setBgColor } = useExamStore((state) => state);
|
||||||
|
const setShuffleMaps = useExamStore((state) => state.setShuffles)
|
||||||
|
|
||||||
const {user} = useUser({redirectTo: "/login"});
|
const { user } = useUser({ redirectTo: "/login" });
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -260,11 +261,11 @@ export default function ExamPage({page}: Props) {
|
|||||||
date: new Date().getTime(),
|
date: new Date().getTime(),
|
||||||
isDisabled: solution.isDisabled,
|
isDisabled: solution.isDisabled,
|
||||||
shuffleMaps: solution.shuffleMaps,
|
shuffleMaps: solution.shuffleMaps,
|
||||||
...(assignment ? {assignment: assignment.id} : {}),
|
...(assignment ? { assignment: assignment.id } : {}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
axios
|
axios
|
||||||
.post<{ok: boolean}>("/api/stats", newStats)
|
.post<{ ok: boolean }>("/api/stats", newStats)
|
||||||
.then((response) => setHasBeenUploaded(response.data.ok))
|
.then((response) => setHasBeenUploaded(response.data.ok))
|
||||||
.catch(() => setHasBeenUploaded(false));
|
.catch(() => setHasBeenUploaded(false));
|
||||||
}
|
}
|
||||||
@@ -282,9 +283,9 @@ export default function ExamPage({page}: Props) {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [statsAwaitingEvaluation]);
|
}, [statsAwaitingEvaluation]);
|
||||||
|
|
||||||
useEffect(()=> {
|
useEffect(() => {
|
||||||
|
|
||||||
if(exam && exam.module === "level" && exam.parts[0].intro && !showSolutions) {
|
if (exam && exam.module === "level" && exam.parts[0].intro && !showSolutions) {
|
||||||
setBgColor("bg-ielts-level-light");
|
setBgColor("bg-ielts-level-light");
|
||||||
}
|
}
|
||||||
}, [exam, showSolutions, setBgColor])
|
}, [exam, showSolutions, setBgColor])
|
||||||
@@ -332,7 +333,7 @@ export default function ExamPage({page}: Props) {
|
|||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
return Object.assign(exam, {parts});
|
return Object.assign(exam, { parts });
|
||||||
}
|
}
|
||||||
|
|
||||||
const exercises = exam.exercises.map((x) =>
|
const exercises = exam.exercises.map((x) =>
|
||||||
@@ -340,7 +341,7 @@ export default function ExamPage({page}: Props) {
|
|||||||
userSolutions: userSolutions.find((y) => x.id === y.exercise)?.solutions,
|
userSolutions: userSolutions.find((y) => x.id === y.exercise)?.solutions,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
return Object.assign(exam, {exercises});
|
return Object.assign(exam, { exercises });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onFinish = async (solutions: UserSolution[]) => {
|
const onFinish = async (solutions: UserSolution[]) => {
|
||||||
@@ -348,6 +349,8 @@ export default function ExamPage({page}: Props) {
|
|||||||
const solutionExams = solutions.map((x) => x.exam);
|
const solutionExams = solutions.map((x) => x.exam);
|
||||||
|
|
||||||
let newSolutions = [...solutions];
|
let newSolutions = [...solutions];
|
||||||
|
console.log("ON finish CALLEDDDDDDDDDDDDDDDDDDDDDDDDDDDD");
|
||||||
|
console.log(newSolutions);
|
||||||
|
|
||||||
if (exam && !solutionExams.includes(exam.id)) return;
|
if (exam && !solutionExams.includes(exam.id)) return;
|
||||||
|
|
||||||
@@ -395,7 +398,7 @@ export default function ExamPage({page}: Props) {
|
|||||||
correct: number;
|
correct: number;
|
||||||
}[] => {
|
}[] => {
|
||||||
const scores: {
|
const scores: {
|
||||||
[key in Module]: {total: number; missing: number; correct: number};
|
[key in Module]: { total: number; missing: number; correct: number };
|
||||||
} = {
|
} = {
|
||||||
reading: {
|
reading: {
|
||||||
total: 0,
|
total: 0,
|
||||||
@@ -437,7 +440,7 @@ export default function ExamPage({page}: Props) {
|
|||||||
|
|
||||||
return Object.keys(scores)
|
return Object.keys(scores)
|
||||||
.filter((x) => scores[x as Module].total > 0)
|
.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 = () => {
|
const renderScreen = () => {
|
||||||
@@ -478,10 +481,13 @@ export default function ExamPage({page}: Props) {
|
|||||||
const indexB = exerciseOrderMap.get(b.exercise) ?? Infinity;
|
const indexB = exerciseOrderMap.get(b.exercise) ?? Infinity;
|
||||||
return indexA - indexB;
|
return indexA - indexB;
|
||||||
});
|
});
|
||||||
|
console.log(orderedSolutions);
|
||||||
setUserSolutions(orderedSolutions);
|
setUserSolutions(orderedSolutions);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
setUserSolutions(userSolutions);
|
setUserSolutions(userSolutions);
|
||||||
}
|
}
|
||||||
|
setShuffleMaps([]);
|
||||||
setShowSolutions(true);
|
setShowSolutions(true);
|
||||||
setModuleIndex(index || 0);
|
setModuleIndex(index || 0);
|
||||||
setExerciseIndex(["reading", "listening"].includes(exams[0].module) ? -1 : 0);
|
setExerciseIndex(["reading", "listening"].includes(exams[0].module) ? -1 : 0);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {Module} from "@/interfaces";
|
import {Module} from "@/interfaces";
|
||||||
import {Exam, ShuffleMap, UserSolution} from "@/interfaces/exam";
|
import {Exam, ShuffleMap, Shuffles, UserSolution} from "@/interfaces/exam";
|
||||||
import {Assignment} from "@/interfaces/results";
|
import {Assignment} from "@/interfaces/results";
|
||||||
import {create} from "zustand";
|
import {create} from "zustand";
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ export interface ExamState {
|
|||||||
exerciseIndex: number;
|
exerciseIndex: number;
|
||||||
questionIndex: number;
|
questionIndex: number;
|
||||||
inactivity: number;
|
inactivity: number;
|
||||||
shuffleMaps: ShuffleMap[];
|
shuffles: Shuffles[];
|
||||||
bgColor: string;
|
bgColor: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ export interface ExamFunctions {
|
|||||||
setExerciseIndex: (exerciseIndex: number) => void;
|
setExerciseIndex: (exerciseIndex: number) => void;
|
||||||
setQuestionIndex: (questionIndex: number) => void;
|
setQuestionIndex: (questionIndex: number) => void;
|
||||||
setInactivity: (inactivity: number) => void;
|
setInactivity: (inactivity: number) => void;
|
||||||
setShuffleMaps: (shuffleMaps: ShuffleMap[]) => void;
|
setShuffles: (shuffles: Shuffles[]) => void;
|
||||||
setBgColor: (bgColor: string) => void;
|
setBgColor: (bgColor: string) => void;
|
||||||
reset: () => void;
|
reset: () => void;
|
||||||
}
|
}
|
||||||
@@ -57,7 +57,7 @@ export const initialState: ExamState = {
|
|||||||
exerciseIndex: -1,
|
exerciseIndex: -1,
|
||||||
questionIndex: 0,
|
questionIndex: 0,
|
||||||
inactivity: 0,
|
inactivity: 0,
|
||||||
shuffleMaps: [],
|
shuffles: [],
|
||||||
bgColor: "bg-white"
|
bgColor: "bg-white"
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -78,8 +78,8 @@ const useExamStore = create<ExamState & ExamFunctions>((set) => ({
|
|||||||
setExerciseIndex: (exerciseIndex: number) => set(() => ({exerciseIndex})),
|
setExerciseIndex: (exerciseIndex: number) => set(() => ({exerciseIndex})),
|
||||||
setQuestionIndex: (questionIndex: number) => set(() => ({questionIndex})),
|
setQuestionIndex: (questionIndex: number) => set(() => ({questionIndex})),
|
||||||
setInactivity: (inactivity: number) => set(() => ({inactivity})),
|
setInactivity: (inactivity: number) => set(() => ({inactivity})),
|
||||||
setShuffleMaps: (shuffleMaps) => set(() => ({shuffleMaps})),
|
setShuffles: (shuffles: Shuffles[]) => set(() => ({shuffles})),
|
||||||
setBgColor: (bgColor) => set(()=> ({bgColor})),
|
setBgColor: (bgColor) => set(() => ({bgColor})),
|
||||||
|
|
||||||
reset: () => set(() => initialState),
|
reset: () => set(() => initialState),
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user