chrone: sperate client and server to different repos.

This commit is contained in:
a.bouhuolia
2021-09-21 17:13:53 +02:00
parent e011b2a82b
commit 18df5530c7
10015 changed files with 17686 additions and 97524 deletions

View File

@@ -0,0 +1,94 @@
import React, { useEffect } from 'react';
import { Formik, Form } from 'formik';
import intl from 'react-intl-universal';
import { If, Alert, T } from 'components';
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 withSubscriptionPlansActions from './withSubscriptionPlansActions';
import { compose } from 'utils';
import { getBillingFormValidationSchema } from './utils';
import withSubscriptions from './withSubscriptions';
/**
* Billing form.
*/
function BillingForm({
// #withDashboardActions
changePageTitle,
// #withBillingActions
requestSubmitBilling,
initSubscriptionPlans,
// #withSubscriptions
isSubscriptionInactive,
}) {
useEffect(() => {
changePageTitle(intl.get('billing'));
}, [changePageTitle]);
React.useEffect(() => {
initSubscriptionPlans();
}, [initSubscriptionPlans]);
// Initial values.
const initialValues = {
plan_slug: 'essentials',
period: 'month',
license_code: '',
};
// Handle form submitting.
const handleSubmit = (values, { setSubmitting }) => {
requestSubmitBilling(values)
.then((response) => {
setSubmitting(false);
})
.catch((errors) => {
setSubmitting(false);
});
};
return (
<DashboardInsider name={'billing-page'}>
<div className={'billing-page'}>
<If condition={isSubscriptionInactive}>
<Alert
intent={'danger'}
title={<T id={'billing.suspend_message.title'} />}
description={<T id={'billing.suspend_message.description'} />}
/>
</If>
<Formik
validationSchema={getBillingFormValidationSchema()}
onSubmit={handleSubmit}
initialValues={initialValues}
>
<Form>
<MasterBillingTabs />
</Form>
</Formik>
</div>
</DashboardInsider>
);
}
export default compose(
withDashboardActions,
withBillingActions,
withSubscriptionPlansActions,
withSubscriptions(
({ isSubscriptionInactive }) => ({ isSubscriptionInactive }),
'main',
),
)(BillingForm);

View File

@@ -0,0 +1,58 @@
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,
value,
selectedOption,
onSelected,
period,
// #withPlan
price,
currencyCode,
}) {
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);

View File

@@ -0,0 +1,48 @@
import React from 'react';
import { Field } from 'formik';
import * as R from 'ramda';
import { T, SubscriptionPeriods } from 'components';
import withPlan from './withPlan';
/**
* Sunscription periods enhanced.
*/
const SubscriptionPeriodsEnhanced = R.compose(
withPlan(({ plan }) => ({ plan })),
)(({ plan, ...restProps }) => {
if (!plan) return null;
return <SubscriptionPeriods periods={plan.periods} {...restProps} />;
});
/**
* Billing periods.
*/
export default function BillingPeriods() {
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'}>
{({ field: { value }, form: { values, setFieldValue } }) => (
<SubscriptionPeriodsEnhanced
selectedPeriod={value}
planSlug={values.plan_slug}
onPeriodSelect={(period) => {
setFieldValue('period', period);
}}
/>
)}
</Field>
</section>
);
}

View File

@@ -0,0 +1,54 @@
import React from 'react';
import classNames from 'classnames';
import { FormattedMessage as T } from 'components';
import intl from 'react-intl-universal';
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'}>{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>
);
}

View File

@@ -0,0 +1,20 @@
import React from 'react';
import 'style/pages/Subscription/BillingPlans.scss';
import BillingPlansInput from './BillingPlansInput';
import BillingPeriodsInput from './BillingPeriodsInput';
// import BillingPaymentMethod from './BillingPaymentMethod';
/**
* Billing plans form.
*/
export default function BillingPlansForm() {
return (
<div class="billing-plans">
<BillingPlansInput />
<BillingPeriodsInput />
{/* <BillingPaymentMethod /> */}
</div>
);
}

View File

@@ -0,0 +1,37 @@
import React from 'react';
import { Field } from 'formik';
import { T, SubscriptionPlans } from 'components';
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">
<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
plans={plans}
value={value}
onSelect={(value) => {
setFieldValue('plan_slug', value);
}}
/>
)}
</Field>
</section>
);
}
export default compose(withPlans(({ plans }) => ({ plans })))(BillingPlans);

View File

@@ -0,0 +1,6 @@
import React from 'react';
import BillingPlansForm from './BillingPlansForm';
export default function BillingTab() {
return (<BillingPlansForm />);
}

View File

