From c321a5cc6947030fab16560ac25799f652f3b542 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Mon, 17 Apr 2023 17:35:26 +0100 Subject: [PATCH] Added the speaking module to the application --- src/components/Exercises/Speaking.tsx | 53 ++++++++++++++++++ src/components/Exercises/Writing.tsx | 2 +- src/components/Exercises/index.tsx | 4 ++ src/exams/Selection.tsx | 26 ++++----- src/exams/Speaking.tsx | 79 +++++++++++++++++++++++++++ src/hooks/useUser.tsx | 1 - src/interfaces/exam.ts | 25 ++++++++- src/pages/exam/index.tsx | 16 +++++- 8 files changed, 186 insertions(+), 20 deletions(-) create mode 100644 src/components/Exercises/Speaking.tsx create mode 100644 src/exams/Speaking.tsx diff --git a/src/components/Exercises/Speaking.tsx b/src/components/Exercises/Speaking.tsx new file mode 100644 index 00000000..8b9fd0b6 --- /dev/null +++ b/src/components/Exercises/Speaking.tsx @@ -0,0 +1,53 @@ +import {errorButtonStyle, infoButtonStyle} from "@/constants/buttonStyles"; +import {SpeakingExercise, WritingExercise} from "@/interfaces/exam"; +import {mdiArrowLeft, mdiArrowRight} from "@mdi/js"; +import Icon from "@mdi/react"; +import clsx from "clsx"; +import {CommonProps} from "."; +import {Fragment, useEffect, useState} from "react"; +import {toast} from "react-toastify"; + +export default function Speaking({id, title, text, prompts, onNext, onBack}: SpeakingExercise & CommonProps) { + return ( +
+
+ {title} + + {text.split("\\n").map((line, index) => ( + + {line} +
+
+ ))} +
+
+ You should talk about the following things: +
+ {prompts.map((x, index) => ( +
  • + {x} +
  • + ))} +
    +
    +
    + +
    + + +
    +
    + ); +} diff --git a/src/components/Exercises/Writing.tsx b/src/components/Exercises/Writing.tsx index ff2fc813..e9f33f5d 100644 --- a/src/components/Exercises/Writing.tsx +++ b/src/components/Exercises/Writing.tsx @@ -7,7 +7,7 @@ import {CommonProps} from "."; import {Fragment, useEffect, useState} from "react"; import {toast} from "react-toastify"; -export default function WriteBlanks({id, prompt, info, wordCounter, onNext, onBack}: WritingExercise & CommonProps) { +export default function Writing({id, prompt, info, wordCounter, onNext, onBack}: WritingExercise & CommonProps) { const [inputText, setInputText] = useState(""); const [isSubmitEnabled, setIsSubmitEnabled] = useState(false); diff --git a/src/components/Exercises/index.tsx b/src/components/Exercises/index.tsx index fd265bb7..bbabe47b 100644 --- a/src/components/Exercises/index.tsx +++ b/src/components/Exercises/index.tsx @@ -3,6 +3,7 @@ import { FillBlanksExercise, MatchSentencesExercise, MultipleChoiceExercise, + SpeakingExercise, UserSolution, WriteBlanksExercise, WritingExercise, @@ -12,6 +13,7 @@ import FillBlanks from "./FillBlanks"; import MultipleChoice from "./MultipleChoice"; import WriteBlanks from "./WriteBlanks"; import Writing from "./Writing"; +import Speaking from "./Speaking"; const MatchSentences = dynamic(() => import("@/components/Exercises/MatchSentences"), {ssr: false}); @@ -32,5 +34,7 @@ export const renderExercise = (exercise: Exercise, onNext: (userSolutions: UserS return ; case "writing": return ; + case "speaking": + return ; } }; diff --git a/src/exams/Selection.tsx b/src/exams/Selection.tsx index d632d668..5b8389db 100644 --- a/src/exams/Selection.tsx +++ b/src/exams/Selection.tsx @@ -56,19 +56,6 @@ export default function Selection({user, onStart}: Props) { Listening -
    toggleModule("speaking")} - className={clsx( - "flex flex-col gap-2 items-center justify-center", - "border-ielts-speaking hover:bg-ielts-speaking text-white", - "border-2 rounded-xl p-4 h-fit w-48 cursor-pointer", - selectedModules.includes("speaking") ? "bg-ielts-speaking " : "bg-ielts-speaking-transparent ", - )}> - - Speaking -
    Writing
    +
    toggleModule("speaking")} + className={clsx( + "flex flex-col gap-2 items-center justify-center", + "border-ielts-speaking hover:bg-ielts-speaking text-white", + "border-2 rounded-xl p-4 h-fit w-48 cursor-pointer", + selectedModules.includes("speaking") ? "bg-ielts-speaking " : "bg-ielts-speaking-transparent ", + )}> + + Speaking +
    + )} +
    + ); +} diff --git a/src/hooks/useUser.tsx b/src/hooks/useUser.tsx index e9699ee3..7a64c02b 100644 --- a/src/hooks/useUser.tsx +++ b/src/hooks/useUser.tsx @@ -10,7 +10,6 @@ export default function useUser({redirectTo = "", redirectIfFound = false} = {}) const {data: user, mutate: mutateUser, isLoading} = useSWR("/api/user", fetcher); useEffect(() => { - console.log("HERE!!!"); // if no redirect needed, just return (example: already on /dashboard) // if user data not yet there (fetch in progress, logged in or not) then don't do anything yet if (!redirectTo || !user) return; diff --git a/src/interfaces/exam.ts b/src/interfaces/exam.ts index db260a82..e881df5d 100644 --- a/src/interfaces/exam.ts +++ b/src/interfaces/exam.ts @@ -1,6 +1,6 @@ import {Module} from "."; -export type Exam = ReadingExam | ListeningExam | WritingExam; +export type Exam = ReadingExam | ListeningExam | WritingExam | SpeakingExam; export interface ReadingExam { text: { @@ -47,7 +47,20 @@ interface WordCounter { limit: number; } -export type Exercise = FillBlanksExercise | MatchSentencesExercise | MultipleChoiceExercise | WriteBlanksExercise | WritingExercise; +export interface SpeakingExam { + id: string; + module: "speaking"; + exercises: Exercise[]; + minTimer: number; +} + +export type Exercise = + | FillBlanksExercise + | MatchSentencesExercise + | MultipleChoiceExercise + | WriteBlanksExercise + | WritingExercise + | SpeakingExercise; export interface WritingExercise { id: string; @@ -57,6 +70,14 @@ export interface WritingExercise { wordCounter: WordCounter; //* The minimum or maximum amount of words that should be written } +export interface SpeakingExercise { + id: string; + type: "speaking"; + title: string; + text: string; + prompts: string[]; +} + export interface FillBlanksExercise { prompt: string; // *EXAMPLE: "Complete the summary below. Click a blank to select the corresponding word for it." type: "fillBlanks"; diff --git a/src/pages/exam/index.tsx b/src/pages/exam/index.tsx index 09f48313..126c32ae 100644 --- a/src/pages/exam/index.tsx +++ b/src/pages/exam/index.tsx @@ -6,7 +6,7 @@ import {Module} from "@/interfaces"; import Selection from "@/exams/Selection"; import Reading from "@/exams/Reading"; -import {Exam, ListeningExam, ReadingExam, UserSolution, WritingExam} from "@/interfaces/exam"; +import {Exam, ListeningExam, ReadingExam, SpeakingExam, UserSolution, WritingExam} from "@/interfaces/exam"; import Listening from "@/exams/Listening"; import Writing from "@/exams/Writing"; import {ToastContainer, toast} from "react-toastify"; @@ -15,6 +15,7 @@ import axios from "axios"; import {withIronSessionSsr} from "iron-session/next"; import {sessionOptions} from "@/lib/session"; import {User} from "@/interfaces/user"; +import Speaking from "@/exams/Speaking"; export const getServerSideProps = withIronSessionSsr(({req, res}) => { const user = req.session.user; @@ -68,9 +69,9 @@ export default function Page({user}: {user: User}) { return newExam.shift() as ListeningExam; case "writing": return newExam.shift() as WritingExam; + case "speaking": + return newExam.shift() as SpeakingExam; } - - return undefined; }; const updateExamWithUserSolutions = (exam: Exam): Exam => { @@ -122,6 +123,15 @@ export default function Page({user}: {user: User}) { return ; } + if (exam && exam.module === "speaking" && showSolutions) { + setModuleIndex((prev) => prev + 1); + return <>; + } + + if (exam && exam.module === "speaking") { + return ; + } + return <>Loading...; };