Turned the code in the register optional

This commit is contained in:
Tiago Ribeiro
2023-11-26 13:59:14 +00:00
parent fa544bf4e8
commit a9bbbe8b52
9 changed files with 111 additions and 20 deletions

View File

@@ -53,7 +53,7 @@ export default function Navbar({user, path, navDisabled = false, focusMode = fal
<div className="flex justify-end -md:items-center gap-4 md:w-5/6 md:mr-8"> <div className="flex justify-end -md:items-center gap-4 md:w-5/6 md:mr-8">
{showExpirationDate() && ( {showExpirationDate() && (
<Link <Link
href="https://encoach.com/join" href="/payment"
data-tip="Expiry date" data-tip="Expiry date"
className={clsx( className={clsx(
"py-2 px-6 w-fit flex justify-center text-sm font-normal rounded-full border focus:outline-none cursor-pointer", "py-2 px-6 w-fit flex justify-center text-sm font-normal rounded-full border focus:outline-none cursor-pointer",

View File

@@ -125,8 +125,8 @@ export default function RegisterCorporate({isLoading, setIsLoading, mutateUser,
type="text" type="text"
name="companyName" name="companyName"
onChange={(e) => setCompanyName(e)} onChange={(e) => setCompanyName(e)}
placeholder="Institution name" placeholder="Corporate name"
label="Institution name" label="Corporate name"
defaultValue={companyName} defaultValue={companyName}
required required
/> />
@@ -134,7 +134,7 @@ export default function RegisterCorporate({isLoading, setIsLoading, mutateUser,
type="number" type="number"
name="companyUsers" name="companyUsers"
onChange={(e) => setCompanyUsers(parseInt(e))} onChange={(e) => setCompanyUsers(parseInt(e))}
label="Amount of users" label="Number of users"
defaultValue={companyUsers} defaultValue={companyUsers}
required required
/> />

View File

@@ -1,4 +1,5 @@
import Button from "@/components/Low/Button"; import Button from "@/components/Low/Button";
import Checkbox from "@/components/Low/Checkbox";
import Input from "@/components/Low/Input"; import Input from "@/components/Low/Input";
import {User} from "@/interfaces/user"; import {User} from "@/interfaces/user";
import {sendEmailVerification} from "@/utils/email"; import {sendEmailVerification} from "@/utils/email";
@@ -21,6 +22,7 @@ export default function RegisterIndividual({queryCode, isLoading, setIsLoading,
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState("");
const [code, setCode] = useState(queryCode || ""); const [code, setCode] = useState(queryCode || "");
const [hasCode, setHasCode] = useState<boolean>(!!queryCode);
const onSuccess = () => toast.success("An e-mail has been sent, please make sure to check your spam folder!"); const onSuccess = () => toast.success("An e-mail has been sent, please make sure to check your spam folder!");
@@ -88,12 +90,27 @@ export default function RegisterIndividual({queryCode, isLoading, setIsLoading,
defaultValue={confirmPassword} defaultValue={confirmPassword}
required required
/> />
<Input type="text" name="code" onChange={(e) => setCode(e)} placeholder="Enter your registration code" defaultValue={code} required />
{/** TODO: Add a checkbox to disable code */}
<div className="flex flex-col gap-4 w-full items-start">
<Checkbox isChecked={hasCode} onChange={setHasCode}>
I have a code
</Checkbox>
{hasCode && (
<Input
type="text"
name="code"
onChange={(e) => setCode(e)}
placeholder="Enter your registration code (optional)"
defaultValue={code}
/>
)}
</div>
<Button <Button
className="lg:mt-8 w-full" className="lg:mt-8 w-full"
color="purple" color="purple"
disabled={isLoading || !email || !name || !password || !confirmPassword || password !== confirmPassword || !code}> disabled={isLoading || !email || !name || !password || !confirmPassword || password !== confirmPassword || (hasCode ? !code : false)}>
Create account Create account
</Button> </Button>
</form> </form>

View File

@@ -19,7 +19,7 @@ import {Divider} from "primereact/divider";
import Input from "@/components/Low/Input"; import Input from "@/components/Low/Input";
import Button from "@/components/Low/Button"; import Button from "@/components/Low/Button";
export default function PaymentDue({user, reload}: {user: User; reload: () => void}) { export default function PaymentDue({user, hasExpired, reload}: {user: User; hasExpired?: boolean; reload: () => void}) {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const {packages} = usePackages(); const {packages} = usePackages();
@@ -48,9 +48,9 @@ export default function PaymentDue({user, reload}: {user: User; reload: () => vo
</div> </div>
)} )}
{user ? ( {user ? (
<Layout user={user} navDisabled> <Layout user={user} navDisabled={hasExpired}>
<div className="flex flex-col items-center justify-center text-center w-full gap-4"> <div className="flex flex-col items-center justify-center text-center w-full gap-4">
<span className="font-bold text-lg">You do not have time credits for your account type!</span> {hasExpired && <span className="font-bold text-lg">You do not have time credits for your account type!</span>}
{isIndividual() && ( {isIndividual() && (
<div className="flex flex-col items-center w-full overflow-x-scroll scrollbar-hide gap-12"> <div className="flex flex-col items-center w-full overflow-x-scroll scrollbar-hide gap-12">
<span className="max-w-lg"> <span className="max-w-lg">

View File

@@ -6,6 +6,7 @@ import {withIronSessionApiRoute} from "iron-session/next";
import {getFirestore, doc, setDoc, query, collection, where, getDocs} from "firebase/firestore"; import {getFirestore, doc, setDoc, query, collection, where, getDocs} from "firebase/firestore";
import {CorporateInformation, DemographicInformation, Type} from "@/interfaces/user"; import {CorporateInformation, DemographicInformation, Type} from "@/interfaces/user";
import {addUserToGroupOnCreation} from "@/utils/registration"; import {addUserToGroupOnCreation} from "@/utils/registration";
import moment from "moment";
const auth = getAuth(app); const auth = getAuth(app);
const db = getFirestore(app); const db = getFirestore(app);
@@ -45,12 +46,12 @@ async function registerIndividual(req: NextApiRequest, res: NextApiResponse) {
const codeQuery = query(collection(db, "codes"), where("code", "==", code)); const codeQuery = query(collection(db, "codes"), where("code", "==", code));
const codeDocs = (await getDocs(codeQuery)).docs.filter((x) => !Object.keys(x.data()).includes("userId")); const codeDocs = (await getDocs(codeQuery)).docs.filter((x) => !Object.keys(x.data()).includes("userId"));
if (codeDocs.length === 0) { if (code && code.length > 0 && codeDocs.length === 0) {
res.status(400).json({error: "Invalid Code!"}); res.status(400).json({error: "Invalid Code!"});
return; return;
} }
const codeData = codeDocs[0].data() as {code: string; type: Type; creator?: string; expiryDate: Date | null}; const codeData = codeDocs.length > 0 ? (codeDocs[0].data() as {code: string; type: Type; creator?: string; expiryDate: Date | null}) : undefined;
createUserWithEmailAndPassword(auth, email, password) createUserWithEmailAndPassword(auth, email, password)
.then(async (userCredentials) => { .then(async (userCredentials) => {
@@ -62,17 +63,20 @@ async function registerIndividual(req: NextApiRequest, res: NextApiResponse) {
desiredLevels: DEFAULT_DESIRED_LEVELS, desiredLevels: DEFAULT_DESIRED_LEVELS,
levels: DEFAULT_LEVELS, levels: DEFAULT_LEVELS,
bio: "", bio: "",
isFirstLogin: codeData.type === "student", isFirstLogin: codeData ? codeData.type === "student" : true,
focus: "academic", focus: "academic",
type: codeData.type, type: codeData ? codeData.type : "student",
subscriptionExpirationDate: codeData.expiryDate, subscriptionExpirationDate: codeData ? codeData.expiryDate : moment().subtract(1, "days").toISOString(),
registrationDate: new Date(), registrationDate: new Date(),
status: code ? "active" : "paymentDue", status: code ? "active" : "paymentDue",
}; };
await setDoc(doc(db, "users", userId), user); await setDoc(doc(db, "users", userId), user);
await setDoc(codeDocs[0].ref, {userId: userId}, {merge: true});
if (codeData.creator) await addUserToGroupOnCreation(userId, codeData.type, codeData.creator); if (codeDocs.length > 0 && codeData) {
await setDoc(codeDocs[0].ref, {userId: userId}, {merge: true});
if (codeData.creator) await addUserToGroupOnCreation(userId, codeData.type, codeData.creator);
}
req.session.user = {...user, id: userId}; req.session.user = {...user, id: userId};
await req.session.save(); await req.session.save();

View File

@@ -92,7 +92,7 @@ export default function Home() {
</div> </div>
</Layout> </Layout>
)} )}
{(user.status === "paymentDue" || checkIfUserExpired()) && <PaymentDue user={user} reload={router.reload} />} {(user.status === "paymentDue" || checkIfUserExpired()) && <PaymentDue hasExpired user={user} reload={router.reload} />}
</> </>
); );
} }

70
src/pages/payment.tsx Normal file
View File

@@ -0,0 +1,70 @@
/* 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} from "react-icons/bs";
import {withIronSessionSsr} from "iron-session/next";
import {sessionOptions} from "@/lib/session";
import {useEffect, useState} from "react";
import useStats from "@/hooks/useStats";
import {averageScore, groupBySession, totalExams} from "@/utils/stats";
import useUser from "@/hooks/useUser";
import Sidebar from "@/components/Sidebar";
import Diagnostic from "@/components/Diagnostic";
import {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 axios from "axios";
import DemographicInformationInput from "@/components/DemographicInformationInput";
import moment from "moment";
import Link from "next/link";
import {MODULE_ARRAY} from "@/utils/moduleUtils";
import ProfileSummary from "@/components/ProfileSummary";
import StudentDashboard from "@/dashboards/Student";
import AdminDashboard from "@/dashboards/Admin";
import CorporateDashboard from "@/dashboards/Corporate";
import TeacherDashboard from "@/dashboards/Teacher";
import AgentDashboard from "@/dashboards/Agent";
import PaymentDue from "./(status)/PaymentDue";
import {useRouter} from "next/router";
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
const user = req.session.user;
if (!user || !user.isVerified) {
res.setHeader("location", "/login");
res.statusCode = 302;
res.end();
return {
props: {
user: null,
},
};
}
return {
props: {user: req.session.user},
};
}, sessionOptions);
export default function Home() {
const {user, mutateUser} = useUser({redirectTo: "/login"});
const router = useRouter();
return (
<>
<Head>
<title>EnCoach</title>
<meta
name="description"
content="A training platform for the IELTS exam provided by the Muscat Training Institute and developed by eCrop."
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
{user && <PaymentDue user={user} reload={router.reload} />}
</>
);
}

View File

@@ -296,9 +296,9 @@ export default function Home() {
</RadioGroup> </RadioGroup>
</div> </div>
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
<label className="font-normal text-base text-mti-gray-dim">Expiry Date</label> <label className="font-normal text-base text-mti-gray-dim">Expiry Date (click to purchase)</label>
<Link <Link
href="https://encoach.com/join" href="/payment"
className={clsx( className={clsx(
"p-6 w-full flex justify-center text-sm font-normal rounded-full border focus:outline-none cursor-pointer", "p-6 w-full flex justify-center text-sm font-normal rounded-full border focus:outline-none cursor-pointer",
"transition duration-300 ease-in-out", "transition duration-300 ease-in-out",

View File

@@ -36,7 +36,7 @@ export default function Register({code: queryCode}: {code: string}) {
<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 min-h-[100vh] h-full flex bg-white text-black"> <main className="w-full h-[100vh] flex bg-white text-black">
<ToastContainer /> <ToastContainer />
<section className="h-full w-fit min-w-fit relative hidden lg:flex"> <section className="h-full w-fit min-w-fit relative hidden lg:flex">
<div className="absolute h-full w-full bg-mti-rose-light z-10 bg-opacity-50" /> <div className="absolute h-full w-full bg-mti-rose-light z-10 bg-opacity-50" />