mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 21:30:31 +00:00
feat: billing page in dashboard and setup.
This commit is contained in:
@@ -1,13 +1,21 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useFormik } from 'formik';
|
||||
import { Button, Intent } from '@blueprintjs/core';
|
||||
import { Formik, Form } from 'formik';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import { MeteredBillingTabs, PaymentMethodTabs } from './SubscriptionTabs';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
|
||||
import 'style/pages/Billing/BillingPage.scss';
|
||||
|
||||
import { MasterBillingTabs } from './SubscriptionTabs';
|
||||
|
||||
import withBillingActions from './withBillingActions';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Billing form.
|
||||
*/
|
||||
function BillingForm({
|
||||
// #withDashboardActions
|
||||
changePageTitle,
|
||||
@@ -23,51 +31,43 @@ function BillingForm({
|
||||
|
||||
const validationSchema = Yup.object().shape({
|
||||
plan_slug: Yup.string()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'plan_slug' })),
|
||||
.required(),
|
||||
period: Yup.string().required(),
|
||||
license_code: Yup.string().trim(),
|
||||
});
|
||||
|
||||
const initialValues = useMemo(
|
||||
() => ({
|
||||
plan_slug: 'free',
|
||||
license_code: '',
|
||||
}),
|
||||
[],
|
||||
);
|
||||
const initialValues = {
|
||||
plan_slug: 'free',
|
||||
period: 'month',
|
||||
license_code: '',
|
||||
};
|
||||
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
validationSchema: validationSchema,
|
||||
initialValues: {
|
||||
...initialValues,
|
||||
},
|
||||
onSubmit: (values, { setSubmitting, resetForm, setErrors }) => {
|
||||
requestSubmitBilling(values)
|
||||
.then((response) => {
|
||||
setSubmitting(false);
|
||||
})
|
||||
.catch((errors) => {
|
||||
setSubmitting(false);
|
||||
});
|
||||
},
|
||||
});
|
||||
const handleSubmit = (values, { setSubmitting }) => {
|
||||
requestSubmitBilling(values)
|
||||
.then((response) => {
|
||||
setSubmitting(false);
|
||||
})
|
||||
.catch((errors) => {
|
||||
setSubmitting(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={'billing-form'}>
|
||||
<form onSubmit={formik.handleSubmit}>
|
||||
<MeteredBillingTabs formik={formik} planId={formik.values.plan_slug} />
|
||||
<div className={'subscribe-button'}>
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
type="submit"
|
||||
loading={formik.isSubmitting}
|
||||
>
|
||||
<T id={'subscribe'} />
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<DashboardInsider name={'billing-page'}>
|
||||
<div className={'billing-page'}>
|
||||
<Formik
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
initialValues={initialValues}
|
||||
>
|
||||
{({ isSubmitting, handleSubmit }) => (
|
||||
<Form>
|
||||
<MasterBillingTabs />
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</div>
|
||||
</DashboardInsider>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
56
client/src/containers/Subscriptions/BillingPeriod.js
Normal file
56
client/src/containers/Subscriptions/BillingPeriod.js
Normal file
@@ -0,0 +1,56 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { get } from 'lodash';
|
||||
import { compose } from 'redux';
|
||||
|
||||
import 'style/pages/Subscription/PlanPeriodRadio.scss';
|
||||
|
||||
import withPlan from 'containers/Subscriptions/withPlan';
|
||||
|
||||
import { saveInvoke } from 'utils';
|
||||
|
||||
/**
|
||||
* Billing period.
|
||||
*/
|
||||
function BillingPeriod({
|
||||
// #ownProps
|
||||
label,
|
||||
currencyCode,
|
||||
value,
|
||||
selectedOption,
|
||||
onSelected,
|
||||
period,
|
||||
price,
|
||||
}) {
|
||||
const handlePeriodClick = () => {
|
||||
saveInvoke(onSelected, value);
|
||||
};
|
||||
return (
|
||||
<div
|
||||
id={`plan-period-${period}`}
|
||||
className={classNames(
|
||||
{
|
||||
'is-selected': value === selectedOption,
|
||||
},
|
||||
'period-radio',
|
||||
)}
|
||||
onClick={handlePeriodClick}
|
||||
>
|
||||
<span className={'period-radio__label'}>{label}</span>
|
||||
|
||||
<div className={'period-radio__price'}>
|
||||
<span className={'period-radio__amount'}>
|
||||
{price} {currencyCode}
|
||||
</span>
|
||||
<span className={'period-radio__period'}>{label}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withPlan(({ plan }, state, { period }) => ({
|
||||
price: get(plan, `price.${period}`),
|
||||
currencyCode: get(plan, 'currencyCode'),
|
||||
})),
|
||||
)(BillingPeriod);
|
||||
42
client/src/containers/Subscriptions/BillingPeriodsInput.js
Normal file
42
client/src/containers/Subscriptions/BillingPeriodsInput.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import { Field } from 'formik';
|
||||
import BillingPeriod from './BillingPeriod';
|
||||
|
||||
import withPlans from './withPlans';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Billing periods.
|
||||
*/
|
||||
function BillingPeriods({ title, description, plansPeriods }) {
|
||||
return (
|
||||
<section class="billing-plans__section">
|
||||
<h1 class="title">{title}</h1>
|
||||
<div class="description">
|
||||
<p className="paragraph">{description}</p>
|
||||
</div>
|
||||
|
||||
<Field name={'period'}>
|
||||
{({ form: { setFieldValue, values } }) => (
|
||||
<div className={'plan-periods'}>
|
||||
{plansPeriods.map((period) => (
|
||||
<BillingPeriod
|
||||
planSlug={values.plan_slug}
|
||||
period={period.slug}
|
||||
label={period.label}
|
||||
value={period.slug}
|
||||
onSelected={(value) => setFieldValue('period', value)}
|
||||
selectedOption={values.period}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</Field>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withPlans(({ plansPeriods }) => ({ plansPeriods })))(
|
||||
BillingPeriods,
|
||||
);
|
||||
57
client/src/containers/Subscriptions/BillingPlan.js
Normal file
57
client/src/containers/Subscriptions/BillingPlan.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
|
||||
import 'style/pages/Subscription/PlanRadio.scss';
|
||||
|
||||
import { saveInvoke } from 'utils';
|
||||
|
||||
/**
|
||||
* Billing plan.
|
||||
*/
|
||||
export default function BillingPlan({
|
||||
name,
|
||||
description,
|
||||
price,
|
||||
currencyCode,
|
||||
|
||||
value,
|
||||
selectedOption,
|
||||
onSelected,
|
||||
}) {
|
||||
const handlePlanClick = () => {
|
||||
saveInvoke(onSelected, value);
|
||||
};
|
||||
return (
|
||||
<div
|
||||
id={'basic-plan'}
|
||||
className={classNames('plan-radio', {
|
||||
'is-selected': selectedOption === value,
|
||||
})}
|
||||
onClick={handlePlanClick}
|
||||
>
|
||||
<div className={'plan-radio__header'}>
|
||||
<div className={'plan-radio__name'}>
|
||||
<T id={name} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={'plan-radio__description'}>
|
||||
<ul>
|
||||
{description.map((line) => (
|
||||
<li>{line}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className={'plan-radio__price'}>
|
||||
<span className={'plan-radio__amount'}>
|
||||
{price} {currencyCode}
|
||||
</span>
|
||||
<span className={'plan-radio__period'}>
|
||||
<T id={'monthly'} />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
30
client/src/containers/Subscriptions/BillingPlansForm.js
Normal file
30
client/src/containers/Subscriptions/BillingPlansForm.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
|
||||
import 'style/pages/Subscription/BillingPlans.scss'
|
||||
|
||||
import BillingPlansInput from 'containers/Subscriptions/BillingPlansInput';
|
||||
import BillingPeriodsInput from 'containers/Subscriptions/BillingPeriodsInput';
|
||||
import BillingPaymentMethod from 'containers/Subscriptions/BillingPaymentMethod';
|
||||
|
||||
/**
|
||||
* Billing plans form.
|
||||
*/
|
||||
export default function BillingPlansForm() {
|
||||
return (
|
||||
<div class="billing-plans">
|
||||
<BillingPlansInput
|
||||
title={<T id={'select_a_plan'} values={{ order: 1 }} />}
|
||||
description={<T id={'please_enter_your_preferred_payment_method'} />}
|
||||
/>
|
||||
<BillingPeriodsInput
|
||||
title={<T id={'choose_your_billing'} values={{ order: 2 }} />}
|
||||
description={<T id={'please_enter_your_preferred_payment_method'} />}
|
||||
/>
|
||||
<BillingPaymentMethod
|
||||
title={<T id={'payment_methods'} values={{ order: 3 }} />}
|
||||
description={<T id={'please_enter_your_preferred_payment_method'} />}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
40
client/src/containers/Subscriptions/BillingPlansInput.js
Normal file
40
client/src/containers/Subscriptions/BillingPlansInput.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import { FastField } from 'formik';
|
||||
import BillingPlan from './BillingPlan';
|
||||
|
||||
import withPlans from './withPlans';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Billing plans.
|
||||
*/
|
||||
function BillingPlans({ plans, title, description, selectedOption }) {
|
||||
return (
|
||||
<section class="billing-plans__section">
|
||||
<h1 class="title">{title}</h1>
|
||||
<div class="description">
|
||||
<p className="paragraph">{description}</p>
|
||||
</div>
|
||||
|
||||
<FastField name={'plan_slug'}>
|
||||
{({ form: { setFieldValue }, field: { value } }) => (
|
||||
<div className={'plan-radios'}>
|
||||
{plans.map((plan) => (
|
||||
<BillingPlan
|
||||
name={plan.name}
|
||||
description={plan.description}
|
||||
slug={plan.slug}
|
||||
price={plan.price.month}
|
||||
currencyCode={plan.currencyCode}
|
||||
value={plan.slug}
|
||||
onSelected={(value) => setFieldValue('plan_slug', value)}
|
||||
selectedOption={value}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</FastField>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
export default compose(withPlans(({ plans }) => ({ plans })))(BillingPlans);
|
||||
@@ -1,16 +1,10 @@
|
||||
import React from 'react';
|
||||
import BillingPlans from 'containers/Subscriptions/billingPlans';
|
||||
import BillingPeriods from 'containers/Subscriptions/billingPeriods';
|
||||
import { BillingPaymentmethod } from 'containers/Subscriptions/billingPaymentmethod';
|
||||
import BillingPlansForm from 'containers/Subscriptions/BillingPlansForm';
|
||||
|
||||
function BillingTab({ formik }) {
|
||||
export default function BillingTab() {
|
||||
return (
|
||||
<div>
|
||||
<BillingPlans title={'a_select_a_plan'} formik={formik} />
|
||||
<BillingPeriods title={'b_choose_your_billing'} formik={formik} />
|
||||
<BillingPaymentmethod title={'c_payment_methods'} formik={formik} />
|
||||
<BillingPlansForm />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default BillingTab;
|
||||
}
|
||||
@@ -1,35 +1,38 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import { InputGroup, FormGroup, Intent } from '@blueprintjs/core';
|
||||
import ErrorMessage from 'components/ErrorMessage';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
|
||||
import { Intent, Button } from '@blueprintjs/core';
|
||||
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import { compose } from 'redux';
|
||||
import { useFormikContext } from 'formik';
|
||||
|
||||
/**
|
||||
* Payment via license code tab.
|
||||
*/
|
||||
function LicenseTab({ openDialog }) {
|
||||
const { submitForm, values } = useFormikContext();
|
||||
|
||||
const handleSubmitBtnClick = () => {
|
||||
submitForm().then(() => {
|
||||
openDialog('payment-via-voucher', { ...values });
|
||||
});
|
||||
};
|
||||
|
||||
function LicenseTab({
|
||||
formik: { errors, touched, setFieldValue, getFieldProps },
|
||||
}) {
|
||||
return (
|
||||
<div className={'license-container'}>
|
||||
<h4>
|
||||
<T id={'license_code'} />
|
||||
</h4>
|
||||
<h3>
|
||||
<T id={'voucher'} />
|
||||
</h3>
|
||||
<p className="paragraph">
|
||||
<T id={'cards_will_be_charged'} />
|
||||
</p>
|
||||
|
||||
<FormGroup
|
||||
label={<T id={'license_number'} />}
|
||||
intent={errors.license_code && touched.license_code && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name="license_code" {...{ errors, touched }} />
|
||||
}
|
||||
className={'form-group-license_code'}
|
||||
>
|
||||
<InputGroup
|
||||
intent={errors.license_code && touched.license_code && Intent.DANGER}
|
||||
{...getFieldProps('license_code')}
|
||||
/>
|
||||
</FormGroup>
|
||||
<Button onClick={handleSubmitBtnClick} intent={Intent.PRIMARY} large={true}>
|
||||
Submit Voucher
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default LicenseTab;
|
||||
export default compose(withDialogActions)(LicenseTab);
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
import { Tabs, Tab } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import { useIntl } from 'react-intl';
|
||||
import BillingTab from './BillingTab';
|
||||
import LicenseTab from './LicenseTab';
|
||||
|
||||
export const MeteredBillingTabs = ({ formik }) => {
|
||||
const [animate, setAnimate] = useState(true);
|
||||
/**
|
||||
* Master billing tabs.
|
||||
*/
|
||||
export const MasterBillingTabs = ({ formik }) => {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Tabs animate={animate} large={true}>
|
||||
<Tabs animate={true} large={true}>
|
||||
<Tab
|
||||
title={formatMessage({ id: 'billing' })}
|
||||
id={'billing'}
|
||||
@@ -20,23 +22,24 @@ export const MeteredBillingTabs = ({ formik }) => {
|
||||
title={formatMessage({ id: 'usage' })}
|
||||
id={'usage'}
|
||||
disabled={true}
|
||||
// panel={'Usage'}
|
||||
/>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Payment methods tabs.
|
||||
*/
|
||||
export const PaymentMethodTabs = ({ formik }) => {
|
||||
const [animate, setAnimate] = useState(true);
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Tabs animate={animate} large={true}>
|
||||
<Tabs animate={true} large={true}>
|
||||
<Tab
|
||||
title={formatMessage({ id: 'license' })}
|
||||
id={'license'}
|
||||
title={formatMessage({ id: 'voucher' })}
|
||||
id={'voucher'}
|
||||
panel={<LicenseTab formik={formik} />}
|
||||
/>
|
||||
<Tab
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import React from 'react';
|
||||
import { PaymentMethodTabs } from './SubscriptionTabs';
|
||||
|
||||
export const BillingPaymentmethod = ({ formik, title }) => {
|
||||
export default ({ formik, title, description }) => {
|
||||
return (
|
||||
<section class="billing-section">
|
||||
<h1 className={'bg-title'}>
|
||||
<T id={title} />
|
||||
</h1>
|
||||
<p className='paragraph'>
|
||||
<T id={'please_enter_your_preferred_payment_method'} />
|
||||
</p>
|
||||
<section class="billing-plans__section">
|
||||
<h1 className="title">{ title }</h1>
|
||||
<p className="paragraph">{ description }</p>
|
||||
|
||||
<PaymentMethodTabs formik={formik} />
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { paymentmethod } from 'common/subscriptionModels';
|
||||
|
||||
function BillingPeriod({ price, period, currency, onSelected, selected }) {
|
||||
return (
|
||||
<a
|
||||
href={'#!'}
|
||||
id={'monthly'}
|
||||
className={`period-container ${classNames({
|
||||
'billing-selected': selected,
|
||||
})} `}
|
||||
>
|
||||
<span className={'bg-period'}>
|
||||
<T id={period} />
|
||||
</span>
|
||||
|
||||
<div className={'plan-price'}>
|
||||
<span className={'amount'}>
|
||||
{price} {currency}
|
||||
</span>
|
||||
<span className={'period'}>
|
||||
<T id={'year'} />
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
function BillingPeriods({ formik, title, selected = 1 }) {
|
||||
const billingRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const billingPriod = billingRef.current.querySelectorAll('a');
|
||||
const billingSelected = billingRef.current.querySelector(
|
||||
'.billing-selected',
|
||||
);
|
||||
billingPriod.forEach((el) => {
|
||||
el.addEventListener('click', () => {
|
||||
billingSelected.classList.remove('billing-selected');
|
||||
el.classList.add('billing-selected');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<section class="billing-section">
|
||||
<h1>
|
||||
<T id={title} />
|
||||
</h1>
|
||||
<p className='paragraph'>
|
||||
<T id={'please_enter_your_preferred_payment_method'} />
|
||||
</p>
|
||||
<div className={'payment-method-continer'} ref={billingRef}>
|
||||
{paymentmethod.map((pay, index) => (
|
||||
<BillingPeriod
|
||||
period={pay.period}
|
||||
price={pay.price}
|
||||
currency={pay.currency}
|
||||
onSelected={()=>formik.setFieldValue('period', pay.period)}
|
||||
selected={selected == index + 1}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default BillingPeriods;
|
||||
@@ -1,89 +0,0 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { plans } from 'common/subscriptionModels';
|
||||
|
||||
function BillingPlan({
|
||||
name,
|
||||
description,
|
||||
price,
|
||||
slug,
|
||||
currency,
|
||||
onSelected,
|
||||
selected,
|
||||
}) {
|
||||
return (
|
||||
<a
|
||||
id={'basic-plan'}
|
||||
className={`plan-wrapper ${classNames({
|
||||
'plan-selected': selected,
|
||||
})} `}
|
||||
onClick={() => onSelected(slug)}
|
||||
>
|
||||
<div className={'plan-header'}>
|
||||
<div className={'plan-name'}>
|
||||
<T id={name} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={'plan-description'}>
|
||||
<ul>
|
||||
{description.map((desc, index) => (
|
||||
<li>{desc}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className={'plan-price'}>
|
||||
<span className={'amount'}>
|
||||
{' '}
|
||||
{price} {currency}
|
||||
</span>
|
||||
<span className={'period'}>
|
||||
<T id={'year_per'} />
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
function BillingPlans({ formik, title, selected = 1 }) {
|
||||
const planRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const plans = planRef.current.querySelectorAll('a');
|
||||
const planSelected = planRef.current.querySelector('.plan-selected');
|
||||
|
||||
plans.forEach((el) => {
|
||||
el.addEventListener('click', () => {
|
||||
planSelected.classList.remove('plan-selected');
|
||||
el.classList.add('plan-selected');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<section class="billing-section">
|
||||
<h1>
|
||||
<T id={title} />
|
||||
</h1>
|
||||
<p className='paragraph'>
|
||||
<T id={'please_enter_your_preferred_payment_method'} />
|
||||
</p>
|
||||
<div className={'billing-form__plan-container'} ref={planRef}>
|
||||
{plans.map((plan, index) => (
|
||||
<BillingPlan
|
||||
name={plan.name}
|
||||
description={plan.description}
|
||||
slug={plan.slug}
|
||||
price={plan.price}
|
||||
currency={plan.currency}
|
||||
onSelected={() => formik.setFieldValue('plan_slug', plan.slug)}
|
||||
selected={selected == index + 1}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default BillingPlans;
|
||||
|
||||
17
client/src/containers/Subscriptions/withPlan.js
Normal file
17
client/src/containers/Subscriptions/withPlan.js
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { getPlanSelector } from 'store/plans/plans.selectors';
|
||||
|
||||
export default (mapState) => {
|
||||
const mapStateToProps = (state, props) => {
|
||||
const getPlan = getPlanSelector();
|
||||
|
||||
const mapped = {
|
||||
plan: getPlan(state, props),
|
||||
};
|
||||
return (mapState) ? mapState(mapped, state, props) : mapped;
|
||||
};
|
||||
return connect(mapStateToProps);
|
||||
};
|
||||
19
client/src/containers/Subscriptions/withPlans.js
Normal file
19
client/src/containers/Subscriptions/withPlans.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
getPlansSelector,
|
||||
getPlansPeriodsSelector,
|
||||
} from 'store/plans/plans.selectors';
|
||||
|
||||
export default (mapState) => {
|
||||
const mapStateToProps = (state, props) => {
|
||||
const getPlans = getPlansSelector();
|
||||
const getPlansPeriods = getPlansPeriodsSelector();
|
||||
|
||||
const mapped = {
|
||||
plans: getPlans(state, props),
|
||||
plansPeriods: getPlansPeriods(state, props),
|
||||
};
|
||||
return mapState ? mapState(mapped, state, props) : mapped;
|
||||
};
|
||||
return connect(mapStateToProps);
|
||||
};
|
||||
Reference in New Issue
Block a user