From 05ca96e476684d47f3a09b33bc72779939428c9f Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Sat, 16 Sep 2023 10:27:17 +0100 Subject: [PATCH] Updated the demographic input to work more as expected --- .../DemographicInformationInput.tsx | 153 ++++++++++++++++++ src/exams/Selection.tsx | 37 +++-- src/pages/api/exam/[module]/index.ts | 4 +- src/pages/api/users/update.ts | 7 +- src/pages/index.tsx | 28 +++- src/pages/profile.tsx | 16 +- src/pages/register.tsx | 139 +--------------- 7 files changed, 219 insertions(+), 165 deletions(-) create mode 100644 src/components/DemographicInformationInput.tsx diff --git a/src/components/DemographicInformationInput.tsx b/src/components/DemographicInformationInput.tsx new file mode 100644 index 00000000..64c3087e --- /dev/null +++ b/src/components/DemographicInformationInput.tsx @@ -0,0 +1,153 @@ +import {EmploymentStatus, EMPLOYMENT_STATUS, Gender, User} from "@/interfaces/user"; +import {FormEvent, useState} from "react"; +import countryCodes from "country-codes-list"; +import {RadioGroup} from "@headlessui/react"; +import Input from "./Low/Input"; +import clsx from "clsx"; +import Button from "./Low/Button"; +import {BsArrowRepeat} from "react-icons/bs"; +import axios from "axios"; +import {toast} from "react-toastify"; +import {KeyedMutator} from "swr"; + +interface Props { + mutateUser: KeyedMutator; +} + +export default function DemographicInformationInput({mutateUser}: Props) { + const [country, setCountry] = useState(); + const [phone, setPhone] = useState(); + const [gender, setGender] = useState(); + const [employment, setEmployment] = useState(); + const [isLoading, setIsLoading] = useState(false); + + const save = (e?: FormEvent) => { + if (e) e.preventDefault(); + setIsLoading(true); + + axios + .patch("/api/users/update", { + demographicInformation: { + country, + phone: `+${countryCodes.findOne("countryCode" as any, country!).countryCallingCode}${phone}`, + gender, + employment, + }, + }) + .then((response) => mutateUser((response.data as {user: User}).user)) + .catch(() => { + toast.error("Something went wrong, please try again later!", {toastId: "user-update-error"}); + }) + .finally(() => setIsLoading(false)); + }; + + return ( +
+

+ Welcome to EnCoach, the ultimate platform dedicated to helping you master the IELTS ! We are thrilled that you have chosen us as your + learning companion on this journey towards achieving your desired IELTS score. +
+
+ To make the most of your learning experience, we kindly request you to complete your profile. By providing some essential information + about yourself. +

+
+
+ + +
+ setPhone(e)} placeholder="Enter phone number" required /> +
+ + + + {({checked}) => ( + + Male + + )} + + + {({checked}) => ( + + Female + + )} + + + {({checked}) => ( + + Other + + )} + + +
+
+ + + {EMPLOYMENT_STATUS.map(({status, label}) => ( + + {({checked}) => ( + + {label} + + )} + + ))} + +
+
+ +
+ +
+
+ ); +} diff --git a/src/exams/Selection.tsx b/src/exams/Selection.tsx index 50af55eb..e7d6fcbf 100644 --- a/src/exams/Selection.tsx +++ b/src/exams/Selection.tsx @@ -4,7 +4,7 @@ import {Module} from "@/interfaces"; import clsx from "clsx"; import {User} from "@/interfaces/user"; import ProgressBar from "@/components/Low/ProgressBar"; -import {BsBook, BsCheckCircle, BsHeadphones, BsMegaphone, BsPen} from "react-icons/bs"; +import {BsBook, BsCheck, BsCheckCircle, BsHeadphones, BsMegaphone, BsPen} from "react-icons/bs"; import {totalExamsByModule} from "@/utils/stats"; import useStats from "@/hooks/useStats"; import Button from "@/components/Low/Button"; @@ -19,6 +19,7 @@ interface Props { export default function Selection({user, onStart, disableSelection = false}: Props) { const [selectedModules, setSelectedModules] = useState([]); + const [avoidRepeatedExams, setAvoidRepeatedExams] = useState(true); const {stats} = useStats(user?.id); const toggleModule = (module: Module) => { @@ -181,15 +182,31 @@ export default function Selection({user, onStart, disableSelection = false}: Pro )} - +
+
setAvoidRepeatedExams((prev) => !prev)}> + +
+ +
+ Avoid Repeated Questions +
+ +
); diff --git a/src/pages/api/exam/[module]/index.ts b/src/pages/api/exam/[module]/index.ts index a45876d9..c8737604 100644 --- a/src/pages/api/exam/[module]/index.ts +++ b/src/pages/api/exam/[module]/index.ts @@ -16,9 +16,9 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { return; } - const {module} = req.query as {module: string}; + const {module, avoidRepeated} = req.query as {module: string; avoidRepeated: string}; const moduleRef = collection(db, module); - const q = query(moduleRef, where("isDiagnostic", "==", false)); + const q = query(moduleRef, where("isDiagnostic", "==", true)); const snapshot = await getDocs(q); diff --git a/src/pages/api/users/update.ts b/src/pages/api/users/update.ts index de83a91f..c2f6e207 100644 --- a/src/pages/api/users/update.ts +++ b/src/pages/api/users/update.ts @@ -59,10 +59,13 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { delete updatedUser.newPassword; await setDoc(userRef, updatedUser, {merge: true}); - req.session.user = {...updatedUser, id: req.session.user.id}; + + const docUser = await getDoc(doc(db, "users", req.session.user.id)); + const user = docUser.data() as User; + req.session.user = {...user, id: req.session.user.id}; await req.session.save(); - res.status(200).json({ok: true}); + res.status(200).json({user}); } export const config = { diff --git a/src/pages/index.tsx b/src/pages/index.tsx index c7f60de5..952791cc 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -17,6 +17,7 @@ import ProgressBar from "@/components/Low/ProgressBar"; import Layout from "@/components/High/Layout"; import {calculateAverageLevel} from "@/utils/score"; import axios from "axios"; +import DemographicInformationInput from "@/components/DemographicInformationInput"; export const getServerSideProps = withIronSessionSsr(({req, res}) => { const user = req.session.user; @@ -39,13 +40,36 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => { export default function Home() { const [showDiagnostics, setShowDiagnostics] = useState(false); - const {user} = useUser({redirectTo: "/login"}); + const [showDemographicInput, setShowDemographicInput] = useState(false); + const {user, mutateUser} = useUser({redirectTo: "/login"}); const {stats} = useStats(user?.id); useEffect(() => { - if (user) setShowDiagnostics(user.isFirstLogin); + if (user) { + setShowDemographicInput(!user.demographicInformation); + setShowDiagnostics(user.isFirstLogin); + } }, [user]); + if (user && showDemographicInput) { + return ( + <> + + EnCoach | Muscat Training Institute + + + + + + + + + ); + } + if (user && showDiagnostics) { return ( <> diff --git a/src/pages/profile.tsx b/src/pages/profile.tsx index f8152bac..b60aaff7 100644 --- a/src/pages/profile.tsx +++ b/src/pages/profile.tsx @@ -1,30 +1,19 @@ /* eslint-disable @next/next/no-img-element */ import Head from "next/head"; -import Navbar from "@/components/Navbar"; -import {BsFileEarmarkText, BsPencil, BsStar, BsBook, BsHeadphones, BsPen, BsMegaphone, BsArrowRepeat} from "react-icons/bs"; import {withIronSessionSsr} from "iron-session/next"; import {sessionOptions} from "@/lib/session"; import {ChangeEvent, useEffect, useRef, useState} from "react"; -import useStats from "@/hooks/useStats"; -import {averageScore, totalExams} from "@/utils/stats"; import useUser from "@/hooks/useUser"; -import Sidebar from "@/components/Sidebar"; -import Diagnostic from "@/components/Diagnostic"; import {toast, ToastContainer} from "react-toastify"; -import {capitalize} from "lodash"; -import {Module} from "@/interfaces"; -import ProgressBar from "@/components/Low/ProgressBar"; import Layout from "@/components/High/Layout"; -import {calculateAverageLevel} from "@/utils/score"; import Input from "@/components/Low/Input"; import Button from "@/components/Low/Button"; -import {useRouter} from "next/router"; import Link from "next/link"; import axios from "axios"; import {ErrorMessage} from "@/constants/errors"; import {RadioGroup} from "@headlessui/react"; import clsx from "clsx"; -import {EmploymentStatus, EMPLOYMENT_STATUS, Gender} from "@/interfaces/user"; +import {EmploymentStatus, EMPLOYMENT_STATUS, Gender, User} from "@/interfaces/user"; import countryCodes from "country-codes-list"; export const getServerSideProps = withIronSessionSsr(({req, res}) => { @@ -61,7 +50,6 @@ export default function Home() { const [employment, setEmployment] = useState(); const profilePictureInput = useRef(null); - const router = useRouter(); const {user, mutateUser} = useUser({redirectTo: "/login"}); @@ -129,7 +117,7 @@ export default function Home() { }); if (request.status === 200) { toast.success("Your profile has been updated!"); - mutateUser(); + mutateUser((request.data as {user: User}).user); setIsLoading(false); return; } diff --git a/src/pages/register.tsx b/src/pages/register.tsx index 711f74d6..d27a4c09 100644 --- a/src/pages/register.tsx +++ b/src/pages/register.tsx @@ -11,22 +11,12 @@ import axios from "axios"; import {Divider} from "primereact/divider"; import {useRouter} from "next/router"; import clsx from "clsx"; -import {EmploymentStatus, EMPLOYMENT_STATUS, Gender} from "@/interfaces/user"; -import countryCodes from "country-codes-list"; -import {RadioGroup} from "@headlessui/react"; export default function Register() { const [name, setName] = useState(""); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState(""); - - const [country, setCountry] = useState(); - const [phone, setPhone] = useState(); - const [gender, setGender] = useState(); - const [employment, setEmployment] = useState(); - - const [step, setStep] = useState<0 | 1>(0); const [isLoading, setIsLoading] = useState(false); const router = useRouter(); @@ -41,7 +31,6 @@ export default function Register() { if (confirmPassword !== password) { toast.error("Your passwords do not match!", {toastId: "password-not-match"}); - setStep(0); return; } @@ -52,12 +41,6 @@ export default function Register() { email, password, profilePicture: "/defaultAvatar.png", - demographicInformation: { - country, - phone, - gender, - employment, - }, }) .then((response) => { mutateUser(response.data.user).then(sendEmailVerification); @@ -67,7 +50,6 @@ export default function Register() { if (error.response.status === 401) { toast.error("There is already a user with that e-mail!"); - setStep(0); return; } toast.error("There was something wrong, please try again!"); @@ -94,12 +76,7 @@ export default function Register() { const initialStep = () => ( <> -
{ - e.preventDefault(); - setStep(1); - }}> + setName(e)} placeholder="Enter your name" defaultValue={name} required /> setEmail(e)} placeholder="Enter email address" defaultValue={email} required /> - Next - -
- - ); - - const demographicStep = () => ( - <> -
-
- - -
- setPhone(e)} - placeholder="Enter phone number" - defaultValue={phone} - required - /> -
- - - - {({checked}) => ( - - Male - - )} - - - {({checked}) => ( - - Female - - )} - - - {({checked}) => ( - - Other - - )} - - -
-
- - - {EMPLOYMENT_STATUS.map(({status, label}) => ( - - {({checked}) => ( - - {label} - - )} - - ))} - -
- - -
@@ -252,12 +122,11 @@ export default function Register() {
EnCoach's Logo -

{step === 1 ? "Demographic Information" : "Create new account"}

+

Create new account

{!user && ( <> - {step === 0 && initialStep()} - {step === 1 && demographicStep()} + {initialStep()} Sign in instead