205 lines
7.6 KiB
TypeScript
205 lines
7.6 KiB
TypeScript
/* eslint-disable @next/next/no-img-element */
|
|
import {WritingExercise} from "@/interfaces/exam";
|
|
import {CommonProps} from ".";
|
|
import {Fragment, useEffect, useState} from "react";
|
|
import Button from "../Low/Button";
|
|
import {Dialog, Tab, Transition} from "@headlessui/react";
|
|
import {writingReverseMarking} from "@/utils/score";
|
|
import clsx from "clsx";
|
|
import ReactDiffViewer, {DiffMethod} from "react-diff-viewer";
|
|
|
|
export default function Writing({id, type, prompt, attachment, userSolutions, onNext, onBack}: WritingExercise & CommonProps) {
|
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
const [showDiff, setShowDiff] = useState(false);
|
|
|
|
return (
|
|
<>
|
|
{attachment && (
|
|
<Transition show={isModalOpen} as={Fragment}>
|
|
<Dialog onClose={() => setIsModalOpen(false)} className="relative z-50">
|
|
<Transition.Child
|
|
as={Fragment}
|
|
enter="ease-out duration-300"
|
|
enterFrom="opacity-0"
|
|
enterTo="opacity-100"
|
|
leave="ease-in duration-200"
|
|
leaveFrom="opacity-100"
|
|
leaveTo="opacity-0">
|
|
<div className="fixed inset-0 bg-black/30" />
|
|
</Transition.Child>
|
|
|
|
<Transition.Child
|
|
as={Fragment}
|
|
enter="ease-out duration-300"
|
|
enterFrom="opacity-0 scale-95"
|
|
enterTo="opacity-100 scale-100"
|
|
leave="ease-in duration-200"
|
|
leaveFrom="opacity-100 scale-100"
|
|
leaveTo="opacity-0 scale-95">
|
|
<div className="fixed inset-0 flex items-center justify-center p-4">
|
|
<Dialog.Panel className="w-fit h-fit rounded-xl bg-white">
|
|
<img src={attachment.url} alt={attachment.description} className="max-w-4xl w-full self-center rounded-xl p-4" />
|
|
</Dialog.Panel>
|
|
</div>
|
|
</Transition.Child>
|
|
</Dialog>
|
|
</Transition>
|
|
)}
|
|
<div className="flex flex-col h-full w-full gap-8 mb-20">
|
|
<div className="flex w-full gap-7 bg-mti-gray-smoke rounded-xl py-8 pb-12 px-16">
|
|
<span className="font-semibold">
|
|
{prompt.split("\\n").map((line, index) => (
|
|
<Fragment key={index}>
|
|
<p>{line}</p>
|
|
<br />
|
|
</Fragment>
|
|
))}
|
|
</span>
|
|
{attachment && (
|
|
<img
|
|
onClick={() => setIsModalOpen(true)}
|
|
src={attachment.url}
|
|
alt={attachment.description}
|
|
className="max-w-[200px] self-center rounded-xl cursor-pointer"
|
|
/>
|
|
)}
|
|
</div>
|
|
|
|
<div className="w-full h-full flex flex-col gap-8">
|
|
{userSolutions && userSolutions.length > 0 && (
|
|
<div className="flex flex-col gap-4 w-full relative">
|
|
{!showDiff && (
|
|
<>
|
|
<span>Your answer:</span>
|
|
<div className="w-full h-full min-h-[320px] cursor-text px-7 py-8 input border-2 border-mti-gray-platinum bg-white rounded-3xl whitespace-pre-wrap">
|
|
{userSolutions[0]!.solution.replaceAll("\\n", "\n")}
|
|
</div>
|
|
</>
|
|
)}
|
|
{showDiff && (
|
|
<>
|
|
<span>Correction:</span>
|
|
<div className="w-full h-full max-h-[320px] overflow-y-scroll scrollbar-hide cursor-text border-2 overflow-x-hidden border-mti-gray-platinum bg-white rounded-3xl">
|
|
<ReactDiffViewer
|
|
styles={{
|
|
contentText: {
|
|
fontFamily: '"Open Sans", system-ui, -apple-system, "Helvetica Neue", sans-serif',
|
|
padding: "32px 28px",
|
|
},
|
|
marker: {display: "none"},
|
|
diffRemoved: {padding: "32px 28px"},
|
|
diffAdded: {padding: "32px 28px"},
|
|
|
|
wordRemoved: {padding: "0px", display: "initial"},
|
|
wordAdded: {padding: "0px", display: "initial"},
|
|
wordDiff: {padding: "0px", display: "initial"},
|
|
}}
|
|
oldValue={userSolutions[0].solution.replaceAll("\\n", "\n")}
|
|
newValue={userSolutions[0].evaluation!.fixed_text!.replaceAll("\\n", "\n")}
|
|
splitView
|
|
hideLineNumbers
|
|
showDiffOnly={false}
|
|
/>
|
|
</div>
|
|
</>
|
|
)}
|
|
{userSolutions[0].solution && userSolutions[0].evaluation?.fixed_text && (
|
|
<Button
|
|
color="green"
|
|
variant="outline"
|
|
className="w-full max-w-[200px] self-end absolute -top-4 right-0 !py-2"
|
|
onClick={() => setShowDiff((prev) => !prev)}>
|
|
{showDiff ? "View answer" : "View correction"}
|
|
</Button>
|
|
)}
|
|
</div>
|
|
)}
|
|
{userSolutions && userSolutions.length > 0 && userSolutions[0].evaluation && typeof userSolutions[0].evaluation !== "string" && (
|
|
<div className="flex flex-col gap-4 w-full">
|
|
<div className="flex gap-4 px-1">
|
|
{Object.keys(userSolutions[0].evaluation!.task_response).map((key) => (
|
|
<div className="bg-ielts-writing text-ielts-writing-light rounded-xl px-4 py-2" key={key}>
|
|
{key}: Level {userSolutions[0].evaluation!.task_response[key]}
|
|
</div>
|
|
))}
|
|
</div>
|
|
{userSolutions[0].evaluation && userSolutions[0].evaluation.perfect_answer ? (
|
|
<Tab.Group>
|
|
<Tab.List className="flex space-x-1 rounded-xl bg-ielts-writing/20 p-1">
|
|
<Tab
|
|
className={({selected}) =>
|
|
clsx(
|
|
"w-full rounded-lg py-2.5 text-sm font-medium leading-5 text-ielts-writing/80",
|
|
"ring-white ring-opacity-60 ring-offset-2 ring-offset-ielts-writing focus:outline-none focus:ring-2",
|
|
"transition duration-300 ease-in-out",
|
|
selected ? "bg-white shadow" : "text-blue-100 hover:bg-white/[0.12] hover:text-ielts-writing",
|
|
)
|
|
}>
|
|
Evaluation
|
|
</Tab>
|
|
<Tab
|
|
className={({selected}) =>
|
|
clsx(
|
|
"w-full rounded-lg py-2.5 text-sm font-medium leading-5 text-ielts-writing/80",
|
|
"ring-white ring-opacity-60 ring-offset-2 ring-offset-ielts-writing focus:outline-none focus:ring-2",
|
|
"transition duration-300 ease-in-out",
|
|
selected ? "bg-white shadow" : "text-blue-100 hover:bg-white/[0.12] hover:text-ielts-writing",
|
|
)
|
|
}>
|
|
Recommended Answer
|
|
</Tab>
|
|
</Tab.List>
|
|
<Tab.Panels>
|
|
<Tab.Panel className="w-full bg-ielts-writing/10 h-fit rounded-xl p-6 flex flex-col gap-4">
|
|
<span className="w-full h-full min-h-fit cursor-text">{userSolutions[0].evaluation!.comment}</span>
|
|
</Tab.Panel>
|
|
<Tab.Panel className="w-full bg-ielts-writing/10 h-fit rounded-xl p-6 flex flex-col gap-4">
|
|
<span className="w-full h-full min-h-fit cursor-text whitespace-pre-wrap">
|
|
{userSolutions[0].evaluation!.perfect_answer.replaceAll(/\s{2,}/g, "\n\n").replaceAll("\\n", "\n")}
|
|
</span>
|
|
</Tab.Panel>
|
|
</Tab.Panels>
|
|
</Tab.Group>
|
|
) : (
|
|
<div className="w-full h-full min-h-fit cursor-text px-7 py-8 bg-ielts-writing/10 rounded-3xl">
|
|
{userSolutions[0].evaluation!.comment}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="self-end flex justify-between w-full gap-8 absolute bottom-8 left-0 px-8">
|
|
<Button
|
|
color="purple"
|
|
variant="outline"
|
|
onClick={() =>
|
|
onBack({
|
|
exercise: id,
|
|
solutions: userSolutions,
|
|
score: {total: 100, missing: 0, correct: writingReverseMarking[userSolutions[0]!.evaluation!.overall] || 0},
|
|
type,
|
|
})
|
|
}
|
|
className="max-w-[200px] self-end w-full">
|
|
Back
|
|
</Button>
|
|
<Button
|
|
color="purple"
|
|
onClick={() =>
|
|
onNext({
|
|
exercise: id,
|
|
solutions: userSolutions,
|
|
score: {total: 100, missing: 0, correct: writingReverseMarking[userSolutions[0]!.evaluation!.overall] || 0},
|
|
type,
|
|
})
|
|
}
|
|
className="max-w-[200px] self-end w-full">
|
|
Next
|
|
</Button>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|