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">
List of words
</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) => (
<button
key={word.word}
onClick={() => onAnswer(word.word)}
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}
</button>
))}
</div>
<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
</button>
</div>
@@ -100,7 +100,7 @@ export default function FillBlanks({id, allowRepetition, prompt, solutions, text
return (
<>
<div className="flex flex-col">
<div className="flex flex-col gap-4">
<WordsPopout
words={words.map((word) => ({word, isDisabled: allowRepetition ? false : userSolutions.map((x) => x.solution).includes(word)}))}
isOpen={!!currentBlankId}
@@ -110,7 +110,7 @@ export default function FillBlanks({id, allowRepetition, prompt, solutions, text
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) => (
<Fragment key={index}>
{line}
@@ -128,7 +128,7 @@ export default function FillBlanks({id, allowRepetition, prompt, solutions, text
</span>
</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}>
<div className="absolute left-4">
<Icon path={mdiArrowLeft} color="white" size={1} />

View File

@@ -8,5 +8,5 @@ interface Props {
export default function LevelLabel({experience, className}: Props) {
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 (
<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>
<progress className={clsx("progress progress-success", progressBarWidth)} value={levelResult.percentage} max="100" />
<span>Lvl. {levelResult.nextLevel}</span>

View File

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

View File

@@ -11,17 +11,17 @@ interface Props {
export default function ProfileCard({user, className}: Props) {
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="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" />
</div>
<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} />
</div>
</div>
<LevelProgressBar experience={user.experience} progressBarWidth="w-96" />
<LevelProgressBar experience={user.experience} progressBarWidth="w-32 md:w-96" />
</div>
);
}

View File

@@ -52,10 +52,9 @@ export default function Finish({user, scores, modules, onViewResults}: Props) {
return (
<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" />
<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
@@ -70,7 +69,7 @@ export default function Finish({user, scores, modules, onViewResults}: Props) {
</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="/">
<button className={clsx("btn btn-wide gap-4 relative text-white", errorButtonStyle)}>
<div className="absolute left-4">

View File

@@ -100,27 +100,27 @@ export default function Reading({exam, showSolutions = false, onFinish}: Props)
};
const renderText = () => (
<>
<div className="flex flex-col">
<span className="text-lg font-semibold">
<div className="flex flex-col gap-4 w-full">
<div className="flex flex-col w-full gap-2">
<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.
</span>
<span className="self-end text-sm">You will be allowed to read the text while doing the exercises</span>
</div>
<Panel header={exam.text.title} className="overflow-scroll">
<p className="overflow-scroll">
<Panel header={exam.text.title}>
<p className="overflow-auto">
{exam.text.content.split("\\n").map((line, index) => (
<p key={index}>{line}</p>
))}
</p>
</Panel>
</>
</div>
);
return (
<>
<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 &&
exerciseIndex < exam.exercises.length &&
@@ -130,7 +130,8 @@ export default function Reading({exam, showSolutions = false, onFinish}: Props)
exerciseIndex < exam.exercises.length &&
showSolutions &&
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 && (
<button
className={clsx(
@@ -145,7 +146,9 @@ export default function Reading({exam, showSolutions = false, onFinish}: Props)
</button>
)}
{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
<div className="absolute right-4">
<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">
<ProfileLevel user={user} className="h-1/2" />
<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
role="button"
tabIndex={0}

View File

@@ -57,7 +57,7 @@ export default function Page({user}: {user: User}) {
useEffect(() => {
if (exam) {
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 () => {
clearInterval(timerInterval);
@@ -158,9 +158,9 @@ export default function Page({user}: {user: User}) {
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className="w-full h-full md:h-screen flex flex-col items-center bg-neutral-100 text-black">
<ToastContainer />
<Navbar profilePicture={user.profilePicture} timer={exam && timer} />
<ToastContainer />
<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()}
</main>
</>

View File

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