feat(webapp): style tweaks to authentication pages

This commit is contained in:
a.bouhuolia
2023-04-04 23:38:04 +02:00
parent b24a367438
commit 7177276b12
19 changed files with 475 additions and 749 deletions

View File

@@ -13,7 +13,7 @@ import AppIntlLoader from './AppIntlLoader';
import PrivateRoute from '@/components/Guards/PrivateRoute'; import PrivateRoute from '@/components/Guards/PrivateRoute';
import GlobalErrors from '@/containers/GlobalErrors/GlobalErrors'; import GlobalErrors from '@/containers/GlobalErrors/GlobalErrors';
import DashboardPrivatePages from '@/components/Dashboard/PrivatePages'; import DashboardPrivatePages from '@/components/Dashboard/PrivatePages';
import Authentication from '@/components/Authentication'; import { Authentication } from '@/containers/Authentication/Authentication';
import { SplashScreen, DashboardThemeProvider } from '../components'; import { SplashScreen, DashboardThemeProvider } from '../components';
import { queryConfig } from '../hooks/query/base'; import { queryConfig } from '../hooks/query/base';

View File

@@ -1,62 +0,0 @@
// @ts-nocheck
import React from 'react';
import { Redirect, Route, Switch, Link, useLocation } from 'react-router-dom';
import BodyClassName from 'react-body-classname';
import { TransitionGroup, CSSTransition } from 'react-transition-group';
import authenticationRoutes from '@/routes/authentication';
import { Icon, FormattedMessage as T } from '@/components';
import { useIsAuthenticated } from '@/hooks/state';
import '@/style/pages/Authentication/Auth.scss';
function PageFade(props) {
return <CSSTransition {...props} classNames="authTransition" timeout={500} />;
}
export default function AuthenticationWrapper({ ...rest }) {
const to = { pathname: '/' };
const location = useLocation();
const isAuthenticated = useIsAuthenticated();
const locationKey = location.pathname;
return (
<>
{isAuthenticated ? (
<Redirect to={to} />
) : (
<BodyClassName className={'authentication'}>
<div class="authentication-page">
<a
href={'http://bigcapital.ly'}
className={'authentication-page__goto-bigcapital'}
>
<T id={'go_to_bigcapital_com'} />
</a>
<div class="authentication-page__form-wrapper">
<div class="authentication-insider">
<div className={'authentication-insider__logo-section'}>
<Icon icon="bigcapital" height={37} width={214} />
</div>
<TransitionGroup>
<PageFade key={locationKey}>
<Switch>
{authenticationRoutes.map((route, index) => (
<Route
key={index}
path={route.path}
exact={route.exact}
component={route.component}
/>
))}
</Switch>
</PageFade>
</TransitionGroup>
</div>
</div>
</div>
</BodyClassName>
)}
</>
);
}

View File

@@ -1,20 +1,7 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import moment from 'moment';
import intl from 'react-intl-universal';
import { Icon } from '@/components/Icon'; import { Icon } from '@/components/Icon';
export default function AuthCopyright() { export default function AuthCopyright() {
return ( return <Icon width={122} height={22} icon={'bigcapital'} />;
<div class="auth-copyright">
<div class="auth-copyright__text">
{intl.get('all_rights_reserved', {
pre: moment().subtract(1, 'years').year(),
current: moment().get('year'),
})}
</div>
<Icon width={122} height={22} icon={'bigcapital'} />
</div>
);
} }

View File

