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

@@ -2,15 +2,15 @@ import intl from 'react-intl-universal';
export const getSetupWizardSteps = () => [ export const getSetupWizardSteps = () => [
{ {
label: intl.get('Plans & Payment'), label: intl.get('setup.plan.plans'),
}, },
{ {
label: intl.get('Initializing'), label: intl.get('setup.plan.initializing'),
}, },
{ {
label: intl.get('Getting started'), label: intl.get('setup.plan.getting_started'),
}, },
{ {
label: intl.get('Congratulations'), label: intl.get('setup.plan.congrats'),
}, },
]; ];

View File

@@ -0,0 +1,17 @@
import React from 'react';
import clsx from 'classnames';
import Style from './style.module.scss';
export function Alert({ title, description, intent }) {
return (
<div
className={clsx(Style.root, {
[`${Style['root_' + intent]}`]: intent,
})}
>
{title && <h3 className={clsx(Style.title)}>{title}</h3>}
{description && <p class={clsx(Style.description)}>{description}</p>}
</div>
);
}

View File

@@ -0,0 +1,32 @@
.root {
border: 1px solid rgb(223, 227, 230);
padding: 12px;
border-radius: 6px;
margin-bottom: 20px;
&_danger {
border-color: rgb(249, 198, 198);
background: rgb(255, 248, 248);
.description {
color: #d95759;
}
.title {
color: rgb(205, 43, 49);
}
}
}
.title {
color: rgb(17, 24, 28);
margin-bottom: 4px;
font-size: 14px;
font-weight: 600;
}
.description {
color: rgb(104, 112, 118);
margin: 0;
}

View File

