From b8b828338507c560eee278248a19e67f92436deb Mon Sep 17 00:00:00 2001 From: elforjani3 Date: Wed, 29 Apr 2020 17:36:02 +0200 Subject: [PATCH] WIP / Design login and register page. --- .../src/connectors/ResetPassword.connect.js | 4 +- .../connectors/SendResetPassword.connect.js | 13 + .../containers/Authentication/InviteAccept.js | 245 ++++++++------ client/src/containers/Authentication/Login.js | 177 ++++++---- .../src/containers/Authentication/Register.js | 318 ++++++++++-------- .../Authentication/ResetPassword.js | 119 ++++--- .../Authentication/SendResetPassword.js | 121 ++++++- .../containers/Authentication/copyright.js | 79 +++++ .../authentication/authentication.actions.js | 69 ++-- client/src/style/App.scss | 82 +++-- client/src/style/pages/authentication.scss | 270 ++++++++++++++- 11 files changed, 1050 insertions(+), 447 deletions(-) create mode 100644 client/src/connectors/SendResetPassword.connect.js create mode 100644 client/src/containers/Authentication/copyright.js diff --git a/client/src/connectors/ResetPassword.connect.js b/client/src/connectors/ResetPassword.connect.js index 5dc209556..782738681 100644 --- a/client/src/connectors/ResetPassword.connect.js +++ b/client/src/connectors/ResetPassword.connect.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { submitResetPassword } from 'store/resetPassword/resetPassword.action'; +import { submitResetPassword } from 'store/authentication/authentication.actions'; export const mapStateToProps = (state, props) => { return {}; @@ -7,7 +7,7 @@ export const mapStateToProps = (state, props) => { export const mapDispatchToProps = (dispatch) => ({ requestResetPassword: (password) => - dispatch(submitResetPassword({password})), + dispatch(submitResetPassword({ password })), }); export default connect(mapStateToProps, mapDispatchToProps); diff --git a/client/src/connectors/SendResetPassword.connect.js b/client/src/connectors/SendResetPassword.connect.js new file mode 100644 index 000000000..3d8904726 --- /dev/null +++ b/client/src/connectors/SendResetPassword.connect.js @@ -0,0 +1,13 @@ +import { connect } from 'react-redux'; +import { submitSendResetPassword } from 'store/authentication/authentication.actions'; + +export const mapStateToProps = (state, props) => { + return {}; +}; + +export const mapDispatchToProps = (dispatch) => ({ + requestSendResetPassword: (email) => + dispatch(submitSendResetPassword({ email })), +}); + +export default connect(mapStateToProps, mapDispatchToProps); diff --git a/client/src/containers/Authentication/InviteAccept.js b/client/src/containers/Authentication/InviteAccept.js index aec099b18..79e45c0f7 100644 --- a/client/src/containers/Authentication/InviteAccept.js +++ b/client/src/containers/Authentication/InviteAccept.js @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import * as Yup from 'yup'; import { useFormik } from 'formik'; import { useIntl } from 'react-intl'; @@ -13,8 +13,14 @@ import { Intent, FormGroup, HTMLSelect, + Icon, } from '@blueprintjs/core'; +import { Row, Col } from 'react-grid-system'; +import IconLog from 'components/Icon'; +import Copyright from './copyright'; +import { Link } from 'react-router-dom'; + function Invite({ requestSubmitInvite }) { const intl = useIntl(); let params = useParams('accept/:token'); @@ -23,18 +29,12 @@ function Invite({ requestSubmitInvite }) { const phoneRegExp = /^((\\+[1-9]{1,4}[ \\-]*)|(\\([0-9]{2,3}\\)[ \\-]*)|([0-9]{2,4})[ \\-]*)*?[0-9]{3,4}?[ \\-]*[0-9]{3,4}?$/; - const language = useMemo(() => [ - { value: null, label: 'Select Country' }, - { value: 'Arabic', label: 'Arabic' }, - { value: 'English', label: 'English' }, - ], []); const ValidationSchema = Yup.object().shape({ first_name: Yup.string().required(), last_name: Yup.string().required(), email: Yup.string().email().required(), phone_number: Yup.string().matches(phoneRegExp).required(), - language: Yup.string().required(), password: Yup.string() .min(4, 'Password has to be longer than 4 characters!') .required('Password is required!'), @@ -51,122 +51,175 @@ function Invite({ requestSubmitInvite }) { }), [] ); - const { - handleSubmit, - errors, - values, - touched, - getFieldProps, - } = useFormik({ + const formik = useFormik({ enableReinitialize: true, validationSchema: ValidationSchema, initialValues: { ...initialValues, }, onSubmit: (values, { setSubmitting }) => { - requestSubmitInvite(values, token).then((response) => { - AppToaster.show({ - message: 'success', + requestSubmitInvite(values, token) + .then((response) => { + AppToaster.show({ + message: 'success', + }); + setSubmitting(false); + }) + .catch((error) => { + setSubmitting(false); }); - setSubmitting(false); - }) - .catch((error) => { - setSubmitting(false); - }); }, }); + const requiredSpan = useMemo(() => *, []); + const [shown, setShown] = useState(false); + + const passwordRevealer = () => { + setShown(!shown); + }; return (
-
- } - > - - + + +
+

Welcome to Bigcapital

+

+ Enter your personal information {'Organization Name'}{' '} + Organization. +

+
- } - > - - + + + } + > + + + + + } + > + + + + +
+ } + > + + +
- } - > - - - - } - > - - - - } > - + */} +
+ passwordRevealer()}> + + Show + + } + className={'form-group--password'} + intent={ + formik.errors.password && formik.touched.password && Intent.DANGER + } + helperText={} + > + + +
- } - > - - - - +
); } diff --git a/client/src/containers/Authentication/Login.js b/client/src/containers/Authentication/Login.js index 1a2319325..34299008c 100644 --- a/client/src/containers/Authentication/Login.js +++ b/client/src/containers/Authentication/Login.js @@ -1,42 +1,47 @@ -import React, { useEffect } from "react"; -import {Link, useHistory} from 'react-router-dom'; +import React, { useEffect, useState } from 'react'; +import { Link, useHistory } from 'react-router-dom'; import * as Yup from 'yup'; -import {useFormik} from 'formik'; -import {connect} from 'react-redux'; -import {useIntl} from 'react-intl'; +import { useFormik } from 'formik'; +import { connect } from 'react-redux'; +import { useIntl } from 'react-intl'; import { Button, InputGroup, Intent, FormGroup, -} from "@blueprintjs/core"; + Checkbox, + Icon, +} from '@blueprintjs/core'; import login from 'store/authentication/authentication.actions'; -import {hasErrorType, isAuthenticated} from 'store/authentication/authentication.reducer'; +import { + hasErrorType, + isAuthenticated, +} from 'store/authentication/authentication.reducer'; import AuthenticationToaster from 'components/AppToaster'; import t from 'store/types'; +import ErrorMessage from 'components/ErrorMessage'; +import IconLog from 'components/Icon'; +import Copyright from './copyright'; const ERRORS_TYPES = { INVALID_DETAILS: 'INVALID_DETAILS', USER_INACTIVE: 'USER_INACTIVE', }; -function Login({ - login, - errors, - clearErrors, - hasError, -}) { +function Login({ login, errors, clearErrors, hasError }) { const intl = useIntl(); const history = useHistory(); + + + const [shown, setShown] = useState(false); // Validation schema. - const loginValidationSchema = Yup.object().shape({ - crediential: Yup - .string() - .required(intl.formatMessage({'id': 'required'})) - .email(intl.formatMessage({id: 'invalid_email_or_phone_numner'})), - password: Yup - .string() - .required(intl.formatMessage({id: 'required'})) + const loginValidationSchema = + Yup.object().shape({ + crediential: Yup.string() + .required(intl.formatMessage({ id: 'required' })) + .email(intl.formatMessage({ id: 'invalid_email_or_phone_numner' })), + password: Yup.string() + .required(intl.formatMessage({ id: 'required' })) .min(4), }); @@ -65,11 +70,13 @@ function Login({ } if (hasError(ERRORS_TYPES.USER_INACTIVE)) { toastBuilders.push({ - message: intl.formatMessage({ id: 'the_user_has_been_suspended_from_admin' }), + message: intl.formatMessage({ + id: 'the_user_has_been_suspended_from_admin', + }), intent: Intent.WARNING, }); } - toastBuilders.forEach(builder => { + toastBuilders.forEach((builder) => { AuthenticationToaster.show(builder); }); }, [hasError, intl]); @@ -81,53 +88,87 @@ function Login({ } }); + const passwordRevealer = () => { + setShown(!shown); + }; + return ( -
+
+
- +
+

Log in

+ Need a Bigcapital account ? + Create an account +
+
+ } + > + + +
+
+ passwordRevealer()}> + + Show + + } + intent={ + formik.errors.password && formik.touched.password && Intent.DANGER + } + helperText={} + > + + +
+
+ Keep me logged in +
- -
- - - - - - - +
+ +
- -
- - {intl.formatMessage({'id': 'reset_password '})} - - - - {intl.formatMessage({'id': 'register '})} - -
+
); } @@ -138,8 +179,8 @@ const mapStateToProps = (state) => ({ }); const mapDispatchToProps = (dispatch) => ({ - login: form => dispatch(login({ form })), + login: (form) => dispatch(login({ form })), clearErrors: () => dispatch({ type: t.LOGIN_CLEAR_ERRORS }), }); -export default connect(mapStateToProps, mapDispatchToProps)(Login); \ No newline at end of file +export default connect(mapStateToProps, mapDispatchToProps)(Login); diff --git a/client/src/containers/Authentication/Register.js b/client/src/containers/Authentication/Register.js index 8370f7ee3..60c3f363d 100644 --- a/client/src/containers/Authentication/Register.js +++ b/client/src/containers/Authentication/Register.js @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import * as Yup from 'yup'; import { useFormik } from 'formik'; import { useIntl } from 'react-intl'; @@ -8,20 +8,27 @@ import { Intent, FormGroup, HTMLSelect, + Icon, } from '@blueprintjs/core'; import RegisterFromConnect from 'connectors/RegisterForm.connect'; import ErrorMessage from 'components/ErrorMessage'; import AppToaster from 'components/AppToaster'; -import { compose, regExpCollection } from 'utils'; +import { compose } from 'utils'; +import { Row, Col } from 'react-grid-system'; +import IconLog from 'components/Icon'; +import Copyright from './copyright'; +import { Link } from 'react-router-dom'; +// import { compose, regExpCollection } from 'utils'; -function Register({ - requestSubmitRegister, -}) { +function Register({ requestSubmitRegister }) { const intl = useIntl(); - const Country = useMemo(() => [ - { value: null, label: 'Select Country' }, - { value: 'libya', label: 'Libya' }, - ], []); + const Country = useMemo( + () => [ + { value: null, label: 'Select Country' }, + { value: 'libya', label: 'Libya' }, + ], + [] + ); const ValidationSchema = Yup.object().shape({ organization_name: Yup.string().required(), @@ -29,32 +36,28 @@ function Register({ last_name: Yup.string().required(), email: Yup.string().email().required(), phone_number: Yup.string() - .matches(regExpCollection.phoneNumber) + .matches() .required(intl.formatMessage({ id: 'required' })), password: Yup.string() .min(4, 'Password has to be longer than 8 characters!') .required('Password is required!'), - country: Yup.string().required(intl.formatMessage({ id: 'required' })), + // country: Yup.string().required(intl.formatMessage({ id: 'required' })), }); - const initialValues = useMemo(() => ({ - organization_name: '', - first_name: '', - last_name: '', - email: '', - phone_number: '', - password: '', - country: '', - }), []); + const initialValues = useMemo( + () => ({ + organization_name: '', + first_name: '', + last_name: '', + email: '', + phone_number: '', + password: '', + // country: '', + }), + [] + ); - const { - getFieldProps, - getFieldMeta, - errors, - values, - touched, - handleSubmit, - } = useFormik({ + const formik = useFormik({ enableReinitialize: true, validationSchema: ValidationSchema, initialValues: { @@ -76,120 +79,173 @@ function Register({ const requiredSpan = useMemo(() => *, []); + const [shown, setShown] = useState(false); + + const passwordRevealer = () => { + setShown(!shown); + }; + return (
-
- } - > - + +
+