@@ -1,6 +1,8 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import styled from 'styled-components';
import AuthCopyright from './AuthCopyright'; import AuthCopyright from './AuthCopyright';
import { AuthInsiderContent, AuthInsiderCopyright } from './_components';
/** /**
* Authentication insider page. * Authentication insider page.
@@ -9,16 +11,21 @@ export default function AuthInsider({
logo = true, logo = true,
copyright = true, copyright = true,
children, children,
classNames,
}) { }) {
return ( return (
<div class="authentication-insider__content"> <AuthInsiderContent>
<div class="authentication-insider__form"> <AuthInsiderContentWrap className={classNames?.content}>
{ children } {children}
</div> </AuthInsiderContentWrap>
<div class="authentication-insider__footer"> {copyright && (
<AuthInsiderCopyright className={classNames?.copyrightWrap}>
<AuthCopyright /> <AuthCopyright />
</div> </AuthInsiderCopyright>
</div> )}
</AuthInsiderContent>
); );
} }
const AuthInsiderContentWrap = styled.div``;

View File

@@ -0,0 +1,66 @@
// @ts-nocheck
import React from 'react';
import { Redirect, Route, Switch, useLocation } from 'react-router-dom';
import BodyClassName from 'react-body-classname';
import styled from 'styled-components';
import { TransitionGroup, CSSTransition } from 'react-transition-group';
import authenticationRoutes from '@/routes/authentication';
import { Icon, FormattedMessage as T } from '@/components';
import { useIsAuthenticated } from '@/hooks/state';
import '@/style/pages/Authentication/Auth.scss';
export function Authentication() {
const to = { pathname: '/' };
const location = useLocation();
const isAuthenticated = useIsAuthenticated();
const locationKey = location.pathname;
if (isAuthenticated) {
return <Redirect to={to} />;
}
return (
<BodyClassName className={'authentication'}>
<AuthPage>
<AuthInsider>
<AuthLogo>
<Icon icon="bigcapital" height={37} width={214} />
</AuthLogo>
<TransitionGroup>
<CSSTransition
timeout={500}
key={locationKey}
classNames="authTransition"
>
<Switch>
{authenticationRoutes.map((route, index) => (
<Route
key={index}
path={route.path}
exact={route.exact}
component={route.component}
/>
))}
</Switch>
</CSSTransition>
</TransitionGroup>
</AuthInsider>
</AuthPage>
</BodyClassName>
);
}
const AuthPage = styled.div``;
const AuthInsider = styled.div`
width: 384px;
margin: 0 auto;
margin-bottom: 40px;
padding-top: 80px;
`;
const AuthLogo = styled.div`
text-align: center;
margin-bottom: 40px;
`;

View File

@@ -11,6 +11,7 @@ import { useInviteAcceptContext } from './InviteAcceptProvider';
import { AppToaster } from '@/components'; import { AppToaster } from '@/components';
import { InviteAcceptSchema } from './utils'; import { InviteAcceptSchema } from './utils';
import InviteAcceptFormContent from './InviteAcceptFormContent'; import InviteAcceptFormContent from './InviteAcceptFormContent';
import { AuthInsiderCard } from './_components';
export default function InviteAcceptForm() { export default function InviteAcceptForm() {
const history = useHistory(); const history = useHistory();
@@ -80,23 +81,13 @@ export default function InviteAcceptForm() {
}; };
return ( return (
<div className={'invite-form'}> <AuthInsiderCard>
<div className={'authentication-page__label-section'}>
<h3>
<T id={'welcome_to_bigcapital'} />
</h3>
<p>
<T id={'enter_your_personal_information'} />{' '}
<b>{inviteValue.organization_name}</b> <T id={'organization'} />
</p>
</div>
<Formik <Formik
validationSchema={InviteAcceptSchema} validationSchema={InviteAcceptSchema}
initialValues={inviteValue} initialValues={inviteValue}
onSubmit={handleSubmit} onSubmit={handleSubmit}
component={InviteAcceptFormContent} component={InviteAcceptFormContent}
/> />
</div> </AuthInsiderCard>
); );
} }

View File

@@ -1,13 +1,14 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { Button, InputGroup, Intent, FormGroup } from '@blueprintjs/core'; import { InputGroup, Intent } from '@blueprintjs/core';
import { Form, ErrorMessage, FastField, useFormikContext } from 'formik'; import { Form, useFormikContext } from 'formik';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Col, Row, FormattedMessage as T } from '@/components';
import { inputIntent } from '@/utils'; import { Col, FFormGroup, Row, FormattedMessage as T } from '@/components';
import { useInviteAcceptContext } from './InviteAcceptProvider'; import { useInviteAcceptContext } from './InviteAcceptProvider';
import { PasswordRevealer } from './components'; import { PasswordRevealer } from './components';
import { AuthSubmitButton } from './_components';
/** /**
* Invite user form. * Invite user form.
@@ -34,75 +35,25 @@ export default function InviteUserFormContent() {
<Form> <Form>
<Row> <Row>
<Col md={6}> <Col md={6}>
<FastField name={'first_name'}> <FFormGroup name={'first_name'} label={<T id={'first_name'} />}>
{({ form, field, meta: { error, touched } }) => ( <InputGroup name={'first_name'} />
<FormGroup </FFormGroup>
label={<T id={'first_name'} />}
className={'form-group--first_name'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'first_name'} />}
>
<InputGroup
intent={inputIntent({ error, touched })}
{...field}
/>
</FormGroup>
)}
</FastField>
</Col> </Col>
<Col md={6}> <Col md={6}>
<FastField name={'last_name'}> <FFormGroup name={'last_name'} label={<T id={'last_name'} />}>
{({ form, field, meta: { error, touched } }) => ( <InputGroup name={'last_name'} />
<FormGroup </FFormGroup>
label={<T id={'last_name'} />}
className={'form-group--last_name'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'last_name'} />}
>
<InputGroup
intent={inputIntent({ error, touched })}
{...field}
/>
</FormGroup>
)}
</FastField>
</Col> </Col>
</Row> </Row>
<FastField name={'phone_number'}> <FFormGroup
{({ form, field, meta: { error, touched } }) => ( name={'password'}
<FormGroup
label={<T id={'phone_number'} />}
className={'form-group--phone_number'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'phone_number'} />}
>
<InputGroup intent={inputIntent({ error, touched })} {...field} />
</FormGroup>
)}
</FastField>
<FastField name={'password'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'password'} />} label={<T id={'password'} />}
labelInfo={ labelInfo={<PasswordRevealer onChange={handlePasswordRevealerChange} />}
<PasswordRevealer onChange={handlePasswordRevealerChange} />
}
className={'form-group--password has-password-revealer'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'password'} />}
> >
<InputGroup <InputGroup name={'password'} />
lang={true} </FFormGroup>
type={passwordType}
intent={inputIntent({ error, touched })}
{...field}
/>
</FormGroup>
)}
</FastField>
<div className={'invite-form__statement-section'}> <div className={'invite-form__statement-section'}>
<p> <p>
@@ -117,16 +68,14 @@ export default function InviteUserFormContent() {
</p> </p>
</div> </div>
<div className={'authentication-page__submit-button-wrap'}> <AuthSubmitButton
<Button
intent={Intent.PRIMARY} intent={Intent.PRIMARY}
type="submit" type="submit"
fill={true} fill={true}
loading={isSubmitting} loading={isSubmitting}
> >
<T id={'create_account'} /> <T id={'create_account'} />
</Button> </AuthSubmitButton>
</div>
</Form> </Form>
); );
} }

View File

@@ -1,14 +1,25 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import { Link } from 'react-router-dom';
import { Formik } from 'formik'; import { Formik } from 'formik';
import { AppToaster as Toaster, FormattedMessage as T } from '@/components'; import { Link } from 'react-router-dom';
import { AppToaster as Toaster, FormattedMessage as T } from '@/components';
import AuthInsider from '@/containers/Authentication/AuthInsider'; import AuthInsider from '@/containers/Authentication/AuthInsider';
import { useAuthLogin } from '@/hooks/query'; import { useAuthLogin } from '@/hooks/query';
import LoginForm from './LoginForm'; import LoginForm from './LoginForm';
import { LoginSchema, transformLoginErrorsToToasts } from './utils'; import { LoginSchema, transformLoginErrorsToToasts } from './utils';
import {
AuthFooterLinks,
AuthFooterLink,
AuthInsiderCard,
} from './_components';
const initialValues = {
crediential: '',
password: '',
keepLoggedIn: false
};
/** /**
* Login page. * Login page.
@@ -38,34 +49,32 @@ export default function Login() {
return ( return (
<AuthInsider> <AuthInsider>
<div className="login-form"> <AuthInsiderCard>
<div className={'authentication-page__label-section'}>
<h3>
<T id={'log_in'} />
</h3>
{/* <T id={'need_bigcapital_account'} />
<Link to="/auth/register">
{' '}
<T id={'create_an_account'} />
</Link> */}
</div>
<Formik <Formik
initialValues={{ initialValues={initialValues}
crediential: '',
password: '',
}}
validationSchema={LoginSchema} validationSchema={LoginSchema}
onSubmit={handleSubmit} onSubmit={handleSubmit}
component={LoginForm} component={LoginForm}
/> />
</AuthInsiderCard>
<div class="authentication-page__footer-links"> <LoginFooterLinks />
<Link to={'/auth/send_reset_password'}>
<T id={'forget_my_password'} />
</Link>
</div>
</div>
</AuthInsider> </AuthInsider>
); );
} }
function LoginFooterLinks() {
return (
<AuthFooterLinks>
<AuthFooterLink>
Don't have an account? <Link to={'/auth/register'}>Sign up</Link>
</AuthFooterLink>
<AuthFooterLink>
<Link to={'/auth/send_reset_password'}>
<T id={'forget_my_password'} />
</Link>
</AuthFooterLink>
</AuthFooterLinks>
);
}

