diff --git a/src/App.tsx b/src/App.tsx index 76ed9b8..0785adb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -51,6 +51,7 @@ import NotFoundPage from './pages/404Page'; import ProjectPage from './pages/ProjectsPage/index'; import JoinTeamsPage from './pages/Competitions/CompetitionTeamPages/JoinTeamsPage'; +import VerifyEmailPage from './pages/Auth/VerifyEmail'; let cookie = getCookie(COOKIE_NAME); diff --git a/src/actions/auth.ts b/src/actions/auth.ts index 00fc129..42391af 100644 --- a/src/actions/auth.ts +++ b/src/actions/auth.ts @@ -4,8 +4,13 @@ import { setCookie, deleteCookie } from '../utils/cookie'; import { User } from '../UserContext'; import { COMPETITIONS_COOKIE_NAME, COOKIE_NAME } from '../configs'; +interface RegisterResponse { + msg: string; + id: string; +} + export const resetPassword = async (data: { - username: string; + userID: string; code: string; password: string; }) => { @@ -18,7 +23,7 @@ export const resetPassword = async (data: { .post( process.env.REACT_APP_API + '/v1/users/' + - data.username + + data.userID + '/resetpassword', body ) @@ -27,11 +32,29 @@ export const resetPassword = async (data: { }) .catch((error) => { message.error('Reset Failed'); + console.log(error); + message.error(error.response.data.error.message); reject(error); }); }); }; +export const verifyEmail = async (data: { code: string, id : string}) => { + return new Promise((resolve, reject) => { + axios + .post(process.env.REACT_APP_API + '/v1/users/verifyEmail', data) + .then((res: AxiosResponse) => { + resolve(res); + }) + .catch((error) => { + message.error('Verification Failed'); + message.error('Please check your code and expiration time'); + reject(error); + }); + }); +} + + export const requestReset = async (username: string) => { return new Promise((resolve, reject) => { axios @@ -43,7 +66,8 @@ export const requestReset = async (username: string) => { }) .catch((error) => { message.error('Request Failed'); - //reject(error); + message.error(error.response.data.error.message); + reject(error); }); }); }; @@ -59,16 +83,16 @@ export const registerUser = async (data: { email: data.email, isUCSD: data.isUCSD, }; - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { axios .post(process.env.REACT_APP_API + '/v1/users', body) .then((res: AxiosResponse) => { - resolve(res); + resolve(res.data); }) .catch((error) => { - //message.error(error.response.data.error.message); + message.error(error.response.data.error.message); console.error(error); - //reject(error); + reject(error); }); }); }; @@ -123,6 +147,20 @@ export const loginUser = async ( }) .catch((error) => { message.error(error.response.data.error.message); + console.error(error); + if (error.response.data.error.message === 'User not verified') { + + axios + .get(process.env.REACT_APP_API + '/v1/users/' + data.username + '/verifyEmail' ) + .then((res: AxiosResponse) => { + resolve(res.data.token); + }) + .catch((error) => { + reject(error); + }); + + message.error("Check your email to verify"); + } //reject(error); }); }); diff --git a/src/pages/Auth/ForgotPassword/index.tsx b/src/pages/Auth/ForgotPassword/index.tsx index a6333a3..e2622f6 100644 --- a/src/pages/Auth/ForgotPassword/index.tsx +++ b/src/pages/Auth/ForgotPassword/index.tsx @@ -15,7 +15,7 @@ function ForgotPasswordPage({ location }: RouteComponentProps) { let queries = query.parse(location.search); let body = { - username: String(queries['username']), + userID: String(queries['username']), // Update the property name to 'userID' code: String(queries['?code']), password: values['password'], }; @@ -117,4 +117,4 @@ function handlePasswordErrors(errors: any) { } } } -export default ForgotPasswordPage; +export default ForgotPasswordPage; \ No newline at end of file diff --git a/src/pages/Auth/LoginPage/index.tsx b/src/pages/Auth/LoginPage/index.tsx index f9feabe..ea36d2b 100644 --- a/src/pages/Auth/LoginPage/index.tsx +++ b/src/pages/Auth/LoginPage/index.tsx @@ -17,9 +17,16 @@ function LoginPage() { // setRegisterStep('processing'); loginUser(DIMENSION_ID, values).then((res: any) => { - setUser(getUserFromToken(res)); - message.success('Logged in!'); - window.location.href = '/'; + if (res) { + // Now we know that user has successfully logged in or verified + setUser(getUserFromToken(res)); + message.success('Logged in!'); + + // Only redirect after verification is done (if the user was already verified) + window.location.href = '/'; // Or use a different page based on your app flow + } + }).catch((error) => { + message.error(error.response.data.error.message); }); }; diff --git a/src/pages/Auth/RegisterPage/index.tsx b/src/pages/Auth/RegisterPage/index.tsx index 7a43c99..47b7539 100644 --- a/src/pages/Auth/RegisterPage/index.tsx +++ b/src/pages/Auth/RegisterPage/index.tsx @@ -21,10 +21,11 @@ function RegisterPage() { handlePasswordErrors(errors); } registerUser({ ...values, isUCSD: checked, admin: isAdmin }).then((res) => { + message.success('Please check your email to verify your account'); message.success(isAdmin ? 'Admin registered! Redirecting to login page' : 'Registered! Redirecting to login page'); - history.push('/login'); + history.push('/verify/?id=' + res.id); }); }; const onCheckChange = (e: any) => { diff --git a/src/pages/Auth/VerifyEmail/index.less b/src/pages/Auth/VerifyEmail/index.less new file mode 100644 index 0000000..8cf0151 --- /dev/null +++ b/src/pages/Auth/VerifyEmail/index.less @@ -0,0 +1,82 @@ +@import '../../../newStyles/text.less'; +@import '../../../newStyles/components.less'; +@import '../../../newStyles/variables.less'; + +.VerifyEmailPage { + + .noContainer(); + + display: flex; + justify-content: center; + align-items: center; + + .VerifyDetails { + .generic-centered-section(); + .constrained-bounds(); + + .VerifyHeader { + margin-top: 4rem; + width: 60%; + max-width: @max-width2; + text-align: center; + @media only screen and (max-width: @xxs) { + width: 90%; + } + + } + + form { + margin-top: 3rem; + width: 60%; + max-width: @max-width1; + display: flex; + flex-direction: column; + justify-content: center; + text-align: center; + + @media only screen and (max-width: @xxs) { + width: 90%; + } + Input { + line-height: 2.5rem ; + font-size: @font3; + + } + + #VerifyButton { + width: 100%; + height: 45px; + .button-black-square(); + margin: auto; + } + + } + } + + + .loginLink { + margin-top: @margin-base2; + text-align: center; + } +} + + +/* +.LoginPage { + + .danger { + color: @danger; + } + .loginButton { + margin-top: @margin-base; + } + .loginCard { + margin: auto; + .smack-center(); + max-width: 90%; + } + .register-info { + margin-top: 12px; + } +} +*/ \ No newline at end of file diff --git a/src/pages/Auth/VerifyEmail/index.tsx b/src/pages/Auth/VerifyEmail/index.tsx new file mode 100644 index 0000000..341605f --- /dev/null +++ b/src/pages/Auth/VerifyEmail/index.tsx @@ -0,0 +1,84 @@ +import React, { useEffect, useState } from 'react'; +import './index.less'; +import DefaultLayout from '../../../components/layouts/default'; +import { Form, Input, message, Button, Layout } from 'antd'; +import { Link, useHistory } from 'react-router-dom'; +import { useForm, Controller } from 'react-hook-form'; +import { verifyEmail } from '../../../actions/auth'; + +const { Content } = Layout; + +const VerifyEmailPage = () => { + const { handleSubmit, control, formState: { errors } } = useForm(); + const history = useHistory(); + + + const onSubmit = (values: any) => { + + const queries = new URLSearchParams(window.location.search); + const id = queries.get('id'); + if (!id) { + message.error('Invalid verification link'); + return; + } + + let body = { + code: values.code, + id: id, + }; + + + verifyEmail(body).then((res) => { + message.success('Email Verified! Redirecting to login page'); + history.push('/login'); // Redirect to login page + }); + }; + + return ( + +
+ +
+

Verify Email

+

Enter the verification code sent to your email

+
+ +
+ ( + + + + )} + name="code" + control={control} + rules={{ required: true }} + /> + + + + +
+ +

Back to Login

+ +
+
+
+
+ ); +}; + +export default VerifyEmailPage; + +