Redesigned the MatchSentences exercise

This commit is contained in:
Tiago Ribeiro
2023-06-22 22:28:29 +01:00
parent fe4a97ec85
commit 79ed521703
9 changed files with 146 additions and 86 deletions

View File

@@ -40,6 +40,7 @@
"react-player": "^2.12.0",
"react-string-replace": "^1.1.0",
"react-toastify": "^9.1.2",
"react-xarrows": "^2.0.2",
"swr": "^2.1.3",
"typescript": "4.9.5",
"uuid": "^9.0.0",

View File

@@ -7,6 +7,7 @@ import {Fragment, useState} from "react";
import LineTo from "react-lineto";
import {CommonProps} from ".";
import Button from "../Low/Button";
import Xarrow from "react-xarrows";
export default function MatchSentences({id, options, type, prompt, sentences, userSolutions, onNext, onBack}: MatchSentencesExercise & CommonProps) {
const [selectedQuestion, setSelectedQuestion] = useState<string>();
@@ -32,8 +33,8 @@ export default function MatchSentences({id, options, type, prompt, sentences, us
return (
<>
<div className="flex flex-col items-center gap-8">
<span className="text-base md:text-lg font-medium text-center px-2 md:px-4 lg:px-48">
<div className="flex flex-col gap-4 mt-4 h-full mb-20">
<span className="text-sm w-full leading-6">
{prompt.split("\\n").map((line, index) => (
<Fragment key={index}>
{line}
@@ -41,55 +42,44 @@ export default function MatchSentences({id, options, type, prompt, sentences, us
</Fragment>
))}
</span>
<div className="grid grid-cols-2 gap-16 place-items-center">
<div className="flex flex-col gap-1">
<div className="flex gap-8 w-full items-center justify-between bg-mti-gray-smoke rounded-xl px-24 py-6">
<div className="flex flex-col gap-4">
{sentences.map(({sentence, id, color}) => (
<div
key={`question_${id}`}
className="flex items-center justify-end gap-2 cursor-pointer"
onClick={() => setSelectedQuestion((prev) => (prev === id ? undefined : id))}>
<span>
<span className="font-semibold">{id}.</span> {sentence}{" "}
</span>
<div
style={{borderColor: color, backgroundColor: selectedQuestion === id ? color : "transparent"}}
className={clsx("border-2 border-blue-500 w-4 h-4 rounded-full", id)}
/>
<div key={`question_${id}`} className="flex items-center justify-end gap-2 cursor-pointer">
<span>{sentence} </span>
<button
id={id}
onClick={() => setSelectedQuestion((prev) => (prev === id ? undefined : id))}
className={clsx(
"bg-mti-green-ultralight text-mti-green hover:text-white hover:bg-mti-green w-8 h-8 rounded-full z-10",
"transition duration-300 ease-in-out",
selectedQuestion === id && "!text-white bg-mti-green",
id,
)}>
{id}
</button>
</div>
))}
</div>
<div className="flex flex-col gap-1">
<div className="flex flex-col gap-4">
{options.map(({sentence, id}) => (
<div
key={`answer_${id}`}
className={clsx("flex items-center justify-start gap-2 cursor-pointer")}
onClick={() => selectOption(id)}>
<div
style={
answers.find((x) => x.option === id)
? {
border: `2px solid ${getSentenceColor(answers.find((x) => x.option === id)!.question)}`,
}
: {}
}
className={clsx("border-2 border-green-500 bg-transparent w-4 h-4 rounded-full", id)}
/>
<span>
<span className="font-semibold">{id}.</span> {sentence}{" "}
</span>
<div key={`answer_${id}`} className={clsx("flex items-center justify-start gap-2 cursor-pointer")}>
<button
id={id}
onClick={() => selectOption(id)}
className={clsx(
"bg-mti-green-ultralight text-mti-green hover:text-white hover:bg-mti-green w-8 h-8 rounded-full z-10",
"transition duration-300 ease-in-out",
id,
)}>
{id}
</button>
<span>{sentence}</span>
</div>
))}
</div>
{answers.map((solution, index) => (
<div key={`solution_${index}`} className="absolute">
<LineTo
className="rounded-full"
from={solution.question}
to={solution.option}
borderColor={sentences.find((x) => x.id === solution.question)!.color}
borderWidth={5}
/>
</div>
<Xarrow key={index} start={solution.question} end={solution.option} lineColor="#307912" showHead={false} />
))}
</div>
</div>

View File

@@ -7,12 +7,13 @@ import ProgressBar from "../Low/ProgressBar";
interface Props {
minTimer: number;
module: Module;
label?: string;
exerciseIndex: number;
totalExercises: number;
disableTimer?: boolean;
}
export default function ModuleTitle({minTimer, module, exerciseIndex, totalExercises, disableTimer = false}: Props) {
export default function ModuleTitle({minTimer, module, label, exerciseIndex, totalExercises, disableTimer = false}: Props) {
const [timer, setTimer] = useState(minTimer * 60);
useEffect(() => {
@@ -55,7 +56,9 @@ export default function ModuleTitle({minTimer, module, exerciseIndex, totalExerc
<div className="w-12 h-12 bg-mti-gray-smoke flex items-center justify-center rounded-lg">{moduleIcon[module]}</div>
<div className="flex flex-col gap-3 w-full">
<div className="w-full flex justify-between">
<span className="text-base font-semibold">{moduleLabels[module]} exam</span>
<span className="text-base font-semibold">
{moduleLabels[module]} exam {label && `- ${label}`}
</span>
<span className="text-xs font-normal self-end text-mti-gray-davy">
Question {exerciseIndex}/{totalExercises}
</span>

View File

@@ -82,6 +82,17 @@ export default function FillBlanksSolutions({prompt, solutions, text, userSoluti
</p>
))}
</span>
<div className="flex gap-4 items-center">
<div className="flex gap-2 items-center">
<div className="w-4 h-4 rounded-full bg-mti-green" />= Correct
</div>
<div className="flex gap-2 items-center">
<div className="w-4 h-4 rounded-full bg-mti-blue" />= Unanswered
</div>
<div className="flex gap-2 items-center">
<div className="w-4 h-4 rounded-full bg-mti-orange" />= Wrong
</div>
</div>
</div>
<div className="self-end flex justify-between w-full gap-8 absolute bottom-8 left-0 px-8">

View File

@@ -7,12 +7,13 @@ import {mdiArrowLeft, mdiArrowRight} from "@mdi/js";
import Icon from "@mdi/react";
import {Fragment} from "react";
import Button from "../Low/Button";
import Xarrow from "react-xarrows";
export default function MatchSentencesSolutions({options, prompt, sentences, userSolutions, onNext, onBack}: MatchSentencesExercise & CommonProps) {
return (
<>
<div className="flex flex-col items-center gap-8">
<span className="text-base md:text-lg font-medium text-center px-2 md:px-4 lg:px-48">
<div className="flex flex-col gap-4 mt-4 h-full mb-20">
<span className="text-sm w-full leading-6">
{prompt.split("\\n").map((line, index) => (
<Fragment key={index}>
{line}
@@ -20,48 +21,67 @@ export default function MatchSentencesSolutions({options, prompt, sentences, use
</Fragment>
))}
</span>
<div className="grid grid-cols-2 gap-16 place-items-center">
<div className="flex flex-col gap-1">
{sentences.map(({sentence, id, color, solution}) => (
<div
key={`question_${id}`}
className={clsx(
"flex items-center justify-end gap-2 cursor-pointer",
userSolutions.find((x) => x.question === id)?.option === solution ? "text-green-500" : "text-red-500",
)}>
<span>
<span className="font-semibold">{id}.</span> {sentence}{" "}
</span>
<div style={{borderColor: color}} className={clsx("border-2 border-blue-500 w-4 h-4 rounded-full", id)} />
<div className="flex gap-8 w-full items-center justify-between bg-mti-gray-smoke rounded-xl px-24 py-6">
<div className="flex flex-col gap-4">
{sentences.map(({sentence, id, solution}) => (
<div key={`question_${id}`} className="flex items-center justify-end gap-2 cursor-pointer">
<span>{sentence} </span>
<button
id={id}
className={clsx(
"w-8 h-8 rounded-full z-10 text-white",
"transition duration-300 ease-in-out",
!userSolutions.find((x) => x.question === id) && "!bg-mti-blue",
userSolutions.find((x) => x.question === id)?.option === solution && "bg-mti-green",
userSolutions.find((x) => x.question === id)?.option !== solution && "bg-mti-orange",
)}>
{id}
</button>
</div>
))}
</div>
<div className="flex flex-col gap-1">
<div className="flex flex-col gap-4">
{options.map(({sentence, id}) => (
<div key={`answer_${id}`} className={clsx("flex items-center justify-start gap-2 cursor-pointer")}>
<div
style={
sentences.find((x) => x.solution === id)
? {
border: `2px solid ${sentences.find((x) => x.solution === id)!.color}`,
}
: {}
}
className={clsx("border-2 border-green-500 bg-transparent w-4 h-4 rounded-full", id)}
/>
<span>
<span className="font-semibold">{id}.</span> {sentence}{" "}
</span>
<button
id={id}
className={clsx(
"bg-mti-green-ultralight text-mti-green hover:text-white hover:bg-mti-green w-8 h-8 rounded-full z-10",
"transition duration-300 ease-in-out",
)}>
{id}
</button>
<span>{sentence}</span>
</div>
))}
</div>
{sentences.map((sentence, index) => (
<div key={`solution_${index}`} className="absolute">
<LineTo className="rounded-full" from={sentence.id} to={sentence.solution} borderColor={sentence.color} borderWidth={5} />
</div>
<Xarrow
key={index}
start={sentence.id}
end={sentence.solution}
lineColor={
!userSolutions.find((x) => x.question === sentence.id)
? "#0696ff"
: userSolutions.find((x) => x.question === sentence.id)?.option === sentence.solution
? "#307912"
: "#FF6000"
}
showHead={false}
/>
))}
</div>
<div className="flex gap-4 items-center">
<div className="flex gap-2 items-center">
<div className="w-4 h-4 rounded-full bg-mti-green" />= Correct
</div>
<div className="flex gap-2 items-center">
<div className="w-4 h-4 rounded-full bg-mti-blue" />= Unanswered
</div>
<div className="flex gap-2 items-center">
<div className="w-4 h-4 rounded-full bg-mti-orange" />= Wrong
</div>
</div>
</div>
<div className="self-end flex justify-between w-full gap-8 absolute bottom-8 left-0 px-8">

View File

@@ -75,14 +75,27 @@ export default function MultipleChoice({prompt, questions, userSolutions, onNext
return (
<>
<div className="flex flex-col gap-2 mt-4 h-full mb-20 bg-mti-gray-smoke rounded-xl px-16 py-8">
<span className="text-xl font-semibold">{prompt}</span>
{questionIndex < questions.length && (
<Question
{...questions[questionIndex]}
userSolution={userSolutions.find((x) => questions[questionIndex].id === x.question)?.option}
/>
)}
<div className="flex flex-col gap-4 w-full h-full mb-20">
<div className="flex flex-col gap-2 mt-4 h-full bg-mti-gray-smoke rounded-xl px-16 py-8">
<span className="text-xl font-semibold">{prompt}</span>
{questionIndex < questions.length && (
<Question
{...questions[questionIndex]}
userSolution={userSolutions.find((x) => questions[questionIndex].id === x.question)?.option}
/>
)}
</div>
<div className="flex gap-4 items-center">
<div className="flex gap-2 items-center">
<div className="w-4 h-4 rounded-full bg-mti-green" />= Correct
</div>
<div className="flex gap-2 items-center">
<div className="w-4 h-4 rounded-full bg-mti-blue" />= Unanswered
</div>
<div className="flex gap-2 items-center">
<div className="w-4 h-4 rounded-full bg-mti-orange" />= Wrong
</div>
</div>
</div>
<div className="self-end flex justify-between w-full gap-8 absolute bottom-8 left-0 px-8">

View File

@@ -110,6 +110,17 @@ export default function WriteBlanksSolutions({
</p>
))}
</span>
<div className="flex gap-4 items-center">
<div className="flex gap-2 items-center">
<div className="w-4 h-4 rounded-full bg-mti-green" />= Correct
</div>
<div className="flex gap-2 items-center">
<div className="w-4 h-4 rounded-full bg-mti-blue" />= Unanswered
</div>
<div className="flex gap-2 items-center">
<div className="w-4 h-4 rounded-full bg-mti-orange" />= Wrong
</div>
</div>
</div>
<div className="self-end flex justify-between w-full gap-8 absolute bottom-8 left-0 px-8">

View File

@@ -4,6 +4,7 @@ import Icon from "@mdi/react";
import {mdiArrowRight, mdiNotebook} from "@mdi/js";
import clsx from "clsx";
import {infoButtonStyle} from "@/constants/buttonStyles";
import {convertCamelCaseToReadable} from "@/utils/string";
import {Dialog, Transition} from "@headlessui/react";
import {renderExercise} from "@/components/Exercises";
import {renderSolution} from "@/components/Solutions";
@@ -148,6 +149,7 @@ export default function Reading({exam, showSolutions = false, onFinish}: Props)
module="reading"
totalExercises={exam.exercises.length}
disableTimer={showSolutions}
label={exerciseIndex === -1 ? undefined : convertCamelCaseToReadable(exam.exercises[exerciseIndex].type)}
/>
{exerciseIndex === -1 && renderText()}
{exerciseIndex > -1 &&

View File

@@ -873,7 +873,7 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190"
integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==
"@types/prop-types@*":
"@types/prop-types@*", "@types/prop-types@^15.7.3":
version "15.7.5"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
@@ -3178,6 +3178,15 @@ react-transition-group@^4.4.1:
loose-envify "^1.4.0"
prop-types "^15.6.2"
react-xarrows@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/react-xarrows/-/react-xarrows-2.0.2.tgz#7555687612339eaefd4ed55fc5c63f2302726d9c"
integrity sha512-tDlAqaxHNmy0vegW/6NdhoWyXJq1LANX/WUAlHyzoHe9BwFVnJPPDghmDjYeVr7XWFmBrVTUrHsrW7GKYI6HtQ==
dependencies:
"@types/prop-types" "^15.7.3"
lodash "^4.17.21"
prop-types "^15.7.2"
react@17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"