Redesigned the MatchSentences exercise
This commit is contained in:
@@ -40,6 +40,7 @@
|
|||||||
"react-player": "^2.12.0",
|
"react-player": "^2.12.0",
|
||||||
"react-string-replace": "^1.1.0",
|
"react-string-replace": "^1.1.0",
|
||||||
"react-toastify": "^9.1.2",
|
"react-toastify": "^9.1.2",
|
||||||
|
"react-xarrows": "^2.0.2",
|
||||||
"swr": "^2.1.3",
|
"swr": "^2.1.3",
|
||||||
"typescript": "4.9.5",
|
"typescript": "4.9.5",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {Fragment, useState} from "react";
|
|||||||
import LineTo from "react-lineto";
|
import LineTo from "react-lineto";
|
||||||
import {CommonProps} from ".";
|
import {CommonProps} from ".";
|
||||||
import Button from "../Low/Button";
|
import Button from "../Low/Button";
|
||||||
|
import Xarrow from "react-xarrows";
|
||||||
|
|
||||||
export default function MatchSentences({id, options, type, prompt, sentences, userSolutions, onNext, onBack}: MatchSentencesExercise & CommonProps) {
|
export default function MatchSentences({id, options, type, prompt, sentences, userSolutions, onNext, onBack}: MatchSentencesExercise & CommonProps) {
|
||||||
const [selectedQuestion, setSelectedQuestion] = useState<string>();
|
const [selectedQuestion, setSelectedQuestion] = useState<string>();
|
||||||
@@ -32,8 +33,8 @@ export default function MatchSentences({id, options, type, prompt, sentences, us
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex flex-col items-center gap-8">
|
<div className="flex flex-col gap-4 mt-4 h-full mb-20">
|
||||||
<span className="text-base md:text-lg font-medium text-center px-2 md:px-4 lg:px-48">
|
<span className="text-sm w-full leading-6">
|
||||||
{prompt.split("\\n").map((line, index) => (
|
{prompt.split("\\n").map((line, index) => (
|
||||||
<Fragment key={index}>
|
<Fragment key={index}>
|
||||||
{line}
|
{line}
|
||||||
@@ -41,55 +42,44 @@ export default function MatchSentences({id, options, type, prompt, sentences, us
|
|||||||
</Fragment>
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
</span>
|
</span>
|
||||||
<div className="grid grid-cols-2 gap-16 place-items-center">
|
<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-1">
|
<div className="flex flex-col gap-4">
|
||||||
{sentences.map(({sentence, id, color}) => (
|
{sentences.map(({sentence, id, color}) => (
|
||||||
<div
|
<div key={`question_${id}`} className="flex items-center justify-end gap-2 cursor-pointer">
|
||||||
key={`question_${id}`}
|
<span>{sentence} </span>
|
||||||
className="flex items-center justify-end gap-2 cursor-pointer"
|
<button
|
||||||
onClick={() => setSelectedQuestion((prev) => (prev === id ? undefined : id))}>
|
id={id}
|
||||||
<span>
|
onClick={() => setSelectedQuestion((prev) => (prev === id ? undefined : id))}
|
||||||
<span className="font-semibold">{id}.</span> {sentence}{" "}
|
className={clsx(
|
||||||
</span>
|
"bg-mti-green-ultralight text-mti-green hover:text-white hover:bg-mti-green w-8 h-8 rounded-full z-10",
|
||||||
<div
|
"transition duration-300 ease-in-out",
|
||||||
style={{borderColor: color, backgroundColor: selectedQuestion === id ? color : "transparent"}}
|
selectedQuestion === id && "!text-white bg-mti-green",
|
||||||
className={clsx("border-2 border-blue-500 w-4 h-4 rounded-full", id)}
|
id,
|
||||||
/>
|
)}>
|
||||||
|
{id}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-4">
|
||||||
{options.map(({sentence, id}) => (
|
{options.map(({sentence, id}) => (
|
||||||
<div
|
<div key={`answer_${id}`} className={clsx("flex items-center justify-start gap-2 cursor-pointer")}>
|
||||||
key={`answer_${id}`}
|
<button
|
||||||
className={clsx("flex items-center justify-start gap-2 cursor-pointer")}
|
id={id}
|
||||||
onClick={() => selectOption(id)}>
|
onClick={() => selectOption(id)}
|
||||||
<div
|
className={clsx(
|
||||||
style={
|
"bg-mti-green-ultralight text-mti-green hover:text-white hover:bg-mti-green w-8 h-8 rounded-full z-10",
|
||||||
answers.find((x) => x.option === id)
|
"transition duration-300 ease-in-out",
|
||||||
? {
|
id,
|
||||||
border: `2px solid ${getSentenceColor(answers.find((x) => x.option === id)!.question)}`,
|
)}>
|
||||||
}
|
{id}
|
||||||
: {}
|
</button>
|
||||||
}
|
<span>{sentence}</span>
|
||||||
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>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{answers.map((solution, index) => (
|
{answers.map((solution, index) => (
|
||||||
<div key={`solution_${index}`} className="absolute">
|
<Xarrow key={index} start={solution.question} end={solution.option} lineColor="#307912" showHead={false} />
|
||||||
<LineTo
|
|
||||||
className="rounded-full"
|
|
||||||
from={solution.question}
|
|
||||||
to={solution.option}
|
|
||||||
borderColor={sentences.find((x) => x.id === solution.question)!.color}
|
|
||||||
borderWidth={5}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,12 +7,13 @@ import ProgressBar from "../Low/ProgressBar";
|
|||||||
interface Props {
|
interface Props {
|
||||||
minTimer: number;
|
minTimer: number;
|
||||||
module: Module;
|
module: Module;
|
||||||
|
label?: string;
|
||||||
exerciseIndex: number;
|
exerciseIndex: number;
|
||||||
totalExercises: number;
|
totalExercises: number;
|
||||||
disableTimer?: boolean;
|
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);
|
const [timer, setTimer] = useState(minTimer * 60);
|
||||||
|
|
||||||
useEffect(() => {
|
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="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="flex flex-col gap-3 w-full">
|
||||||
<div className="w-full flex justify-between">
|
<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">
|
<span className="text-xs font-normal self-end text-mti-gray-davy">
|
||||||
Question {exerciseIndex}/{totalExercises}
|
Question {exerciseIndex}/{totalExercises}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -82,6 +82,17 @@ export default function FillBlanksSolutions({prompt, solutions, text, userSoluti
|
|||||||
</p>
|
</p>
|
||||||
))}
|
))}
|
||||||
</span>
|
</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>
|
||||||
|
|
||||||
<div className="self-end flex justify-between w-full gap-8 absolute bottom-8 left-0 px-8">
|
<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 Icon from "@mdi/react";
|
||||||
import {Fragment} from "react";
|
import {Fragment} from "react";
|
||||||
import Button from "../Low/Button";
|
import Button from "../Low/Button";
|
||||||
|
import Xarrow from "react-xarrows";
|
||||||
|
|
||||||
export default function MatchSentencesSolutions({options, prompt, sentences, userSolutions, onNext, onBack}: MatchSentencesExercise & CommonProps) {
|
export default function MatchSentencesSolutions({options, prompt, sentences, userSolutions, onNext, onBack}: MatchSentencesExercise & CommonProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex flex-col items-center gap-8">
|
<div className="flex flex-col gap-4 mt-4 h-full mb-20">
|
||||||
<span className="text-base md:text-lg font-medium text-center px-2 md:px-4 lg:px-48">
|
<span className="text-sm w-full leading-6">
|
||||||
{prompt.split("\\n").map((line, index) => (
|
{prompt.split("\\n").map((line, index) => (
|
||||||
<Fragment key={index}>
|
<Fragment key={index}>
|
||||||
{line}
|
{line}
|
||||||
@@ -20,48 +21,67 @@ export default function MatchSentencesSolutions({options, prompt, sentences, use
|
|||||||
</Fragment>
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
</span>
|
</span>
|
||||||
|
<div className="flex gap-8 w-full items-center justify-between bg-mti-gray-smoke rounded-xl px-24 py-6">
|
||||||
<div className="grid grid-cols-2 gap-16 place-items-center">
|
<div className="flex flex-col gap-4">
|
||||||
<div className="flex flex-col gap-1">
|
{sentences.map(({sentence, id, solution}) => (
|
||||||
{sentences.map(({sentence, id, color, solution}) => (
|
<div key={`question_${id}`} className="flex items-center justify-end gap-2 cursor-pointer">
|
||||||
<div
|
<span>{sentence} </span>
|
||||||
key={`question_${id}`}
|
<button
|
||||||
className={clsx(
|
id={id}
|
||||||
"flex items-center justify-end gap-2 cursor-pointer",
|
className={clsx(
|
||||||
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",
|
||||||
<span>
|
!userSolutions.find((x) => x.question === id) && "!bg-mti-blue",
|
||||||
<span className="font-semibold">{id}.</span> {sentence}{" "}
|
userSolutions.find((x) => x.question === id)?.option === solution && "bg-mti-green",
|
||||||
</span>
|
userSolutions.find((x) => x.question === id)?.option !== solution && "bg-mti-orange",
|
||||||
<div style={{borderColor: color}} className={clsx("border-2 border-blue-500 w-4 h-4 rounded-full", id)} />
|
)}>
|
||||||
|
{id}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-4">
|
||||||
{options.map(({sentence, id}) => (
|
{options.map(({sentence, id}) => (
|
||||||
<div key={`answer_${id}`} className={clsx("flex items-center justify-start gap-2 cursor-pointer")}>
|
<div key={`answer_${id}`} className={clsx("flex items-center justify-start gap-2 cursor-pointer")}>
|
||||||
<div
|
<button
|
||||||
style={
|
id={id}
|
||||||
sentences.find((x) => x.solution === 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",
|
||||||
border: `2px solid ${sentences.find((x) => x.solution === id)!.color}`,
|
"transition duration-300 ease-in-out",
|
||||||
}
|
)}>
|
||||||
: {}
|
{id}
|
||||||
}
|
</button>
|
||||||
className={clsx("border-2 border-green-500 bg-transparent w-4 h-4 rounded-full", id)}
|
<span>{sentence}</span>
|
||||||
/>
|
|
||||||
<span>
|
|
||||||
<span className="font-semibold">{id}.</span> {sentence}{" "}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{sentences.map((sentence, index) => (
|
{sentences.map((sentence, index) => (
|
||||||
<div key={`solution_${index}`} className="absolute">
|
<Xarrow
|
||||||
<LineTo className="rounded-full" from={sentence.id} to={sentence.solution} borderColor={sentence.color} borderWidth={5} />
|
key={index}
|
||||||
</div>
|
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>
|
||||||
|
<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>
|
||||||
|
|
||||||
<div className="self-end flex justify-between w-full gap-8 absolute bottom-8 left-0 px-8">
|
<div className="self-end flex justify-between w-full gap-8 absolute bottom-8 left-0 px-8">
|
||||||
|
|||||||
@@ -75,14 +75,27 @@ export default function MultipleChoice({prompt, questions, userSolutions, onNext
|
|||||||
|
|
||||||
return (
|
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">
|
||||||
<span className="text-xl font-semibold">{prompt}</span>
|
<div className="flex flex-col gap-2 mt-4 h-full bg-mti-gray-smoke rounded-xl px-16 py-8">
|
||||||
{questionIndex < questions.length && (
|
<span className="text-xl font-semibold">{prompt}</span>
|
||||||
<Question
|
{questionIndex < questions.length && (
|
||||||
{...questions[questionIndex]}
|
<Question
|
||||||
userSolution={userSolutions.find((x) => questions[questionIndex].id === x.question)?.option}
|
{...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>
|
||||||
|
|
||||||
<div className="self-end flex justify-between w-full gap-8 absolute bottom-8 left-0 px-8">
|
<div className="self-end flex justify-between w-full gap-8 absolute bottom-8 left-0 px-8">
|
||||||
|
|||||||
@@ -110,6 +110,17 @@ export default function WriteBlanksSolutions({
|
|||||||
</p>
|
</p>
|
||||||
))}
|
))}
|
||||||
</span>
|
</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>
|
||||||
|
|
||||||
<div className="self-end flex justify-between w-full gap-8 absolute bottom-8 left-0 px-8">
|
<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 {mdiArrowRight, mdiNotebook} from "@mdi/js";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import {infoButtonStyle} from "@/constants/buttonStyles";
|
import {infoButtonStyle} from "@/constants/buttonStyles";
|
||||||
|
import {convertCamelCaseToReadable} from "@/utils/string";
|
||||||
import {Dialog, Transition} from "@headlessui/react";
|
import {Dialog, Transition} from "@headlessui/react";
|
||||||
import {renderExercise} from "@/components/Exercises";
|
import {renderExercise} from "@/components/Exercises";
|
||||||
import {renderSolution} from "@/components/Solutions";
|
import {renderSolution} from "@/components/Solutions";
|
||||||
@@ -148,6 +149,7 @@ export default function Reading({exam, showSolutions = false, onFinish}: Props)
|
|||||||
module="reading"
|
module="reading"
|
||||||
totalExercises={exam.exercises.length}
|
totalExercises={exam.exercises.length}
|
||||||
disableTimer={showSolutions}
|
disableTimer={showSolutions}
|
||||||
|
label={exerciseIndex === -1 ? undefined : convertCamelCaseToReadable(exam.exercises[exerciseIndex].type)}
|
||||||
/>
|
/>
|
||||||
{exerciseIndex === -1 && renderText()}
|
{exerciseIndex === -1 && renderText()}
|
||||||
{exerciseIndex > -1 &&
|
{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"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190"
|
||||||
integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==
|
integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==
|
||||||
|
|
||||||
"@types/prop-types@*":
|
"@types/prop-types@*", "@types/prop-types@^15.7.3":
|
||||||
version "15.7.5"
|
version "15.7.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
|
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
|
||||||
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
|
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
|
||||||
@@ -3178,6 +3178,15 @@ react-transition-group@^4.4.1:
|
|||||||
loose-envify "^1.4.0"
|
loose-envify "^1.4.0"
|
||||||
prop-types "^15.6.2"
|
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:
|
react@17.0.2:
|
||||||
version "17.0.2"
|
version "17.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
||||||
|
|||||||
Reference in New Issue
Block a user