@@ -0,0 +1,42 @@
import React from 'react';
import { FormattedMessage as T } from 'components';
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 });
});
};
return (
<div className={'license-container'}>
<h3>
<T id={'voucher'} />
</h3>
<p className="paragraph">
<T id={'cards_will_be_charged'} />
</p>
<Button
onClick={handleSubmitBtnClick}
intent={Intent.PRIMARY}
large={true}
>
<T id={'submit_voucher'} />
</Button>
</div>
);
}
export default compose(withDialogActions)(LicenseTab);

View File

@@ -0,0 +1,46 @@
import React from 'react';
import { Tabs, Tab } from '@blueprintjs/core';
import intl from 'react-intl-universal';
import BillingTab from './BillingTab';
import LicenseTab from './LicenseTab';
/**
* Master billing tabs.
*/
export const MasterBillingTabs = ({ formik }) => {
return (
<div>
<Tabs animate={true} large={true}>
<Tab
title={intl.get('billing')}
id={'billing'}
panel={<BillingTab formik={formik} />}
/>
<Tab title={intl.get('usage')} id={'usage'} disabled={true} />
</Tabs>
</div>
);
};
/**
* Payment methods tabs.
*/
export const PaymentMethodTabs = ({ formik }) => {
return (
<div>
<Tabs animate={true} large={true}>
<Tab
title={intl.get('voucher')}
id={'voucher'}
panel={<LicenseTab formik={formik} />}
/>
<Tab
title={intl.get('credit_card')}
id={'credit_card'}
disabled={true}
/>
<Tab title={intl.get('paypal')} id={'paypal'} disabled={true} />
</Tabs>
</div>
);
};

View File

@@ -0,0 +1,15 @@
import React from 'react';
import { T } from 'components';
import { PaymentMethodTabs } from './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,8 @@
import * as Yup from 'yup';
export const getBillingFormValidationSchema = () =>
Yup.object().shape({
plan_slug: Yup.string().required(),
period: Yup.string().required(),
license_code: Yup.string().trim(),
});

View File

@@ -0,0 +1,8 @@
import { connect } from 'react-redux';
import { submitBilling } from 'store/billing/Billing.action';
export const mapDispatchToProps = (dispatch) => ({
requestSubmitBilling: (form) => dispatch(submitBilling({ form })),
});
export default connect(null, mapDispatchToProps);

View 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);
};

View File

@@ -0,0 +1,16 @@
import { connect } from 'react-redux';
import {
getPlansSelector,
} from 'store/plans/plans.selectors';
export default (mapState) => {
const mapStateToProps = (state, props) => {
const getPlans = getPlansSelector();
const mapped = {
plans: getPlans(state, props),
};
return mapState ? mapState(mapped, state, props) : mapped;
};
return connect(mapStateToProps);
};

View File

@@ -0,0 +1,8 @@
import { connect } from 'react-redux';
import { initSubscriptionPlans } from 'store/plans/plans.actions';
export const mapDispatchToProps = (dispatch) => ({
initSubscriptionPlans: () => dispatch(initSubscriptionPlans()),
});
export default connect(null, mapDispatchToProps);

View File

@@ -0,0 +1,22 @@
import { connect } from 'react-redux';
import {
isSubscriptionOnTrialFactory,
isSubscriptionInactiveFactory,
isSubscriptionActiveFactory,
} from 'store/subscription/subscription.selectors';
export default (mapState, slug) => {
const isSubscriptionOnTrial = isSubscriptionOnTrialFactory(slug);
const isSubscriptionInactive = isSubscriptionInactiveFactory(slug);
const isSubscriptionActive = isSubscriptionActiveFactory(slug);
const mapStateToProps = (state, props) => {
const mapped = {
isSubscriptionOnTrial: isSubscriptionOnTrial(state, props),
isSubscriptionInactive: isSubscriptionInactive(state, props),
isSubscriptionActive: isSubscriptionActive(state, props),
};
return (mapState) ? mapState(mapped, state, props) : mapped;
};
return connect(mapStateToProps);
};

View File

@@ -0,0 +1,10 @@
import { connect } from 'react-redux';
import {
fetchSubscriptions,
} from 'store/subscription/subscription.actions'
const mapDispatchToProps = (dispatch) => ({
requestFetchSubscriptions: () => dispatch(fetchSubscriptions()),
});
export default connect(null, mapDispatchToProps);

View File

@@ -0,0 +1,19 @@
import { connect } from 'react-redux';
import {
isSubscriptionsInactiveFactory,
isSubscriptionsActiveFactory
} from 'store/subscription/subscription.selectors';
export default (mapState) => {
const isSubscriptionsInactive = isSubscriptionsInactiveFactory();
const isSubscriptionsActive = isSubscriptionsActiveFactory();
const mapStateToProps = (state, props) => {
const mapped = {
isSubscriptionsInactive: isSubscriptionsInactive(state, props),
isSubscriptionsActive: isSubscriptionsActive(state, props),
};
return (mapState) ? mapState(mapped, state, props) : mapped;
};
return connect(mapStateToProps);
};