Created a Finish screen for when a user finishes an exam
This commit is contained in:
@@ -0,0 +1,93 @@
|
|||||||
|
import ProfileLevel from "@/components/ProfileLevel";
|
||||||
|
import {errorButtonStyle, infoButtonStyle} from "@/constants/buttonStyles";
|
||||||
|
import {Module} from "@/interfaces";
|
||||||
|
import {User} from "@/interfaces/user";
|
||||||
|
import {ICONS} from "@/resources/modules";
|
||||||
|
import {mdiArrowLeft, mdiArrowRight} from "@mdi/js";
|
||||||
|
import Icon from "@mdi/react";
|
||||||
|
import clsx from "clsx";
|
||||||
|
import Link from "next/link";
|
||||||
|
import router from "next/router";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
user: User;
|
||||||
|
modules: Module[];
|
||||||
|
scores: {
|
||||||
|
module?: Module;
|
||||||
|
correct: number;
|
||||||
|
total: number;
|
||||||
|
}[];
|
||||||
|
onViewResults: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Finish({user, scores, modules, onViewResults}: Props) {
|
||||||
|
const renderModuleScore = (module: Module) => {
|
||||||
|
const moduleScores = scores.filter((x) => x.module === module);
|
||||||
|
if (moduleScores.length === 0) {
|
||||||
|
return <>0</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return moduleScores.map((x, index) => (
|
||||||
|
<span key={index}>
|
||||||
|
{x.correct} / {x.total}
|
||||||
|
</span>
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderModuleTotal = (module: Module) => {
|
||||||
|
const moduleScores = scores.filter((x) => x.module === module);
|
||||||
|
if (moduleScores.length === 0) {
|
||||||
|
return <>0</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const total = moduleScores.reduce((accumulator, current) => accumulator + current.total, 0);
|
||||||
|
const correct = moduleScores.reduce((accumulator, current) => accumulator + current.correct, 0);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
{correct} / {total} | {Math.floor((correct / total) * 100)}%
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full h-full relative">
|
||||||
|
<section className="h-full w-full flex flex-col items-center justify-center">
|
||||||
|
<ProfileLevel user={user} className="h-1/2" />
|
||||||
|
<div className="h-2/3 w-1/2 flex flex-col items-center gap-4">
|
||||||
|
<h1>You have finished the exam!</h1>
|
||||||
|
<div className="rounded-xl p-4 items-center flex justify-center gap-4">
|
||||||
|
{modules.map((module) => (
|
||||||
|
<div
|
||||||
|
className={`flex flex-col gap-12 min-w-[176px] items-center justify-center border-2 border-ielts-${module} bg-ielts-${module}-transparent p-4 rounded-xl text-white font-semibold`}
|
||||||
|
key={module}>
|
||||||
|
<div className="flex flex-col items-center">
|
||||||
|
<Icon path={ICONS[module]} color="white" size={2} />
|
||||||
|
<span>{module.toUpperCase()}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col">{renderModuleScore(module)}</div>
|
||||||
|
<div>TOTAL: {renderModuleTotal(module)}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="w-full flex justify-between mt-24">
|
||||||
|
<Link href="/">
|
||||||
|
<button className={clsx("btn btn-wide gap-4 relative text-white", errorButtonStyle)}>
|
||||||
|
<div className="absolute left-4">
|
||||||
|
<Icon path={mdiArrowLeft} color="white" size={1} />
|
||||||
|
</div>
|
||||||
|
Go Home
|
||||||
|
</button>
|
||||||
|
</Link>
|
||||||
|
<button className={clsx("btn btn-wide gap-4 relative text-white", infoButtonStyle)} onClick={onViewResults}>
|
||||||
|
View Solutions
|
||||||
|
<div className="absolute right-4">
|
||||||
|
<Icon path={mdiArrowRight} color="white" size={1} />
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import Listening from "@/exams/Listening";
|
|||||||
import Writing from "@/exams/Writing";
|
import Writing from "@/exams/Writing";
|
||||||
import {ToastContainer} from "react-toastify";
|
import {ToastContainer} from "react-toastify";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import Finish from "@/exams/Finish";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const [selectedModules, setSelectedModules] = useState<Module[]>([]);
|
const [selectedModules, setSelectedModules] = useState<Module[]>([]);
|
||||||
@@ -67,20 +68,15 @@ export default function Home() {
|
|||||||
|
|
||||||
if (moduleIndex >= selectedModules.length) {
|
if (moduleIndex >= selectedModules.length) {
|
||||||
return (
|
return (
|
||||||
<>
|
<Finish
|
||||||
Finished!{" "}
|
user={JSON_USER}
|
||||||
<button
|
modules={selectedModules}
|
||||||
className="btn btn-wide"
|
onViewResults={() => {
|
||||||
onClick={() => {
|
|
||||||
setShowSolutions(true);
|
setShowSolutions(true);
|
||||||
setModuleIndex(0);
|
setModuleIndex(0);
|
||||||
}}>
|
}}
|
||||||
View Solutions
|
scores={userSolutions.map((x) => ({...x.score, module: x.module}))}
|
||||||
</button>
|
/>
|
||||||
<Link href="/">
|
|
||||||
<button className="btn btn-wide">Go Home</button>
|
|
||||||
</Link>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
9
src/resources/modules.ts
Normal file
9
src/resources/modules.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import {Module} from "@/interfaces";
|
||||||
|
import {mdiAccountVoice, mdiBookOpen, mdiHeadphones, mdiPen} from "@mdi/js";
|
||||||
|
|
||||||
|
export const ICONS: {[key in Module]: string} = {
|
||||||
|
listening: mdiHeadphones,
|
||||||
|
reading: mdiBookOpen,
|
||||||
|
speaking: mdiAccountVoice,
|
||||||
|
writing: mdiPen,
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user