View File

@@ -1,89 +1,63 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React, { useState } from 'react';
import { import { Button, Intent } from '@blueprintjs/core';
Button, import { Form } from 'formik';
InputGroup, import { Tooltip2 } from '@blueprintjs/popover2';
Intent,
FormGroup, import { FFormGroup, FInputGroup, FCheckbox, T } from '@/components';
Checkbox, import { AuthSubmitButton } from './_components';
} from '@blueprintjs/core';
import { Form, ErrorMessage, Field } from 'formik';
import { T } from '@/components';
import { inputIntent } from '@/utils';
import { PasswordRevealer } from './components';
/** /**
* Login form. * Login form.
*/ */
export default function LoginForm({ isSubmitting }) { export default function LoginForm({ isSubmitting }) {
const [passwordType, setPasswordType] = React.useState('password'); const [showPassword, setShowPassword] = useState<boolean>(false);
// Handle password revealer changing. // Handle password revealer changing.
const handlePasswordRevealerChange = React.useCallback( const handleLockClick = () => {
(shown) => { setShowPassword(!showPassword);
const type = shown ? 'text' : 'password'; };
setPasswordType(type);
}, const lockButton = (
[setPasswordType], <Tooltip2 content={`${showPassword ? 'Hide' : 'Show'} Password`}>
<Button
icon={showPassword ? 'unlock' : 'lock'}
intent={Intent.WARNING}
minimal={true}
onClick={handleLockClick}
small={true}
/>
</Tooltip2>
); );
return ( return (
<Form className={'authentication-page__form'}> <Form>
<Field name={'crediential'}> <FFormGroup name={'crediential'} label={<T id={'email'} />}>
{({ form, field, meta: { error, touched } }) => ( <FInputGroup name={'crediential'} large={true} />
<FormGroup </FFormGroup>
label={<T id={'email_or_phone_number'} />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'crediential'} />}
className={'form-group--crediential'}
>
<InputGroup
intent={inputIntent({ error, touched })}
large={true}
{...field}
/>
</FormGroup>
)}
</Field>
<Field name={'password'}> <FFormGroup name={'password'} label={<T id={'password'} />}>
{({ form, field, meta: { error, touched } }) => ( <FInputGroup
<FormGroup name={'password'}
label={<T id={'password'} />}
labelInfo={
<PasswordRevealer onChange={handlePasswordRevealerChange} />
}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'password'} />}
className={'form-group--password has-password-revealer'}
>
<InputGroup
large={true} large={true}
intent={inputIntent({ error, touched })} type={showPassword ? 'text' : 'password'}
type={passwordType} rightElement={lockButton}
{...field}
/> />
</FormGroup> </FFormGroup>
)}
</Field>
<div className={'login-form__checkbox-section'}> <FCheckbox name={'keepLoggedIn'}>
<Checkbox large={true} className={'checkbox--remember-me'}>
<T id={'keep_me_logged_in'} /> <T id={'keep_me_logged_in'} />
</Checkbox> </FCheckbox>
</div>
<div className={'authentication-page__submit-button-wrap'}> <AuthSubmitButton
<Button
type={'submit'} type={'submit'}
intent={Intent.PRIMARY} intent={Intent.PRIMARY}
fill={true} fill={true}
lang={true} large={true}
loading={isSubmitting} loading={isSubmitting}
> >
<T id={'log_in'} /> <T id={'log_in'} />
</Button> </AuthSubmitButton>
</div>
</Form> </Form>
); );
} }