@@ -12,6 +12,33 @@ import DashboardSplitPane from 'components/Dashboard/DashboardSplitePane';
import GlobalHotkeys from './GlobalHotkeys'; import GlobalHotkeys from './GlobalHotkeys';
import DashboardProvider from './DashboardProvider'; import DashboardProvider from './DashboardProvider';
import DrawersContainer from 'components/DrawersContainer'; import DrawersContainer from 'components/DrawersContainer';
import EnsureSubscriptionIsActive from '../Guards/EnsureSubscriptionIsActive';
/**
* Dashboard preferences.
*/
function DashboardPreferences() {
return (
<EnsureSubscriptionIsActive>
<DashboardSplitPane>
<Sidebar />
<PreferencesPage />
</DashboardSplitPane>
</EnsureSubscriptionIsActive>
);
}
/**
* Dashboard other routes.
*/
function DashboardAnyPage() {
return (
<DashboardSplitPane>
<Sidebar />
<DashboardContent />
</DashboardSplitPane>
);
}
/** /**
* Dashboard page. * Dashboard page.
@@ -20,19 +47,8 @@ export default function Dashboard() {
return ( return (
<DashboardProvider> <DashboardProvider>
<Switch> <Switch>
<Route path="/preferences"> <Route path="/preferences" component={DashboardPreferences} />
<DashboardSplitPane> <Route path="/" component={DashboardAnyPage} />
<Sidebar />
<PreferencesPage />
</DashboardSplitPane>
</Route>
<Route path="/">
<DashboardSplitPane>
<Sidebar />
<DashboardContent />
</DashboardSplitPane>
</Route>
</Switch> </Switch>
<DashboardUniversalSearch /> <DashboardUniversalSearch />

View File

@@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { ErrorBoundary } from 'react-error-boundary'; import { ErrorBoundary } from 'react-error-boundary';
import DashboardTopbar from 'components/Dashboard/DashboardTopbar'; import DashboardTopbar from 'components/Dashboard/DashboardTopbar';
import DashboardContentRoute from 'components/Dashboard/DashboardContentRoute'; import DashboardContentRoutes from 'components/Dashboard/DashboardContentRoute';
import DashboardFooter from 'components/Dashboard/DashboardFooter'; import DashboardFooter from 'components/Dashboard/DashboardFooter';
import DashboardErrorBoundary from './DashboardErrorBoundary'; import DashboardErrorBoundary from './DashboardErrorBoundary';
@@ -10,7 +10,7 @@ export default React.forwardRef(({}, ref) => {
<ErrorBoundary FallbackComponent={DashboardErrorBoundary}> <ErrorBoundary FallbackComponent={DashboardErrorBoundary}>
<div className="dashboard-content" id="dashboard" ref={ref}> <div className="dashboard-content" id="dashboard" ref={ref}>
<DashboardTopbar /> <DashboardTopbar />
<DashboardContentRoute /> <DashboardContentRoutes />
<DashboardFooter /> <DashboardFooter />
</div> </div>
</ErrorBoundary> </ErrorBoundary>

View File

@@ -2,8 +2,43 @@ import React from 'react';
import { Route, Switch } from 'react-router-dom'; import { Route, Switch } from 'react-router-dom';
import { getDashboardRoutes } from 'routes/dashboard'; import { getDashboardRoutes } from 'routes/dashboard';
import EnsureSubscriptionsIsActive from '../Guards/EnsureSubscriptionsIsActive';
import EnsureSubscriptionsIsInactive from '../Guards/EnsureSubscriptionsIsInactive';
import DashboardPage from './DashboardPage'; import DashboardPage from './DashboardPage';
/**
* Dashboard inner route content.
*/
function DashboardContentRouteContent({ route }) {
const content = (
<DashboardPage
name={route.name}
Component={route.component}
pageTitle={route.pageTitle}
backLink={route.backLink}
hint={route.hint}
sidebarExpand={route.sidebarExpand}
pageType={route.pageType}
defaultSearchResource={route.defaultSearchResource}
/>
);
return route.subscriptionActive ? (
<EnsureSubscriptionsIsInactive
subscriptionTypes={route.subscriptionActive}
children={content}
redirectTo={'/billing'}
/>
) : route.subscriptionInactive ? (
<EnsureSubscriptionsIsActive
subscriptionTypes={route.subscriptionInactive}
children={content}
redirectTo={'/'}
/>
) : (
content
);
}
/** /**
* Dashboard content route. * Dashboard content route.
*/ */
@@ -14,21 +49,8 @@ export default function DashboardContentRoute() {
<Route pathname="/"> <Route pathname="/">
<Switch> <Switch>
{routes.map((route, index) => ( {routes.map((route, index) => (
<Route <Route exact={route.exact} key={index} path={`${route.path}`}>
exact={route.exact} <DashboardContentRouteContent route={route} />
key={index}
path={`${route.path}`}
>
<DashboardPage
name={route.name}
Component={route.component}
pageTitle={route.pageTitle}
backLink={route.backLink}
hint={route.hint}
sidebarExpand={route.sidebarExpand}
pageType={route.pageType}
defaultSearchResource={route.defaultSearchResource}
/>
</Route> </Route>
))} ))}
</Switch> </Switch>

View File

@@ -1,15 +1,12 @@
import React from 'react'; import React from 'react';
import DashboardLoadingIndicator from './DashboardLoadingIndicator'; import DashboardLoadingIndicator from './DashboardLoadingIndicator';
import { useSettings } from 'hooks/query';
/** /**
* Dashboard provider. * Dashboard provider.
*/ */
export default function DashboardProvider({ children }) { export default function DashboardProvider({ children }) {
const { isLoading } = useSettings();
return ( return (
<DashboardLoadingIndicator isLoading={isLoading}> <DashboardLoadingIndicator isLoading={false}>
{ children } { children }
</DashboardLoadingIndicator> </DashboardLoadingIndicator>
) )

View File

@@ -23,6 +23,43 @@ import withSettings from 'containers/Settings/withSettings';
import QuickNewDropdown from 'containers/QuickNewDropdown/QuickNewDropdown'; import QuickNewDropdown from 'containers/QuickNewDropdown/QuickNewDropdown';
import { compose } from 'utils'; import { compose } from 'utils';
import withSubscriptions from '../../containers/Subscriptions/withSubscriptions';
function DashboardTopbarSubscriptionMessage() {
return (
<div class="dashboard__topbar-subscription-msg">
<span>
<T id={'dashboard.subscription_msg.period_over'} />
</span>
</div>
);
}
function DashboardHamburgerButton({ ...props }) {
return (
<Button minimal={true} {...props}>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
role="img"
focusable="false"
>
<title>
<T id={'menu'} />
</title>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-miterlimit="5"
stroke-width="2"
d="M4 7h15M4 12h15M4 17h15"
></path>
</svg>
</Button>
);
}
/** /**
* Dashboard topbar. * Dashboard topbar.
@@ -44,6 +81,10 @@ function DashboardTopbar({
// #withGlobalSearch // #withGlobalSearch
openGlobalSearch, openGlobalSearch,
// #withSubscriptions
isSubscriptionActive,
isSubscriptionInactive,
}) { }) {
const history = useHistory(); const history = useHistory();
@@ -69,27 +110,7 @@ function DashboardTopbar({
} }
position={Position.RIGHT} position={Position.RIGHT}
> >
<Button minimal={true} onClick={handleSidebarToggleBtn}> <DashboardHamburgerButton onClick={handleSidebarToggleBtn} />
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
role="img"
focusable="false"
>
<title>
<T id={'menu'} />
</title>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-miterlimit="5"
stroke-width="2"
d="M4 7h15M4 12h15M4 17h15"
></path>
</svg>
</Button>
</Tooltip> </Tooltip>
</div> </div>
@@ -114,13 +135,17 @@ function DashboardTopbar({
<div class="dashboard__breadcrumbs"> <div class="dashboard__breadcrumbs">
<DashboardBreadcrumbs /> <DashboardBreadcrumbs />
</div> </div>
<DashboardBackLink /> <DashboardBackLink />
</div> </div>
<div class="dashboard__topbar-right"> <div class="dashboard__topbar-right">
<If condition={isSubscriptionInactive}>
<DashboardTopbarSubscriptionMessage />
</If>
<Navbar class="dashboard__topbar-navbar"> <Navbar class="dashboard__topbar-navbar">
<NavbarGroup> <NavbarGroup>
<If condition={isSubscriptionActive}>
<Button <Button
onClick={() => openGlobalSearch(true)} onClick={() => openGlobalSearch(true)}
className={Classes.MINIMAL} className={Classes.MINIMAL}
@@ -128,6 +153,7 @@ function DashboardTopbar({
text={<T id={'quick_find'} />} text={<T id={'quick_find'} />}
/> />
<QuickNewDropdown /> <QuickNewDropdown />
<Tooltip <Tooltip
content={<T id={'notifications'} />} content={<T id={'notifications'} />}
position={Position.BOTTOM} position={Position.BOTTOM}
@@ -137,6 +163,8 @@ function DashboardTopbar({
icon={<Icon icon={'notification-24'} iconSize={20} />} icon={<Icon icon={'notification-24'} iconSize={20} />}
/> />
</Tooltip> </Tooltip>
</If>
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon={'help-24'} iconSize={20} />} icon={<Icon icon={'help-24'} iconSize={20} />}
@@ -166,4 +194,11 @@ export default compose(
organizationName: organizationSettings.name, organizationName: organizationSettings.name,
})), })),
withDashboardActions, withDashboardActions,
withSubscriptions(
({ isSubscriptionActive, isSubscriptionInactive }) => ({
isSubscriptionActive,
isSubscriptionInactive,
}),
'main',
),
)(DashboardTopbar); )(DashboardTopbar);

View File

@@ -8,15 +8,21 @@ import {
Popover, Popover,
Position, Position,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { FormattedMessage as T } from 'components'; import { If, FormattedMessage as T } from 'components';
import { firstLettersArgs } from 'utils'; import { firstLettersArgs } from 'utils';
import { useAuthActions, useAuthUser } from 'hooks/state'; import { useAuthActions, useAuthUser } from 'hooks/state';
import withDialogActions from 'containers/Dialog/withDialogActions'; import withDialogActions from 'containers/Dialog/withDialogActions';
import { compose } from 'utils'; import { compose } from 'utils';
import withSubscriptions from '../../containers/Subscriptions/withSubscriptions';
function DashboardTopbarUser({ openDialog }) { function DashboardTopbarUser({
openDialog,
// #withSubscriptions
isSubscriptionActive
}) {
const history = useHistory(); const history = useHistory();
const { setLogout } = useAuthActions(); const { setLogout } = useAuthActions();
const user = useAuthUser(); const user = useAuthUser();
@@ -48,6 +54,7 @@ function DashboardTopbarUser({ openDialog }) {
} }
/> />
<MenuDivider /> <MenuDivider />
<If condition={isSubscriptionActive}>
<MenuItem <MenuItem
text={<T id={'keyboard_shortcuts'} />} text={<T id={'keyboard_shortcuts'} />}
onClick={onKeyboardShortcut} onClick={onKeyboardShortcut}
@@ -56,6 +63,7 @@ function DashboardTopbarUser({ openDialog }) {
text={<T id={'preferences'} />} text={<T id={'preferences'} />}
onClick={() => history.push('/preferences')} onClick={() => history.push('/preferences')}
/> />
</If>
<MenuItem text={<T id={'logout'} />} onClick={onClickLogout} /> <MenuItem text={<T id={'logout'} />} onClick={onClickLogout} />
</Menu> </Menu>
} }
@@ -69,4 +77,10 @@ function DashboardTopbarUser({ openDialog }) {
</Popover> </Popover>
); );
} }
export default compose(withDialogActions)(DashboardTopbarUser); export default compose(
withDialogActions,
withSubscriptions(
({ isSubscriptionActive }) => ({ isSubscriptionActive }),
'main',
),
)(DashboardTopbarUser);

View File

@@ -0,0 +1,31 @@
import React from 'react';
import { includes } from 'lodash';
import { compose } from 'utils';
import { Redirect } from 'react-router-dom';
import withSubscriptions from '../../containers/Subscriptions/withSubscriptions';
/**
* Ensures the given subscription type is active or redirect to the given route.
*/
function EnsureSubscriptionIsActive({
children,
subscriptionType = 'main',
redirectTo = '/billing',
routePath,
exclude,
isSubscriptionActive,
}) {
return isSubscriptionActive || includes(exclude, routePath) ? (
children
) : (
<Redirect to={{ pathname: redirectTo }} />
);
}
export default compose(
withSubscriptions(
({ isSubscriptionActive }) => ({ isSubscriptionActive }),
'main',
),
)(EnsureSubscriptionIsActive);

View File

@@ -0,0 +1,31 @@
import React from 'react';
import { includes } from 'lodash';
import { compose } from 'utils';
import { Redirect } from 'react-router-dom';
import withSubscriptions from '../../containers/Subscriptions/withSubscriptionss';
/**
* Ensures the given subscription type is active or redirect to the given route.
*/
function EnsureSubscriptionsIsActive({
children,
subscriptionType = 'main',
redirectTo = '/billing',
routePath,
exclude,
isSubscriptionsActive,
}) {
return !isSubscriptionsActive || includes(exclude, routePath) ? (
children
) : (
<Redirect to={{ pathname: redirectTo }} />
);
}
export default compose(
withSubscriptions(
({ isSubscriptionsActive }) => ({ isSubscriptionsActive }),
'main',
),
)(EnsureSubscriptionsIsActive);

View File

@@ -0,0 +1,31 @@
import React from 'react';
import { includes } from 'lodash';
import { compose } from 'utils';
import { Redirect } from 'react-router-dom';
import withSubscriptions from '../../containers/Subscriptions/withSubscriptionss';
/**
* Ensures the given subscription type is active or redirect to the given route.
*/
function EnsureSubscriptionsIsInactive({
children,
subscriptionType = 'main',
redirectTo = '/billing',
routePath,
exclude,
isSubscriptionsInactive,
}) {
return !isSubscriptionsInactive || includes(exclude, routePath) ? (
children
) : (
<Redirect to={{ pathname: redirectTo }} />
);
}
export default compose(
withSubscriptions(
({ isSubscriptionsInactive }) => ({ isSubscriptionsInactive }),
'main',
),
)(EnsureSubscriptionsIsInactive);

View File

@@ -1,10 +1,8 @@
import React from 'react'; import React from 'react';
import { Route, Switch, Redirect } from 'react-router-dom'; import { Route, Switch } from 'react-router-dom';
import preferencesRoutes from 'routes/preferences' import preferencesRoutes from 'routes/preferences';
export default function DashboardContentRoute() { export default function DashboardContentRoute() {
return ( return (
<Route pathname="/preferences"> <Route pathname="/preferences">
<Switch> <Switch>

View File

@@ -5,6 +5,7 @@ import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withDashboard from 'containers/Dashboard/withDashboard'; import withDashboard from 'containers/Dashboard/withDashboard';
import { compose } from 'utils'; import { compose } from 'utils';
import withSubscriptions from '../../containers/Subscriptions/withSubscriptions';
function SidebarContainer({ function SidebarContainer({
// #ownProps // #ownProps
@@ -15,6 +16,9 @@ function SidebarContainer({
// #withDashboard // #withDashboard
sidebarExpended, sidebarExpended,
// #withSubscription
isSubscriptionActive,
}) { }) {
const sidebarScrollerRef = React.useRef(); const sidebarScrollerRef = React.useRef();
@@ -31,7 +35,7 @@ function SidebarContainer({
const handleSidebarMouseLeave = () => { const handleSidebarMouseLeave = () => {
if (!sidebarExpended && sidebarScrollerRef.current) { if (!sidebarExpended && sidebarScrollerRef.current) {
sidebarScrollerRef.current.scrollTo({ top: 0, left: 0, }); sidebarScrollerRef.current.scrollTo({ top: 0, left: 0 });
} }
}; };
@@ -43,6 +47,7 @@ function SidebarContainer({
<div <div
className={classNames('sidebar', { className={classNames('sidebar', {
'sidebar--mini-sidebar': !sidebarExpended, 'sidebar--mini-sidebar': !sidebarExpended,
'is-subscription-inactive': !isSubscriptionActive,
})} })}
id="sidebar" id="sidebar"
onMouseLeave={handleSidebarMouseLeave} onMouseLeave={handleSidebarMouseLeave}
@@ -64,4 +69,8 @@ export default compose(
withDashboard(({ sidebarExpended }) => ({ withDashboard(({ sidebarExpended }) => ({
sidebarExpended, sidebarExpended,
})), })),
withSubscriptions(
({ isSubscriptionActive }) => ({ isSubscriptionActive }),
'main',
),
)(SidebarContainer); )(SidebarContainer);

View File

@@ -8,6 +8,8 @@ import MenuItem from 'components/MenuItem';
import { MenuItemLabel } from 'components'; import { MenuItemLabel } from 'components';
import classNames from 'classnames'; import classNames from 'classnames';
import SidebarOverlay from 'components/SidebarOverlay'; import SidebarOverlay from 'components/SidebarOverlay';
import { compose } from 'redux';
import withSubscriptions from '../../containers/Subscriptions/withSubscriptions';
const DEFAULT_ITEM = { const DEFAULT_ITEM = {
text: '', text: '',
@@ -19,12 +21,10 @@ function matchPath(pathname, path, matchExact) {
} }
function SidebarMenuItemSpace({ space }) { function SidebarMenuItemSpace({ space }) {
return ( return <div class="bp3-menu-spacer" style={{ height: `${space}px` }} />;
<div class="bp3-menu-spacer" style={{ height: `${space}px` }} />
)
} }
export default function SidebarMenu() { function SidebarMenu({ isSubscriptionActive }) {
const history = useHistory(); const history = useHistory();
const location = useLocation(); const location = useLocation();
@@ -92,7 +92,11 @@ export default function SidebarMenu() {
); );
}); });
}; };
const items = menuItemsMapper(sidebarMenuList);
const filterItems = sidebarMenuList.filter(
(item) => isSubscriptionActive || item.enableBilling,
);
const items = menuItemsMapper(filterItems);
const handleSidebarOverlayClose = () => { const handleSidebarOverlayClose = () => {
setIsOpen(false); setIsOpen(false);
@@ -110,3 +114,10 @@ export default function SidebarMenu() {
</div> </div>
); );
} }
export default compose(
withSubscriptions(
({ isSubscriptionActive }) => ({ isSubscriptionActive }),
'main',
),
)(SidebarMenu);

View File

@@ -0,0 +1,135 @@
import React from 'react';
import classNames from 'classnames';
import { T } from 'components';
import { saveInvoke } from 'utils';
import 'style/pages/Subscription/PlanRadio.scss';
import 'style/pages/Subscription/PlanPeriodRadio.scss';
export function SubscriptionPlans({ value, plans, onSelect }) {
const handleSelect = (value) => {
onSelect && onSelect(value);
};
return (
<div className={'plan-radios'}>
{plans.map((plan) => (
<SubscriptionPlan
name={plan.name}
description={plan.description}
slug={plan.slug}
price={plan.price}
currencyCode={plan.currencyCode}
value={plan.slug}
onSelected={handleSelect}
selectedOption={value}
/>
))}
</div>
);
}
export function SubscriptionPlan({
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>
);
}
/**
* Subscription periods.
*/
export function SubscriptionPeriods({ periods, selectedPeriod, onPeriodSelect }) {
const handleSelected = (value) => {
saveInvoke(onPeriodSelect, value);
};
return (
<div className={'plan-periods'}>
{periods.map((period) => (
<SubscriptionPeriod
period={period.slug}
label={period.label}
onSelected={handleSelected}
price={period.price}
selectedPeriod={selectedPeriod}
/>
))}
</div>
);
}
/**
* Billing period.
*/
export function SubscriptionPeriod({
// #ownProps
label,
selectedPeriod,
onSelected,
period,
price,
currencyCode,
}) {
const handlePeriodClick = () => {
saveInvoke(onSelected, period);
};
return (
<div
id={`plan-period-${period}`}
className={classNames(
{ 'is-selected': period === selectedPeriod },
'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>
);
}

View File

@@ -72,6 +72,8 @@ export * from './Details';
export * from './Drawer/DrawerInsider'; export * from './Drawer/DrawerInsider';
export * from './Drawer/DrawerMainTabs'; export * from './Drawer/DrawerMainTabs';
export * from './TotalLines/index' export * from './TotalLines/index'
export * from './Alert';
export * from './Subscriptions';
const Hint = FieldHint; const Hint = FieldHint;

View File

@@ -298,6 +298,7 @@ export default [
}, },
{ {
text: <T id={'system'} />, text: <T id={'system'} />,
enableBilling: true,
label: true, label: true,
}, },
{ {
@@ -307,5 +308,6 @@ export default [
{ {
text: <T id={'billing'} />, text: <T id={'billing'} />,
href: '/billing', href: '/billing',
enableBilling: true,
}, },
]; ];

View File

@@ -1,9 +1,10 @@
import React from 'react'; import React from 'react';
import { Formik } from 'formik'; import { Formik } from 'formik';
import { Intent } from '@blueprintjs/core';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import * as Yup from 'yup'; import * as Yup from 'yup';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import Toaster from 'components/AppToaster'; import Toaster from 'components/AppToaster';
import 'style/pages/Setup/PaymentViaVoucherDialog.scss'; import 'style/pages/Setup/PaymentViaVoucherDialog.scss';
@@ -14,7 +15,6 @@ import PaymentViaLicenseForm from './PaymentViaVoucherForm';
import withDialogActions from 'containers/Dialog/withDialogActions'; import withDialogActions from 'containers/Dialog/withDialogActions';
import { compose } from 'utils'; import { compose } from 'utils';
import { Intent } from '@blueprintjs/core';
/** /**
* Payment via license dialog content. * Payment via license dialog content.
@@ -35,10 +35,11 @@ function PaymentViaLicenseDialogContent({
const handleSubmit = (values, { setSubmitting, setErrors }) => { const handleSubmit = (values, { setSubmitting, setErrors }) => {
setSubmitting(true); setSubmitting(true);
// Payment via voucher mutate.
paymentViaVoucherMutate({ ...values }) paymentViaVoucherMutate({ ...values })
.then(() => { .then(() => {
Toaster.show({ Toaster.show({
message: intl.get('payment_has_been_done_successfully'), message: intl.get('payment_via_voucher.success_message'),
intent: Intent.SUCCESS, intent: Intent.SUCCESS,
}); });
return closeDialog('payment-via-voucher'); return closeDialog('payment-via-voucher');
@@ -54,7 +55,7 @@ function PaymentViaLicenseDialogContent({
}) => { }) => {
if (errors.find((e) => e.type === 'LICENSE.CODE.IS.INVALID')) { if (errors.find((e) => e.type === 'LICENSE.CODE.IS.INVALID')) {
setErrors({ setErrors({
license_code: 'The license code is not valid, please try agin.', license_code: 'payment_via_voucher.license_code_not_valid',
}); });
} }
}, },

View File

@@ -31,7 +31,7 @@ function PaymentViaLicenseForm({
<Form> <Form>
<div className={CLASSES.DIALOG_BODY}> <div className={CLASSES.DIALOG_BODY}>
<p> <p>
<T id={'Pleasse enter your voucher number that you received from reseller.'} /> <T id={'payment_via_voucher.dialog.description'} />
</p> </p>
<FastField name="license_code"> <FastField name="license_code">

View File

@@ -1,5 +1,7 @@
import React from 'react';
import { Intent } from '@blueprintjs/core'; import { Intent } from '@blueprintjs/core';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { useHistory } from 'react-router-dom';
import AppToaster from 'components/AppToaster'; import AppToaster from 'components/AppToaster';
import withGlobalErrors from './withGlobalErrors'; import withGlobalErrors from './withGlobalErrors';
@@ -17,29 +19,31 @@ function GlobalErrors({
globalErrorsSet, globalErrorsSet,
}) { }) {
if (globalErrors.something_wrong) { if (globalErrors.something_wrong) {
toastKeySessionExpired = AppToaster.show({ toastKeySessionExpired = AppToaster.show(
{
message: intl.get('ops_something_went_wrong'), message: intl.get('ops_something_went_wrong'),
intent: Intent.DANGER, intent: Intent.DANGER,
onDismiss: () => { onDismiss: () => {
globalErrorsSet({ something_wrong: false }); globalErrorsSet({ something_wrong: false });
} },
}, toastKeySessionExpired); },
toastKeySessionExpired,
);
} }
if (globalErrors.session_expired) { if (globalErrors.session_expired) {
toastKeySomethingWrong = AppToaster.show({ toastKeySomethingWrong = AppToaster.show(
{
message: intl.get('session_expired'), message: intl.get('session_expired'),
intent: Intent.DANGER, intent: Intent.DANGER,
onDismiss: () => { onDismiss: () => {
globalErrorsSet({ session_expired: false }); globalErrorsSet({ session_expired: false });
},
},
toastKeySomethingWrong,
);
} }
}, toastKeySomethingWrong);
}
return null; return null;
} }
export default compose( export default compose(withGlobalErrors, withGlobalErrorsActions)(GlobalErrors);
withGlobalErrors,
withGlobalErrorsActions,
)(GlobalErrors);

View File

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

View File

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

View File

@@ -3,11 +3,14 @@ import { Icon, For } from 'components';
import { FormattedMessage as T } from 'components'; import { FormattedMessage as T } from 'components';
import { getFooterLinks } from 'config/footerLinks'; import { getFooterLinks } from 'config/footerLinks';
import { useAuthActions, useAuthOrganizationId } from 'hooks/state'; import { useAuthActions } from 'hooks/state';
/**
* Footer item link.
*/
function FooterLinkItem({ title, link }) { function FooterLinkItem({ title, link }) {
return ( return (
<div class=""> <div class="content__links-item">
<a href={link} target="_blank"> <a href={link} target="_blank">
{title} {title}
</a> </a>
@@ -16,20 +19,65 @@ function FooterLinkItem({ title, link }) {
} }
/** /**
* Wizard setup left section. * Setup left section footer.
*/ */
export default function SetupLeftSection() { function SetupLeftSectionFooter() {
const { setLogout } = useAuthActions();
const organizationId = useAuthOrganizationId();
// Retrieve the footer links. // Retrieve the footer links.
const footerLinks = getFooterLinks(); 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. // Handle logout link click.
const onClickLogout = () => { const onClickLogout = () => {
setLogout(); 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 ( return (
<section className={'setup-page__left-section'}> <section className={'setup-page__left-section'}>
<div className={'content'}> <div className={'content'}>
@@ -41,40 +89,8 @@ export default function SetupLeftSection() {
width={190} width={190}
/> />
</div> </div>
<SetupLeftSectionHeader />
<h1 className={'content__title'}> <SetupLeftSectionFooter />
<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>
</div> </div>
</section> </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 { TimezonePicker } from '@blueprintjs/timezone';
import { FormattedMessage as T } from 'components'; import { FormattedMessage as T } from 'components';
import { FieldRequiredHint, Col, Row, ListSelect } from 'components'; import { Col, Row, ListSelect } from 'components';
import { import {
momentFormatter, momentFormatter,
tansformDateValue, tansformDateValue,
inputIntent, inputIntent,
handleDateChange handleDateChange,
} from 'utils'; } from 'utils';
import { getFiscalYear } from 'common/fiscalYearOptions'; import { getFiscalYear } from 'common/fiscalYearOptions';
import { getLanguages } from 'common/languagesOptions'; import { getLanguages } from 'common/languagesOptions';
import { getCurrencies } from 'common/currencies'; import { getCurrencies } from 'common/currencies';
/** /**
* Setup organization form. * Setup organization form.
*/ */
@@ -46,22 +44,24 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
<FastField name={'organization_name'}> <FastField name={'organization_name'}>
{({ form, field, meta: { error, touched } }) => ( {({ form, field, meta: { error, touched } }) => (
<FormGroup <FormGroup
labelInfo={<FieldRequiredHint />}
label={<T id={'legal_organization_name'} />} label={<T id={'legal_organization_name'} />}
className={'form-group--name'} className={'form-group--name'}
intent={inputIntent({ error, touched })} intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'organization_name'} />} helperText={<ErrorMessage name={'organization_name'} />}
> >
<InputGroup {...field} /> <InputGroup {...field} intent={inputIntent({ error, touched })} />
</FormGroup> </FormGroup>
)} )}
</FastField> </FastField>
{/* ---------- Financial starting date ---------- */} {/* ---------- Financial starting date ---------- */}
<FastField name={'financialDateStart'}> <FastField name={'financialDateStart'}>
{({ form: { setFieldValue }, field: { value }, meta: { error, touched } }) => ( {({
form: { setFieldValue },
field: { value },
meta: { error, touched },
}) => (
<FormGroup <FormGroup
labelInfo={<FieldRequiredHint />}
label={<T id={'financial_starting_date'} />} label={<T id={'financial_starting_date'} />}
intent={inputIntent({ error, touched })} intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="financialDateStart" />} helperText={<ErrorMessage name="financialDateStart" />}
@@ -74,6 +74,7 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
onChange={handleDateChange((formattedDate) => { onChange={handleDateChange((formattedDate) => {
setFieldValue('financialDateStart', formattedDate); setFieldValue('financialDateStart', formattedDate);
})} })}
intent={inputIntent({ error, touched })}
/> />
</FormGroup> </FormGroup>
)} )}
@@ -89,7 +90,6 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
meta: { error, touched }, meta: { error, touched },
}) => ( }) => (
<FormGroup <FormGroup
labelInfo={<FieldRequiredHint />}
label={<T id={'base_currency'} />} label={<T id={'base_currency'} />}
className={classNames( className={classNames(
'form-group--base-currency', 'form-group--base-currency',
@@ -101,7 +101,9 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
> >
<ListSelect <ListSelect
items={Currencies} items={Currencies}
noResults={<MenuItem disabled={true} text={<T id={'no_results'} />} />} noResults={
<MenuItem disabled={true} text={<T id={'no_results'} />} />
}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
onItemSelect={(item) => { onItemSelect={(item) => {
setFieldValue('baseCurrency', item.code); setFieldValue('baseCurrency', item.code);
@@ -110,6 +112,7 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
textProp={'name'} textProp={'name'}
defaultText={<T id={'select_base_currency'} />} defaultText={<T id={'select_base_currency'} />}
selectedItem={value} selectedItem={value}
intent={inputIntent({ error, touched })}
/> />
</FormGroup> </FormGroup>
)} )}
@@ -136,7 +139,9 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
> >
<ListSelect <ListSelect
items={Languages} items={Languages}
noResults={<MenuItem disabled={true} text={<T id={'no_results'} />} />} noResults={
<MenuItem disabled={true} text={<T id={'no_results'} />} />
}
onItemSelect={(item) => { onItemSelect={(item) => {
setFieldValue('language', item.value); setFieldValue('language', item.value);
}} }}
@@ -146,6 +151,7 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
defaultText={<T id={'select_language'} />} defaultText={<T id={'select_language'} />}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
filterable={false} filterable={false}
intent={inputIntent({ error, touched })}
/> />
</FormGroup> </FormGroup>
)} )}
@@ -154,9 +160,12 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
</Row> </Row>
{/* --------- Fiscal Year ----------- */} {/* --------- Fiscal Year ----------- */}
<FastField name={'fiscalYear'}> <FastField name={'fiscalYear'}>
{({ form: { setFieldValue }, field: { value }, meta: { error, touched } }) => ( {({
form: { setFieldValue },
field: { value },
meta: { error, touched },
}) => (
<FormGroup <FormGroup
labelInfo={<FieldRequiredHint />}
label={<T id={'fiscal_year'} />} label={<T id={'fiscal_year'} />}
className={classNames( className={classNames(
'form-group--fiscal_year', 'form-group--fiscal_year',
@@ -168,14 +177,16 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
> >
<ListSelect <ListSelect
items={FiscalYear} items={FiscalYear}
noResults={<MenuItem disabled={true} text={<T id={'no_results'} />} />} noResults={
<MenuItem disabled={true} text={<T id={'no_results'} />} />
}
selectedItem={value} selectedItem={value}
selectedItemProp={'value'} selectedItemProp={'value'}
textProp={'name'} textProp={'name'}
defaultText={<T id={'select_fiscal_year'} />} defaultText={<T id={'select_fiscal_year'} />}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
onItemSelect={(item) => { onItemSelect={(item) => {
setFieldValue('fiscalYear', item.value) setFieldValue('fiscalYear', item.value);
}} }}
filterable={false} filterable={false}
/> />
@@ -191,7 +202,6 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
meta: { error, touched }, meta: { error, touched },
}) => ( }) => (
<FormGroup <FormGroup
labelInfo={<FieldRequiredHint />}
label={<T id={'time_zone'} />} label={<T id={'time_zone'} />}
className={classNames( className={classNames(
'form-group--time-zone', 'form-group--time-zone',
@@ -216,20 +226,11 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
</FastField> </FastField>
<p className={'register-org-note'}> <p className={'register-org-note'}>
<T <T id={'setup.organization.note_you_can_change_your_preferences'} />
id={
'note_you_can_change_your_preferences_later_in_dashboard_if_needed'
}
/>
</p> </p>
<div className={'register-org-button'}> <div className={'register-org-button'}>
<Button <Button intent={Intent.PRIMARY} loading={isSubmitting} type="submit">
intent={Intent.PRIMARY}
disabled={isSubmitting}
loading={isSubmitting}
type="submit"
>
<T id={'save_continue'} /> <T id={'save_continue'} />
</Button> </Button>
</div> </div>

View File

@@ -1,9 +1,8 @@
import React from 'react'; import React from 'react';
import * as Yup from 'yup';
import { Formik } from 'formik'; import { Formik } from 'formik';
import moment from 'moment'; import moment from 'moment';
import { FormattedMessage as T } from 'components'; import { FormattedMessage as T } from 'components';
import intl from 'react-intl-universal';
import 'style/pages/Setup/Organization.scss'; import 'style/pages/Setup/Organization.scss';
@@ -13,42 +12,9 @@ import { useOrganizationSetup } from 'hooks/query';
import withSettingsActions from 'containers/Settings/withSettingsActions'; import withSettingsActions from 'containers/Settings/withSettingsActions';
import withOrganizationActions from 'containers/Organization/withOrganizationActions'; import withOrganizationActions from 'containers/Organization/withOrganizationActions';
import { import { compose, transfromToSnakeCase } from 'utils';
compose, import { getSetupOrganizationValidation } from './SetupOrganization.schema';
transfromToSnakeCase,
} from 'utils';
/**
* Setup organization form.
*/
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. // Initial values.
const defaultValues = { const defaultValues = {
@@ -60,6 +26,16 @@ function SetupOrganizationPage({
timeZone: '', timeZone: '',
}; };
/**
* Setup organization form.
*/
function SetupOrganizationPage({ wizard, setOrganizationSetupCompleted }) {
const { mutateAsync: organizationSetupMutate } = useOrganizationSetup();
// Validation schema.
const validationSchema = getSetupOrganizationValidation();
// Initialize values.
const initialValues = { const initialValues = {
...defaultValues, ...defaultValues,
}; };
@@ -83,10 +59,10 @@ function SetupOrganizationPage({
<div className={'setup-organization'}> <div className={'setup-organization'}>
<div className={'setup-organization__title-wrap'}> <div className={'setup-organization__title-wrap'}>
<h1> <h1>
<T id={'let_s_get_started'} /> <T id={'setup.organization.title'} />
</h1> </h1>
<p class="paragraph"> <p class="paragraph">
<T id={'tell_the_system_a_little_bit_about_your_organization'} /> <T id={'setup.organization.description'} />
</p> </p>
</div> </div>

View File

@@ -4,7 +4,7 @@ import * as R from 'ramda';
import 'style/pages/Setup/Subscription.scss'; import 'style/pages/Setup/Subscription.scss';
import SetupSubscriptionForm from './SetupSubscriptionForm'; import SetupSubscriptionForm from './SetupSubscription/SetupSubscriptionForm';
import { getSubscriptionFormSchema } from './SubscriptionForm.schema'; import { getSubscriptionFormSchema } from './SubscriptionForm.schema';
import withSubscriptionPlansActions from '../Subscriptions/withSubscriptionPlansActions'; import withSubscriptionPlansActions from '../Subscriptions/withSubscriptionPlansActions';
@@ -13,13 +13,11 @@ import withSubscriptionPlansActions from '../Subscriptions/withSubscriptionPlans
*/ */
function SetupSubscription({ function SetupSubscription({
// #withSubscriptionPlansActions // #withSubscriptionPlansActions
initSubscriptionPlans initSubscriptionPlans,
}) { }) {
React.useEffect(() => { React.useEffect(() => {
initSubscriptionPlans(); initSubscriptionPlans();
}, [ }, [initSubscriptionPlans]);
initSubscriptionPlans
]);
// Initial values. // Initial values.
const initialValues = { const initialValues = {
@@ -30,7 +28,11 @@ function SetupSubscription({
// Handle form submit. // Handle form submit.
const handleSubmit = () => {}; const handleSubmit = () => {};
const SubscriptionFormSchema = getSubscriptionFormSchema(); // Retrieve momerized subscription form schema.
const SubscriptionFormSchema = React.useMemo(
() => getSubscriptionFormSchema(),
[],
);
return ( return (
<div className={'setup-subscription-form'}> <div className={'setup-subscription-form'}>
@@ -44,6 +46,4 @@ function SetupSubscription({
); );
} }
export default R.compose( export default R.compose(withSubscriptionPlansActions)(SetupSubscription);
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 }) { function WizardSetupStep({ label, isActive = false }) {
return ( return (
<li className={classNames({ 'is-active': isActive })}> <li className={classNames({ 'is-active': isActive })}>
<p className={'wizard-info'}> <p className={'wizard-info'}>{label}</p>
{ label }
</p>
</li> </li>
); );
} }

View File

@@ -1,7 +1,8 @@
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import * as Yup from 'yup';
import { Formik, Form } from 'formik'; import { Formik, Form } from 'formik';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { If, Alert, T } from 'components';
import DashboardInsider from 'components/Dashboard/DashboardInsider'; import DashboardInsider from 'components/Dashboard/DashboardInsider';
import 'style/pages/Billing/BillingPage.scss'; import 'style/pages/Billing/BillingPage.scss';
@@ -10,8 +11,11 @@ import { MasterBillingTabs } from './SubscriptionTabs';
import withBillingActions from './withBillingActions'; import withBillingActions from './withBillingActions';
import withDashboardActions from 'containers/Dashboard/withDashboardActions'; import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withSubscriptionPlansActions from './withSubscriptionPlansActions';
import { compose } from 'utils'; import { compose } from 'utils';
import { getBillingFormValidationSchema } from './utils';
import withSubscriptions from './withSubscriptions';
/** /**
* Billing form. * Billing form.
@@ -22,24 +26,28 @@ function BillingForm({
// #withBillingActions // #withBillingActions
requestSubmitBilling, requestSubmitBilling,
initSubscriptionPlans,
// #withSubscriptions
isSubscriptionInactive,
}) { }) {
useEffect(() => { useEffect(() => {
changePageTitle(intl.get('billing')); changePageTitle(intl.get('billing'));
}, [changePageTitle]); }, [changePageTitle]);
const validationSchema = Yup.object().shape({ React.useEffect(() => {
plan_slug: Yup.string() initSubscriptionPlans();
.required(), }, [initSubscriptionPlans]);
period: Yup.string().required(),
license_code: Yup.string().trim(),
});
// Initial values.
const initialValues = { const initialValues = {
plan_slug: 'free', plan_slug: 'free',
period: 'month', period: 'month',
license_code: '', license_code: '',
}; };
// Handle form submitting.
const handleSubmit = (values, { setSubmitting }) => { const handleSubmit = (values, { setSubmitting }) => {
requestSubmitBilling(values) requestSubmitBilling(values)
.then((response) => { .then((response) => {
@@ -53,20 +61,34 @@ function BillingForm({
return ( return (
<DashboardInsider name={'billing-page'}> <DashboardInsider name={'billing-page'}>
<div className={'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 <Formik
validationSchema={validationSchema} validationSchema={getBillingFormValidationSchema()}
onSubmit={handleSubmit} onSubmit={handleSubmit}
initialValues={initialValues} initialValues={initialValues}
> >
{({ isSubmitting, handleSubmit }) => (
<Form> <Form>
<MasterBillingTabs /> <MasterBillingTabs />
</Form> </Form>
)}
</Formik> </Formik>
</div> </div>
</DashboardInsider> </DashboardInsider>
); );
} }
export default compose(withDashboardActions, withBillingActions)(BillingForm); export default compose(
withDashboardActions,
withBillingActions,
withSubscriptionPlansActions,
withSubscriptions(
({ isSubscriptionInactive }) => ({ isSubscriptionInactive }),
'main',
),
)(BillingForm);

View File

@@ -15,12 +15,14 @@ import { saveInvoke } from 'utils';
function BillingPeriod({ function BillingPeriod({
// #ownProps // #ownProps
label, label,
currencyCode,
value, value,
selectedOption, selectedOption,
onSelected, onSelected,
period, period,
// #withPlan
price, price,
currencyCode,
}) { }) {
const handlePeriodClick = () => { const handlePeriodClick = () => {
saveInvoke(onSelected, value); saveInvoke(onSelected, value);

View File

@@ -1,42 +1,46 @@
import React from 'react'; import React from 'react';
import { Field } from 'formik'; import { Field } from 'formik';
import BillingPeriod from './BillingPeriod'; import * as R from 'ramda';
import withPlans from './withPlans'; import { T, SubscriptionPeriods } from 'components';
import { compose } from 'utils'; import withPlan from './withPlan';
/**
* Sunscription periods enhanced.
*/
const SubscriptionPeriodsEnhanced = R.compose(
withPlan(({ plan }) => ({ plan })),
)(({ plan, ...restProps }) => {
return <SubscriptionPeriods periods={plan.periods} {...restProps} />;
});
/** /**
* Billing periods. * Billing periods.
*/ */
function BillingPeriods({ title, description, plansPeriods }) { export default function BillingPeriods() {
return ( return (
<section class="billing-plans__section"> <section class="billing-plans__section">
<h1 class="title">{title}</h1> <h1 class="title">
<T id={'setup.plans.select_period.title'} />
</h1>
<div class="description"> <div class="description">
<p className="paragraph">{description}</p> <p className="paragraph">
<T id={'setup.plans.select_period.description'} />
</p>
</div> </div>
<Field name={'period'}> <Field name={'period'}>
{({ form: { setFieldValue, values } }) => ( {({ field: { value }, form: { values, setFieldValue } }) => (
<div className={'plan-periods'}> <SubscriptionPeriodsEnhanced
{plansPeriods.map((period) => ( selectedPeriod={value}
<BillingPeriod
planSlug={values.plan_slug} planSlug={values.plan_slug}
period={period.slug} onPeriodSelect={(period) => {
label={period.label} setFieldValue('period', period);
value={period.slug} }}
onSelected={(value) => setFieldValue('period', value)}
selectedOption={values.period}
/> />
))}
</div>
)} )}
</Field> </Field>
</section> </section>
); );
} }
export default compose(withPlans(({ plansPeriods }) => ({ plansPeriods })))(
BillingPeriods,
);

View File

@@ -3,8 +3,6 @@ import classNames from 'classnames';
import { FormattedMessage as T } from 'components'; import { FormattedMessage as T } from 'components';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import 'style/pages/Subscription/PlanRadio.scss';
import { saveInvoke } from 'utils'; import { saveInvoke } from 'utils';
/** /**
@@ -32,9 +30,7 @@ export default function BillingPlan({
onClick={handlePlanClick} onClick={handlePlanClick}
> >
<div className={'plan-radio__header'}> <div className={'plan-radio__header'}>
<div className={'plan-radio__name'}> <div className={'plan-radio__name'}>{name}</div>
{intl.get('plan_radio_name', { name: name })}
</div>
</div> </div>
<div className={'plan-radio__description'}> <div className={'plan-radio__description'}>

View File

@@ -1,12 +1,10 @@
import React from 'react'; import React from 'react';
import { FormattedMessage as T } from 'components';
import intl from 'react-intl-universal';
import 'style/pages/Subscription/BillingPlans.scss'; import 'style/pages/Subscription/BillingPlans.scss';
import BillingPlansInput from 'containers/Subscriptions/BillingPlansInput'; import BillingPlansInput from './BillingPlansInput';
import BillingPeriodsInput from 'containers/Subscriptions/BillingPeriodsInput'; import BillingPeriodsInput from './BillingPeriodsInput';
import BillingPaymentMethod from 'containers/Subscriptions/BillingPaymentMethod'; import BillingPaymentMethod from './BillingPaymentMethod';
/** /**
* Billing plans form. * Billing plans form.
@@ -14,18 +12,9 @@ import BillingPaymentMethod from 'containers/Subscriptions/BillingPaymentMethod'
export default function BillingPlansForm() { export default function BillingPlansForm() {
return ( return (
<div class="billing-plans"> <div class="billing-plans">
<BillingPlansInput <BillingPlansInput />
title={intl.get('select_a_plan')} <BillingPeriodsInput />
description={<T id={'please_enter_your_preferred_payment_method'} />} <BillingPaymentMethod />
/>
<BillingPeriodsInput
title={intl.get('choose_your_billing')}
description={<T id={'please_enter_your_preferred_payment_method'} />}
/>
<BillingPaymentMethod
title={intl.get('payment_methods')}
description={<T id={'please_enter_your_preferred_payment_method'} />}
/>
</div> </div>
); );
} }

View File

@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { FastField, Field } from 'formik'; import { Field } from 'formik';
import BillingPlan from './BillingPlan'; import { T, SubscriptionPlans } from 'components';
import withPlans from './withPlans'; import withPlans from './withPlans';
import { compose } from 'utils'; import { compose } from 'utils';
@@ -11,27 +11,24 @@ import { compose } from 'utils';
function BillingPlans({ plans, title, description, selectedOption }) { function BillingPlans({ plans, title, description, selectedOption }) {
return ( return (
<section class="billing-plans__section"> <section class="billing-plans__section">
<h1 class="title">{title}</h1> <h1 class="title">
<T id={'setup.plans.select_plan.title'} />
</h1>
<div class="description"> <div class="description">
<p className="paragraph">{description}</p> <p className="paragraph">
<T id={'setup.plans.select_plan.description'} />
</p>
</div> </div>
<Field name={'plan_slug'}> <Field name={'plan_slug'}>
{({ form: { setFieldValue }, field: { value } }) => ( {({ form: { setFieldValue }, field: { value } }) => (
<div className={'plan-radios'}> <SubscriptionPlans
{plans.map((plan) => ( plans={plans}
<BillingPlan value={value}
name={plan.name} onSelect={(value) => {
description={plan.description} setFieldValue('plan_slug', value);
slug={plan.slug} }}
price={plan.price.month}
currencyCode={plan.currencyCode}
value={plan.slug}
onSelected={(value) => setFieldValue('plan_slug', value)}
selectedOption={value}
/> />
))}
</div>
)} )}
</Field> </Field>
</section> </section>

View File

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

View File

@@ -16,11 +16,7 @@ export const MasterBillingTabs = ({ formik }) => {
id={'billing'} id={'billing'}
panel={<BillingTab formik={formik} />} panel={<BillingTab formik={formik} />}
/> />
<Tab <Tab title={intl.get('usage')} id={'usage'} disabled={true} />
title={intl.get('usage')}
id={'usage'}
disabled={true}
/>
</Tabs> </Tabs>
</div> </div>
); );
@@ -43,11 +39,7 @@ export const PaymentMethodTabs = ({ formik }) => {
id={'credit_card'} id={'credit_card'}
disabled={true} disabled={true}
/> />
<Tab <Tab title={intl.get('paypal')} id={'paypal'} disabled={true} />
title={intl.get('paypal')}
id={'paypal'}
disabled={true}
/>
</Tabs> </Tabs>
</div> </div>
); );

View File

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

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

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

View File

@@ -23,23 +23,18 @@ export function useSaveSettings(props) {
function useSettingsQuery(key, query, props) { function useSettingsQuery(key, query, props) {
const setSettings = useSetSettings(); const setSettings = useSetSettings();
const state = useRequestQuery( return useRequestQuery(
key, key,
{ method: 'get', url: 'settings', params: query }, { method: 'get', url: 'settings', params: query },
{ {
select: (res) => res.data.settings, select: (res) => res.data.settings,
defaultData: [], defaultData: [],
onSuccess: (settings) => {
setSettings(settings);
},
...props, ...props,
}, },
); );
useEffect(() => {
if (state.isSuccess) {
setSettings(state.data);
}
}, [state.data, state.isSuccess, setSettings]);
return state.data;
} }
/** /**

View File

@@ -584,7 +584,7 @@
"payment_mades": "سندات الموردين", "payment_mades": "سندات الموردين",
"subscription": "الاشتراك", "subscription": "الاشتراك",
"plan_slug": "سبيكة خطة", "plan_slug": "سبيكة خطة",
"billing": "الفواتير", "billing": "الاشتراك",
"the_billing_has_been_created_successfully": "تم إنشاء الفواتير بنجاح.", "the_billing_has_been_created_successfully": "تم إنشاء الفواتير بنجاح.",
"select_a_plan": "حدد الباقة", "select_a_plan": "حدد الباقة",
"choose_your_billing": "اختر مدة الدفع", "choose_your_billing": "اختر مدة الدفع",
@@ -1114,12 +1114,10 @@
"filter_": "البحث...", "filter_": "البحث...",
"all_rights_reserved": "© {pre}-{current} كل الحقوق محفوظة.", "all_rights_reserved": "© {pre}-{current} كل الحقوق محفوظة.",
"congrats_your_account_has_been_created_and_invited": "مبروك! تم إنشاء حسابك ودعوتك إلى <strong>{organization_name} </strong> بنجاح.", "congrats_your_account_has_been_created_and_invited": "مبروك! تم إنشاء حسابك ودعوتك إلى <strong>{organization_name} </strong> بنجاح.",
"it_s_time_to_make_your_accounting_really_simple": "حان الوقت لجعل عملية المحاسبة الخاصة بك بسيطة واكتر دقة!",
"while_we_set_up_your_account_please_remember_to_verify_your_account": "أثناء قيامنا بإعداد حسابك ، يرجى تذكر التحقق من حسابك بالنقر فوق الارتباط الذي أرسلناه إلى عنوان بريدك الإلكتروني المسجل", "while_we_set_up_your_account_please_remember_to_verify_your_account": "أثناء قيامنا بإعداد حسابك ، يرجى تذكر التحقق من حسابك بالنقر فوق الارتباط الذي أرسلناه إلى عنوان بريدك الإلكتروني المسجل",
"something_went_wrong": "هناك خطأ ما!",
"please_refresh_the_page": "يرجى تحديث الصفحة",
"waiting_to_redirect": "في انتظار إعادة التوجيه",
"refresh_the_page_if_redirect_not_worked": "قم بتحديث الصفحة إذا لم تنجح إعادة التوجيه.",
"blog": "المدونة", "blog": "المدونة",
"support": "الدعم النفي", "support": "الدعم النفي",
"service_status": "حالة الخدمة", "service_status": "حالة الخدمة",
@@ -1220,6 +1218,67 @@
"pdf_preview.preview.button": "معاينة", "pdf_preview.preview.button": "معاينة",
"invoice_preview.dialog.title": "معاينة فاتورة PDF", "invoice_preview.dialog.title": "معاينة فاتورة PDF",
"estimate_preview.dialog.title": "معاينة عرض PDF", "estimate_preview.dialog.title": "معاينة عرض PDF",
"receipt_preview.dialog.title":"معاينة إيصال PDF" "receipt_preview.dialog.title":"معاينة إيصال PDF",
"edit_contact": "Edit {name}",
"item.sell_description": "Sell description",
"item.purchase_description": "Purchase description",
"closed_date": "Closed date",
"payment_made.details.payment_number": "Payment #",
"payment_receive.details.payment_number": "Payment #",
"estimate.details.estimate_number": "Estimate #",
"receipt.details.receipt_number": "Receipt #",
"bill.details.bill_number": "Bill #",
"setup.left_side.title": "سجل منشأة جديدة الأن.",
"setup.left_side.description": "حسابك علي Bigcapital",
"setup.left_side.footer_help": "نحن هنا للمساعدة!",
"setup.plan.plans": "الخطط والدفع",
"setup.plan.initializing": "التهيئة",
"setup.plan.getting_started": "البدء",
"setup.plan.congrats": "تهانينا",
"setup.plans.select_plan.description": "الرجاء إدخال طريقة الدفع المفضلة لديك أدناه. يمكنك استخدام بطاقة الائتمان / الخصم أو الدفع المسبق من خلال PayPal. ",
"setup.plans.select_plan.title": "اختر الخطة",
"setup.plans.select_period.title": "اختر الفترة",
"setup.plans.select_period.description": "يمكن تسديد الخطة شهرياً او سنوياً.",
"setup.plans.payment_methods.title": "طرق الدفع",
"setup.plans.payment_methods.description": "",
"setup.initializing.title": "حان الوقت لجعل عملية المحاسبة الخاصة بك بسيطة واكتر دقة!",
"setup.initializing.description": "أثناء قيامنا بإعداد حسابك ، يرجى تذكر التحقق من حسابك بالنقر فوق الارتباط الذي أرسلناه إلى عنوان بريدك الإلكتروني المسجل",
"setup.initializing.waiting_to_redirect": "في انتظار إعادة التوجيه",
"setup.initializing.refresh_the_page_if_redirect_not_worked": "قم بتحديث الصفحة إذا لم تنجح إعادة التوجيه.",
"setup.initializing.something_went_wrong": "هناك خطأ ما!",
"setup.initializing.please_refresh_the_page": "يرجى تحديث الصفحة",
"setup.organization.title": "دعنا نبدأ",
"setup.organization.description": "أخبر النظام قليلاً عن مؤسستك.",
"plan.essential.title": "الاساسية",
"plan.plus.title": "الاضافية",
"plan.professional.title": "الاحترافية",
"plan.feature.sale_purchase_invoice": "فواتير البيع والشراء.",
"plan.feature.receivable_payable_accounts": "حسابات العملاء والموردين.",
"plan.feature.expenses_tracking": "تتبع المصروفات",
"plan.feature.manual_journal": "القيود اليدوية",
"plan.feature.financial_reports": "القوائم المالية",
"plan.feature.one_user_with_accountant": "لمستخدم واحد والمحاسب",
"plan.feature.all_capital_essential": "جميع مميزات الباقة الاساسية",
"plan.feature.multi_currency": "تعدد العملات",
"plan.feature.purchase_sell_orders": "أوامر الشراء والبيع.",
"plan.feature.multi_inventory_managment": "تعدد المخازن.",
"plan.feature.three_users": "ثلاثة مستخدمين مع المحاسب",
"plan.feature.advanced_financial_reports": "تقارير المالية المتقدمة",
"plan.feature.tracking_multi_locations": "تتبع الفروع والمواقع",
"plan.feture.all_capital_professional_features": "جميع مميزات الباقة الاحترافية",
"plan.feature.projects_accounting": "محاسبة المشاريع والجداول الزمنية",
"plan.feature.accounting_dimensions": "محاسبة ثلاثية الابعاد",
"plan.monthly": "شهريا",
"plan.yearly": "سنوياً",
"payment_via_voucher.success_message": "تم الدفع وتجديد واشتراكك بنجاح.",
"payment_via_voucher.license_code_not_valid": "رقم الرخصة غير صالح ، يرجي المحاولة مرة أخرى",
"payment_via_voucher.dialog.description": "الرجاء إدخال رقم الرخصة التي استلمتها عند تجديد او طلب الاشتراك .",
"setup.organization.note_you_can_change_your_preferences": "ملاحظة: يمكنك تغيير تفضيلاتك لاحقًا في لوحة التحكم ، إذا لزم الأمر.",
"setup.congrats.title": "تهانينا! حسابك جاهز للعمل",
"setup.congrats.description": "تهانينا ، تم إعداد مؤسستك بنجاح. يمكنك البدء في العمل بمجرد الانتقال إلى لوحة التحكم.",
"setup.congrats.go_to_dashboard": "اذهب إلي لوحة التحكم",
"billing.suspend_message.title": "حسابك معلق :(",
"billing.suspend_message.description": "تم تعليق حسابك بسبب انتهاء فترة الاشتراك. الرجاء تجديد الاشتراك لتفعيل الحساب.",
"dashboard.subscription_msg.period_over": "انتهت فترة الاشتراك"
} }

View File

@@ -578,7 +578,6 @@
"plan_slug": "Plan slug", "plan_slug": "Plan slug",
"billing": "Billing", "billing": "Billing",
"the_billing_has_been_created_successfully": "The billing has been created successfully.", "the_billing_has_been_created_successfully": "The billing has been created successfully.",
"select_a_plan": "Select a plan",
"choose_your_billing": "Choose your billing", "choose_your_billing": "Choose your billing",
"payment_methods": "Payment methods", "payment_methods": "Payment methods",
"usage": "Usage", "usage": "Usage",
@@ -591,7 +590,6 @@
"yearly": "Yearly", "yearly": "Yearly",
"license_code": "License Code", "license_code": "License Code",
"year": "Year", "year": "Year",
"please_enter_your_preferred_payment_method": "Please enter your preferred payment method below. You can use a credit / debit card or prepay through PayPal. ",
"cards_will_be_charged": "Cards will be charged either at the end of the month or whenever your balance exceeds the usage threshold. All major credit / debit cards accepted.", "cards_will_be_charged": "Cards will be charged either at the end of the month or whenever your balance exceeds the usage threshold. All major credit / debit cards accepted.",
"license_number": "License number", "license_number": "License number",
"subscribe": "Subscribe", "subscribe": "Subscribe",
@@ -616,20 +614,15 @@
"purchasable": "Purchasable", "purchasable": "Purchasable",
"sell_account": "Sell Account", "sell_account": "Sell Account",
"cost_account": "Cost Account", "cost_account": "Cost Account",
"register_a_new_organization_now": "Register a New Organization now!.",
"you_have_a_bigcapital_account": "You have a Bigcapital account ",
"contact_us_technical_support": "Contact us - Technical Support", "contact_us_technical_support": "Contact us - Technical Support",
"let_s_get_started": "Lets Get Started",
"tell_the_system_a_little_bit_about_your_organization": "Tell the system a little bit about your organization.",
"organization_details": "Organization details", "organization_details": "Organization details",
"financial_starting_date": "Financial starting date ", "financial_starting_date": "Financial starting date ",
"note_you_can_change_your_preferences_later_in_dashboard_if_needed": "Note: You can change your preferences later in dashboard, if needed.",
"save_continue": "Save & Continue", "save_continue": "Save & Continue",
"organization_register": "Organization Register", "organization_register": "Organization Register",
"fiscal_year_": "Fiscal year", "fiscal_year_": "Fiscal year",
"welcome": "Welcome ", "welcome": "Welcome ",
"sign_out": "Sign out", "sign_out": "Sign out",
"we_re_here_to_help": "Were Here to Help!",
"date_start_": "Date start", "date_start_": "Date start",
"something_wentwrong": "Something went wrong.", "something_wentwrong": "Something went wrong.",
"license_code_": "License code", "license_code_": "License code",
@@ -906,7 +899,6 @@
"accounting": "Accounting", "accounting": "Accounting",
"system": "SYSTEM", "system": "SYSTEM",
"it_s_time_to_send_estimates_to_your_customers": "It's time to send estimates to your customers", "it_s_time_to_send_estimates_to_your_customers": "It's time to send estimates to your customers",
"it_is_a_long_established_fact_that_a_reader": "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.",
"new_sale_estimate": "New sale estimate", "new_sale_estimate": "New sale estimate",
"learn_more": "Learn more", "learn_more": "Learn more",
"back_to_list": "Back to list.", "back_to_list": "Back to list.",
@@ -1068,8 +1060,7 @@
"products_you_buy_and_or_sell": "<strong> Inventory :</strong> Products you buy and/or sell and that you track quantities of.", "products_you_buy_and_or_sell": "<strong> Inventory :</strong> Products you buy and/or sell and that you track quantities of.",
"products_you_buy_and_or_sell_but_don_t_need": "<strong> Non-Inventory:</strong> Products you buy and/or sell but dont need to (or cant) track quantities of, for example, nuts and bolts used in an installation.", "products_you_buy_and_or_sell_but_don_t_need": "<strong> Non-Inventory:</strong> Products you buy and/or sell but dont need to (or cant) track quantities of, for example, nuts and bolts used in an installation.",
"there_is_no_items_in_the_table_yet": "There is no items in the table yet.", "there_is_no_items_in_the_table_yet": "There is no items in the table yet.",
"congrats_you_are_ready_to_go": "Congrats! You are ready to go",
"go_to_dashboard": "Go to dashboard",
"mr": "Mr.", "mr": "Mr.",
"mrs": "Mrs.", "mrs": "Mrs.",
"ms": "Ms.", "ms": "Ms.",
@@ -1101,25 +1092,13 @@
"filter_": "Filter...", "filter_": "Filter...",
"all_rights_reserved": "© {pre}-{current} All Rights Reserved.", "all_rights_reserved": "© {pre}-{current} All Rights Reserved.",
"congrats_your_account_has_been_created_and_invited": "Congrats! Your account has been created and invited to <strong>{organization_name} </strong> organization successfully.", "congrats_your_account_has_been_created_and_invited": "Congrats! Your account has been created and invited to <strong>{organization_name} </strong> organization successfully.",
"it_s_time_to_make_your_accounting_really_simple": "It's time to make your accounting really simple!",
"while_we_set_up_your_account_please_remember_to_verify_your_account": "while we set up your account, please remember to verify your account by clicking on the link we sent to your registered email address",
"something_went_wrong": "Something went wrong!", "something_went_wrong": "Something went wrong!",
"please_refresh_the_page": "Please refresh the page",
"waiting_to_redirect": "Waiting to redirect",
"refresh_the_page_if_redirect_not_worked": "Refresh the page if redirect not worked.",
"blog": "Blog", "blog": "Blog",
"support": "Support", "support": "Support",
"service_status": "Service Status", "service_status": "Service Status",
"pricing": "Pricing", "pricing": "Pricing",
"reseller_partner": "Reseller Partner", "reseller_partner": "Reseller Partner",
"docs": "Docs", "docs": "Docs",
"Pleasse enter your voucher number that you received from reseller.": "Pleasse enter your voucher number that you received from reseller.",
"Sale and purchase invoices.": "Sale and purchase invoices.",
"Customers/vendors accounts.": "Customers/vendors accounts.",
"Expenses tracking.": "Expenses tracking.",
"Manual journals.": "Manual journals.",
"Financial reports.": "Financial reports.",
"All Capital Starter features.": "All Capital Starter features.",
"Multi-currency.": "Multi-currency.", "Multi-currency.": "Multi-currency.",
"Purchase and sell orders.": "Purchase and sell orders.", "Purchase and sell orders.": "Purchase and sell orders.",
"Inventory management.": "Inventory management.", "Inventory management.": "Inventory management.",
@@ -1129,14 +1108,13 @@
"Track multi-branches and locations.": "Track multi-branches and locations.", "Track multi-branches and locations.": "Track multi-branches and locations.",
"Projects accounting and timesheets.": "Projects accounting and timesheets.", "Projects accounting and timesheets.": "Projects accounting and timesheets.",
"Accounting dimensions.": "Accounting dimensions.", "Accounting dimensions.": "Accounting dimensions.",
"For one user and accountant.": "For one user and accountant.",
"Monthly": "Monthly", "Monthly": "Monthly",
"Yearly": "Yearly", "Yearly": "Yearly",
"Plans & Payment": "Plans & Payment", "Plans & Payment": "Plans & Payment",
"Initializing": "Initializing", "Initializing": "Initializing",
"Getting started": "Getting started", "Getting started": "Getting started",
"Congratulations": "Congratulations", "Congratulations": "Congratulations",
"payment_has_been_done_successfully": "Payment has been done successfully.",
"manual_journal_number": "Manual journal {number}", "manual_journal_number": "Manual journal {number}",
"conditions_and_terms": "Conditions and terms", "conditions_and_terms": "Conditions and terms",
"allocate_landed_coast": "Allocate landed cost", "allocate_landed_coast": "Allocate landed cost",
@@ -1217,5 +1195,57 @@
"payment_receive.details.payment_number": "Payment #", "payment_receive.details.payment_number": "Payment #",
"estimate.details.estimate_number": "Estimate #", "estimate.details.estimate_number": "Estimate #",
"receipt.details.receipt_number": "Receipt #", "receipt.details.receipt_number": "Receipt #",
"bill.details.bill_number": "Bill #" "bill.details.bill_number": "Bill #",
"setup.left_side.title": "Register a New Organization now!.",
"setup.left_side.description": "You have a Bigcapital account",
"setup.left_side.footer_help": "Were Here to Help!",
"setup.plan.plans": "Plans & Payment",
"setup.plan.initializing": "Initializing",
"setup.plan.getting_started": "Getting started",
"setup.plan.congrats": "Congratulations",
"setup.plans.select_plan.description": "Please enter your preferred payment method below. You can use a credit / debit card or prepay through PayPal. ",
"setup.plans.select_plan.title": "Select a plan",
"setup.plans.select_period.title": "Plan period",
"setup.plans.select_period.description": "The plan could be billed monthly or annually.",
"setup.plans.payment_methods.title": "Payment methods",
"setup.plans.payment_methods.description": "",
"setup.initializing.title": "It's time to make your accounting really simple!",
"setup.initializing.description": "while we set up your account, please remember to verify your account by clicking on the link we sent to your registered email address",
"setup.initializing.waiting_to_redirect": "Waiting to redirect",
"setup.initializing.refresh_the_page_if_redirect_not_worked": "Refresh the page if redirect not worked.",
"setup.initializing.something_went_wrong": "Something went wrong!",
"setup.initializing.please_refresh_the_page": "Please refresh the page",
"setup.organization.title": "Lets Get Started",
"setup.organization.description": "Tell the system a little bit about your organization.",
"plan.professional.title": "Pro",
"plan.essential.title": "Essential",
"plan.plus.title": "Plus+",
"plan.feature.sale_purchase_invoice": "Sale and purchase invoices.",
"plan.feature.receivable_payable_accounts": "Customers/vendors accounts.",
"plan.feature.expenses_tracking": "Expenses tracking",
"plan.feature.manual_journal": "Manual journals.",
"plan.feature.financial_reports": "Financial reports.",
"plan.feature.one_user_with_accountant": "For one user and accountant",
"plan.feture.all_capital_professional_features": "All Capital Pro features.",
"plan.feature.multi_currency": "Multi-currency.",
"plan.feature.purchase_sell_orders": "Purchase and sell orders.",
"plan.feature.multi_inventory_managment": "Mutli-inventory managment.",
"plan.feature.three_users": "Three users with your accountant",
"plan.feature.advanced_financial_reports": "Advanced financial reports",
"plan.feature.tracking_multi_locations": "Track multi-branches and locations",
"plan.feature.all_capital_essential": "All Capital Essential features.",
"plan.feature.projects_accounting": "Projects accounting and timesheets",
"plan.feature.accounting_dimensions": "Accounting dimensions.",
"plan.monthly": "Monthly",
"plan.yearly": "Yearly",
"payment_via_voucher.success_message": "Payment has been done successfully.",
"payment_via_voucher.license_code_not_valid": "The license code is not valid, please try agin.",
"payment_via_voucher.dialog.description": "Pleasse enter your voucher number that you received from reseller.",
"setup.organization.note_you_can_change_your_preferences": "Note: You can change your preferences later in dashboard, if needed.",
"setup.congrats.title": "Congrats! You are ready to go",
"setup.congrats.description": "Congrats, Your organization is set up successfully. you can start working once go to dashboard.",
"setup.congrats.go_to_dashboard": "Go to dashboard",
"billing.suspend_message.title": "Your account is suspended :(",
"billing.suspend_message.description": "Your account has been suspended due to the expiration of the subscription period. Please renew the subscription to activate the account.",
"dashboard.subscription_msg.period_over": "Subscription period is over"
} }

View File

@@ -3,10 +3,14 @@ import { lazy } from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { RESOURCES_TYPES } from '../common/resourcesTypes'; import { RESOURCES_TYPES } from '../common/resourcesTypes';
const SUBSCRIPTION_TYPE = {
MAIN: 'main',
}
// const BASE_URL = '/dashboard'; // const BASE_URL = '/dashboard';
export const getDashboardRoutes = () => [ export const getDashboardRoutes = () => [
// // Accounts. // Accounts.
{ {
path: `/accounts`, path: `/accounts`,
component: lazy(() => import('containers/Accounts/AccountsChart')), component: lazy(() => import('containers/Accounts/AccountsChart')),
@@ -14,6 +18,7 @@ export const getDashboardRoutes = () => [
hotkey: 'shift+a', hotkey: 'shift+a',
pageTitle: intl.get('accounts_chart'), pageTitle: intl.get('accounts_chart'),
defaultSearchResource: RESOURCES_TYPES.ACCOUNT, defaultSearchResource: RESOURCES_TYPES.ACCOUNT,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
// Accounting. // Accounting.
{ {
@@ -27,6 +32,7 @@ export const getDashboardRoutes = () => [
sidebarExpand: false, sidebarExpand: false,
backLink: true, backLink: true,
defaultSearchResource: RESOURCES_TYPES.MANUAL_JOURNAL, defaultSearchResource: RESOURCES_TYPES.MANUAL_JOURNAL,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/manual-journals/:id/edit`, path: `/manual-journals/:id/edit`,
@@ -38,6 +44,7 @@ export const getDashboardRoutes = () => [
sidebarExpand: false, sidebarExpand: false,
backLink: true, backLink: true,
defaultSearchResource: RESOURCES_TYPES.MANUAL_JOURNAL, defaultSearchResource: RESOURCES_TYPES.MANUAL_JOURNAL,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/manual-journals`, path: `/manual-journals`,
@@ -48,6 +55,7 @@ export const getDashboardRoutes = () => [
hotkey: 'shift+m', hotkey: 'shift+m',
pageTitle: intl.get('manual_journals'), pageTitle: intl.get('manual_journals'),
defaultSearchResource: RESOURCES_TYPES.MANUAL_JOURNAL, defaultSearchResource: RESOURCES_TYPES.MANUAL_JOURNAL,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/items/categories`, path: `/items/categories`,
@@ -57,6 +65,7 @@ export const getDashboardRoutes = () => [
breadcrumb: intl.get('categories'), breadcrumb: intl.get('categories'),
pageTitle: intl.get('category_list'), pageTitle: intl.get('category_list'),
defaultSearchResource: RESOURCES_TYPES.ITEM, defaultSearchResource: RESOURCES_TYPES.ITEM,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
// Items. // Items.
{ {
@@ -67,6 +76,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('edit_item'), pageTitle: intl.get('edit_item'),
backLink: true, backLink: true,
defaultSearchResource: RESOURCES_TYPES.ITEM, defaultSearchResource: RESOURCES_TYPES.ITEM,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/items/new?duplicate=/:id`, path: `/items/new?duplicate=/:id`,
@@ -75,6 +85,7 @@ export const getDashboardRoutes = () => [
}), }),
breadcrumb: intl.get('duplicate_item'), breadcrumb: intl.get('duplicate_item'),
defaultSearchResource: RESOURCES_TYPES.ITEM, defaultSearchResource: RESOURCES_TYPES.ITEM,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/items/new`, path: `/items/new`,
@@ -85,6 +96,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('new_item'), pageTitle: intl.get('new_item'),
backLink: true, backLink: true,
defaultSearchResource: RESOURCES_TYPES.ITEM, defaultSearchResource: RESOURCES_TYPES.ITEM,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/items`, path: `/items`,
@@ -93,6 +105,7 @@ export const getDashboardRoutes = () => [
hotkey: 'shift+w', hotkey: 'shift+w',
pageTitle: intl.get('items_list'), pageTitle: intl.get('items_list'),
defaultSearchResource: RESOURCES_TYPES.ITEM, defaultSearchResource: RESOURCES_TYPES.ITEM,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
// Inventory adjustments. // Inventory adjustments.
@@ -104,6 +117,7 @@ export const getDashboardRoutes = () => [
breadcrumb: intl.get('inventory_adjustments'), breadcrumb: intl.get('inventory_adjustments'),
pageTitle: intl.get('inventory_adjustment_list'), pageTitle: intl.get('inventory_adjustment_list'),
defaultSearchResource: RESOURCES_TYPES.ITEM, defaultSearchResource: RESOURCES_TYPES.ITEM,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
// Financial Reports. // Financial Reports.
@@ -119,6 +133,7 @@ export const getDashboardRoutes = () => [
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
defaultSearchResource: RESOURCES_TYPES.INVENTORY_ADJUSTMENT, defaultSearchResource: RESOURCES_TYPES.INVENTORY_ADJUSTMENT,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/financial-reports/balance-sheet`, path: `/financial-reports/balance-sheet`,
@@ -131,6 +146,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('balance_sheet'), pageTitle: intl.get('balance_sheet'),
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/financial-reports/trial-balance-sheet`, path: `/financial-reports/trial-balance-sheet`,
@@ -145,6 +161,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('trial_balance_sheet'), pageTitle: intl.get('trial_balance_sheet'),
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/financial-reports/profit-loss-sheet`, path: `/financial-reports/profit-loss-sheet`,
@@ -157,6 +174,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('profit_loss_sheet'), pageTitle: intl.get('profit_loss_sheet'),
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: '/financial-reports/receivable-aging-summary', path: '/financial-reports/receivable-aging-summary',
@@ -168,6 +186,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('receivable_aging_summary'), pageTitle: intl.get('receivable_aging_summary'),
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: '/financial-reports/payable-aging-summary', path: '/financial-reports/payable-aging-summary',
@@ -179,6 +198,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('payable_aging_summary'), pageTitle: intl.get('payable_aging_summary'),
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/financial-reports/journal-sheet`, path: `/financial-reports/journal-sheet`,
@@ -191,6 +211,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('journal_sheet'), pageTitle: intl.get('journal_sheet'),
sidebarExpand: false, sidebarExpand: false,
backLink: true, backLink: true,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/financial-reports/purchases-by-items`, path: `/financial-reports/purchases-by-items`,
@@ -204,6 +225,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('purchases_by_items'), pageTitle: intl.get('purchases_by_items'),
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/financial-reports/sales-by-items`, path: `/financial-reports/sales-by-items`,
@@ -217,6 +239,7 @@ export const getDashboardRoutes = () => [
), ),
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/financial-reports/inventory-valuation`, path: `/financial-reports/inventory-valuation`,
@@ -230,6 +253,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('inventory_valuation'), pageTitle: intl.get('inventory_valuation'),
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/financial-reports/customers-balance-summary`, path: `/financial-reports/customers-balance-summary`,
@@ -243,6 +267,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('customers_balance_summary'), pageTitle: intl.get('customers_balance_summary'),
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/financial-reports/vendors-balance-summary`, path: `/financial-reports/vendors-balance-summary`,
@@ -256,6 +281,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('vendors_balance_summary'), pageTitle: intl.get('vendors_balance_summary'),
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/financial-reports/transactions-by-customers`, path: `/financial-reports/transactions-by-customers`,
@@ -269,6 +295,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('customers_transactions'), pageTitle: intl.get('customers_transactions'),
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/financial-reports/transactions-by-vendors`, path: `/financial-reports/transactions-by-vendors`,
@@ -282,6 +309,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('vendors_transactions'), pageTitle: intl.get('vendors_transactions'),
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/financial-reports/cash-flow`, path: `/financial-reports/cash-flow`,
@@ -295,6 +323,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('cash_flow_statement'), pageTitle: intl.get('cash_flow_statement'),
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/financial-reports/inventory-item-details`, path: `/financial-reports/inventory-item-details`,
@@ -308,6 +337,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('inventory_item_details'), pageTitle: intl.get('inventory_item_details'),
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: '/financial-reports', path: '/financial-reports',
@@ -316,6 +346,7 @@ export const getDashboardRoutes = () => [
), ),
breadcrumb: intl.get('financial_reports'), breadcrumb: intl.get('financial_reports'),
pageTitle: intl.get('all_financial_reports'), pageTitle: intl.get('all_financial_reports'),
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
// Exchange Rates // Exchange Rates
{ {
@@ -323,6 +354,7 @@ export const getDashboardRoutes = () => [
component: lazy(() => import('containers/ExchangeRates/ExchangeRatesList')), component: lazy(() => import('containers/ExchangeRates/ExchangeRatesList')),
breadcrumb: intl.get('exchange_rates_list'), breadcrumb: intl.get('exchange_rates_list'),
pageTitle: intl.get('exchange_rates_list'), pageTitle: intl.get('exchange_rates_list'),
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
// Expenses. // Expenses.
{ {
@@ -336,6 +368,7 @@ export const getDashboardRoutes = () => [
sidebarExpand: false, sidebarExpand: false,
backLink: true, backLink: true,
defaultSearchResource: RESOURCES_TYPES.EXPENSE, defaultSearchResource: RESOURCES_TYPES.EXPENSE,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/expenses/:id/edit`, path: `/expenses/:id/edit`,
@@ -347,6 +380,7 @@ export const getDashboardRoutes = () => [
sidebarExpand: false, sidebarExpand: false,
backLink: true, backLink: true,
defaultSearchResource: RESOURCES_TYPES.EXPENSE, defaultSearchResource: RESOURCES_TYPES.EXPENSE,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/expenses`, path: `/expenses`,
@@ -357,6 +391,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('expenses_list'), pageTitle: intl.get('expenses_list'),
hotkey: 'shift+x', hotkey: 'shift+x',
defaultSearchResource: RESOURCES_TYPES.EXPENSE, defaultSearchResource: RESOURCES_TYPES.EXPENSE,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
// Customers // Customers
@@ -370,6 +405,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('edit_customer'), pageTitle: intl.get('edit_customer'),
backLink: true, backLink: true,
defaultSearchResource: RESOURCES_TYPES.CUSTOMER, defaultSearchResource: RESOURCES_TYPES.CUSTOMER,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/customers/new`, path: `/customers/new`,
@@ -382,6 +418,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('new_customer'), pageTitle: intl.get('new_customer'),
backLink: true, backLink: true,
defaultSearchResource: RESOURCES_TYPES.CUSTOMER, defaultSearchResource: RESOURCES_TYPES.CUSTOMER,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/customers`, path: `/customers`,
@@ -392,6 +429,7 @@ export const getDashboardRoutes = () => [
hotkey: 'shift+c', hotkey: 'shift+c',
pageTitle: intl.get('customers_list'), pageTitle: intl.get('customers_list'),
defaultSearchResource: RESOURCES_TYPES.CUSTOMER, defaultSearchResource: RESOURCES_TYPES.CUSTOMER,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/customers/contact_duplicate=/:id`, path: `/customers/contact_duplicate=/:id`,
@@ -403,6 +441,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('new_customer'), pageTitle: intl.get('new_customer'),
backLink: true, backLink: true,
defaultSearchResource: RESOURCES_TYPES.CUSTOMER, defaultSearchResource: RESOURCES_TYPES.CUSTOMER,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
// Vendors // Vendors
@@ -416,6 +455,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('edit_vendor'), pageTitle: intl.get('edit_vendor'),
backLink: true, backLink: true,
defaultSearchResource: RESOURCES_TYPES.VENDOR, defaultSearchResource: RESOURCES_TYPES.VENDOR,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/vendors/new`, path: `/vendors/new`,
@@ -428,6 +468,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('new_vendor'), pageTitle: intl.get('new_vendor'),
backLink: true, backLink: true,
defaultSearchResource: RESOURCES_TYPES.VENDOR, defaultSearchResource: RESOURCES_TYPES.VENDOR,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/vendors`, path: `/vendors`,
@@ -438,6 +479,7 @@ export const getDashboardRoutes = () => [
hotkey: 'shift+v', hotkey: 'shift+v',
pageTitle: intl.get('vendors_list'), pageTitle: intl.get('vendors_list'),
defaultSearchResource: RESOURCES_TYPES.VENDOR, defaultSearchResource: RESOURCES_TYPES.VENDOR,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/vendors/contact_duplicate=/:id`, path: `/vendors/contact_duplicate=/:id`,
@@ -449,6 +491,7 @@ export const getDashboardRoutes = () => [
pageTitle: intl.get('new_vendor'), pageTitle: intl.get('new_vendor'),
backLink: true, backLink: true,
defaultSearchResource: RESOURCES_TYPES.VENDOR, defaultSearchResource: RESOURCES_TYPES.VENDOR,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
// Estimates // Estimates
@@ -463,6 +506,7 @@ export const getDashboardRoutes = () => [
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
defaultSearchResource: RESOURCES_TYPES.ESTIMATE, defaultSearchResource: RESOURCES_TYPES.ESTIMATE,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/invoices/new?from_estimate_id=/:id`, path: `/invoices/new?from_estimate_id=/:id`,
@@ -475,6 +519,7 @@ export const getDashboardRoutes = () => [
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
defaultSearchResource: RESOURCES_TYPES.INVOICE, defaultSearchResource: RESOURCES_TYPES.INVOICE,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/estimates/new`, path: `/estimates/new`,
@@ -488,6 +533,7 @@ export const getDashboardRoutes = () => [
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
defaultSearchResource: RESOURCES_TYPES.ESTIMATE, defaultSearchResource: RESOURCES_TYPES.ESTIMATE,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/estimates`, path: `/estimates`,
@@ -499,6 +545,7 @@ export const getDashboardRoutes = () => [
hotkey: 'shift+e', hotkey: 'shift+e',
pageTitle: intl.get('estimates_list'), pageTitle: intl.get('estimates_list'),
defaultSearchResource: RESOURCES_TYPES.ESTIMATE, defaultSearchResource: RESOURCES_TYPES.ESTIMATE,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
// Invoices. // Invoices.
@@ -513,6 +560,7 @@ export const getDashboardRoutes = () => [
sidebarExpand: false, sidebarExpand: false,
backLink: true, backLink: true,
defaultSearchResource: RESOURCES_TYPES.INVOICE, defaultSearchResource: RESOURCES_TYPES.INVOICE,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/invoices/new`, path: `/invoices/new`,
@@ -526,6 +574,7 @@ export const getDashboardRoutes = () => [
sidebarExpand: false, sidebarExpand: false,
backLink: true, backLink: true,
defaultSearchResource: RESOURCES_TYPES.INVOICE, defaultSearchResource: RESOURCES_TYPES.INVOICE,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/invoices`, path: `/invoices`,
@@ -536,6 +585,7 @@ export const getDashboardRoutes = () => [
hotkey: 'shift+i', hotkey: 'shift+i',
pageTitle: intl.get('invoices_list'), pageTitle: intl.get('invoices_list'),
defaultSearchResource: RESOURCES_TYPES.INVOICE, defaultSearchResource: RESOURCES_TYPES.INVOICE,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
// Sales Receipts. // Sales Receipts.
@@ -550,6 +600,7 @@ export const getDashboardRoutes = () => [
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
defaultSearchResource: RESOURCES_TYPES.RECEIPT, defaultSearchResource: RESOURCES_TYPES.RECEIPT,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/receipts/new`, path: `/receipts/new`,
@@ -563,6 +614,7 @@ export const getDashboardRoutes = () => [
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
defaultSearchResource: RESOURCES_TYPES.RECEIPT, defaultSearchResource: RESOURCES_TYPES.RECEIPT,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/receipts`, path: `/receipts`,
@@ -573,6 +625,7 @@ export const getDashboardRoutes = () => [
hotkey: 'shift+r', hotkey: 'shift+r',
pageTitle: intl.get('receipts_list'), pageTitle: intl.get('receipts_list'),
defaultSearchResource: RESOURCES_TYPES.RECEIPT, defaultSearchResource: RESOURCES_TYPES.RECEIPT,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
// Payment receives // Payment receives
@@ -589,6 +642,7 @@ export const getDashboardRoutes = () => [
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
defaultSearchResource: RESOURCES_TYPES.PAYMENT_RECEIVE, defaultSearchResource: RESOURCES_TYPES.PAYMENT_RECEIVE,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/payment-receives/new`, path: `/payment-receives/new`,
@@ -603,6 +657,7 @@ export const getDashboardRoutes = () => [
backLink: true, backLink: true,
sidebarExpand: false, sidebarExpand: false,
defaultSearchResource: RESOURCES_TYPES.PAYMENT_RECEIVE, defaultSearchResource: RESOURCES_TYPES.PAYMENT_RECEIVE,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/payment-receives`, path: `/payment-receives`,
@@ -614,6 +669,7 @@ export const getDashboardRoutes = () => [
breadcrumb: intl.get('payment_receives_list'), breadcrumb: intl.get('payment_receives_list'),
pageTitle: intl.get('payment_receives_list'), pageTitle: intl.get('payment_receives_list'),
defaultSearchResource: RESOURCES_TYPES.PAYMENT_RECEIVE, defaultSearchResource: RESOURCES_TYPES.PAYMENT_RECEIVE,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
// Bills // Bills
@@ -628,6 +684,7 @@ export const getDashboardRoutes = () => [
sidebarExpand: false, sidebarExpand: false,
backLink: true, backLink: true,
defaultSearchResource: RESOURCES_TYPES.BILL, defaultSearchResource: RESOURCES_TYPES.BILL,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/bills/new`, path: `/bills/new`,
@@ -641,6 +698,7 @@ export const getDashboardRoutes = () => [
sidebarExpand: false, sidebarExpand: false,
backLink: true, backLink: true,
defaultSearchResource: RESOURCES_TYPES.BILL, defaultSearchResource: RESOURCES_TYPES.BILL,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/bills`, path: `/bills`,
@@ -651,6 +709,7 @@ export const getDashboardRoutes = () => [
hotkey: 'shift+b', hotkey: 'shift+b',
pageTitle: intl.get('bills_list'), pageTitle: intl.get('bills_list'),
defaultSearchResource: RESOURCES_TYPES.BILL, defaultSearchResource: RESOURCES_TYPES.BILL,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
// Subscription billing. // Subscription billing.
@@ -658,6 +717,7 @@ export const getDashboardRoutes = () => [
path: `/billing`, path: `/billing`,
component: lazy(() => import('containers/Subscriptions/BillingForm')), component: lazy(() => import('containers/Subscriptions/BillingForm')),
breadcrumb: intl.get('new_billing'), breadcrumb: intl.get('new_billing'),
subscriptionInactive: [SUBSCRIPTION_TYPE.MAIN]
}, },
// Payment modes. // Payment modes.
{ {
@@ -673,6 +733,7 @@ export const getDashboardRoutes = () => [
sidebarExpand: false, sidebarExpand: false,
backLink: true, backLink: true,
defaultSearchResource: RESOURCES_TYPES.PAYMENT_MADE, defaultSearchResource: RESOURCES_TYPES.PAYMENT_MADE,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/payment-mades/new`, path: `/payment-mades/new`,
@@ -687,6 +748,7 @@ export const getDashboardRoutes = () => [
sidebarExpand: false, sidebarExpand: false,
backLink: true, backLink: true,
defaultSearchResource: RESOURCES_TYPES.PAYMENT_MADE, defaultSearchResource: RESOURCES_TYPES.PAYMENT_MADE,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{ {
path: `/payment-mades`, path: `/payment-mades`,
@@ -698,11 +760,13 @@ export const getDashboardRoutes = () => [
breadcrumb: intl.get('payment_made_list'), breadcrumb: intl.get('payment_made_list'),
pageTitle: intl.get('payment_made_list'), pageTitle: intl.get('payment_made_list'),
defaultSearchResource: RESOURCES_TYPES.PAYMENT_MADE, defaultSearchResource: RESOURCES_TYPES.PAYMENT_MADE,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
// Homepage // Homepage
{ {
path: `/`, path: `/`,
component: lazy(() => import('containers/Homepage/Homepage')), component: lazy(() => import('containers/Homepage/Homepage')),
breadcrumb: intl.get('homepage'), breadcrumb: intl.get('homepage'),
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
]; ];

View File

@@ -1,69 +1,95 @@
import React from 'react';
import { createReducer } from '@reduxjs/toolkit'; import { createReducer } from '@reduxjs/toolkit';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import t from 'store/types'; import t from 'store/types';
const getSubscriptionPlans = () => [
{
name: intl.get('Starter'),
slug: 'free',
// slug: 'starter',
description: [
intl.get('Sale and purchase invoices.'),
intl.get('Customers/vendors accounts.'),
intl.get('Expenses tracking'),
intl.get('Manual journals.'),
intl.get('Financial reports.'),
intl.get('For one user and accountant.'),
],
price: {
month: '100',
year: '1,200',
},
currencyCode: 'LYD',
},
{
name: intl.get('Essential'),
slug: 'plus',
description: [
intl.get('All Capital Starter features.'),
intl.get('Multi-currency.'),
intl.get('Purchase and sell orders.'),
intl.get('Inventory management.'),
intl.get('Three users with your accountant.'),
intl.get('Advanced financial reports.'),
],
price: {
month: '200',
year: '2,400',
},
currencyCode: 'LYD',
},
{
name: intl.get('Enterprise'),
slug: 'enterprise',
description: [
intl.get('All Capital Essential features.'),
intl.get('Track multi-branches and locations.'),
intl.get('Projects accounting and timesheets.'),
intl.get('Accounting dimensions.'),
],
price: {
month: '300',
year: '3,400',
},
currencyCode: 'LYD',
},
];
const getSubscriptionPeriods = () => [ const getSubscriptionPeriods = () => [
{ {
slug: 'month', slug: 'month',
label: intl.get('Monthly'), label: intl.get('plan.monthly'),
}, },
{ {
slug: 'year', slug: 'year',
label: intl.get('Yearly'), label: intl.get('plan.yearly'),
},
];
const getSubscriptionPlans = () => [
{
name: intl.get('plan.essential.title'),
slug: 'free',
description: [
intl.get('plan.feature.sale_purchase_invoice'),
intl.get('plan.feature.receivable_payable_accounts'),
intl.get('plan.feature.expenses_tracking'),
intl.get('plan.feature.manual_journal'),
intl.get('plan.feature.financial_reports'),
intl.get('plan.feature.one_user_with_accountant'),
],
price: '100',
periods: [
{
slug: 'month',
label: intl.get('plan.monthly'),
price: '100'
},
{
slug: 'year',
label: intl.get('plan.yearly'),
price: '1,200',
},
],
currencyCode: 'LYD',
},
{
name: intl.get('plan.professional.title'),
slug: 'plus',
description: [
intl.get('plan.feature.all_capital_essential'),
intl.get('plan.feature.multi_currency'),
intl.get('plan.feature.purchase_sell_orders'),
intl.get('plan.feature.multi_inventory_managment'),
intl.get('plan.feature.three_users'),
intl.get('plan.feature.advanced_financial_reports'),
],
price: '200',
currencyCode: 'LYD',
periods: [
{
slug: 'month',
label: intl.get('plan.monthly'),
price: '200'
},
{
slug: 'year',
label: intl.get('plan.yearly'),
price: '1,200',
},
],
},
{
name: intl.get('plan.plus.title'),
slug: 'enterprise',
description: [
intl.get('plan.feture.all_capital_professional_features'),
intl.get('plan.feature.tracking_multi_locations'),
intl.get('plan.feature.projects_accounting'),
intl.get('plan.feature.accounting_dimensions'),
],
price: '300',
currencyCode: 'LYD',
periods: [
{
slug: 'month',
label: intl.get('plan.monthly'),
price: '300'
},
{
slug: 'year',
label: intl.get('plan.yearly'),
price: '1,200',
},
],
}, },
]; ];

View File

@@ -4,8 +4,6 @@ const plansSelector = (state) => state.plans.plans;
const planSelector = (state, props) => state.plans.plans const planSelector = (state, props) => state.plans.plans
.find((plan) => plan.slug === props.planSlug); .find((plan) => plan.slug === props.planSlug);
const plansPeriodsSelector = (state) => state.plans.periods;
// Retrieve manual jounral current page results. // Retrieve manual jounral current page results.
export const getPlansSelector = () => createSelector( export const getPlansSelector = () => createSelector(
plansSelector, plansSelector,
@@ -14,14 +12,6 @@ export const getPlansSelector = () => createSelector(
}, },
); );
// Retrieve manual jounral current page results.
export const getPlansPeriodsSelector = () => createSelector(
plansPeriodsSelector,
(periods) => {
return periods;
},
);
// Retrieve plan details. // Retrieve plan details.
export const getPlanSelector = () => createSelector( export const getPlanSelector = () => createSelector(
planSelector, planSelector,

View File

@@ -1,23 +1,45 @@
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { includes } from 'lodash';
const subscriptionSelector = (slug) => (state, props) => { const subscriptionSelector = (slug) => (state, props) => {
const subscriptions = Object.values(state.subscriptions.data); const subscriptions = Object.values(state.subscriptions.data);
return subscriptions.find((subscription) => subscription.slug === slug); return subscriptions.find(
(subscription) => subscription.slug === (slug || props.subscriptionType),
);
}; };
export const isSubscriptionOnTrialFactory = (slug) => createSelector( const subscriptionsSelector = (state, props) => {
const subscriptions = Object.values(state.subscriptions.data);
return subscriptions.filter(
(subscription) =>
includes(props.subscriptionTypes, subscription.slug) ||
!props.subscriptionTypes,
);
};
export const isSubscriptionOnTrialFactory = (slug) =>
createSelector(
subscriptionSelector(slug), subscriptionSelector(slug),
(subscription) => !!subscription?.on_trial, (subscription) => !!subscription?.on_trial,
); );
export const isSubscriptionActiveFactory = (slug) => createSelector( export const isSubscriptionActiveFactory = (slug) =>
subscriptionSelector(slug), createSelector(subscriptionSelector(slug), (subscription) => {
(subscription) => {
return !!subscription?.active; return !!subscription?.active;
} });
);
export const isSubscriptionInactiveFactory = (slug) => createSelector( export const isSubscriptionInactiveFactory = (slug) =>
createSelector(
subscriptionSelector(slug), subscriptionSelector(slug),
(subscription) => !!subscription?.inactive, (subscription) => !!subscription?.inactive,
); );
export const isSubscriptionsInactiveFactory = () =>
createSelector(subscriptionsSelector, (subscriptions) =>
subscriptions.some((subscription) => subscription?.inactive),
);
export const isSubscriptionsActiveFactory = () =>
createSelector(subscriptionsSelector, (subscriptions) =>
subscriptions.some((subscription) => subscription?.active),
);

View File

@@ -69,12 +69,14 @@
&:not([class*="bp3-intent-"]):not(.bp3-minimal) { &:not([class*="bp3-intent-"]):not(.bp3-minimal) {
color: rgb(255, 255, 255); color: rgb(255, 255, 255);
} }
&:hover, &:hover,
&:focus, &:focus,
&:active, &:active,
&.bp3-active { &.bp3-active {
background: transparent; background: transparent;
} }
.bp3-button-text { .bp3-button-text {
margin-right: 4px; margin-right: 4px;
text-overflow: ellipsis; text-overflow: ellipsis;
@@ -82,6 +84,7 @@
white-space: nowrap; white-space: nowrap;
display: block; display: block;
} }
svg { svg {
fill: rgba(255, 255, 255, 0.3); fill: rgba(255, 255, 255, 0.3);
} }
@@ -104,6 +107,10 @@
.sidebar__head-logo { .sidebar__head-logo {
transition: transform 0.05s ease-in-out; transition: transform 0.05s ease-in-out;
} }
.is-subscription-inactive:not(.sidebar--mini-sidebar) & {
opacity: 0.6;
}
} }
&__scroll-wrapper { &__scroll-wrapper {
@@ -188,6 +195,7 @@
padding-bottom: 6px; padding-bottom: 6px;
padding-top: 6px; padding-top: 6px;
} }
.#{$ns}-menu-item { .#{$ns}-menu-item {
padding: 8px 20px; padding: 8px 20px;
font-size: 15px; font-size: 15px;
@@ -242,10 +250,10 @@
opacity: 1; opacity: 1;
visibility: visible; visibility: visible;
} }
// Hide text of bigcapital logo.
&-logo {
} // Hide text of bigcapital logo.
&-logo {}
&-organization { &-organization {
opacity: 0; opacity: 0;
transition: opacity 0.3s ease-in-out; transition: opacity 0.3s ease-in-out;
@@ -326,8 +334,10 @@
} }
} }
} }
.menu--dashboard-organization { .menu--dashboard-organization {
padding: 10px; padding: 10px;
.org-item { .org-item {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@@ -2,7 +2,7 @@
.billing-page{ .billing-page{
padding: 0 60px; padding: 0 60px;
margin-top: 20px; margin-top: 40px;
max-width: 820px; max-width: 820px;
.bp3-tab-list{ .bp3-tab-list{

View File

@@ -95,6 +95,17 @@ $dashboard-views-bar-height: 44px;
} }
} }
} }
&-subscription-msg{
display: flex;
flex-direction: row;
padding-right: 6px;
font-size: 12px;
span{
margin: auto;
}
}
} }
&__breadcrumbs { &__breadcrumbs {

View File

@@ -1,5 +1,5 @@
.setup-congrats { .setup-congrats {
width: 600px; width: 500px;
margin: 0 auto; margin: 0 auto;
text-align: center; text-align: center;
padding-top: 80px; padding-top: 80px;
@@ -15,7 +15,7 @@
margin-bottom: 12px; margin-bottom: 12px;
} }
.paragraph { .paragraph {
font-size: 15px; font-size: 16px;
opacity: 0.85; opacity: 0.85;
margin-bottom: 14px; margin-bottom: 14px;
} }

View File

@@ -19,7 +19,9 @@
form { form {
h3 { h3 {
margin-bottom: 1rem; color: #4e5764;
margin-bottom: 1.2rem;
font-weight: 500;
} }
} }

View File

@@ -1,3 +1,5 @@
@import "../../Base.scss";
.setup-page { .setup-page {
max-width: 1600px; max-width: 1600px;
@@ -9,15 +11,21 @@
&:before { &:before {
content: ''; content: '';
display: block; display: block;
width: 30%; width: 100%;
height: 1px; height: 1px;
max-width: 350px;
min-width: 350px;
@media only screen and (max-width: 1200px) {
min-width: 300px; min-width: 300px;
max-width: 350px; max-width: 350px;
} }
}
h1 { h1 {
font-size: 22px; font-size: 22px;
} }
h1, h1,
h3 { h3 {
font-weight: 500; font-weight: 500;
@@ -36,11 +44,14 @@
overflow: auto; overflow: auto;
z-index: 1; z-index: 1;
height: 100%; height: 100%;
width: 30%; width: 100%;
left: 0; left: 0;
top: 0; top: 0;
max-width: 350px; width: 350px;
min-width: 300px;
@media only screen and (max-width: 1200px) {
width: 300px;
}
.content { .content {
display: flex; display: flex;
@@ -101,6 +112,7 @@
opacity: 0.75; opacity: 0.75;
padding-bottom: 5px; padding-bottom: 5px;
border-bottom: 1px solid rgba(255, 255, 255, 0.15); border-bottom: 1px solid rgba(255, 255, 255, 0.15);
p>span { p>span {
unicode-bidi: plaintext; unicode-bidi: plaintext;
} }
@@ -133,9 +145,11 @@
margin: 0 auto; margin: 0 auto;
padding: 50px 0 0; padding: 50px 0 0;
} }
ul { ul {
display: flex; display: flex;
} }
li { li {
position: relative; position: relative;
list-style-type: none; list-style-type: none;
@@ -155,6 +169,7 @@
border-radius: 50%; border-radius: 50%;
background-color: #75859c; background-color: #75859c;
} }
&::after { &::after {
width: 100%; width: 100%;
height: 2px; height: 2px;
@@ -165,21 +180,26 @@
left: -50%; left: -50%;
z-index: -1; z-index: -1;
} }
&:first-child::after { &:first-child::after {
display: none; display: none;
} }
&.is-active { &.is-active {
text-decoration: underline; text-decoration: underline;
&::before { &::before {
background-color: #75859c; background-color: #75859c;
} }
~li { ~li {
&:before, &:before,
&:after { &:after {
background: #e0e0e0; background: #e0e0e0;
} }
} }
p.wizard-info { p.wizard-info {
color: #004dd0; color: #004dd0;
} }