Redesigned the MatchSentences exercise
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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}`}
|
||||
<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(
|
||||
"flex items-center justify-end gap-2 cursor-pointer",
|
||||
userSolutions.find((x) => x.question === id)?.option === solution ? "text-green-500" : "text-red-500",
|
||||
"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",
|
||||
)}>
|
||||
<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)} />
|
||||
{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">
|
||||
|
||||
@@ -75,7 +75,8 @@ 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">
|
||||
<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
|
||||
@@ -84,6 +85,18 @@ export default function MultipleChoice({prompt, questions, userSolutions, onNext
|
||||
/>
|
||||
)}
|
||||
</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">
|
||||
<Button color="green" variant="outline" onClick={back} className="max-w-[200px] w-full">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
11
yarn.lock
11
yarn.lock
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user