Made some of the code a bit more responsive

This commit is contained in:
Tiago Ribeiro
2023-04-18 12:12:26 +01:00
parent 13c8fae588
commit d61592b73e
10 changed files with 52 additions and 43 deletions

View File

@@ -44,20 +44,20 @@ function WordsPopout({words, isOpen, onCancel, onAnswer}: WordsPopoutProps) {
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-900"> <Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-900">
List of words List of words
</Dialog.Title> </Dialog.Title>
<div className="mt-4 grid grid-cols-3 gap-4"> <div className="mt-4 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{words.map((word) => ( {words.map((word) => (
<button <button
key={word.word} key={word.word}
onClick={() => onAnswer(word.word)} onClick={() => onAnswer(word.word)}
disabled={word.isDisabled} disabled={word.isDisabled}
className={clsx("btn btn-wide gap-4 relative text-white", infoButtonStyle)}> className={clsx("btn sm:btn-wide gap-4 relative text-white", infoButtonStyle)}>
{word.word} {word.word}
</button> </button>
))} ))}
</div> </div>
<div className="mt-4 self-end"> <div className="mt-4 self-end">
<button onClick={onCancel} className={clsx("btn btn-wide gap-4 relative text-white", errorButtonStyle)}> <button onClick={onCancel} className={clsx("btn md:btn-wide gap-4 relative text-white", errorButtonStyle)}>
Close Close
</button> </button>
</div> </div>
@@ -100,7 +100,7 @@ export default function FillBlanks({id, allowRepetition, prompt, solutions, text
return ( return (
<> <>
<div className="flex flex-col"> <div className="flex flex-col gap-4">
<WordsPopout <WordsPopout
words={words.map((word) => ({word, isDisabled: allowRepetition ? false : userSolutions.map((x) => x.solution).includes(word)}))} words={words.map((word) => ({word, isDisabled: allowRepetition ? false : userSolutions.map((x) => x.solution).includes(word)}))}
isOpen={!!currentBlankId} isOpen={!!currentBlankId}
@@ -110,7 +110,7 @@ export default function FillBlanks({id, allowRepetition, prompt, solutions, text
setCurrentBlankId(undefined); setCurrentBlankId(undefined);
}} }}
/> />
<span className="text-lg font-medium text-center px-48"> <span className="text-base md:text-lg font-medium text-center px-2 md:px-4 lg:px-48">
{prompt.split("\\n").map((line, index) => ( {prompt.split("\\n").map((line, index) => (
<Fragment key={index}> <Fragment key={index}>
{line} {line}
@@ -128,7 +128,7 @@ export default function FillBlanks({id, allowRepetition, prompt, solutions, text
</span> </span>
</div> </div>
<div className="self-end flex gap-8"> <div className="self-end flex flex-col items-center md:items-start md:flex-row gap-8">
<button className={clsx("btn btn-wide gap-4 relative text-white", errorButtonStyle)} onClick={onBack}> <button className={clsx("btn btn-wide gap-4 relative text-white", errorButtonStyle)} onClick={onBack}>
<div className="absolute left-4"> <div className="absolute left-4">
<Icon path={mdiArrowLeft} color="white" size={1} /> <Icon path={mdiArrowLeft} color="white" size={1} />

View File

@@ -8,5 +8,5 @@ interface Props {
export default function LevelLabel({experience, className}: Props) { export default function LevelLabel({experience, className}: Props) {
const {label} = levelCalculator(experience); const {label} = levelCalculator(experience);
return <span className={clsx("text-xl font-semibold text-success", className)}>{label}</span>; return <span className={clsx("text-base md:text-xl font-semibold text-success", className)}>{label}</span>;
} }

View File

@@ -12,7 +12,7 @@ export default function LevelProgressBar({experience, className, progressBarWidt
return ( return (
<div className={clsx("flex flex-col items-center", className)}> <div className={clsx("flex flex-col items-center", className)}>
<div className="flex gap-3 items-center"> <div className="flex gap-3 items-center text-sm md:text-base">
<span>Lvl. {levelResult.currentLevel}</span> <span>Lvl. {levelResult.currentLevel}</span>
<progress className={clsx("progress progress-success", progressBarWidth)} value={levelResult.percentage} max="100" /> <progress className={clsx("progress progress-success", progressBarWidth)} value={levelResult.percentage} max="100" />
<span>Lvl. {levelResult.nextLevel}</span> <span>Lvl. {levelResult.nextLevel}</span>

View File

@@ -1,16 +1,18 @@
import axios from "axios"; import axios from "axios";
import Link from "next/link"; import Link from "next/link";
import {useRouter} from "next/router"; import {useRouter} from "next/router";
import {Button} from "primereact/button";
import {Menubar} from "primereact/menubar"; import {Menubar} from "primereact/menubar";
import {MenuItem} from "primereact/menuitem"; import {MenuItem} from "primereact/menuitem";
interface Props { interface Props {
profilePicture: string; profilePicture: string;
timer?: number; timer?: number;
showExamEnd?: boolean;
} }
/* eslint-disable @next/next/no-img-element */ /* eslint-disable @next/next/no-img-element */
export default function Navbar({profilePicture, timer}: Props) { export default function Navbar({profilePicture, timer, showExamEnd = false}: Props) {
const router = useRouter(); const router = useRouter();
const logout = async () => { const logout = async () => {
@@ -50,7 +52,7 @@ export default function Navbar({profilePicture, timer}: Props) {
}, },
]; ];
const end = timer && ( const endTimer = timer && (
<span className="pr-2 font-semibold"> <span className="pr-2 font-semibold">
{Math.floor(timer / 60) < 10 ? "0" : ""} {Math.floor(timer / 60) < 10 ? "0" : ""}
{Math.floor(timer / 60)}:{timer % 60 < 10 ? "0" : ""} {Math.floor(timer / 60)}:{timer % 60 < 10 ? "0" : ""}
@@ -58,9 +60,15 @@ export default function Navbar({profilePicture, timer}: Props) {
</span> </span>
); );
const endNewExam = (
<Link href="/exam" className="pr-2">
<Button text label="Exam" severity="secondary" size="small" />
</Link>
);
return ( return (
<div className="bg-neutral-100 z-10 w-full p-2"> <div className="bg-neutral-100 z-10 w-full p-2">
<Menubar model={items} end={end} /> <Menubar model={items} end={showExamEnd ? endNewExam : endTimer} />
</div> </div>
); );
} }

View File

@@ -11,17 +11,17 @@ interface Props {
export default function ProfileCard({user, className}: Props) { export default function ProfileCard({user, className}: Props) {
return ( return (
<div className={clsx("bg-white drop-shadow-xl p-8 rounded-xl w-full flex flex-col gap-6", className)}> <div className={clsx("bg-white drop-shadow-xl p-4 md:p-8 rounded-xl w-full flex flex-col gap-6", className)}>
<div className="flex w-full items-center gap-8"> <div className="flex w-full items-center gap-8">
<div className="w-24 rounded-full border-4 border-white drop-shadow-xl"> <div className="w-16 md:w-24 rounded-full border-2 md:border-4 border-white drop-shadow-md md:drop-shadow-xl">
<img src={user.profilePicture} alt="Profile picture" className="rounded-full" /> <img src={user.profilePicture} alt="Profile picture" className="rounded-full" />
</div> </div>
<div className="flex flex-col justify-center"> <div className="flex flex-col justify-center">
<span className="text-neutral-600 font-bold text-2xl">{user.name}</span> <span className="text-neutral-600 font-bold text-xl lg:text-2xl">{user.name}</span>
<LevelLabel experience={user.experience} /> <LevelLabel experience={user.experience} />
</div> </div>
</div> </div>
<LevelProgressBar experience={user.experience} progressBarWidth="w-96" /> <LevelProgressBar experience={user.experience} progressBarWidth="w-32 md:w-96" />
</div> </div>
); );
} }

View File

@@ -52,10 +52,9 @@ export default function Finish({user, scores, modules, onViewResults}: Props) {
return ( return (
<div className="w-full h-full relative"> <div className="w-full h-full relative">
<section className="h-full w-full flex flex-col items-center justify-center"> <section className="h-full w-full flex flex-col items-center justify-center gap-4">
<ProfileLevel user={user} className="h-1/2" /> <ProfileLevel user={user} className="h-1/2" />
<div className="h-2/3 w-1/2 flex flex-col items-center gap-4"> <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"> <div className="rounded-xl p-4 items-center flex justify-center gap-4">
{modules.map((module) => ( {modules.map((module) => (
<div <div
@@ -70,7 +69,7 @@ export default function Finish({user, scores, modules, onViewResults}: Props) {
</div> </div>
))} ))}
</div> </div>
<div className="w-full flex justify-between mt-24"> <div className="w-full flex flex-col gap-4 lg:gap-0 md:flex-row justify-center items-center lg:justify-between">
<Link href="/"> <Link href="/">
<button className={clsx("btn btn-wide gap-4 relative text-white", errorButtonStyle)}> <button className={clsx("btn btn-wide gap-4 relative text-white", errorButtonStyle)}>
<div className="absolute left-4"> <div className="absolute left-4">

View File

@@ -100,27 +100,27 @@ export default function Reading({exam, showSolutions = false, onFinish}: Props)
}; };
const renderText = () => ( const renderText = () => (
<> <div className="flex flex-col gap-4 w-full">
<div className="flex flex-col"> <div className="flex flex-col w-full gap-2">
<span className="text-lg font-semibold"> <span className="text-base md:text-lg font-semibold">
Please read the following excerpt attentively, you will then be asked questions about the text you&apos;ve read. Please read the following excerpt attentively, you will then be asked questions about the text you&apos;ve read.
</span> </span>
<span className="self-end text-sm">You will be allowed to read the text while doing the exercises</span> <span className="self-end text-sm">You will be allowed to read the text while doing the exercises</span>
</div> </div>
<Panel header={exam.text.title} className="overflow-scroll"> <Panel header={exam.text.title}>
<p className="overflow-scroll"> <p className="overflow-auto">
{exam.text.content.split("\\n").map((line, index) => ( {exam.text.content.split("\\n").map((line, index) => (
<p key={index}>{line}</p> <p key={index}>{line}</p>
))} ))}
</p> </p>
</Panel> </Panel>
</> </div>
); );
return ( return (
<> <>
<TextModal {...exam.text} isOpen={showTextModal} onClose={() => setShowTextModal(false)} /> <TextModal {...exam.text} isOpen={showTextModal} onClose={() => setShowTextModal(false)} />
<div className="w-full h-full relative flex flex-col gap-8 items-center justify-center p-8 px-16 overflow-hidden"> <div className="w-full h-full relative flex flex-col gap-8 items-center justify-center p-2 md:p-8 px-4 md:px-16 overflow-hidden">
{exerciseIndex === -1 && renderText()} {exerciseIndex === -1 && renderText()}
{exerciseIndex > -1 && {exerciseIndex > -1 &&
exerciseIndex < exam.exercises.length && exerciseIndex < exam.exercises.length &&
@@ -130,7 +130,8 @@ export default function Reading({exam, showSolutions = false, onFinish}: Props)
exerciseIndex < exam.exercises.length && exerciseIndex < exam.exercises.length &&
showSolutions && showSolutions &&
renderSolution(exam.exercises[exerciseIndex], nextExercise, previousExercise)} renderSolution(exam.exercises[exerciseIndex], nextExercise, previousExercise)}
<div className={clsx("flex gap-8", exerciseIndex > -1 ? "w-full justify-between" : "self-end")}> <div
className={clsx("flex gap-8", exerciseIndex > -1 ? "w-full justify-center md:justify-between" : "self-end w-full md:w-fit flex")}>
{exerciseIndex > -1 && ( {exerciseIndex > -1 && (
<button <button
className={clsx( className={clsx(
@@ -145,7 +146,9 @@ export default function Reading({exam, showSolutions = false, onFinish}: Props)
</button> </button>
)} )}
{exerciseIndex === -1 && ( {exerciseIndex === -1 && (
<button className={clsx("btn btn-wide gap-4 relative text-white self-end", infoButtonStyle)} onClick={() => nextExercise()}> <button
className={clsx("btn w-full md:btn-wide gap-4 relative text-white self-end", infoButtonStyle)}
onClick={() => nextExercise()}>
Next Next
<div className="absolute right-4"> <div className="absolute right-4">
<Icon path={mdiArrowRight} color="white" size={1} /> <Icon path={mdiArrowRight} color="white" size={1} />

View File

@@ -29,7 +29,7 @@ export default function Selection({user, onStart}: Props) {
<section className="h-full w-full flex flex-col items-center justify-center gap-8"> <section className="h-full w-full flex flex-col items-center justify-center gap-8">
<ProfileLevel user={user} className="h-1/2" /> <ProfileLevel user={user} className="h-1/2" />
<div className="h-1/2 flex flex-col gap-8"> <div className="h-1/2 flex flex-col gap-8">
<div className="h-1/2 items-center flex flex-col md:flex-row gap-8"> <div className="h-1/2 items-center flex flex-col lg:flex-row gap-8">
<div <div
role="button" role="button"
tabIndex={0} tabIndex={0}

View File

@@ -57,7 +57,7 @@ export default function Page({user}: {user: User}) {
useEffect(() => { useEffect(() => {
if (exam) { if (exam) {
setTimer(exam.minTimer * 60); setTimer(exam.minTimer * 60);
const timerInterval = setInterval(() => setTimer((prev) => prev && prev - 1), 1000); const timerInterval = setInterval(() => setTimer((prev) => (prev && prev > 0 ? prev - 1 : 0)), 1000);
return () => { return () => {
clearInterval(timerInterval); clearInterval(timerInterval);
@@ -158,9 +158,9 @@ export default function Page({user}: {user: User}) {
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
</Head> </Head>
<main className="w-full h-full md:h-screen flex flex-col items-center bg-neutral-100 text-black">
<ToastContainer /> <ToastContainer />
<Navbar profilePicture={user.profilePicture} timer={exam && timer} /> <main className="w-full h-full min-h-[100vh] flex flex-col items-center bg-neutral-100 text-black pb-4 gap-4">
<Navbar profilePicture={user.profilePicture} timer={exam ? timer : undefined} />
{renderScreen()} {renderScreen()}
</main> </main>
</> </>

View File

@@ -15,6 +15,7 @@ import JSON_RESULTS from "@/demo/user_results.json";
import {withIronSessionSsr} from "iron-session/next"; import {withIronSessionSsr} from "iron-session/next";
import {sessionOptions} from "@/lib/session"; import {sessionOptions} from "@/lib/session";
import {User} from "@/interfaces/user"; import {User} from "@/interfaces/user";
import {useEffect, useState} from "react";
export const getServerSideProps = withIronSessionSsr(({req, res}) => { export const getServerSideProps = withIronSessionSsr(({req, res}) => {
const user = req.session.user; const user = req.session.user;
@@ -36,6 +37,10 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => {
}, sessionOptions); }, sessionOptions);
export default function Home({user}: {user: User}) { export default function Home({user}: {user: User}) {
const [showEndExam, setShowEndExam] = useState(false);
useEffect(() => setShowEndExam(window.innerWidth <= 960), []);
return ( return (
<> <>
<Head> <Head>
@@ -47,20 +52,14 @@ export default function Home({user}: {user: User}) {
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
</Head> </Head>
<main className="w-full h-screen flex flex-col items-center bg-neutral-100"> <main className="w-full h-full min-h-[100vh] flex flex-col items-center bg-neutral-100">
<Navbar profilePicture={user.profilePicture} /> <Navbar profilePicture={user.profilePicture} showExamEnd={showEndExam} />
<div className="w-full h-full p-4 relative"> <div className="w-full h-full p-4 relative">
<Link href="/exam"> <section className="h-full w-full flex flex-col lg:flex-row gap-12 justify-center items-center md:items-start">
<button className={clsx("btn gap-2 top-12 right-12 absolute", infoButtonStyle)}> <section className="w-full lg:w-1/2 h-full flex items-center">
<Icon path={mdiPlus} color="white" size={1} />
New Exam
</button>
</Link>
<section className="h-full w-full flex items-center p-8 gap-12 justify-center">
<section className="w-1/2 h-full flex items-center">
<ProfileCard user={user} className="text-black self-start" /> <ProfileCard user={user} className="text-black self-start" />
</section> </section>
<section className="w-1/2 h-full flex items-center justify-center"> <section className="w-full lg:w-1/3 h-full flex items-center justify-center">
<UserResultChart results={JSON_RESULTS} resultKey="total" label="Total exams" className="w-2/3" /> <UserResultChart results={JSON_RESULTS} resultKey="total" label="Total exams" className="w-2/3" />
</section> </section>
</section> </section>