From 6e31a05f210d11b4849bf7c76bcce0177ff03cce Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Fri, 27 Oct 2023 15:50:02 +0100 Subject: [PATCH] Updated the register to allow the difference between a individual and corporate --- src/components/Low/Input.tsx | 4 +- src/interfaces/user.ts | 16 +- src/pages/(auth)/EmailVerification.tsx | 57 ++++++ src/pages/(register)/RegisterCorporate.tsx | 122 +++++++++++++ src/pages/(register)/RegisterIndividual.tsx | 100 +++++++++++ src/pages/login.tsx | 43 +---- src/pages/register.tsx | 188 ++++++-------------- src/utils/email.ts | 10 ++ 8 files changed, 364 insertions(+), 176 deletions(-) create mode 100644 src/pages/(auth)/EmailVerification.tsx create mode 100644 src/pages/(register)/RegisterCorporate.tsx create mode 100644 src/pages/(register)/RegisterIndividual.tsx create mode 100644 src/utils/email.ts diff --git a/src/components/Low/Input.tsx b/src/components/Low/Input.tsx index b9020d39..bd1fb466 100644 --- a/src/components/Low/Input.tsx +++ b/src/components/Low/Input.tsx @@ -2,11 +2,11 @@ import clsx from "clsx"; import {useState} from "react"; interface Props { - type: "email" | "text" | "password" | "tel"; + type: "email" | "text" | "password" | "tel" | "number"; required?: boolean; label?: string; placeholder?: string; - defaultValue?: string; + defaultValue?: string | number; className?: string; disabled?: boolean; name: string; diff --git a/src/interfaces/user.ts b/src/interfaces/user.ts index e1f149f5..c80e5cc6 100644 --- a/src/interfaces/user.ts +++ b/src/interfaces/user.ts @@ -14,13 +14,25 @@ export interface User { bio: string; isVerified: boolean; demographicInformation?: DemographicInformation; - companyInformation?: CompanyInformation; + corporateInformation?: CorporateInformation; subscriptionExpirationDate?: null | Date; isDisabled?: boolean; registrationDate?: Date; } -export interface CompanyInformation {} +export interface CorporateInformation { + companyInformation: CompanyInformation; + payment?: { + value: number; + currency: string; + }; + allowedUserAmount?: number; +} + +export interface CompanyInformation { + name: string; + userAmount: number; +} export interface DemographicInformation { country: string; diff --git a/src/pages/(auth)/EmailVerification.tsx b/src/pages/(auth)/EmailVerification.tsx new file mode 100644 index 00000000..79213ab3 --- /dev/null +++ b/src/pages/(auth)/EmailVerification.tsx @@ -0,0 +1,57 @@ +import Button from "@/components/Low/Button"; +import {User} from "@/interfaces/user"; +import {sendEmailVerification} from "@/utils/email"; +import axios from "axios"; +import {useRouter} from "next/router"; +import {Divider} from "primereact/divider"; +import {toast} from "react-toastify"; + +interface Props { + user: User; + isLoading: boolean; + setIsLoading: (isLoading: boolean) => void; +} + +export default function EmailVerification({user, isLoading, setIsLoading}: Props) { + const router = useRouter(); + + const onSuccess = () => toast.success("An e-mail has been sent, please make sure to check your spam folder!"); + + const onError = (e: Error) => { + console.error(e); + toast.error("Something went wrong, please logout and re-login.", {toastId: "send-verify-error"}); + }; + + const logout = async () => { + axios.post("/api/logout").finally(() => { + setTimeout(() => router.reload(), 500); + }); + }; + + return ( + <> + +
+

Please confirm your account!

+ + An e-mail has been sent to {user?.email}, please click the link in it to + confirm your account to be able to use the application.

+ Please refresh this page once it has been verified. +
+ +
+ + + + + + ); +} diff --git a/src/pages/(register)/RegisterCorporate.tsx b/src/pages/(register)/RegisterCorporate.tsx new file mode 100644 index 00000000..9487d123 --- /dev/null +++ b/src/pages/(register)/RegisterCorporate.tsx @@ -0,0 +1,122 @@ +import Button from "@/components/Low/Button"; +import Input from "@/components/Low/Input"; +import {User} from "@/interfaces/user"; +import {sendEmailVerification} from "@/utils/email"; +import axios from "axios"; +import {Divider} from "primereact/divider"; +import {useState} from "react"; +import {toast} from "react-toastify"; +import {KeyedMutator} from "swr"; + +interface Props { + isLoading: boolean; + setIsLoading: (isLoading: boolean) => void; + mutateUser: KeyedMutator; + sendEmailVerification: typeof sendEmailVerification; +} + +export default function RegisterCorporate({isLoading, setIsLoading, mutateUser, sendEmailVerification}: Props) { + const [name, setName] = useState(""); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + + const [companyName, setCompanyName] = useState(""); + const [companyUsers, setCompanyUsers] = useState(0); + + const onSuccess = () => toast.success("An e-mail has been sent, please make sure to check your spam folder!"); + + const onError = (e: Error) => { + console.error(e); + toast.error("Something went wrong, please logout and re-login.", {toastId: "send-verify-error"}); + }; + + const register = (e: any) => { + e.preventDefault(); + + if (confirmPassword !== password) { + toast.error("Your passwords do not match!", {toastId: "password-not-match"}); + return; + } + + setIsLoading(true); + axios + .post("/api/register", { + name, + email, + password, + type: "corporate", + profilePicture: "/defaultAvatar.png", + }) + .then((response) => { + mutateUser(response.data.user).then(() => sendEmailVerification(setIsLoading, onSuccess, onError)); + }) + .catch((error) => { + console.log(error.response.data); + + if (error.response.status === 401) { + toast.error("There is already a user with that e-mail!"); + return; + } + + if (error.response.status === 400) { + toast.error("The provided code is invalid!"); + return; + } + + toast.error("There was something wrong, please try again!"); + }) + .finally(() => setIsLoading(false)); + }; + + return ( +
+ setName(e)} placeholder="Enter your name" defaultValue={name} required /> + setEmail(e)} placeholder="Enter email address" defaultValue={email} required /> +
+ setPassword(e)} + placeholder="Enter your password" + defaultValue={password} + required + /> + setConfirmPassword(e)} + placeholder="Confirm your password" + defaultValue={confirmPassword} + required + /> +
+ + + + setCompanyName(e)} + placeholder="Institution name" + defaultValue={companyName} + required + /> + setCompanyUsers(parseInt(e))} + placeholder="Institution name" + defaultValue={companyUsers} + required + /> + + + + ); +} diff --git a/src/pages/(register)/RegisterIndividual.tsx b/src/pages/(register)/RegisterIndividual.tsx new file mode 100644 index 00000000..68f0f825 --- /dev/null +++ b/src/pages/(register)/RegisterIndividual.tsx @@ -0,0 +1,100 @@ +import Button from "@/components/Low/Button"; +import Input from "@/components/Low/Input"; +import {User} from "@/interfaces/user"; +import {sendEmailVerification} from "@/utils/email"; +import axios from "axios"; +import {useState} from "react"; +import {toast} from "react-toastify"; +import {KeyedMutator} from "swr"; + +interface Props { + queryCode?: string; + isLoading: boolean; + setIsLoading: (isLoading: boolean) => void; + mutateUser: KeyedMutator; + sendEmailVerification: typeof sendEmailVerification; +} + +export default function RegisterIndividual({queryCode, isLoading, setIsLoading, mutateUser, sendEmailVerification}: Props) { + const [name, setName] = useState(""); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [code, setCode] = useState(queryCode || ""); + + const onSuccess = () => toast.success("An e-mail has been sent, please make sure to check your spam folder!"); + + const onError = (e: Error) => { + console.error(e); + toast.error("Something went wrong, please logout and re-login.", {toastId: "send-verify-error"}); + }; + + const register = (e: any) => { + e.preventDefault(); + + if (confirmPassword !== password) { + toast.error("Your passwords do not match!", {toastId: "password-not-match"}); + return; + } + + setIsLoading(true); + axios + .post("/api/register", { + name, + email, + password, + code, + profilePicture: "/defaultAvatar.png", + }) + .then((response) => { + mutateUser(response.data.user).then(() => sendEmailVerification(setIsLoading, onSuccess, onError)); + }) + .catch((error) => { + console.log(error.response.data); + + if (error.response.status === 401) { + toast.error("There is already a user with that e-mail!"); + return; + } + + if (error.response.status === 400) { + toast.error("The provided code is invalid!"); + return; + } + + toast.error("There was something wrong, please try again!"); + }) + .finally(() => setIsLoading(false)); + }; + + return ( +
+ setName(e)} placeholder="Enter your name" defaultValue={name} required /> + setEmail(e)} placeholder="Enter email address" defaultValue={email} required /> + setPassword(e)} + placeholder="Enter your password" + defaultValue={password} + required + /> + setConfirmPassword(e)} + placeholder="Confirm your password" + defaultValue={confirmPassword} + required + /> + setCode(e)} placeholder="Enter your registration code" defaultValue={code} required /> + + +
+ ); +} diff --git a/src/pages/login.tsx b/src/pages/login.tsx index aab46e97..e8fca220 100644 --- a/src/pages/login.tsx +++ b/src/pages/login.tsx @@ -12,6 +12,7 @@ import Link from "next/link"; import Input from "@/components/Low/Input"; import clsx from "clsx"; import {useRouter} from "next/router"; +import EmailVerification from "./(auth)/EmailVerification"; const EMAIL_REGEX = new RegExp(/^[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*@[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*$/g); @@ -68,26 +69,6 @@ export default function Login() { .finally(() => setIsLoading(false)); }; - const sendEmailVerification = () => { - setIsLoading(true); - axios - .post<{ok: boolean}>("/api/reset/sendVerification", {}) - .then(() => { - toast.success("An e-mail has been sent, please make sure to check your spam folder!"); - }) - .catch((e) => { - console.log(e); - toast.error("Something went wrong, please logout and re-login.", {toastId: "send-verify-error"}); - }) - .finally(() => setIsLoading(false)); - }; - - const logout = async () => { - axios.post("/api/logout").finally(() => { - setTimeout(() => router.reload(), 500); - }); - }; - return ( <> @@ -150,27 +131,7 @@ export default function Login() { )} - {user && !user.isVerified && ( - <> -
-

Please confirm your account!

- - An e-mail has been sent to {user.email}, please click the - link in it to confirm your account to be able to use the application.

- Please refresh this page once it has been verified. -
- -
- - - - - - )} + {user && !user.isVerified && } diff --git a/src/pages/register.tsx b/src/pages/register.tsx index 120f4fc5..9c2f6ea9 100644 --- a/src/pages/register.tsx +++ b/src/pages/register.tsx @@ -1,18 +1,15 @@ /* eslint-disable @next/next/no-img-element */ -import {toast, ToastContainer} from "react-toastify"; +import {ToastContainer} from "react-toastify"; import {useState} from "react"; import Head from "next/head"; import useUser from "@/hooks/useUser"; -import Button from "@/components/Low/Button"; -import {BsArrowRepeat} from "react-icons/bs"; import Link from "next/link"; -import Input from "@/components/Low/Input"; -import axios from "axios"; -import {Divider} from "primereact/divider"; -import {useRouter} from "next/router"; import clsx from "clsx"; -import {NextPageContext} from "next"; -import {NextRequest, NextResponse} from "next/server"; +import {Tab} from "@headlessui/react"; +import RegisterIndividual from "./(register)/RegisterIndividual"; +import RegisterCorporate from "./(register)/RegisterCorporate"; +import EmailVerification from "./(auth)/EmailVerification"; +import {sendEmailVerification} from "@/utils/email"; export const getServerSideProps = (context: any) => { const {code} = context.query; @@ -23,110 +20,13 @@ export const getServerSideProps = (context: any) => { }; export default function Register({code: queryCode}: {code: string}) { - const [name, setName] = useState(""); - const [email, setEmail] = useState(""); - const [password, setPassword] = useState(""); - const [confirmPassword, setConfirmPassword] = useState(""); - const [code, setCode] = useState(queryCode || ""); const [isLoading, setIsLoading] = useState(false); - const router = useRouter(); - const {user, mutateUser} = useUser({ redirectTo: "/", redirectIfFound: true, }); - const register = (e: any) => { - e.preventDefault(); - - if (confirmPassword !== password) { - toast.error("Your passwords do not match!", {toastId: "password-not-match"}); - return; - } - - setIsLoading(true); - axios - .post("/api/register", { - name, - email, - password, - code, - profilePicture: "/defaultAvatar.png", - }) - .then((response) => { - mutateUser(response.data.user).then(sendEmailVerification); - }) - .catch((error) => { - console.log(error.response.data); - - if (error.response.status === 401) { - toast.error("There is already a user with that e-mail!"); - return; - } - - if (error.response.status === 400) { - toast.error("The provided code is invalid!"); - return; - } - - toast.error("There was something wrong, please try again!"); - }) - .finally(() => setIsLoading(false)); - }; - - const sendEmailVerification = () => { - setIsLoading(true); - axios - .post<{ok: boolean}>("/api/reset/sendVerification", {}) - .then(() => { - toast.success("An e-mail has been sent, please make sure to check your spam folder!"); - }) - .catch((e) => { - console.log(e); - toast.error("Something went wrong, please logout and re-login.", {toastId: "send-verify-error"}); - }) - .finally(() => setIsLoading(false)); - }; - - const logout = async () => { - axios.post("/api/logout").finally(() => { - setTimeout(() => router.reload(), 500); - }); - }; - - const initialStep = () => ( - <> -
- setName(e)} placeholder="Enter your name" defaultValue={name} required /> - setEmail(e)} placeholder="Enter email address" defaultValue={email} required /> - setPassword(e)} - placeholder="Enter your password" - defaultValue={password} - required - /> - setConfirmPassword(e)} - placeholder="Confirm your password" - defaultValue={confirmPassword} - required - /> - setCode(e)} placeholder="Enter your registration code" defaultValue={code} required /> - -
- - ); - return ( <> @@ -142,40 +42,66 @@ export default function Register({code: queryCode}: {code: string}) { People smiling looking at a tablet
-
- EnCoach's Logo +
+ EnCoach's Logo

Create new account

{!user && ( <> - {initialStep()} +
+ + + + clsx( + "w-full rounded-lg py-2.5 text-sm font-medium leading-5 text-mti-purple-light", + "ring-white ring-opacity-60 ring-offset-2 ring-offset-mti-purple-light focus:outline-none focus:ring-2", + "transition duration-300 ease-in-out", + selected ? "bg-white shadow" : "text-blue-100 hover:bg-white/[0.12] hover:text-mti-purple-dark", + ) + }> + Individual + + + clsx( + "w-full rounded-lg py-2.5 text-sm font-medium leading-5 text-mti-purple-light", + "ring-white ring-opacity-60 ring-offset-2 ring-offset-mti-purple-light focus:outline-none focus:ring-2", + "transition duration-300 ease-in-out", + selected ? "bg-white shadow" : "text-blue-100 hover:bg-white/[0.12] hover:text-mti-purple-dark", + ) + }> + Corporate + + + + + + + + + + + + +
Sign in instead )} - {user && !user.isVerified && ( - <> - -
-

Please confirm your account!

- - An e-mail has been sent to {user.email}, please click the - link in it to confirm your account to be able to use the application.

- Please refresh this page once it has been verified. -
- -
- - - - - - )} + {user && !user.isVerified && }
diff --git a/src/utils/email.ts b/src/utils/email.ts new file mode 100644 index 00000000..c518f36e --- /dev/null +++ b/src/utils/email.ts @@ -0,0 +1,10 @@ +import axios from "axios"; + +export const sendEmailVerification = (setIsLoading: (isLoading: boolean) => void, onSuccess: () => void, onError: (e: Error) => void) => { + setIsLoading(true); + axios + .post<{ok: boolean}>("/api/reset/sendVerification", {}) + .then(onSuccess) + .catch(onError) + .finally(() => setIsLoading(false)); +};