feat: ensure to access dashboard user's subscription is active.

This commit is contained in:
a.bouhuolia
2021-08-30 10:42:39 +02:00
parent 47da64e625
commit 9dca9f3317
59 changed files with 1299 additions and 546 deletions

View File

@@ -1,13 +1,15 @@
import React, { useCallback } from 'react';
import { Button, Intent } from '@blueprintjs/core';
import { useHistory } from 'react-router-dom';
import WorkflowIcon from './WorkflowIcon';
import { FormattedMessage as T } from 'components';
import withOrganizationActions from 'containers/Organization/withOrganizationActions';
import { compose } from 'utils';
import 'style/pages/Setup/Congrats.scss';
import { compose } from 'utils';
/**
* Setup congrats page.
@@ -28,15 +30,15 @@ function SetupCongratsPage({ setOrganizationSetupCompleted }) {
<div class="setup-congrats__text">
<h1>
<T id={'congrats_you_are_ready_to_go'} />
<T id={'setup.congrats.title'} />
</h1>
<p class="paragraph">
<T id={'it_is_a_long_established_fact_that_a_reader'} />
<T id={'setup.congrats.description'} />
</p>
<Button intent={Intent.PRIMARY} type="submit" onClick={handleBtnClick}>
<T id={'go_to_dashboard'} />
<T id={'setup.congrats.go_to_dashboard'} />
</Button>
</div>
</div>

View File

@@ -15,7 +15,7 @@ export default function SetupInitializingForm() {
isError,
} = useBuildTenant();
useEffect(() => {
React.useEffect(() => {
buildTenantMutate();
}, [buildTenantMutate]);
@@ -27,32 +27,32 @@ export default function SetupInitializingForm() {
{isLoading ? (
<>
<h1>
<T id={'it_s_time_to_make_your_accounting_really_simple'} />
<T id={'setup.initializing.title'} />
</h1>
<p className={'paragraph'}>
<T
id={
'while_we_set_up_your_account_please_remember_to_verify_your_account'
}
/>
<T id={'setup.initializing.description'} />
</p>
</>
) : isError ? (
<>
<h1>
<T id={'something_went_wrong'} />
<T id={'setup.initializing.something_went_wrong'} />
</h1>
<p class="paragraph">
<T id={'please_refresh_the_page'} />
<T id={'setup.initializing.please_refresh_the_page'} />
</p>
</>
) : (
<>
<h1>
<T id={'waiting_to_redirect'} />
<T id={'setup.initializing.waiting_to_redirect'} />
</h1>
<p class="paragraph">
<T id={'refresh_the_page_if_redirect_not_worked'} />
<T
id={
'setup.initializing.refresh_the_page_if_redirect_not_worked'
}
/>
</p>
</>
)}

View File

@@ -3,11 +3,14 @@ import { Icon, For } from 'components';
import { FormattedMessage as T } from 'components';
import { getFooterLinks } from 'config/footerLinks';
import { useAuthActions, useAuthOrganizationId } from 'hooks/state';
import { useAuthActions } from 'hooks/state';
/**
* Footer item link.
*/
function FooterLinkItem({ title, link }) {
return (
<div class="">
<div class="content__links-item">
<a href={link} target="_blank">
{title}
</a>
@@ -16,20 +19,65 @@ function FooterLinkItem({ title, link }) {
}
/**
* Wizard setup left section.
* Setup left section footer.
*/
export default function SetupLeftSection() {
const { setLogout } = useAuthActions();
const organizationId = useAuthOrganizationId();
function SetupLeftSectionFooter() {
// Retrieve the footer links.
const footerLinks = getFooterLinks();
return (
<div className={'content__footer'}>
<div className={'content__contact-info'}>
<p>
<T id={'setup.left_side.footer_help'} />{' '}
<span>{'+21892-738-1987'}</span>
</p>
</div>
<div className={'content__links'}>
<For render={FooterLinkItem} of={footerLinks} />
</div>
</div>
);
}
/**
* Setup left section header.
*/
function SetupLeftSectionHeader() {
const { setLogout } = useAuthActions();
// Handle logout link click.
const onClickLogout = () => {
setLogout();
};
return (
<div className={'content__header'}>
<h1 className={'content__title'}>
<T id={'setup.left_side.title'} />
</h1>
<p className={'content__text'}>
<T id={'setup.left_side.description'} />
</p>
<div class="content__divider"></div>
<div className={'content__organization'}>
<span class="signout">
<a onClick={onClickLogout} href="#">
<T id={'sign_out'} />
</a>
</span>
</div>
</div>
);
}
/**
* Wizard setup left section.
*/
export default function SetupLeftSection() {
return (
<section className={'setup-page__left-section'}>
<div className={'content'}>
@@ -41,40 +89,8 @@ export default function SetupLeftSection() {
width={190}
/>
</div>
<h1 className={'content__title'}>
<T id={'register_a_new_organization_now'} />
</h1>
<p className={'content__text'}>
<T id={'you_have_a_bigcapital_account'} />
</p>
<span class="content__divider"></span>
<div className={'content__organization'}>
<span class="organization-id">
<T id={'organization_id'} />:{' '}
<span class="id">{organizationId}</span>,
</span>
<br />
<span class="signout">
<a onClick={onClickLogout} href="#">
<T id={'sign_out'} />
</a>
</span>
</div>
<div className={'content__footer'}>
<div className={'content__contact-info'}>
<p>
<T id={'we_re_here_to_help'} /> <span>{'+21892-738-1987'}</span>
</p>
</div>
<div className={'content__links'}>
<For render={FooterLinkItem} of={footerLinks} />
</div>
</div>
<SetupLeftSectionHeader />
<SetupLeftSectionFooter />
</div>
</section>
);

View File

@@ -0,0 +1,15 @@
import * as Yup from 'yup';
import intl from 'react-intl-universal';
// Retrieve the setup organization form validation.
export const getSetupOrganizationValidation = () =>
Yup.object().shape({
organization_name: Yup.string()
.required()
.label(intl.get('organization_name_')),
financialDateStart: Yup.date().required().label(intl.get('date_start_')),
baseCurrency: Yup.string().required().label(intl.get('base_currency_')),
language: Yup.string().required().label(intl.get('language')),
fiscalYear: Yup.string().required().label(intl.get('fiscal_year_')),
timeZone: Yup.string().required().label(intl.get('time_zone_')),
});

View File

@@ -14,20 +14,18 @@ import classNames from 'classnames';
import { TimezonePicker } from '@blueprintjs/timezone';
import { FormattedMessage as T } from 'components';
import { FieldRequiredHint, Col, Row, ListSelect } from 'components';
import { Col, Row, ListSelect } from 'components';
import {
momentFormatter,
tansformDateValue,
inputIntent,
handleDateChange
handleDateChange,
} from 'utils';
import { getFiscalYear } from 'common/fiscalYearOptions';
import { getLanguages } from 'common/languagesOptions';
import { getCurrencies } from 'common/currencies';
/**
* Setup organization form.
*/
@@ -46,22 +44,24 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
<FastField name={'organization_name'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
labelInfo={<FieldRequiredHint />}
label={<T id={'legal_organization_name'} />}
className={'form-group--name'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'organization_name'} />}
>
<InputGroup {...field} />
<InputGroup {...field} intent={inputIntent({ error, touched })} />
</FormGroup>
)}
</FastField>
{/* ---------- Financial starting date ---------- */}
<FastField name={'financialDateStart'}>
{({ form: { setFieldValue }, field: { value }, meta: { error, touched } }) => (
{({
form: { setFieldValue },
field: { value },
meta: { error, touched },
}) => (
<FormGroup
labelInfo={<FieldRequiredHint />}
label={<T id={'financial_starting_date'} />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="financialDateStart" />}
@@ -74,6 +74,7 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
onChange={handleDateChange((formattedDate) => {
setFieldValue('financialDateStart', formattedDate);
})}
intent={inputIntent({ error, touched })}
/>
</FormGroup>
)}
@@ -89,7 +90,6 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
meta: { error, touched },
}) => (
<FormGroup
labelInfo={<FieldRequiredHint />}
label={<T id={'base_currency'} />}
className={classNames(
'form-group--base-currency',
@@ -101,7 +101,9 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
>
<ListSelect
items={Currencies}
noResults={<MenuItem disabled={true} text={<T id={'no_results'} />} />}
noResults={
<MenuItem disabled={true} text={<T id={'no_results'} />} />
}
popoverProps={{ minimal: true }}
onItemSelect={(item) => {
setFieldValue('baseCurrency', item.code);
@@ -110,6 +112,7 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
textProp={'name'}
defaultText={<T id={'select_base_currency'} />}
selectedItem={value}
intent={inputIntent({ error, touched })}
/>
</FormGroup>
)}
@@ -136,7 +139,9 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
>
<ListSelect
items={Languages}
noResults={<MenuItem disabled={true} text={<T id={'no_results'} />} />}
noResults={
<MenuItem disabled={true} text={<T id={'no_results'} />} />
}
onItemSelect={(item) => {
setFieldValue('language', item.value);
}}
@@ -146,6 +151,7 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
defaultText={<T id={'select_language'} />}
popoverProps={{ minimal: true }}
filterable={false}
intent={inputIntent({ error, touched })}
/>
</FormGroup>
)}
@@ -154,9 +160,12 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
</Row>
{/* --------- Fiscal Year ----------- */}
<FastField name={'fiscalYear'}>
{({ form: { setFieldValue }, field: { value }, meta: { error, touched } }) => (
{({
form: { setFieldValue },
field: { value },
meta: { error, touched },
}) => (
<FormGroup
labelInfo={<FieldRequiredHint />}
label={<T id={'fiscal_year'} />}
className={classNames(
'form-group--fiscal_year',
@@ -168,14 +177,16 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
>
<ListSelect
items={FiscalYear}
noResults={<MenuItem disabled={true} text={<T id={'no_results'} />} />}
noResults={
<MenuItem disabled={true} text={<T id={'no_results'} />} />
}
selectedItem={value}
selectedItemProp={'value'}
textProp={'name'}
defaultText={<T id={'select_fiscal_year'} />}
popoverProps={{ minimal: true }}
onItemSelect={(item) => {
setFieldValue('fiscalYear', item.value)
setFieldValue('fiscalYear', item.value);
}}
filterable={false}
/>
@@ -191,7 +202,6 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
meta: { error, touched },
}) => (
<FormGroup
labelInfo={<FieldRequiredHint />}
label={<T id={'time_zone'} />}
className={classNames(
'form-group--time-zone',
@@ -216,20 +226,11 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
</FastField>
<p className={'register-org-note'}>
<T
id={
'note_you_can_change_your_preferences_later_in_dashboard_if_needed'
}
/>
<T id={'setup.organization.note_you_can_change_your_preferences'} />
</p>
<div className={'register-org-button'}>
<Button
intent={Intent.PRIMARY}
disabled={isSubmitting}
loading={isSubmitting}
type="submit"
>
<Button intent={Intent.PRIMARY} loading={isSubmitting} type="submit">
<T id={'save_continue'} />
</Button>
</div>

View File

@@ -1,9 +1,8 @@
import React from 'react';
import * as Yup from 'yup';
import { Formik } from 'formik';
import moment from 'moment';
import { FormattedMessage as T } from 'components';
import intl from 'react-intl-universal';
import 'style/pages/Setup/Organization.scss';
@@ -13,53 +12,30 @@ import { useOrganizationSetup } from 'hooks/query';
import withSettingsActions from 'containers/Settings/withSettingsActions';
import withOrganizationActions from 'containers/Organization/withOrganizationActions';
import {
compose,
transfromToSnakeCase,
} from 'utils';
import { compose, transfromToSnakeCase } from 'utils';
import { getSetupOrganizationValidation } from './SetupOrganization.schema';
// Initial values.
const defaultValues = {
organization_name: '',
financialDateStart: moment(new Date()).format('YYYY-MM-DD'),
baseCurrency: '',
language: 'en',
fiscalYear: '',
timeZone: '',
};
/**
* Setup organization form.
*/
function SetupOrganizationPage({
wizard,
setOrganizationSetupCompleted,
}) {
function SetupOrganizationPage({ wizard, setOrganizationSetupCompleted }) {
const { mutateAsync: organizationSetupMutate } = useOrganizationSetup();
// Validation schema.
const validationSchema = Yup.object().shape({
organization_name: Yup.string()
.required()
.label(intl.get('organization_name_')),
financialDateStart: Yup.date()
.required()
.label(intl.get('date_start_')),
baseCurrency: Yup.string()
.required()
.label(intl.get('base_currency_')),
language: Yup.string()
.required()
.label(intl.get('language')),
fiscalYear: Yup.string()
.required()
.label(intl.get('fiscal_year_')),
timeZone: Yup.string()
.required()
.label(intl.get('time_zone_')),
});
// Initial values.
const defaultValues = {
organization_name: '',
financialDateStart: moment(new Date()).format('YYYY-MM-DD'),
baseCurrency: '',
language: 'en',
fiscalYear: '',
timeZone: '',
};
const validationSchema = getSetupOrganizationValidation();
// Initialize values.
const initialValues = {
...defaultValues,
};
@@ -83,10 +59,10 @@ function SetupOrganizationPage({
<div className={'setup-organization'}>
<div className={'setup-organization__title-wrap'}>
<h1>
<T id={'let_s_get_started'} />
<T id={'setup.organization.title'} />
</h1>
<p class="paragraph">
<T id={'tell_the_system_a_little_bit_about_your_organization'} />
<T id={'setup.organization.description'} />
</p>
</div>

View File

@@ -4,7 +4,7 @@ import * as R from 'ramda';
import 'style/pages/Setup/Subscription.scss';
import SetupSubscriptionForm from './SetupSubscriptionForm';
import SetupSubscriptionForm from './SetupSubscription/SetupSubscriptionForm';
import { getSubscriptionFormSchema } from './SubscriptionForm.schema';
import withSubscriptionPlansActions from '../Subscriptions/withSubscriptionPlansActions';
@@ -13,13 +13,11 @@ import withSubscriptionPlansActions from '../Subscriptions/withSubscriptionPlans
*/
function SetupSubscription({
// #withSubscriptionPlansActions
initSubscriptionPlans
initSubscriptionPlans,
}) {
React.useEffect(() => {
initSubscriptionPlans();
}, [
initSubscriptionPlans
]);
}, [initSubscriptionPlans]);
// Initial values.
const initialValues = {
@@ -30,10 +28,14 @@ function SetupSubscription({
// Handle form submit.
const handleSubmit = () => {};
const SubscriptionFormSchema = getSubscriptionFormSchema();
// Retrieve momerized subscription form schema.
const SubscriptionFormSchema = React.useMemo(
() => getSubscriptionFormSchema(),
[],
);
return (
<div className={'setup-subscription-form'}>
<div className={'setup-subscription-form'}>
<Formik
validationSchema={SubscriptionFormSchema}
initialValues={initialValues}
@@ -44,6 +46,4 @@ function SetupSubscription({
);
}
export default R.compose(
withSubscriptionPlansActions,
)(SetupSubscription);
export default R.compose(withSubscriptionPlansActions)(SetupSubscription);

View File

@@ -0,0 +1,16 @@
import React from 'react';
import SubscriptionPlansSection from './SubscriptionPlansSection';
import SubscriptionPeriodsSection from './SubscriptionPeriodsSection';
import SubscriptionPaymentMethodsSection from './SubscriptionPaymentsMethodsSection';
export default function SetupSubscriptionForm() {
return (
<div class="billing-plans">
<SubscriptionPlansSection />
<SubscriptionPeriodsSection />
<SubscriptionPaymentMethodsSection />
</div>
);
}

View File

@@ -0,0 +1,19 @@
import React from 'react';
import { T } from 'components';
import { PaymentMethodTabs } from '../../Subscriptions/SubscriptionTabs';
export default ({ formik, title, description }) => {
return (
<section class="billing-plans__section">
<h1 className="title">
<T id={'setup.plans.payment_methods.title'} />
</h1>
<p className="paragraph">
<T id={'setup.plans.payment_methods.description'} />
</p>
<PaymentMethodTabs formik={formik} />
</section>
);
};

View File

@@ -0,0 +1,43 @@
import React from 'react';
import { Field } from 'formik';
import * as R from 'ramda';
import { T, SubscriptionPeriods } from 'components';
import withPlan from '../../Subscriptions/withPlan';
const SubscriptionPeriodsEnhanced = R.compose(
withPlan(({ plan }) => ({ plan })),
)(({ plan, ...restProps }) => {
return <SubscriptionPeriods periods={plan.periods} {...restProps} />;
});
/**
* Billing periods.
*/
export default function SubscriptionPeriodsSection() {
return (
<section class="billing-plans__section">
<h1 class="title">
<T id={'setup.plans.select_period.title'} />
</h1>
<div class="description">
<p className="paragraph">
<T id={'setup.plans.select_period.description'} />
</p>
</div>
<Field name={'period'}>
{({ form: { setFieldValue, values }, field: { value } }) => (
<SubscriptionPeriodsEnhanced
planSlug={values.plan_slug}
selectedPeriod={value}
onPeriodSelect={(period) => {
setFieldValue('period', period);
}}
/>
)}
</Field>
</section>
);
}

View File

@@ -0,0 +1,41 @@
import React from 'react';
import { Field } from 'formik';
import { T, SubscriptionPlans } from 'components';
import { compose } from 'utils';
import withPlans from '../../Subscriptions/withPlans';
/**
* Billing plans.
*/
function SubscriptionPlansSection({ plans }) {
return (
<section class="billing-plans__section">
<h1 class="title">
<T id={'setup.plans.select_plan.title'} />
</h1>
<div class="description">
<p className="paragraph">
<T id={'setup.plans.select_plan.description'} />
</p>
</div>
<Field name={'plan_slug'}>
{({ form: { setFieldValue }, field: { value } }) => (
<SubscriptionPlans
value={value}
plans={plans}
onSelect={(value) => {
setFieldValue('plan_slug', value);
}}
/>
)}
</Field>
</section>
);
}
export default compose(withPlans(({ plans }) => ({ plans })))(
SubscriptionPlansSection,
);

View File

@@ -1,15 +0,0 @@
import React from 'react';
import { Form } from 'formik';
import BillingPlansForm from 'containers/Subscriptions/BillingPlansForm';
/**
* Subscription step of wizard setup.
*/
export default function SetupSubscriptionForm() {
return (
<Form>
<BillingPlansForm />
</Form>
);
}

View File

@@ -5,9 +5,7 @@ import { getSetupWizardSteps } from 'common/registerWizard';
function WizardSetupStep({ label, isActive = false }) {
return (
<li className={classNames({ 'is-active': isActive })}>
<p className={'wizard-info'}>
{ label }
</p>
<p className={'wizard-info'}>{label}</p>
</li>
);
}