+ Register a New
+ Organization. +

+ You have a bigcapital account ? Login +
+
+ - + helperText={} + > + + +
+ + + } + > + + + + + } + > + + + + - } - > - - - - } - > - - - - } - > - - - - } - > - - - - } - > - - - - } - > - - - - +
); } -export default compose( - RegisterFromConnect, -)(Register); +export default compose(RegisterFromConnect)(Register); diff --git a/client/src/containers/Authentication/ResetPassword.js b/client/src/containers/Authentication/ResetPassword.js index 11d29c6e0..cd2b2d4cd 100644 --- a/client/src/containers/Authentication/ResetPassword.js +++ b/client/src/containers/Authentication/ResetPassword.js @@ -12,11 +12,12 @@ import { import ErrorMessage from 'components/ErrorMessage'; import AppToaster from 'components/AppToaster'; import { compose } from 'utils'; -import SendResetPasswordConnect from 'connectors/ResetPassword.connect'; +import ResetPasswordConnect from 'connectors/ResetPassword.connect'; +import Icon from 'components/Icon'; +import Copyright from './copyright'; +import { Link } from 'react-router-dom'; -function ResetPassword({ - requestSendResetPassword, -}) { +function ResetPassword({ requestSendResetPassword }) { const intl = useIntl(); const ValidationSchema = Yup.object().shape({ password: Yup.string() @@ -27,19 +28,15 @@ function ResetPassword({ .required('Confirm Password is required'), }); - const initialValues = useMemo(() => ({ - password: '', - confirm_password: '', - }), []); + const initialValues = useMemo( + () => ({ + password: '', + confirm_password: '', + }), + [] + ); - const { - errors, - values, - touched, - getFieldMeta, - getFieldProps, - handleSubmit, - } = useFormik({ + const formik= useFormik({ enableReinitialize: true, validationSchema: ValidationSchema, initialValues: { @@ -62,52 +59,66 @@ function ResetPassword({ const requiredSpan = useMemo(() => *, []); return ( -
-
- } - > - - - - } - > - +
+ +
+ +
+

Choose a new password

+ You remembered your password ? Login +
+
+ } + > + + +
+
+ - + helperText={} + > + + +
- +
); } -export default compose(SendResetPasswordConnect)(ResetPassword); +export default compose(ResetPasswordConnect)(ResetPassword); diff --git a/client/src/containers/Authentication/SendResetPassword.js b/client/src/containers/Authentication/SendResetPassword.js index d4b78f2f0..45b7aec3d 100644 --- a/client/src/containers/Authentication/SendResetPassword.js +++ b/client/src/containers/Authentication/SendResetPassword.js @@ -1,29 +1,114 @@ -import * as React from 'react'; -import { Link } from 'react-router-dom'; -import { Button, InputGroup } from '@blueprintjs/core'; +import React, { useEffect, useMemo, useState } from 'react'; +import * as Yup from 'yup'; +import { useFormik } from 'formik'; +import { useIntl } from 'react-intl'; +import { Link, useHistory } from 'react-router-dom'; +import { Button, InputGroup, Intent, FormGroup } from '@blueprintjs/core'; import { FormattedMessage } from 'react-intl'; +import ErrorMessage from 'components/ErrorMessage'; +import Icon from 'components/Icon'; +import Copyright from './copyright'; +import SendResetPasswordConnect from 'connectors/SendResetPassword.connect'; +import { compose } from 'utils'; +import AppToaster from 'components/AppToaster'; -export default function SendResetPassword() { +function SendResetPassword({ requestSendResetPassword }) { + const intl = useIntl(); + + const phoneRegExp = /^((\\+[1-9]{1,4}[ \\-]*)|(\\([0-9]{2,3}\\)[ \\-]*)|([0-9]{2,4})[ \\-]*)*?[0-9]{3,4}?[ \\-]*[0-9]{3,4}?$/; + + const history = useHistory(); + + // Validation schema. + + const ValidationSchema = Yup.object().shape({ + crediential: Yup.string('') + .required(intl.formatMessage({ id: 'required' })) + .email(intl.formatMessage({ id: 'invalid_email_or_phone_numner' })), + }); + + const initialValues = useMemo( + () => ({ + crediential: '', + }), + [] + ); + + // Formik validation + const formik = useFormik({ + enableReinitialize: true, + validationSchema: ValidationSchema, + initialValues: { + ...initialValues, + }, + onSubmit: (values, { setSubmitting }) => { + requestSendResetPassword(values.crediential) + .then((response) => { + AppToaster.show({ + message: + 'Check your email for a link to reset your password. If it doesn’t appear within a few minutes, check your spam folder.', + }); + history.push('/auth/login'); + setSubmitting(false); + }) + .catch((error) => { + setSubmitting(false); + }); + }, + }); return ( -