View File

@@ -11,6 +11,18 @@ import { useAuthLogin, useAuthRegister } from '@/hooks/query/authentication';
import RegisterForm from './RegisterForm'; import RegisterForm from './RegisterForm';
import { RegisterSchema, transformRegisterErrorsToForm } from './utils'; import { RegisterSchema, transformRegisterErrorsToForm } from './utils';
import {
AuthFooterLinks,
AuthFooterLink,
AuthInsiderCard,
} from './_components';
const initialValues = {
first_name: '',
last_name: '',
email: '',
password: '',
};
/** /**
* Register form. * Register form.
@@ -19,18 +31,6 @@ export default function RegisterUserForm() {
const { mutateAsync: authLoginMutate } = useAuthLogin(); const { mutateAsync: authLoginMutate } = useAuthLogin();
const { mutateAsync: authRegisterMutate } = useAuthRegister(); const { mutateAsync: authRegisterMutate } = useAuthRegister();
const initialValues = useMemo(
() => ({
first_name: '',
last_name: '',
email: '',
phone_number: '',
password: '',
country: 'LY',
}),
[],
);
const handleSubmit = (values, { setSubmitting, setErrors }) => { const handleSubmit = (values, { setSubmitting, setErrors }) => {
authRegisterMutate(values) authRegisterMutate(values)
.then((response) => { .then((response) => {
@@ -66,24 +66,32 @@ export default function RegisterUserForm() {
return ( return (
<AuthInsider> <AuthInsider>
<div className={'register-form'}> <AuthInsiderCard>
<div className={'authentication-page__label-section'}>
<h3>
<T id={'register_a_new_organization'} />
</h3>
<T id={'you_have_a_bigcapital_account'} />
<Link to="/auth/login">
<T id={'login'} />
</Link>
</div>
<Formik <Formik
initialValues={initialValues} initialValues={initialValues}
validationSchema={RegisterSchema} validationSchema={RegisterSchema}
onSubmit={handleSubmit} onSubmit={handleSubmit}
component={RegisterForm} component={RegisterForm}
/> />
</div> </AuthInsiderCard>
<RegisterFooterLinks />
</AuthInsider> </AuthInsider>
); );
} }
function RegisterFooterLinks() {
return (
<AuthFooterLinks>
<AuthFooterLink>
Return to <Link to={'/auth/login'}>Sign In</Link>
</AuthFooterLink>
<AuthFooterLink>
<Link to={'/auth/send_reset_password'}>
<T id={'forget_my_password'} />
</Link>
</AuthFooterLink>
</AuthFooterLinks>
);
}

View File

@@ -1,148 +1,97 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import { Form } from 'formik';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { import { Intent, Button } from '@blueprintjs/core';
Button,
InputGroup,
Intent,
FormGroup,
Spinner,
} from '@blueprintjs/core';
import { ErrorMessage, Field, Form } from 'formik';
import { FormattedMessage as T } from '@/components';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Row, Col, If } from '@/components'; import { Tooltip2 } from '@blueprintjs/popover2';
import { PasswordRevealer } from './components'; import styled from 'styled-components';
import { inputIntent } from '@/utils';
import {
FFormGroup,
FInputGroup,
Row,
Col,
FormattedMessage as T,
} from '@/components';
import { AuthSubmitButton, AuthenticationLoadingOverlay } from './_components';
/** /**
* Register form. * Register form.
*/ */
export default function RegisterForm({ isSubmitting }) { export default function RegisterForm({ isSubmitting }) {
const [passwordType, setPasswordType] = React.useState('password'); const [showPassword, setShowPassword] = React.useState<boolean>(false);
// Handle password revealer changing. // Handle password revealer changing.
const handlePasswordRevealerChange = React.useCallback( const handleLockClick = () => {
(shown) => { setShowPassword(!showPassword);
const type = shown ? 'text' : 'password'; };
setPasswordType(type);
}, const lockButton = (
[setPasswordType], <Tooltip2 content={`${showPassword ? 'Hide' : 'Show'} Password`}>
<Button
icon={showPassword ? 'unlock' : 'lock'}
intent={Intent.WARNING}
minimal={true}
onClick={handleLockClick}
small={true}
/>
</Tooltip2>
); );
return ( return (
<Form className={'authentication-page__form'}> <Form className={'authentication-page__form'}>
<Row className={'name-section'}> <Row className={'name-section'}>
<Col md={6}> <Col md={6}>
<Field name={'first_name'}> <FFormGroup name={'first_name'} label={<T id={'first_name'} />}>
{({ form, field, meta: { error, touched } }) => ( <FInputGroup name={'first_name'} large={true} />
<FormGroup </FFormGroup>
label={<T id={'first_name'} />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'first_name'} />}
className={'form-group--first-name'}
>
<InputGroup
intent={inputIntent({ error, touched })}
{...field}
/>
</FormGroup>
)}
</Field>
</Col> </Col>
<Col md={6}> <Col md={6}>
<Field name={'last_name'}> <FFormGroup name={'last_name'} label={<T id={'last_name'} />}>
{({ form, field, meta: { error, touched } }) => ( <FInputGroup name={'last_name'} large={true} />
<FormGroup </FFormGroup>
label={<T id={'last_name'} />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'last_name'} />}
className={'form-group--last-name'}
>
<InputGroup
intent={inputIntent({ error, touched })}
{...field}
/>
</FormGroup>
)}
</Field>
</Col> </Col>
</Row> </Row>
<Field name={'phone_number'}> <FFormGroup name={'email'} label={<T id={'email'} />}>
{({ form, field, meta: { error, touched } }) => ( <FInputGroup name={'email'} large={true} />
<FormGroup </FFormGroup>
label={<T id={'phone_number'} />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'phone_number'} />}
className={'form-group--phone-number'}
>
<InputGroup intent={inputIntent({ error, touched })} {...field} />
</FormGroup>
)}
</Field>
<Field name={'email'}> <FFormGroup name={'password'} label={<T id={'password'} />}>
{({ form, field, meta: { error, touched } }) => ( <FInputGroup
<FormGroup name={'password'}
label={<T id={'email'} />} type={showPassword ? 'text' : 'password'}
intent={inputIntent({ error, touched })} rightElement={lockButton}
helperText={<ErrorMessage name={'email'} />} large={true}
className={'form-group--email'}
>
<InputGroup intent={inputIntent({ error, touched })} {...field} />
</FormGroup>
)}
</Field>
<Field name={'password'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'password'} />}
labelInfo={
<PasswordRevealer onChange={handlePasswordRevealerChange} />
}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'password'} />}
className={'form-group--password has-password-revealer'}
>
<InputGroup
lang={true}
type={passwordType}
intent={inputIntent({ error, touched })}
{...field}
/> />
</FormGroup> </FFormGroup>
)}
</Field>
<div className={'register-form__agreement-section'}> <TermsConditionsText>
<p>
{intl.getHTML('signing_in_or_creating', { {intl.getHTML('signing_in_or_creating', {
terms: (msg) => <Link>{msg}</Link>, terms: (msg) => <Link>{msg}</Link>,
privacy: (msg) => <Link>{msg}</Link>, privacy: (msg) => <Link>{msg}</Link>,
})} })}
</p> </TermsConditionsText>
</div>
<div className={'authentication-page__submit-button-wrap'}> <AuthSubmitButton
<Button
className={'btn-register'} className={'btn-register'}
intent={Intent.PRIMARY} intent={Intent.PRIMARY}
type="submit" type="submit"
fill={true} fill={true}
large={true}
loading={isSubmitting} loading={isSubmitting}
> >
<T id={'register'} /> <T id={'register'} />
</Button> </AuthSubmitButton>
</div>
<If condition={isSubmitting}> {isSubmitting && <AuthenticationLoadingOverlay />}
<div class="authentication-page__loading-overlay">
<Spinner size={50} />
</div>
</If>
</Form> </Form>
); );
} }
const TermsConditionsText = styled.p`
opacity: 0.8;
margin-bottom: 1.4rem;
`;

