diff --git a/src/components/Diagnostic.tsx b/src/components/Diagnostic.tsx new file mode 100644 index 00000000..dde882ec --- /dev/null +++ b/src/components/Diagnostic.tsx @@ -0,0 +1,86 @@ +import {infoButtonStyle} from "@/constants/buttonStyles"; +import {Module} from "@/interfaces"; +import {User} from "@/interfaces/user"; +import useExamStore from "@/stores/examStore"; +import {getExamById} from "@/utils/exams"; +import axios from "axios"; +import clsx from "clsx"; +import {useRouter} from "next/router"; +import {useEffect, useState} from "react"; +import {ToastContainer, toast} from "react-toastify"; + +interface Props { + user: User; +} + +const DIAGNOSTIC_EXAMS = [ + ["reading", ""], + ["listening", ""], + ["writing", ""], + ["speaking", ""], +]; + +export default function Diagnostic({user}: Props) { + const [focus, setFocus] = useState<"academic" | "general">(); + const [isInsert, setIsInsert] = useState(false); + + const router = useRouter(); + + const setExams = useExamStore((state) => state.setExams); + const setSelectedModules = useExamStore((state) => state.setSelectedModules); + + const selectExam = () => { + const examPromises = DIAGNOSTIC_EXAMS.map((exam) => getExamById(exam[0] as Module, exam[1])); + + Promise.all(examPromises).then((exams) => { + if (exams.every((x) => !!x)) { + setExams(exams.map((x) => x!)); + setSelectedModules(exams.map((x) => x!.module)); + router.push("/exam"); + } + }); + }; + + const onPerformDiagnosis = async () => { + axios + .post("/api/users/update", {focus, isFirstLogin: false}) + .then(selectExam) + .catch(() => { + toast.error("Something went wrong, please try again later!", {toastId: "user-update-error"}); + }); + }; + + useEffect(() => { + toast.error("Something went wrong, please try again later!", {toastId: "user-update-error"}); + }, []); + + if (!focus) { + return ( +
+

What is your focus?

+
+ + +
+
+ ); + } + + return ( +
+

What is your current IELTS level?

+
+ + +
+
+ ); +} diff --git a/src/interfaces/exam.ts b/src/interfaces/exam.ts index ea4102f4..572228ea 100644 --- a/src/interfaces/exam.ts +++ b/src/interfaces/exam.ts @@ -11,6 +11,7 @@ export interface ReadingExam { exercises: Exercise[]; module: "reading"; minTimer: number; + type: "academic" | "general"; } export interface ListeningExam { diff --git a/src/interfaces/user.ts b/src/interfaces/user.ts index 5bec2c57..fc39e641 100644 --- a/src/interfaces/user.ts +++ b/src/interfaces/user.ts @@ -6,6 +6,8 @@ export interface User { profilePicture: string; id: string; experience: number; + isFirstLogin: boolean; + focus: "academic" | "general"; type: Type; } diff --git a/src/pages/api/users/update.ts b/src/pages/api/users/update.ts new file mode 100644 index 00000000..4c353f70 --- /dev/null +++ b/src/pages/api/users/update.ts @@ -0,0 +1,26 @@ +// Next.js API route support: https://nextjs.org/docs/api-routes/introduction +import type {NextApiRequest, NextApiResponse} from "next"; +import {app} from "@/firebase"; +import {getFirestore, collection, getDocs, getDoc, doc, setDoc} from "firebase/firestore"; +import {withIronSessionApiRoute} from "iron-session/next"; +import {sessionOptions} from "@/lib/session"; +import {User} from "@/interfaces/user"; + +const db = getFirestore(app); + +export default withIronSessionApiRoute(handler, sessionOptions); + +async function handler(req: NextApiRequest, res: NextApiResponse) { + if (!req.session.user) { + res.status(401).json({ok: false}); + return; + } + + const docUser = await getDoc(doc(db, "users", req.session.user.id)); + const user = docUser.data() as User; + + const userRef = doc(db, "users", user.id); + setDoc(userRef, req.body, {merge: true}); + + res.status(200).json({ok: true}); +} diff --git a/src/pages/history.tsx b/src/pages/history.tsx index d8ae5a92..2083e7a7 100644 --- a/src/pages/history.tsx +++ b/src/pages/history.tsx @@ -24,6 +24,8 @@ import {toast} from "react-toastify"; import {useRouter} from "next/router"; import Icon from "@mdi/react"; import {mdiArrowRight, mdiChevronRight} from "@mdi/js"; +import {uniqBy} from "lodash"; +import {getExamById} from "@/utils/exams"; export const getServerSideProps = withIronSessionSsr(({req, res}) => { const user = req.session.user; @@ -64,27 +66,6 @@ export default function History({user}: {user: User}) { } }, [stats, isStatsLoading]); - const getExam = async (module: Module): Promise => { - const examRequest = await axios(`/api/exam/${module}`); - if (examRequest.status !== 200) { - toast.error("Something went wrong!"); - return undefined; - } - - const newExam = examRequest.data; - - switch (module) { - case "reading": - return newExam.shift() as ReadingExam; - case "listening": - return newExam.shift() as ListeningExam; - case "writing": - return newExam.shift() as WritingExam; - case "speaking": - return newExam.shift() as SpeakingExam; - } - }; - const formatTimestamp = (timestamp: string) => { const date = moment(parseInt(timestamp)); const formatter = "YYYY/MM/DD - HH:mm"; @@ -100,9 +81,7 @@ export default function History({user}: {user: User}) { const total = dateStats.reduce((accumulator, current) => accumulator + current.score.total, 0); const selectExam = () => { - const examPromises = formatModuleTotalStats(dateStats) - .filter((x) => x.value > 0) - .map((module) => getExam(module.label.toLowerCase() as Module)); + const examPromises = uniqBy(dateStats, "exam").map((stat) => getExamById(stat.module, stat.exam)); Promise.all(examPromises).then((exams) => { if (exams.every((x) => !!x)) { diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 639cb0a3..a388fff6 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -11,6 +11,8 @@ import useStats from "@/hooks/useStats"; import {averageScore, formatModuleTotalStats, totalExams} from "@/utils/stats"; import {Divider} from "primereact/divider"; import useUser from "@/hooks/useUser"; +import Diagnostic from "@/components/Diagnostic"; +import {ToastContainer} from "react-toastify"; export const getServerSideProps = withIronSessionSsr(({req, res}) => { const user = req.session.user; @@ -41,6 +43,25 @@ export default function Home() { useEffect(() => setShowEndExam(window.innerWidth <= 960), []); useEffect(() => setWindowWidth(window.innerWidth), []); + if (user && user.isFirstLogin) { + return ( + <> + + IELTS GPT | Muscat Training Institute + + + + +
+ +
+ + ); + } + return ( <> @@ -52,6 +73,7 @@ export default function Home() { + {user && (
diff --git a/src/utils/exams.ts b/src/utils/exams.ts new file mode 100644 index 00000000..83a2498e --- /dev/null +++ b/src/utils/exams.ts @@ -0,0 +1,23 @@ +import {Module} from "@/interfaces"; +import {Exam, ReadingExam, ListeningExam, WritingExam, SpeakingExam} from "@/interfaces/exam"; +import axios from "axios"; + +export const getExamById = async (module: Module, id: string): Promise => { + const examRequest = await axios(`/api/exam/${module}/${id}`); + if (examRequest.status !== 200) { + return undefined; + } + + const newExam = examRequest.data; + + switch (module) { + case "reading": + return newExam as ReadingExam; + case "listening": + return newExam as ListeningExam; + case "writing": + return newExam as WritingExam; + case "speaking": + return newExam as SpeakingExam; + } +};