Feature - Login integration (#7)

* Added: formik and validation schema

* Added: Validation schema

* Added: logout test integration

* Added: Enter key to submit

* Added: incorrect password error message

* Removed: logger

* Updated: logout initial state

Co-authored-by: Llewellyn D'souza <lledsouza2209@gmail.com>
Jira ticket ref: WMS-27
This commit is contained in:
bluestreamlds
2022-01-20 10:08:50 +05:30
committed by GitHub
parent f8f47ae983
commit f7a0bf64f2
5 changed files with 97 additions and 295 deletions

View File

@@ -1,5 +1,3 @@
import { useState } from 'react';
// react-router-dom components
import { Link } from 'react-router-dom';
@@ -21,23 +19,40 @@ import { API } from 'constant';
// Image
import bgImage from 'assets/images/illustrations/illustration-reset.jpg';
import { useFormik } from 'formik';
import schema from 'services/ValidationServices';
import { useState } from 'react';
function LoginScreen() {
const [rememberMe, setRememberMe] = useState(false);
const handleSetRememberMe = () => setRememberMe(!rememberMe);
const dispatch = useDispatch();
const onPressLogin = () => {
dispatch(
AuthActions.loginRequest({
loader: 'login-request',
slug: API.LOGIN_USER,
method: 'post',
data: { email: 'satizkris+1@gmail.com', password: 'mypassword' }
})
);
};
const [errorMessage, setErrorMessage] = useState(null);
const formik = useFormik({
initialValues: {
email: '',
password: '',
rememberMe: true
},
validationSchema: schema.login,
onSubmit: (values, { resetForm, setSubmitting }) => {
const onFailedLogin = (errorMessage) => {
resetForm();
setSubmitting(false);
setErrorMessage(errorMessage);
};
dispatch(
AuthActions.loginRequest({
loader: 'login-request',
slug: API.LOGIN_USER,
method: 'post',
data: { email: values.email, password: values.password },
onFailedLogin
// data: { email: 'satizkris+1@gmail.com', password: 'mypassword' }
})
);
}
});
return (
<AuthLayout
@@ -45,27 +60,60 @@ function LoginScreen() {
description="Enter your email and password to sign in"
illustration={bgImage}
>
<MDBox component="form" role="form">
<MDBox component="form" role="form" onSubmit={formik.handleSubmit}>
<MDBox mb={2}>
<MDInput fullWidth type="email" label="Email" />
<MDInput
fullWidth
name="email"
type="email"
label="Email"
value={formik.values.email}
error={formik.touched.email && Boolean(formik.errors.email)}
helperText={formik.touched.email && formik.errors.email}
onChange={formik.handleChange}
/>
</MDBox>
<MDBox mb={2}>
<MDInput fullWidth type="password" label="Password" />
<MDInput
fullWidth
type="password"
name="password"
label="Password"
value={formik.values.password}
error={formik.touched.password && Boolean(formik.errors.password)}
helperText={formik.touched.password && formik.errors.password}
onChange={formik.handleChange}
/>
</MDBox>
<MDBox display="flex" alignItems="center" ml={-1}>
<Switch checked={rememberMe} onChange={handleSetRememberMe} />
<Switch
name="rememberMe"
checked={formik.values.rememberMe}
onChange={formik.handleChange}
/>
<MDTypography
variant="button"
fontWeight="regular"
color="text"
sx={{ cursor: 'pointer', userSelect: 'none', ml: -1 }}
onClick={handleSetRememberMe}
onClick={formik.handleChange}
>
&nbsp;&nbsp;Remember me
</MDTypography>
</MDBox>
<MDBox mt={4} mb={1}>
<MDButton fullWidth variant="gradient" color="info" size="large" onClick={onPressLogin}>
<MDTypography mb={2} fontSize={14} textAlign="center" color="error">
{errorMessage ? errorMessage : ''}
</MDTypography>
<MDBox mt={1} mb={1}>
<MDButton
fullWidth
variant="gradient"
color="info"
size="large"
type="submit"
disabled={formik.isSubmitting}
onClick={formik.handleSubmit}
>
sign in
</MDButton>
</MDBox>

View File

@@ -1,15 +1,20 @@
import MDBox from 'components/MDBox';
import { useDispatch } from 'react-redux';
import DashboardLayout from 'layouts/DashboardLayout';
import MDBox from 'components/MDBox';
import DashboardNavbar from 'components/DashboardNavbar';
import Footer from 'components/Footer';
import DashboardLayout from 'layouts/DashboardLayout';
import AuthActions from 'redux/AuthRedux';
function DashboardScreen() {
const dispatch = useDispatch();
const handleLogout = () => dispatch(AuthActions.logout());
return (
<DashboardLayout>
<DashboardNavbar />
<MDBox py={3}>
<h1>Hello Dashboard</h1>
<button onClick={handleLogout}>Logout</button>
</MDBox>
<Footer />
</DashboardLayout>

View File

@@ -49,9 +49,12 @@ export const onLoginFailure = (state, { error }) =>
error: { ...state.error, [error?.loader]: error?.error }
});
export const onLogout = () => INITIAL_STATE;
/* ------------- Hookup Reducers To Types ------------- */
export const authReducer = createReducer(INITIAL_STATE, {
[Types.LOGIN_REQUEST]: onLoginRequest,
[Types.LOGIN_SUCCESS]: onLoginSuccess,
[Types.LOGIN_FAILURE]: onLoginFailure
[Types.LOGIN_FAILURE]: onLoginFailure,
[Types.LOGOUT]: onLogout
});

View File

@@ -23,6 +23,7 @@ export function* onRequestLogin({ payload }) {
})
);
} else {
payload.onFailedLogin(response.data.error);
yield put(
AuthActions.loginFailure({
loader: payload?.loader,

View File

@@ -1,277 +1,22 @@
import * as Yup from 'yup';
import { Strings } from '../constants';
const PASSWORD_REGEX = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*./-]).{8,}$/;
// eslint-disable-next-line no-useless-escape
const emailRegex = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/; // has been disabled because we need escape characters
const MOBILE_REG = /^[0-9]{10}$/; // Change this regex based on requirement
const NAME_REG = /^[-a-zA-Z-()]+(\s+[-a-zA-Z-()]+)*$/;
const WEBSITE_REG =
/(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/g;
const HASHTAG = /^[a-z0-9\-&#\s]+$/i;
// const PASSWORD_REGEX = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*./-]).{8,}$/;
// // eslint-disable-next-line no-useless-escape
// const emailRegex = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/; // has been disabled because we need escape characters
// const MOBILE_REG = /^[0-9]{10}$/; // Change this regex based on requirement
// const NAME_REG = /^[-a-zA-Z-()]+(\s+[-a-zA-Z-()]+)*$/;
// const WEBSITE_REG =
// /(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/g;
// const HASHTAG = /^[a-z0-9\-&#\s]+$/i;
const schema = {
login: Yup.object({
email: Yup.string().email(Strings.invalidEmail).required(Strings.emptyEmail),
password: Yup.string().required(Strings.emptyPassword)
}),
register: Yup.object({
firstname: Yup.string()
.min(2, Strings.validName)
.max(75, Strings.inValidName)
.matches(NAME_REG, Strings.valid)
.required(Strings.emptyFirstName),
lastname: Yup.string()
.max(75, Strings.inValidName)
.min(2, Strings.validName)
.matches(NAME_REG, Strings.valid)
.required(Strings.emptyLastName),
email: Yup.string().email(Strings.invalidEmail).required(Strings.emptyEmail),
mobile_no: Yup.string().min(14, Strings.invalidMobileNumber).required(Strings.emptyMobile_no),
password: Yup.string()
.matches(PASSWORD_REGEX, Strings.invalidPassword)
.required(Strings.emptyPassword)
}),
forgotPassword: Yup.object({
emailOrPhone: Yup.string(Strings.errorEmailPhone)
.required(Strings.errorEmailPhone)
.test('test-name', 'Enter Valid Phone/Email', (value) => {
const isValidEmail = emailRegex.test(value);
const isValidPhone = MOBILE_REG.test(value);
if (!isValidEmail && !isValidPhone) {
return false;
}
return true;
})
}),
resetPassword: Yup.object({
newPassword: Yup.string()
.matches(PASSWORD_REGEX, Strings.invalidPassword)
.required(Strings.emptyPassword),
confirmPassword: Yup.string()
.matches(PASSWORD_REGEX, Strings.invalidPassword)
.required(Strings.emptyPassword)
}),
addCompany: Yup.object({
companyName: Yup.string()
.trim(Strings.extraSpace)
.strict()
.min(2, Strings.validName)
.max(150, Strings.max150)
.required(Strings.emptyCompanyName),
companyType: Yup.number().strict().required(Strings.emptyCompanyType),
companyWebsite: Yup.string()
.required(Strings.emptywebsite)
.matches(WEBSITE_REG, Strings.validCompanyWebSite)
.max(150, Strings.max150)
}),
editProfile: Yup.object({
firstname: Yup.string()
.min(2, Strings.validName)
.max(75, Strings.inValidName)
.matches(NAME_REG, Strings.valid)
.required(Strings.emptyFirstName),
lastname: Yup.string()
.min(2, Strings.validName)
.max(75, Strings.inValidName)
.matches(NAME_REG, Strings.valid)
.required(Strings.emptyLastName),
email: Yup.string().email(Strings.invalidEmail).required(Strings.emptyEmail),
mobile_no: Yup.string()
.min(10, Strings.invalidMobileNumber)
.max(14, Strings.invalidMobileNumber)
.required(Strings.emptyMobile_no),
company_name: Yup.string()
.trim(Strings.extraSpace)
.strict()
.min(2, Strings.validName)
.max(150, Strings.max150)
.required(Strings.emptyCompanyName),
company_website: Yup.string()
.required(Strings.emptywebsite)
.max(150, Strings.max150)
.matches(WEBSITE_REG, Strings.validCompanyWebSite),
tagline: Yup.string().trim(Strings.extraSpace).strict().nullable().max(75, Strings.max75),
interest: Yup.string().trim(Strings.extraSpace).strict().max(150, Strings.max150)
}),
directditProfile: Yup.object({
firstname: Yup.string()
.min(2, Strings.validName)
.max(75, Strings.inValidName)
.matches(NAME_REG, Strings.valid)
.required(Strings.emptyFirstName),
lastname: Yup.string()
.min(2, Strings.validName)
.max(75, Strings.inValidName)
.matches(NAME_REG, Strings.valid)
.required(Strings.emptyLastName),
email: Yup.string().email(Strings.invalidEmail).required(Strings.emptyEmail),
mobile_no: Yup.string()
.min(10, Strings.invalidMobileNumber)
.max(14, Strings.invalidMobileNumber)
.required(Strings.emptyMobile_no),
tagline: Yup.string().trim(Strings.extraSpace).strict().nullable().max(75, Strings.max75),
interest: Yup.string().trim(Strings.extraSpace).strict().max(150, Strings.max150)
}),
changePassword: Yup.object({
currentPassword: Yup.string()
.matches(PASSWORD_REGEX, Strings.invalidPassword)
.required(Strings.emptyPassword),
newPassword: Yup.string()
.matches(PASSWORD_REGEX, Strings.invalidPassword)
.required(Strings.emptyPassword),
confirmPassword: Yup.string()
.matches(PASSWORD_REGEX, Strings.invalidPassword)
.required(Strings.emptyPassword)
}),
editEducation: Yup.object({
school: Yup.string()
.trim(Strings.extraSpace)
.strict()
.min(2, Strings.validName)
.max(250, Strings.maxSchool)
.required(Strings.emptySchool),
degree: Yup.string()
.trim(Strings.extraSpace)
.strict()
.min(2, Strings.validName)
.max(100, Strings.max100)
.required(Strings.emptyDegree),
field_of_study: Yup.string()
.trim(Strings.extraSpace)
.strict()
.min(2, Strings.validName)
.max(100, Strings.max100)
.required(Strings.emptyField),
start_year: Yup.string().required(Strings.emptyStartYear),
end_year: Yup.string().required(Strings.emptyEndYear)
}),
editEmploy: Yup.object({
hospital_name: Yup.string()
.min(2, Strings.validName)
.max(100, Strings.max100)
.matches(NAME_REG, Strings.valid)
.required(Strings.emptyCompany),
location: Yup.string().required(Strings.emptyLocation)
}),
needHelp: Yup.object({
subjectTextInput: Yup.string().required(Strings.inValidSubject).max(150, Strings.max150),
subjectDescriptionTextInput: Yup.string()
.required(Strings.inValidSubject)
.max(1000, Strings.max1000)
}),
editAbout: Yup.object({
about: Yup.string()
.trim(Strings.extraSpace)
.strict()
.max(500, Strings.validAbout)
.required(Strings.inValidSubject)
}),
createPost: Yup.object({
description: Yup.string()
.max(15000, Strings.validateDescriptionChar)
.required(Strings.emptyDesc),
hashtag: Yup.string().matches(HASHTAG, Strings.hashtagErr).max(1000, Strings.hashtagLimitErr),
link: Yup.string()
.matches(WEBSITE_REG, Strings.urlError)
.max(15000, Strings.validateDescriptionChar)
}),
createPoll: Yup.object({
description: Yup.string()
.max(15000, Strings.validateDescriptionChar)
.required(Strings.emptyDesc),
link: Yup.string()
.matches(WEBSITE_REG, Strings.urlError)
.max(15000, Strings.validateDescriptionChar),
question: Yup.string().max(70, Strings.validateQueChar).required(Strings.emptyQue),
optionOne: Yup.string().max(50, Strings.validateAnsChar).required(Strings.emptyAns1),
optionTwo: Yup.string().max(50, Strings.validateAnsChar).required(Strings.emptyAns2),
Option3: Yup.string().max(50, Strings.validateAnsChar),
Option4: Yup.string().max(50, Strings.validateAnsChar),
Option5: Yup.string().max(50, Strings.validateAnsChar)
}),
shareStatus: Yup.object({
shareStatus: Yup.string().max(15000, Strings.validateDescriptionChar)
}),
createEvent: Yup.object({
eventName: Yup.string().required(Strings.eventNameError).max(75),
location: Yup.string().required(Strings.emptyLocation),
venue: Yup.string().required(Strings.emptyVenue).max(150),
timezone: Yup.object().required(Strings.emptyTimezone),
description: Yup.string().required(Strings.emptyDesc).max(1000)
}),
onlineEvent: Yup.object({
eventName: Yup.string().required(Strings.eventNameError).max(75),
broadcastLink: Yup.string()
.matches(WEBSITE_REG, Strings.urlError)
.max(15000, Strings.validateDescriptionChar)
.required(Strings.emptyLink),
timezone: Yup.object().required(Strings.emptyTimezone),
description: Yup.string().required(Strings.emptyDesc).max(1000)
}),
orthoEvent: Yup.object({
eventName: Yup.string().required(Strings.eventNameError).max(75),
timezone: Yup.object().required(Strings.emptyTimezone),
description: Yup.string().required(Strings.emptyDesc).max(1000)
}),
createLivePost: Yup.object({
type: Yup.object().required(Strings.liveStreamTypeValidation),
privacy: Yup.object().required(Strings.liveStreamPrivacyValidation),
group: Yup.object().when('privacy', (privacy) => {
if (privacy?.label?.toLowerCase()?.trim() === Strings.group?.toLowerCase()?.trim()) {
return Yup.object().required(Strings.liveStreamGroupValidation);
}
}),
title: Yup.string().required(Strings.liveStreamTitleValidation).max(100, Strings.max100),
description: Yup.string().max(15000, Strings.max15000),
allow_comments: Yup.bool()
}),
createGroup: Yup.object({
groupName: Yup.string().required(Strings.groupNameErr).max(70, Strings.groupNameCharErr),
description: Yup.string()
.max(15000, Strings.validateDescriptionChar)
.required(Strings.groupDescErr),
groupType: Yup.string().required(Strings.groupTypeErr),
groupAccess: Yup.string().required(Strings.groupAccessErr),
speciality_ids: Yup.array().required(Strings.selectSpecialty)
}),
addEditEducation: Yup.object({
selectedTyped: Yup.string().required(Strings.educationSelectSchool).max(250, Strings.maxSchool)
}),
addEditEmployer: Yup.object({
selectedHospital: Yup.string()
.required(Strings.employerSelectHospital)
.max(250, Strings.maxSchool)
/* @COMMENTED for future scope */
/* selectedLocation: Yup.string()
.required(Strings.emptyLocation)
.max(250, Strings.maxSchool) */
}),
createPage: Yup.object({
companyPageName: Yup.string()
.required(Strings.companyNameErr)
.max(100, Strings.companyNamelimitErr),
publicUrl: Yup.string().required(Strings.publicUrlErr).max(50, Strings.publicUrllimitErr),
website: Yup.string()
.required(Strings.emptywebsite)
.matches(WEBSITE_REG, Strings.validCompanyWebSite)
.max(150, Strings.max150),
companyType: Yup.string().required(Strings.emptyCompanyType),
companySize: Yup.string().required(Strings.companySizeErr),
tagline: Yup.string().max(100, Strings.companyNamelimitErr)
}),
inviteUsers: Yup.object({
emailId: Yup.string().email(Strings.invalidEmail).required(Strings.emptyEmail)
}),
checkoutCard: Yup.object({
name: Yup.string()
.min(2, Strings.validName)
.max(75, Strings.inValidName)
.matches(NAME_REG, Strings.valid)
.required(Strings.emptyName),
email: Yup.string().email(Strings.invalidEmail).required(Strings.emptyEmail),
phone: Yup.string().min(14, Strings.invalidMobileNumber).required(Strings.emptyMobile_no),
card: Yup.object()
email: Yup.string('Enter your email')
.email('Enter a valid email')
.required('Email is required'),
password: Yup.string('Enter your password')
.min(8, 'Password should be of minimum 8 characters length')
.required('Password is required')
})
};