mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 20:30:33 +00:00
refactoring: setup wizard pages with simple architecture.
This commit is contained in:
@@ -19,18 +19,15 @@ import Icon from 'components/Icon';
|
||||
import { If } from 'components';
|
||||
|
||||
import withAuthenticationActions from './withAuthenticationActions';
|
||||
import withOrganizationsActions from 'containers/Organization/withOrganizationActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
|
||||
const ERRORS_TYPES = {
|
||||
INVALID_DETAILS: 'INVALID_DETAILS',
|
||||
USER_INACTIVE: 'USER_INACTIVE',
|
||||
};
|
||||
function Login({
|
||||
requestLogin,
|
||||
requestOrganizationsList,
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
const history = useHistory();
|
||||
@@ -105,7 +102,7 @@ function Login({
|
||||
<div className={'authentication-page__label-section'}>
|
||||
<h3><T id={'log_in'} /></h3>
|
||||
<T id={'need_bigcapital_account'} />
|
||||
<Link to='/register'> <T id={'create_an_account'} /></Link>
|
||||
<Link to='/auth/register'> <T id={'create_an_account'} /></Link>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className={'authentication-page__form'}>
|
||||
@@ -170,5 +167,4 @@ function Login({
|
||||
|
||||
export default compose(
|
||||
withAuthenticationActions,
|
||||
withOrganizationsActions,
|
||||
)(Login);
|
||||
284
client/src/containers/Authentication/Register.js
Normal file
284
client/src/containers/Authentication/Register.js
Normal file
@@ -0,0 +1,284 @@
|
||||
import React, { useMemo, useState, useCallback } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useFormik } from 'formik';
|
||||
import { Row, Col } from 'react-grid-system';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import {
|
||||
Button,
|
||||
InputGroup,
|
||||
Intent,
|
||||
FormGroup,
|
||||
Spinner,
|
||||
} from '@blueprintjs/core';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
|
||||
import AppToaster from 'components/AppToaster';
|
||||
import AuthInsider from 'containers/Authentication/AuthInsider';
|
||||
|
||||
import ErrorMessage from 'components/ErrorMessage';
|
||||
import Icon from 'components/Icon';
|
||||
import { If } from 'components';
|
||||
import withAuthenticationActions from 'containers/Authentication/withAuthenticationActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
function RegisterUserForm({ requestRegister, requestLogin }) {
|
||||
const { formatMessage } = useIntl();
|
||||
const history = useHistory();
|
||||
const [shown, setShown] = useState(false);
|
||||
const passwordRevealer = useCallback(() => {
|
||||
setShown(!shown);
|
||||
}, [shown]);
|
||||
|
||||
const ValidationSchema = Yup.object().shape({
|
||||
first_name: Yup.string()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'first_name_' })),
|
||||
last_name: Yup.string()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'last_name_' })),
|
||||
email: Yup.string()
|
||||
.email()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'email' })),
|
||||
phone_number: Yup.string()
|
||||
.matches()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'phone_number_' })),
|
||||
password: Yup.string()
|
||||
.min(4)
|
||||
.required()
|
||||
.label(formatMessage({ id: 'password' })),
|
||||
});
|
||||
|
||||
const initialValues = useMemo(
|
||||
() => ({
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
email: '',
|
||||
phone_number: '',
|
||||
password: '',
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
const {
|
||||
errors,
|
||||
touched,
|
||||
handleSubmit,
|
||||
getFieldProps,
|
||||
isSubmitting,
|
||||
} = useFormik({
|
||||
enableReinitialize: true,
|
||||
validationSchema: ValidationSchema,
|
||||
initialValues: {
|
||||
...initialValues,
|
||||
country: 'libya',
|
||||
},
|
||||
onSubmit: (values, { setSubmitting, setErrors }) => {
|
||||
requestRegister(values)
|
||||
.then((response) => {
|
||||
requestLogin({
|
||||
crediential: values.email,
|
||||
password: values.password,
|
||||
})
|
||||
.then(() => {
|
||||
history.push('/register/subscription');
|
||||
setSubmitting(false);
|
||||
})
|
||||
.catch((errors) => {
|
||||
AppToaster.show({
|
||||
message: formatMessage({ id: 'something_wentwrong' }),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((errors) => {
|
||||
if (errors.some((e) => e.type === 'PHONE_NUMBER_EXISTS')) {
|
||||
setErrors({
|
||||
phone_number: formatMessage({
|
||||
id: 'the_phone_number_already_used_in_another_account',
|
||||
}),
|
||||
});
|
||||
}
|
||||
if (errors.some((e) => e.type === 'EMAIL_EXISTS')) {
|
||||
setErrors({
|
||||
email: formatMessage({
|
||||
id: 'the_email_already_used_in_another_account',
|
||||
}),
|
||||
});
|
||||
}
|
||||
setSubmitting(false);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const passwordRevealerTmp = useMemo(
|
||||
() => (
|
||||
<span class="password-revealer" onClick={() => passwordRevealer()}>
|
||||
<If condition={shown}>
|
||||
<>
|
||||
<Icon icon="eye-slash" />{' '}
|
||||
<span class="text">
|
||||
<T id={'hide'} />
|
||||
</span>
|
||||
</>
|
||||
</If>
|
||||
<If condition={!shown}>
|
||||
<>
|
||||
<Icon icon="eye" />{' '}
|
||||
<span class="text">
|
||||
<T id={'show'} />
|
||||
</span>
|
||||
</>
|
||||
</If>
|
||||
</span>
|
||||
),
|
||||
[shown, passwordRevealer],
|
||||
);
|
||||
|
||||
return (
|
||||
<AuthInsider>
|
||||
<div className={'register-form'}>
|
||||
<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>
|
||||
|
||||
<form onSubmit={handleSubmit} className={'authentication-page__form'}>
|
||||
|
||||
<Row className={'name-section'}>
|
||||
<Col md={6}>
|
||||
<FormGroup
|
||||
label={<T id={'first_name'} />}
|
||||
intent={
|
||||
errors.first_name && touched.first_name && Intent.DANGER
|
||||
}
|
||||
helperText={
|
||||
<ErrorMessage name={'first_name'} {...{ errors, touched }} />
|
||||
}
|
||||
className={'form-group--first-name'}
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.first_name && touched.first_name && Intent.DANGER
|
||||
}
|
||||
{...getFieldProps('first_name')}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
|
||||
<Col md={6}>
|
||||
<FormGroup
|
||||
label={<T id={'last_name'} />}
|
||||
intent={errors.last_name && touched.last_name && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name={'last_name'} {...{ errors, touched }} />
|
||||
}
|
||||
className={'form-group--last-name'}
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.last_name && touched.last_name && Intent.DANGER
|
||||
}
|
||||
{...getFieldProps('last_name')}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<FormGroup
|
||||
label={<T id={'phone_number'} />}
|
||||
intent={
|
||||
errors.phone_number && touched.phone_number && Intent.DANGER
|
||||
}
|
||||
helperText={
|
||||
<ErrorMessage name={'phone_number'} {...{ errors, touched }} />
|
||||
}
|
||||
className={'form-group--phone-number'}
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.phone_number && touched.phone_number && Intent.DANGER
|
||||
}
|
||||
{...getFieldProps('phone_number')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
label={<T id={'email'} />}
|
||||
intent={errors.email && touched.email && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name={'email'} {...{ errors, touched }} />
|
||||
}
|
||||
className={'form-group--email'}
|
||||
>
|
||||
<InputGroup
|
||||
intent={errors.email && touched.email && Intent.DANGER}
|
||||
{...getFieldProps('email')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
label={<T id={'password'} />}
|
||||
labelInfo={passwordRevealerTmp}
|
||||
intent={errors.password && touched.password && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name={'password'} {...{ errors, touched }} />
|
||||
}
|
||||
className={'form-group--password has-password-revealer'}
|
||||
>
|
||||
<InputGroup
|
||||
lang={true}
|
||||
type={shown ? 'text' : 'password'}
|
||||
intent={errors.password && touched.password && Intent.DANGER}
|
||||
{...getFieldProps('password')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<div className={'register-form__agreement-section'}>
|
||||
<p>
|
||||
<T id={'signing_in_or_creating'} /> <br />
|
||||
<Link>
|
||||
<T id={'terms_conditions'} />
|
||||
</Link>{' '}
|
||||
<T id={'and'} />
|
||||
<Link>
|
||||
{' '}
|
||||
<T id={'privacy_statement'} />
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={'authentication-page__submit-button-wrap'}>
|
||||
<Button
|
||||
className={'btn-register'}
|
||||
intent={Intent.PRIMARY}
|
||||
type="submit"
|
||||
fill={true}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
<T id={'register'} />
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<If condition={isSubmitting}>
|
||||
<div class="authentication-page__loading-overlay">
|
||||
<Spinner size={50} />
|
||||
</div>
|
||||
</If>
|
||||
</div>
|
||||
</AuthInsider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAuthenticationActions,
|
||||
)(RegisterUserForm);
|
||||
@@ -1,14 +0,0 @@
|
||||
import React from 'react';
|
||||
import RegisterRightSection from './RegisterRightSection';
|
||||
import RegisterLeftSection from './RegisterLeftSection';
|
||||
|
||||
function RegisterWizardPage() {
|
||||
return (
|
||||
<div class="register-page">
|
||||
<RegisterLeftSection />
|
||||
<RegisterRightSection />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default RegisterWizardPage;
|
||||
@@ -1,55 +0,0 @@
|
||||
import React from 'react';
|
||||
import { TransitionGroup, CSSTransition } from 'react-transition-group';
|
||||
import { Wizard, Steps, Step } from 'react-albus';
|
||||
import { useHistory } from "react-router-dom";
|
||||
import RegisterWizardSteps from './RegisterWizardSteps';
|
||||
import PrivateRoute from 'components/PrivateRoute';
|
||||
|
||||
import RegisterUserForm from 'containers/Authentication/Register/RegisterUserForm';
|
||||
import RegisterSubscriptionForm from 'containers/Authentication/Register/RegisterSubscriptionForm';
|
||||
import RegisterOrganizationForm from 'containers/Authentication/Register/RegisterOrganizationForm';
|
||||
|
||||
export default function RegisterRightSection () {
|
||||
const history = useHistory();
|
||||
|
||||
return (
|
||||
<section className={'register-page__right-section'}>
|
||||
<Wizard
|
||||
basename={'/register'}
|
||||
history={history}
|
||||
render={({ step, steps }) => (
|
||||
<div>
|
||||
<RegisterWizardSteps currentStep={steps.indexOf(step) + 1} />
|
||||
|
||||
<TransitionGroup>
|
||||
<CSSTransition
|
||||
key={step.id}
|
||||
classNames="example"
|
||||
timeout={{ enter: 500, exit: 500 }}
|
||||
>
|
||||
<div class="register-page-form">
|
||||
<Steps key={step.id} step={step}>
|
||||
<Step id="user">
|
||||
<RegisterUserForm />
|
||||
</Step>
|
||||
|
||||
<Step id="subscription">
|
||||
<PrivateRoute component={RegisterSubscriptionForm} />
|
||||
</Step>
|
||||
|
||||
<Step id="organization">
|
||||
<PrivateRoute component={RegisterOrganizationForm} />
|
||||
</Step>
|
||||
|
||||
<Step id="congratulations">
|
||||
<h1 className="text-align-center">Ice King</h1>
|
||||
</Step>
|
||||
</Steps>
|
||||
</div>
|
||||
</CSSTransition>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
)} />
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -1,281 +0,0 @@
|
||||
import React, { useMemo, useState, useCallback } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useFormik } from 'formik';
|
||||
import { Row, Col } from 'react-grid-system';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import {
|
||||
Button,
|
||||
InputGroup,
|
||||
Intent,
|
||||
FormGroup,
|
||||
Spinner,
|
||||
} from '@blueprintjs/core';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
|
||||
import AppToaster from 'components/AppToaster';
|
||||
|
||||
import ErrorMessage from 'components/ErrorMessage';
|
||||
import Icon from 'components/Icon';
|
||||
import { If } from 'components';
|
||||
import withAuthenticationActions from 'containers/Authentication/withAuthenticationActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
function RegisterUserForm({ requestRegister, requestLogin }) {
|
||||
const { formatMessage } = useIntl();
|
||||
const history = useHistory();
|
||||
const [shown, setShown] = useState(false);
|
||||
const passwordRevealer = useCallback(() => {
|
||||
setShown(!shown);
|
||||
}, [shown]);
|
||||
|
||||
const ValidationSchema = Yup.object().shape({
|
||||
first_name: Yup.string()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'first_name_' })),
|
||||
last_name: Yup.string()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'last_name_' })),
|
||||
email: Yup.string()
|
||||
.email()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'email' })),
|
||||
phone_number: Yup.string()
|
||||
.matches()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'phone_number_' })),
|
||||
password: Yup.string()
|
||||
.min(4)
|
||||
.required()
|
||||
.label(formatMessage({ id: 'password' })),
|
||||
});
|
||||
|
||||
const initialValues = useMemo(
|
||||
() => ({
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
email: '',
|
||||
phone_number: '',
|
||||
password: '',
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
const {
|
||||
errors,
|
||||
touched,
|
||||
handleSubmit,
|
||||
getFieldProps,
|
||||
isSubmitting,
|
||||
} = useFormik({
|
||||
enableReinitialize: true,
|
||||
validationSchema: ValidationSchema,
|
||||
initialValues: {
|
||||
...initialValues,
|
||||
country: 'libya',
|
||||
},
|
||||
onSubmit: (values, { setSubmitting, setErrors }) => {
|
||||
requestRegister(values)
|
||||
.then((response) => {
|
||||
requestLogin({
|
||||
crediential: values.email,
|
||||
password: values.password,
|
||||
})
|
||||
.then(() => {
|
||||
history.push('/register/subscription');
|
||||
setSubmitting(false);
|
||||
})
|
||||
.catch((errors) => {
|
||||
AppToaster.show({
|
||||
message: formatMessage({
|
||||
id: 'something_wentwrong',
|
||||
}),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((errors) => {
|
||||
if (errors.some((e) => e.type === 'PHONE_NUMBER_EXISTS')) {
|
||||
setErrors({
|
||||
phone_number: formatMessage({
|
||||
id: 'the_phone_number_already_used_in_another_account',
|
||||
}),
|
||||
});
|
||||
}
|
||||
if (errors.some((e) => e.type === 'EMAIL_EXISTS')) {
|
||||
setErrors({
|
||||
email: formatMessage({
|
||||
id: 'the_email_already_used_in_another_account',
|
||||
}),
|
||||
});
|
||||
}
|
||||
setSubmitting(false);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const passwordRevealerTmp = useMemo(
|
||||
() => (
|
||||
<span class="password-revealer" onClick={() => passwordRevealer()}>
|
||||
<If condition={shown}>
|
||||
<>
|
||||
<Icon icon="eye-slash" />{' '}
|
||||
<span class="text">
|
||||
<T id={'hide'} />
|
||||
</span>
|
||||
</>
|
||||
</If>
|
||||
<If condition={!shown}>
|
||||
<>
|
||||
<Icon icon="eye" />{' '}
|
||||
<span class="text">
|
||||
<T id={'show'} />
|
||||
</span>
|
||||
</>
|
||||
</If>
|
||||
</span>
|
||||
),
|
||||
[shown, passwordRevealer],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={'register-form'}>
|
||||
<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>
|
||||
|
||||
<form onSubmit={handleSubmit} className={'authentication-page__form'}>
|
||||
|
||||
<Row className={'name-section'}>
|
||||
<Col md={6}>
|
||||
<FormGroup
|
||||
label={<T id={'first_name'} />}
|
||||
intent={
|
||||
errors.first_name && touched.first_name && Intent.DANGER
|
||||
}
|
||||
helperText={
|
||||
<ErrorMessage name={'first_name'} {...{ errors, touched }} />
|
||||
}
|
||||
className={'form-group--first-name'}
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.first_name && touched.first_name && Intent.DANGER
|
||||
}
|
||||
{...getFieldProps('first_name')}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
|
||||
<Col md={6}>
|
||||
<FormGroup
|
||||
label={<T id={'last_name'} />}
|
||||
intent={errors.last_name && touched.last_name && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name={'last_name'} {...{ errors, touched }} />
|
||||
}
|
||||
className={'form-group--last-name'}
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.last_name && touched.last_name && Intent.DANGER
|
||||
}
|
||||
{...getFieldProps('last_name')}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<FormGroup
|
||||
label={<T id={'phone_number'} />}
|
||||
intent={
|
||||
errors.phone_number && touched.phone_number && Intent.DANGER
|
||||
}
|
||||
helperText={
|
||||
<ErrorMessage name={'phone_number'} {...{ errors, touched }} />
|
||||
}
|
||||
className={'form-group--phone-number'}
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.phone_number && touched.phone_number && Intent.DANGER
|
||||
}
|
||||
{...getFieldProps('phone_number')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
label={<T id={'email'} />}
|
||||
intent={errors.email && touched.email && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name={'email'} {...{ errors, touched }} />
|
||||
}
|
||||
className={'form-group--email'}
|
||||
>
|
||||
<InputGroup
|
||||
intent={errors.email && touched.email && Intent.DANGER}
|
||||
{...getFieldProps('email')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
label={<T id={'password'} />}
|
||||
labelInfo={passwordRevealerTmp}
|
||||
intent={errors.password && touched.password && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name={'password'} {...{ errors, touched }} />
|
||||
}
|
||||
className={'form-group--password has-password-revealer'}
|
||||
>
|
||||
<InputGroup
|
||||
lang={true}
|
||||
type={shown ? 'text' : 'password'}
|
||||
intent={errors.password && touched.password && Intent.DANGER}
|
||||
{...getFieldProps('password')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<div className={'register-form__agreement-section'}>
|
||||
<p>
|
||||
<T id={'signing_in_or_creating'} /> <br />
|
||||
<Link>
|
||||
<T id={'terms_conditions'} />
|
||||
</Link>{' '}
|
||||
<T id={'and'} />
|
||||
<Link>
|
||||
{' '}
|
||||
<T id={'privacy_statement'} />
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={'authentication-page__submit-button-wrap'}>
|
||||
<Button
|
||||
className={'btn-register'}
|
||||
intent={Intent.PRIMARY}
|
||||
type="submit"
|
||||
fill={true}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
<T id={'register'} />
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<If condition={isSubmitting}>
|
||||
<div class="authentication-page__loading-overlay">
|
||||
<Spinner size={50} />
|
||||
</div>
|
||||
</If>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withAuthenticationActions)(RegisterUserForm);
|
||||
@@ -6,6 +6,7 @@ export default (mapState) => {
|
||||
const mapped = {
|
||||
isAuthorized: isAuthenticated(state),
|
||||
user: state.authentication.user,
|
||||
currentOrganizationId: state.authentication?.tenant?.organization_id,
|
||||
};
|
||||
return mapState ? mapState(mapped, state, props) : mapped;
|
||||
};
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import {
|
||||
buildTenant,
|
||||
seedTenant,
|
||||
} from 'store/organization/organization.actions';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
requestBuildTenant: (id, token) => dispatch(buildTenant({ id, token })),
|
||||
requestSeedTenant: (id, token) => dispatch(seedTenant({ id, token })),
|
||||
});
|
||||
|
||||
export default connect(null, mapDispatchToProps);
|
||||
@@ -1,10 +1,14 @@
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
fetchOrganizations,
|
||||
buildTenant,
|
||||
seedTenant,
|
||||
} from 'store/organizations/organizations.actions';
|
||||
|
||||
export const mapDispatchToProps = (dispatch) => ({
|
||||
requestOrganizationsList: () => dispatch(fetchOrganizations()),
|
||||
requestBuildTenant: () => dispatch(buildTenant()),
|
||||
requestSeedTenant: () => dispatch(seedTenant()),
|
||||
});
|
||||
|
||||
export default connect(null, mapDispatchToProps);
|
||||
@@ -1,7 +1,7 @@
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
getOrganizationByOrgIdFactory,
|
||||
} from 'store/organizations/organizations.selector';
|
||||
} from 'store/organizations/organizations.selectors';
|
||||
|
||||
export default (mapState) => {
|
||||
const getOrganizationByOrgId = getOrganizationByOrgIdFactory();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
getOrganizationByTenantIdFactory,
|
||||
} from 'store/organizations/organizations.selector';
|
||||
} from 'store/organizations/organizations.selectors';
|
||||
|
||||
export default (mapState) => {
|
||||
const getOrgByTenId = getOrganizationByTenantIdFactory();
|
||||
|
||||
25
client/src/containers/Setup/EnsureOrganizationIsNotReady.js
Normal file
25
client/src/containers/Setup/EnsureOrganizationIsNotReady.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { compose } from 'utils';
|
||||
import withAuthentication from 'containers/Authentication/withAuthentication';
|
||||
import withOrganizationByOrgId from 'containers/Organization/withOrganizationByOrgId';
|
||||
|
||||
function EnsureOrganizationIsNotReady({
|
||||
children,
|
||||
|
||||
// #withOrganizationByOrgId
|
||||
organization,
|
||||
}) {
|
||||
return (organization.is_ready) ? (
|
||||
<Redirect to={{ pathname: '/' }} />
|
||||
) : children;
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAuthentication(),
|
||||
connect((state, props) => ({
|
||||
organizationId: props.currentOrganizationId,
|
||||
})),
|
||||
withOrganizationByOrgId(),
|
||||
)(EnsureOrganizationIsNotReady);
|
||||
@@ -7,8 +7,10 @@ import withAuthenticationActions from 'containers/Authentication/withAuthenticat
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
|
||||
function RegisterLeftSection({
|
||||
/**
|
||||
* Wizard setup left section.
|
||||
*/
|
||||
function SetupLeftSection({
|
||||
requestLogout,
|
||||
isAuthorized
|
||||
}) {
|
||||
@@ -19,7 +21,7 @@ function RegisterLeftSection({
|
||||
}, [requestLogout]);
|
||||
|
||||
return (
|
||||
<section className={'register-page__left-section'}>
|
||||
<section className={'setup-page__left-section'}>
|
||||
<div className={'content'}>
|
||||
<div className={'content-logo'}>
|
||||
<Icon
|
||||
@@ -38,7 +40,6 @@ function RegisterLeftSection({
|
||||
<T id={'you_have_a_bigcapital_account'} />
|
||||
</p>
|
||||
|
||||
|
||||
<If condition={!!isAuthorized}>
|
||||
<div className={'content-org'}>
|
||||
<span>
|
||||
@@ -71,4 +72,4 @@ function RegisterLeftSection({
|
||||
export default compose(
|
||||
withAuthentication(({ isAuthorized }) => ({ isAuthorized })),
|
||||
withAuthenticationActions,
|
||||
)(RegisterLeftSection);
|
||||
)(SetupLeftSection);
|
||||
@@ -20,11 +20,13 @@ import { momentFormatter, tansformDateValue } from 'utils';
|
||||
import AppToaster from 'components/AppToaster';
|
||||
import { ListSelect, ErrorMessage, FieldRequiredHint } from 'components';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import withSettingsActions from 'containers/Settings/withSettingsActions';
|
||||
import withRegisterOrganizationActions from 'containers/Authentication/withRegisterOrganizationActions';
|
||||
import withOrganizationActions from 'containers/Organization/withOrganizationActions';
|
||||
|
||||
import { compose, optionsMapToArray } from 'utils';
|
||||
|
||||
function RegisterOrganizationForm({ requestSubmitOptions, requestSeedTenant }) {
|
||||
function SetupOrganizationForm({ requestSubmitOptions, requestSeedTenant }) {
|
||||
const { formatMessage } = useIntl();
|
||||
const [selected, setSelected] = useState();
|
||||
const history = useHistory();
|
||||
@@ -414,5 +416,5 @@ function RegisterOrganizationForm({ requestSubmitOptions, requestSeedTenant }) {
|
||||
|
||||
export default compose(
|
||||
withSettingsActions,
|
||||
withRegisterOrganizationActions,
|
||||
)(RegisterOrganizationForm);
|
||||
withOrganizationActions,
|
||||
)(SetupOrganizationForm);
|
||||
70
client/src/containers/Setup/SetupRightSection.js
Normal file
70
client/src/containers/Setup/SetupRightSection.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { TransitionGroup, CSSTransition } from 'react-transition-group';
|
||||
import { Wizard, Steps, Step } from 'react-albus';
|
||||
import { useHistory } from "react-router-dom";
|
||||
import WizardSetupSteps from './WizardSetupSteps';
|
||||
|
||||
import SetupSubscriptionForm from './SetupSubscriptionForm';
|
||||
import SetupOrganizationForm from './SetupOrganizationForm';
|
||||
|
||||
import withAuthentication from 'containers/Authentication/withAuthentication';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Wizard setup right section.
|
||||
*/
|
||||
function SetupRightSection ({
|
||||
isTenantHasSubscriptions: hasSubscriptions = false,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const handleSkip = useCallback(({ step, push }) => {
|
||||
const scenarios = [
|
||||
{ condition: hasSubscriptions, redirectTo: 'organization' },
|
||||
{ condition: !hasSubscriptions, redirectTo: 'subscription' },
|
||||
];
|
||||
const scenario = scenarios.find((scenario) => scenario.condition);
|
||||
|
||||
if (scenario) {
|
||||
push(scenario.redirectTo);
|
||||
}
|
||||
}, [hasSubscriptions]);
|
||||
|
||||
return (
|
||||
<section className={'setup-page__right-section'}>
|
||||
<Wizard
|
||||
onNext={handleSkip}
|
||||
basename={'/setup'}
|
||||
history={history}
|
||||
render={({ step, steps }) => (
|
||||
<div>
|
||||
<WizardSetupSteps currentStep={steps.indexOf(step) + 1} />
|
||||
|
||||
<TransitionGroup>
|
||||
<CSSTransition key={step.id} timeout={{ enter: 500, exit: 500 }}>
|
||||
<div class="register-page-form">
|
||||
<Steps key={step.id} step={step}>
|
||||
<Step id="subscription">
|
||||
<SetupSubscriptionForm />
|
||||
</Step>
|
||||
|
||||
<Step id="organization">
|
||||
<SetupOrganizationForm />
|
||||
</Step>
|
||||
|
||||
<Step id="congratulations">
|
||||
<h1 className="text-align-center">Ice King</h1>
|
||||
</Step>
|
||||
</Steps>
|
||||
</div>
|
||||
</CSSTransition>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
)} />
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAuthentication(({ isAuthorized }) => ({ isAuthorized })),
|
||||
)(SetupRightSection);
|
||||
@@ -1,11 +1,13 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useFormik } from 'formik';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { Button, Intent } from '@blueprintjs/core';
|
||||
import BillingTab from 'containers/Subscriptions/BillingTab';
|
||||
|
||||
function RegisterSubscriptionForm({}) {
|
||||
export default function SetupSubscriptionForm({
|
||||
|
||||
}) {
|
||||
const ValidationSchema = Yup.object().shape({});
|
||||
|
||||
const initialValues = useMemo(() => ({}), []);
|
||||
@@ -36,5 +38,3 @@ function RegisterSubscriptionForm({}) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default RegisterSubscriptionForm;
|
||||
19
client/src/containers/Setup/WizardSetupPage.js
Normal file
19
client/src/containers/Setup/WizardSetupPage.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
|
||||
import EnsureOrganizationIsNotReady from './EnsureOrganizationIsNotReady';
|
||||
import SetupRightSection from './SetupRightSection';
|
||||
import SetupLeftSection from './SetupLeftSection';
|
||||
|
||||
|
||||
export default function WizardSetupPage({
|
||||
organizationId,
|
||||
}) {
|
||||
return (
|
||||
<EnsureOrganizationIsNotReady>
|
||||
<div class="setup-page">
|
||||
<SetupLeftSection />
|
||||
<SetupRightSection />
|
||||
</div>
|
||||
</EnsureOrganizationIsNotReady>
|
||||
);
|
||||
};
|
||||
@@ -3,7 +3,7 @@ import classNames from 'classnames';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { registerWizardSteps } from 'common/registerWizard'
|
||||
|
||||
function RegisterWizardStep({
|
||||
function WizardSetupStep({
|
||||
label,
|
||||
isActive = false
|
||||
}) {
|
||||
@@ -14,15 +14,15 @@ function RegisterWizardStep({
|
||||
);
|
||||
}
|
||||
|
||||
function RegisterWizardSteps({
|
||||
function WizardSetupSteps({
|
||||
currentStep = 1,
|
||||
}) {
|
||||
return (
|
||||
<div className={'register-wizard-steps'}>
|
||||
<div className={'wizard-container'}>
|
||||
<ul className={'wizard-wrapper'}>
|
||||
<div className={'setup-page-steps-container'}>
|
||||
<div className={'setup-page-steps'}>
|
||||
<ul>
|
||||
{registerWizardSteps.map((step, index) => (
|
||||
<RegisterWizardStep
|
||||
<WizardSetupStep
|
||||
label={step.label}
|
||||
isActive={(index + 1) <= currentStep}
|
||||
/>
|
||||
@@ -33,4 +33,4 @@ function RegisterWizardSteps({
|
||||
);
|
||||
}
|
||||
|
||||
export default RegisterWizardSteps;
|
||||
export default WizardSetupSteps;
|
||||
Reference in New Issue
Block a user