View File

@@ -4,14 +4,23 @@ import intl from 'react-intl-universal';
import { Formik } from 'formik'; import { Formik } from 'formik';
import { Intent, Position } from '@blueprintjs/core'; import { Intent, Position } from '@blueprintjs/core';
import { Link, useParams, useHistory } from 'react-router-dom'; import { Link, useParams, useHistory } from 'react-router-dom';
import { AppToaster, FormattedMessage as T } from '@/components';
import { AppToaster } from '@/components';
import { useAuthResetPassword } from '@/hooks/query'; import { useAuthResetPassword } from '@/hooks/query';
import AuthInsider from '@/containers/Authentication/AuthInsider'; import AuthInsider from '@/containers/Authentication/AuthInsider';
import {
AuthFooterLink,
AuthFooterLinks,
AuthInsiderCard,
} from './_components';
import ResetPasswordForm from './ResetPasswordForm'; import ResetPasswordForm from './ResetPasswordForm';
import { ResetPasswordSchema } from './utils'; import { ResetPasswordSchema } from './utils';
const initialValues = {
password: '',
confirm_password: '',
};
/** /**
* Reset password page. * Reset password page.
*/ */
@@ -22,15 +31,6 @@ export default function ResetPassword() {
// Authentication reset password. // Authentication reset password.
const { mutateAsync: authResetPasswordMutate } = useAuthResetPassword(); const { mutateAsync: authResetPasswordMutate } = useAuthResetPassword();
// Initial values of the form.
const initialValues = useMemo(
() => ({
password: '',
confirm_password: '',
}),
[],
);
// Handle the form submitting. // Handle the form submitting.
const handleSubmit = (values, { setSubmitting }) => { const handleSubmit = (values, { setSubmitting }) => {
authResetPasswordMutate([token, values]) authResetPasswordMutate([token, values])
@@ -64,24 +64,30 @@ export default function ResetPassword() {
return ( return (
<AuthInsider> <AuthInsider>
<div className={'submit-np-form'}> <AuthInsiderCard>
<div className={'authentication-page__label-section'}>
<h3>
<T id={'choose_a_new_password'} />
</h3>
<T id={'you_remembered_your_password'} />{' '}
<Link to="/auth/login">
<T id={'login'} />
</Link>
</div>
<Formik <Formik
initialValues={initialValues} initialValues={initialValues}
validationSchema={ResetPasswordSchema} validationSchema={ResetPasswordSchema}
onSubmit={handleSubmit} onSubmit={handleSubmit}
component={ResetPasswordForm} component={ResetPasswordForm}
/> />
</div> </AuthInsiderCard>
<ResetPasswordFooterLinks />
</AuthInsider> </AuthInsider>
); );
} }
function ResetPasswordFooterLinks() {
return (
<AuthFooterLinks>
<AuthFooterLink>
Don't have an account? <Link to={'/auth/register'}>Sign up</Link>
</AuthFooterLink>
<AuthFooterLink>
Return to <Link to={'/auth/login'}>Sign In</Link>
</AuthFooterLink>
</AuthFooterLinks>
);
}

View File

@@ -1,9 +1,9 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import { Button, InputGroup, Intent, FormGroup } from '@blueprintjs/core'; import { Intent } from '@blueprintjs/core';
import { Form, ErrorMessage, FastField } from 'formik'; import { Form } from 'formik';
import { FormattedMessage as T } from '@/components'; import { FFormGroup, FInputGroup, FormattedMessage as T } from '@/components';
import { inputIntent } from '@/utils'; import { AuthSubmitButton } from './_components';
/** /**
* Reset password form. * Reset password form.
@@ -11,54 +11,23 @@ import { inputIntent } from '@/utils';
export default function ResetPasswordForm({ isSubmitting }) { export default function ResetPasswordForm({ isSubmitting }) {
return ( return (
<Form> <Form>
<FastField name={'password'}> <FFormGroup name={'password'} label={<T id={'new_password'} />}>
{({ form, field, meta: { error, touched } }) => ( <FInputGroup name={'password'} type={'password'} large={true} />
<FormGroup </FFormGroup>
label={<T id={'new_password'} />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'password'} />}
className={'form-group--password'}
>
<InputGroup
lang={true}
type={'password'}
intent={inputIntent({ error, touched })}
{...field}
/>
</FormGroup>
)}
</FastField>
<FastField name={'confirm_password'}> <FFormGroup name={'confirm_password'} label={<T id={'new_password'} />}>
{({ form, field, meta: { error, touched } }) => ( <FInputGroup name={'confirm_password'} type={'password'} large={true} />
<FormGroup </FFormGroup>
label={<T id={'new_password'} />}
labelInfo={'(again):'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'confirm_password'} />}
className={'form-group--confirm-password'}
>
<InputGroup
lang={true}
type={'password'}
intent={inputIntent({ error, touched })}
{...field}
/>
</FormGroup>
)}
</FastField>
<div className={'authentication-page__submit-button-wrap'}> <AuthSubmitButton
<Button
fill={true} fill={true}
className={'btn-new'}
intent={Intent.PRIMARY} intent={Intent.PRIMARY}
type="submit" type="submit"
loading={isSubmitting} loading={isSubmitting}
large={true}
> >
<T id={'submit'} /> <T id={'submit'} />
</Button> </AuthSubmitButton>
</div>
</Form> </Form>
); );
} }

View File

@@ -5,33 +5,32 @@ import { Formik } from 'formik';
import { Link, useHistory } from 'react-router-dom'; import { Link, useHistory } from 'react-router-dom';
import { Intent } from '@blueprintjs/core'; import { Intent } from '@blueprintjs/core';
import { AppToaster, FormattedMessage as T } from '@/components'; import { AppToaster } from '@/components';
import { useAuthSendResetPassword } from '@/hooks/query'; import { useAuthSendResetPassword } from '@/hooks/query';
import SendResetPasswordForm from './SendResetPasswordForm'; import SendResetPasswordForm from './SendResetPasswordForm';
import {
AuthFooterLink,
AuthFooterLinks,
AuthInsiderCard,
} from './_components';
import { import {
SendResetPasswordSchema, SendResetPasswordSchema,
transformSendResetPassErrorsToToasts, transformSendResetPassErrorsToToasts,
} from './utils'; } from './utils';
import AuthInsider from '@/containers/Authentication/AuthInsider'; import AuthInsider from '@/containers/Authentication/AuthInsider';
const initialValues = {
crediential: '',
};
/** /**
* Send reset password page. * Send reset password page.
*/ */
export default function SendResetPassword({ requestSendResetPassword }) { export default function SendResetPassword({ requestSendResetPassword }) {
const history = useHistory(); const history = useHistory();
const { mutateAsync: sendResetPasswordMutate } = useAuthSendResetPassword(); const { mutateAsync: sendResetPasswordMutate } = useAuthSendResetPassword();
// Initial values.
const initialValues = useMemo(
() => ({
crediential: '',
}),
[],
);
// Handle form submitting. // Handle form submitting.
const handleSubmit = (values, { setSubmitting }) => { const handleSubmit = (values, { setSubmitting }) => {
sendResetPasswordMutate({ email: values.crediential }) sendResetPasswordMutate({ email: values.crediential })
@@ -61,28 +60,30 @@ export default function SendResetPassword({ requestSendResetPassword }) {
return ( return (
<AuthInsider> <AuthInsider>
<div className="reset-form"> <AuthInsiderCard>
<div className={'authentication-page__label-section'}>
<h3>
<T id={'you_can_t_login'} />
</h3>
<p>
<T id={'we_ll_send_a_recovery_link_to_your_email'} />
</p>
</div>
<Formik <Formik
initialValues={initialValues} initialValues={initialValues}
onSubmit={handleSubmit} onSubmit={handleSubmit}
validationSchema={SendResetPasswordSchema} validationSchema={SendResetPasswordSchema}
component={SendResetPasswordForm} component={SendResetPasswordForm}
/> />
<div class="authentication-page__footer-links"> </AuthInsiderCard>
<Link to="/auth/login">
<T id={'return_to_log_in'} /> <SendResetPasswordFooterLinks />
</Link>
</div>
</div>
</AuthInsider> </AuthInsider>
); );
} }
function SendResetPasswordFooterLinks() {
return (
<AuthFooterLinks>
<AuthFooterLink>
Don't have an account? <Link to={'/auth/register'}>Sign up</Link>
</AuthFooterLink>
<AuthFooterLink>
Return to <Link to={'/auth/login'}>Sign In</Link>
</AuthFooterLink>
</AuthFooterLinks>
);
}

View File

@@ -1,43 +1,44 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import { Button, InputGroup, Intent, FormGroup } from '@blueprintjs/core'; import { Intent } from '@blueprintjs/core';
import { Form, ErrorMessage, FastField } from 'formik'; import { Form } from 'formik';
import { FormattedMessage as T } from '@/components'; import styled from 'styled-components';
import { inputIntent } from '@/utils';
import { FInputGroup, FFormGroup, FormattedMessage as T } from '@/components';
import { AuthSubmitButton } from './_components';
/** /**
* Send reset password form. * Send reset password form.
*/ */
export default function SendResetPasswordForm({ isSubmitting }) { export default function SendResetPasswordForm({ isSubmitting }) {
return ( return (
<Form className={'send-reset-password'}> <Form>
<FastField name={'crediential'}> <TopParagraph>
{({ form, field, meta: { error, touched } }) => ( Enter the email address associated with your account and we'll send you
<FormGroup a link to reset your password.
label={<T id={'email_or_phone_number'} />} </TopParagraph>
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'crediential'} />}
className={'form-group--crediential'}
>
<InputGroup
intent={inputIntent({ error, touched })}
large={true}
{...field}
/>
</FormGroup>
)}
</FastField>
<div className={'authentication-page__submit-button-wrap'}> <FFormGroup
<Button name={'crediential'}
label={<T id={'email_or_phone_number'} />}
>
<FInputGroup name={'crediential'} large={true} />
</FFormGroup>
<AuthSubmitButton
type={'submit'} type={'submit'}
intent={Intent.PRIMARY} intent={Intent.PRIMARY}
fill={true} fill={true}
large={true}
loading={isSubmitting} loading={isSubmitting}
> >
<T id={'send_reset_password_mail'} /> Reset Password
</Button> </AuthSubmitButton>
</div>
</Form> </Form>
); );
} }
const TopParagraph = styled.p`
margin-bottom: 1.6rem;
opacity: 0.8;
`;

View File

@@ -0,0 +1,69 @@
import React from 'react';
import styled from 'styled-components';
import { Spinner } from '@blueprintjs/core';
import { Button } from '@blueprintjs/core';
export function AuthenticationLoadingOverlay() {
return (
<AuthOverlayRoot>
<Spinner size={50} />
</AuthOverlayRoot>
);
}
const AuthOverlayRoot = styled.div`
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: rgba(252, 253, 255, 0.5);
display: flex;
justify-content: center;
`;
export const AuthInsiderContent = styled.div`
position: relative;
`;
export const AuthInsiderCard = styled.div`
border: 1px solid #d5d5d5;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
padding: 26px 22px;
background: #ffff;
border-radius: 3px;
`;
export const AuthInsiderCopyright = styled.div`
text-align: center;
font-size: 12px;
color: #666;
margin-top: 1.2rem;
.bp3-icon-bigcapital {
svg {
path {
fill: #a3a3a3;
}
}
}
`;
export const AuthFooterLinks = styled.div`
display: flex;
flex-direction: column;
gap: 10px;
padding-left: 1.2rem;
padding-right: 1.2rem;
margin-top: 1rem;
`;
export const AuthFooterLink = styled.p`
color: #666;
margin: 0;
`;
export const AuthSubmitButton = styled(Button)`
background-color: #0052cc;
min-height: 45px;
margin-top: 20px;
`;

View File

@@ -16,15 +16,9 @@ export function PasswordRevealer({ defaultShown = false, onChange }) {
<span class="password-revealer" onClick={handleClick}> <span class="password-revealer" onClick={handleClick}>
<If condition={shown}> <If condition={shown}>
<Icon icon="eye-slash" />{' '} <Icon icon="eye-slash" />{' '}
<span class="text">
<T id={'hide'} />
</span>
</If> </If>
<If condition={!shown}> <If condition={!shown}>
<Icon icon="eye" />{' '} <Icon icon="eye" />{' '}
<span class="text">
<T id={'show'} />
</span>
</If> </If>
</span> </span>
); );

View File

@@ -31,7 +31,7 @@
"phone_number": "Phone Number", "phone_number": "Phone Number",
"you_email_address_is": "You email address is", "you_email_address_is": "You email address is",
"you_will_use_this_address_to_sign_in_to_bigcapital": "You will use this address to sign in to Bigcapital.", "you_will_use_this_address_to_sign_in_to_bigcapital": "You will use this address to sign in to Bigcapital.",
"signing_in_or_creating": "By signing in or creating an account, you agree with our <br/> <a>Terms & Conditions </a> and <a> Privacy Statement </a> ", "signing_in_or_creating": "By signing in or creating an account, you agree with our <a>Terms & Conditions </a> and <a> Privacy Statement </a> ",
"and": "And", "and": "And",
"create_account": "Create Account", "create_account": "Create Account",
"success": "Success", "success": "Success",

View File

@@ -1,45 +1,8 @@
body.authentication { body.authentication {
background-color: #fcfdff; background-color: #fcfdff;
} }
.authentication-insider { .authTransition {
width: 384px;
margin: 0 auto;
margin-bottom: 40px;
padding-top: 80px;
&__logo-section {
text-align: center;
margin-bottom: 60px;
}
&__content {
position: relative;
}
&__footer {
.auth-copyright {
text-align: center;
font-size: 12px;
color: #666;
.bp3-icon-bigcapital {
margin-top: 9px;
svg {
path {
fill: #a3a3a3;
}
}
}
}
}
}
.authTransition{
&-enter { &-enter {
opacity: 0; opacity: 0;
} }
@@ -61,164 +24,9 @@ body.authentication {
opacity: 0.5; opacity: 0.5;
transition: opacity 250ms ease-in-out; transition: opacity 250ms ease-in-out;
} }
&-exit-active { &-exit-active {
opacity: 0; opacity: 0;
display: none; display: none;
} }
}
.authentication-page {
&__goto-bigcapital {
position: fixed;
margin-top: 30px;
margin-left: 30px;
color: #777;
}
.bp3-input {
min-height: 40px;
}
.bp3-form-group {
margin-bottom: 25px;
}
.bp3-form-group.has-password-revealer {
.bp3-label {
display: flex;
justify-content: space-between;
}
.password-revealer {
.text {
font-size: 12px;
}
}
}
.bp3-button.bp3-fill.bp3-intent-primary {
font-size: 16px;
}
&__label-section {
margin-bottom: 30px;
color: #555;
h3 {
font-weight: 500;
font-size: 22px;
color: #2d2b43;
margin: 0 0 12px;
}
a {
text-decoration: underline;
color: #0040bd;
}
}
&__form-wrapper {
width: 100%;
margin: 0 auto;
}
&__footer-links {
padding: 9px;
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
text-align: center;
margin-bottom: 1.2rem;
a {
color: #0052cc;
}
}
&__loading-overlay {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: rgba(252, 253, 255, 0.5);
display: flex;
justify-content: center;
}
&__submit-button-wrap {
margin: 0px 0px 24px 0px;
.bp3-button {
background-color: #0052cc;
min-height: 45px;
}
}
// Login Form
// ------------------------------
.login-form {
// width: 690px;
// margin: 0px auto;
// padding: 85px 50px;
.checkbox {
&--remember-me {
margin: -6px 0 26px 0px;
font-size: 14px;
}
}
}
// Register form
// ----------------------------
.register-form {
&__agreement-section {
margin-top: -10px;
p {
font-size: 13px;
margin-top: -10px;
margin-bottom: 24px;
line-height: 1.65;
}
}
&__submit-button-wrap {
margin: 25px 0px 25px 0px;
.bp3-button {
min-height: 45px;
background-color: #0052cc;
}
}
}
// Send reset password
// ----------------------------
.send-reset-password {
.form-group--crediential {
margin-bottom: 36px;
}
}
// Invite form.
// ----------------
.invite-form {
&__statement-section {
margin-top: -10px;
p {
font-size: 14px;
margin-bottom: 20px;
line-height: 1.65;
}
}
.authentication-page__loading-overlay {
background: rgba(252, 253, 255, 0.9);
}
}
} }