Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Yadhap/verify email self registered user #931

Merged
merged 4 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 35 additions & 20 deletions Tombolo/client-reactjs/src/components/InitialExperience/Wizard.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,55 @@
import React from 'react';
import { Form } from 'antd';
// Libraries
import React, { useState } from 'react';
import { Form, message } from 'antd';
import { CheckCircleFilled } from '@ant-design/icons';

// Local imoprts
import RegisterUserForm from '../login/registerUserForm';
import { getDeviceInfo } from '../login/utils';
import { authActions } from '../../redux/actions/Auth';
import { Constants } from '../common/Constants';
import BasicLayout from '../common/BasicLayout';
import { Route, Switch } from 'react-router-dom';

const Wizard = () => {
// Hooks
const [form] = Form.useForm();

// States
const [registrationComplete, setRegistrationComplete] = useState(false);

// When register button is clicked
const onFinish = async (values) => {
try {
values.deviceInfo = getDeviceInfo();
const result = await authActions.registerOwner(values);

if (result && result.type === Constants.LOGIN_SUCCESS) {
//reload page
window.location.reload(false);
}
await authActions.registerOwner(values);
setRegistrationComplete(true);
} catch (e) {
console.log(e);
message.error(e.message);
}
};

// Page content
const pageContent = (
<>
{registrationComplete ? (
<div>
<p style={{ width: '100%', textAlign: 'center', marginTop: '1rem', fontSize: '1.1rem' }}>
<CheckCircleFilled style={{ marginRight: '1rem', color: 'green' }} twoToneColor="#eb2f96" fill="green" />
Registration complete. Please check your email to verify your account.
</p>
</div>
) : (
<>
<h3>Welcome to Tombolo, to get started, please register your ownership account.</h3>
<RegisterUserForm form={form} onFinish={onFinish} msEnabled={false} />
</>
)}
</>
);

// Wizard content
const WizardContent = () => {
return (
<BasicLayout
content={
<>
<h3>Welcome to Tombolo, to get started, please register your ownership account.</h3>
<RegisterUserForm form={form} onFinish={onFinish} msEnabled={false} />
</>
}
width="40rem"></BasicLayout>
);
return <BasicLayout content={pageContent} width="40rem"></BasicLayout>;
};

//return router with all paths leading to the wizard content
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const changeBasicUserInfo = async (values) => {
return data;
};

export const applicationStringBuilder = (applications) => {
export const applicationStringBuilder = (applications = []) => {
let applicationString = '';
applications.forEach((application, index) => {
applicationString += application.application.title;
Expand All @@ -58,7 +58,7 @@ export const applicationStringBuilder = (applications) => {
return applicationString;
};

export const roleStringBuilder = (roles) => {
export const roleStringBuilder = (roles = []) => {
let roleString = '';
roles.forEach((role, index) => {
roleString += role.role_details.roleName;
Expand Down
2 changes: 1 addition & 1 deletion Tombolo/client-reactjs/src/components/login/AuthRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const AuthRoutes = () => {
<Route path="/register" component={Register} />
<Route path="/reset-password/:resetToken" component={ResetPassword} />
<Route path="/forgot-password" component={ForgotPassword} />
<Route path="/reset-temporary-password/:resetToken" component={resetTempPassword} />
<Route path="/reset-temporary-password" component={resetTempPassword} />
{/* redirect all other routes hit to login */}
<Route path="*" component={Login} />
</Switch>
Expand Down
97 changes: 84 additions & 13 deletions Tombolo/client-reactjs/src/components/login/register.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,104 @@
import React from 'react';
import { Form } from 'antd';
import React, { useState, useEffect } from 'react';
import { Form, message } from 'antd';
import { CheckCircleFilled, LoadingOutlined, CloseCircleFilled } from '@ant-design/icons';
import { useLocation } from 'react-router-dom';

import RegisterUserForm from './registerUserForm';
import { getDeviceInfo } from './utils';
import { authActions } from '../../redux/actions/Auth';

import { Constants } from '../common/Constants';
import { verifyEmail } from './utils';

const Register = () => {
const [form] = Form.useForm();
const [registrationComplete, setRegistrationComplete] = useState(false);
const [regId, setRegId] = useState(null);
const [verifying, setVerifying] = useState(false);
const [verificationFailed, setVerificationFailed] = useState(null);
const location = useLocation();

// When component loads look for regId in the url
useEffect(() => {
const params = new URLSearchParams(location.search);
const id = params.get('regId');

if (id) {
setRegId(id);
}
}, [location]);

// When regId is set, verify the email
useEffect(() => {
if (regId) {
setVerifying(true);
const verifyUserAc = async () => {
try {
const response = await verifyEmail(regId);

if (!response.success) {
throw new Error(response?.data?.message || 'Verification failed');
}

message.success('Verification completed successfully');
setRegistrationComplete(true);
setVerifying(false);
localStorage.setItem('user', JSON.stringify(response.data));
window.location.href = '/';
} catch (err) {
setVerifying(false);
setVerificationFailed(err.message);
}
};

verifyUserAc(regId);
}
}, [regId]);

// When form is submitted
const onFinish = async (values) => {
try {
values.deviceInfo = getDeviceInfo();
const result = await authActions.registerBasicUser(values);
authActions.registerBasicUser(values);

if (result && result.type === Constants.LOGIN_SUCCESS) {
window.location.href = '/';
}
setRegistrationComplete(true);
} catch (e) {
console.log(e);
setVerificationFailed(e.message);
}
};

return (
<>
<RegisterUserForm form={form} onFinish={onFinish} msEnabled={true} />
<p style={{ width: '100%', textAlign: 'center', marginTop: '1rem' }}>
<span>Already have an account?</span> <a href="/login">Login</a>
</p>
{regId ? (
<div style={{ textAlign: 'center', marginTop: '2rem', display: 'flex', justifyContent: 'center' }}>
{verifying && (
<>
<LoadingOutlined style={{ marginRight: '1rem' }} />
<div>Verifying your E-mail</div>
</>
)}
{verificationFailed && (
<div>
<p style={{ width: '100%', textAlign: 'center', marginTop: '1rem', fontSize: '1.1rem' }}>
<CloseCircleFilled style={{ marginRight: '1rem', color: 'red' }} twoToneColor="#eb2f96" fill="red" />
{verificationFailed}
</p>
</div>
)}
</div>
) : registrationComplete ? (
<div>
<p style={{ width: '100%', textAlign: 'center', marginTop: '1rem', fontSize: '1.1rem' }}>
<CheckCircleFilled style={{ marginRight: '1rem', color: 'green' }} twoToneColor="#eb2f96" fill="green" />
Registration complete. Please check your email to verify your account.
</p>
</div>
) : (
<>
<RegisterUserForm form={form} onFinish={onFinish} msEnabled={true} />
<p style={{ width: '100%', textAlign: 'center', marginTop: '1rem' }}>
<span>Already have an account?</span> <a href="/login">Login</a>
</p>
</>
)}
</>
);
};
Expand Down
22 changes: 22 additions & 0 deletions Tombolo/client-reactjs/src/components/login/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,25 @@ export const resetTempPassword = async (resetData) => {

return responseJson;
};

// Make POST request to api/auth/verifyEmail with token in body
export const verifyEmail = async (token) => {
// eslint-disable-next-line no-unreachable
const payload = {
method: 'POST',
headers: authHeader(),
body: JSON.stringify({ token }),
};

const response = await fetch('/api/auth/verifyEmail', payload);

// Get the data from the response
const responseJson = await response.json();

// Check if the response is ok
if (!response.ok) {
throw new Error(responseJson.message);
}

return responseJson;
};
68 changes: 10 additions & 58 deletions Tombolo/client-reactjs/src/redux/actions/Auth.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import { Constants } from '../../components/common/Constants';
import { authHeader, handleError } from '../../components/common/AuthHeader';

export const authActions = {
login,
logout,
registerBasicUser,
loadUserFromStorage,
registerOwner,
};

async function login({ email, password, deviceInfo }) {
const user = await loginBasicUserFunc(email, password, deviceInfo);

Expand Down Expand Up @@ -50,55 +42,7 @@ function loadUserFromStorage() {
return;
}

async function registerBasicUser(values) {
const user = await registerBasicUserFunc(values);

if (user) {
//log in user
const loginResponse = await loginBasicUserFunc(values.email, values.password, values.deviceInfo);

if (loginResponse) {
let data = loginResponse.data;
data.isAuthenticated = true;

//set item in local storage
localStorage.setItem('user', JSON.stringify(data));

return {
type: Constants.LOGIN_SUCCESS,
payload: data,
};
}
}

return;
}

async function registerOwner(values) {
const user = await registerOwnerFunc(values);

if (user) {
//log in user
const loginResponse = await loginBasicUserFunc(values.email, values.password, values.deviceInfo);

if (loginResponse) {
let data = loginResponse.data;
data.isAuthenticated = true;

//set item in local storage
localStorage.setItem('user', JSON.stringify(data));

return {
type: Constants.LOGIN_SUCCESS,
payload: data,
};
}
}

return;
}

const registerBasicUserFunc = async (values) => {
const registerBasicUser = async (values) => {
const url = '/api/auth/registerBasicUser';

values.registrationMethod = 'traditional';
Expand Down Expand Up @@ -150,7 +94,7 @@ const loginBasicUserFunc = async (email, password, deviceInfo) => {
return data;
};

const registerOwnerFunc = async (values) => {
const registerOwner = async (values) => {
const url = '/api/auth/registerApplicationOwner';

values.registrationMethod = 'traditional';
Expand All @@ -176,3 +120,11 @@ const registerOwnerFunc = async (values) => {

return data;
};

export const authActions = {
login,
logout,
registerBasicUser,
loadUserFromStorage,
registerOwner,
};
Loading