Turned the code in the register optional
This commit is contained in:
@@ -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",
|
||||||
|
|||||||
@@ -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
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
70
src/pages/payment.tsx
Normal 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} />}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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",
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
Reference in New Issue
Block a user