mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 04:40:32 +00:00
feat: ensure to access dashboard user's subscription is active.
This commit is contained in:
@@ -2,15 +2,15 @@ import intl from 'react-intl-universal';
|
||||
|
||||
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'),
|
||||
},
|
||||
];
|
||||
17
client/src/components/Alert/index.js
Normal file
17
client/src/components/Alert/index.js
Normal 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>
|
||||
);
|
||||
}
|
||||
32
client/src/components/Alert/style.module.scss
Normal file
32
client/src/components/Alert/style.module.scss
Normal 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;
|
||||
}
|
||||
@@ -12,6 +12,33 @@ import DashboardSplitPane from 'components/Dashboard/DashboardSplitePane';
|
||||
import GlobalHotkeys from './GlobalHotkeys';
|
||||
import DashboardProvider from './DashboardProvider';
|
||||
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.
|
||||
@@ -20,19 +47,8 @@ export default function Dashboard() {
|
||||
return (
|
||||
<DashboardProvider>
|
||||
<Switch>
|
||||
<Route path="/preferences">
|
||||
<DashboardSplitPane>
|
||||
<Sidebar />
|
||||
<PreferencesPage />
|
||||
</DashboardSplitPane>
|
||||
</Route>
|
||||
|
||||
<Route path="/">
|
||||
<DashboardSplitPane>
|
||||
<Sidebar />
|
||||
<DashboardContent />
|
||||
</DashboardSplitPane>
|
||||
</Route>
|
||||
<Route path="/preferences" component={DashboardPreferences} />
|
||||
<Route path="/" component={DashboardAnyPage} />
|
||||
</Switch>
|
||||
|
||||
<DashboardUniversalSearch />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { ErrorBoundary } from 'react-error-boundary';
|
||||
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 DashboardErrorBoundary from './DashboardErrorBoundary';
|
||||
|
||||
@@ -10,7 +10,7 @@ export default React.forwardRef(({}, ref) => {
|
||||
<ErrorBoundary FallbackComponent={DashboardErrorBoundary}>
|
||||
<div className="dashboard-content" id="dashboard" ref={ref}>
|
||||
<DashboardTopbar />
|
||||
<DashboardContentRoute />
|
||||
<DashboardContentRoutes />
|
||||
<DashboardFooter />
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
|
||||
@@ -2,8 +2,43 @@ import React from 'react';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
|
||||
import { getDashboardRoutes } from 'routes/dashboard';
|
||||
import EnsureSubscriptionsIsActive from '../Guards/EnsureSubscriptionsIsActive';
|
||||
import EnsureSubscriptionsIsInactive from '../Guards/EnsureSubscriptionsIsInactive';
|
||||
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.
|
||||
*/
|
||||
@@ -14,21 +49,8 @@ export default function DashboardContentRoute() {
|
||||
<Route pathname="/">
|
||||
<Switch>
|
||||
{routes.map((route, index) => (
|
||||
<Route
|
||||
exact={route.exact}
|
||||
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 exact={route.exact} key={index} path={`${route.path}`}>
|
||||
<DashboardContentRouteContent route={route} />
|
||||
</Route>
|
||||
))}
|
||||
</Switch>
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import React from 'react';
|
||||
import DashboardLoadingIndicator from './DashboardLoadingIndicator';
|
||||
import { useSettings } from 'hooks/query';
|
||||
|
||||
/**
|
||||
* Dashboard provider.
|
||||
*/
|
||||
export default function DashboardProvider({ children }) {
|
||||
const { isLoading } = useSettings();
|
||||
|
||||
return (
|
||||
<DashboardLoadingIndicator isLoading={isLoading}>
|
||||
<DashboardLoadingIndicator isLoading={false}>
|
||||
{ children }
|
||||
</DashboardLoadingIndicator>
|
||||
)
|
||||
|
||||
@@ -23,6 +23,43 @@ import withSettings from 'containers/Settings/withSettings';
|
||||
|
||||
import QuickNewDropdown from 'containers/QuickNewDropdown/QuickNewDropdown';
|
||||
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.
|
||||
@@ -44,6 +81,10 @@ function DashboardTopbar({
|
||||
|
||||
// #withGlobalSearch
|
||||
openGlobalSearch,
|
||||
|
||||
// #withSubscriptions
|
||||
isSubscriptionActive,
|
||||
isSubscriptionInactive,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
@@ -69,27 +110,7 @@ function DashboardTopbar({
|
||||
}
|
||||
position={Position.RIGHT}
|
||||
>
|
||||
<Button minimal={true} 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>
|
||||
<DashboardHamburgerButton onClick={handleSidebarToggleBtn} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
@@ -114,29 +135,36 @@ function DashboardTopbar({
|
||||
<div class="dashboard__breadcrumbs">
|
||||
<DashboardBreadcrumbs />
|
||||
</div>
|
||||
|
||||
<DashboardBackLink />
|
||||
</div>
|
||||
|
||||
<div class="dashboard__topbar-right">
|
||||
<If condition={isSubscriptionInactive}>
|
||||
<DashboardTopbarSubscriptionMessage />
|
||||
</If>
|
||||
|
||||
<Navbar class="dashboard__topbar-navbar">
|
||||
<NavbarGroup>
|
||||
<Button
|
||||
onClick={() => openGlobalSearch(true)}
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'search-24'} iconSize={20} />}
|
||||
text={<T id={'quick_find'} />}
|
||||
/>
|
||||
<QuickNewDropdown />
|
||||
<Tooltip
|
||||
content={<T id={'notifications'} />}
|
||||
position={Position.BOTTOM}
|
||||
>
|
||||
<If condition={isSubscriptionActive}>
|
||||
<Button
|
||||
onClick={() => openGlobalSearch(true)}
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'notification-24'} iconSize={20} />}
|
||||
icon={<Icon icon={'search-24'} iconSize={20} />}
|
||||
text={<T id={'quick_find'} />}
|
||||
/>
|
||||
</Tooltip>
|
||||
<QuickNewDropdown />
|
||||
|
||||
<Tooltip
|
||||
content={<T id={'notifications'} />}
|
||||
position={Position.BOTTOM}
|
||||
>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'notification-24'} iconSize={20} />}
|
||||
/>
|
||||
</Tooltip>
|
||||
</If>
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'help-24'} iconSize={20} />}
|
||||
@@ -166,4 +194,11 @@ export default compose(
|
||||
organizationName: organizationSettings.name,
|
||||
})),
|
||||
withDashboardActions,
|
||||
withSubscriptions(
|
||||
({ isSubscriptionActive, isSubscriptionInactive }) => ({
|
||||
isSubscriptionActive,
|
||||
isSubscriptionInactive,
|
||||
}),
|
||||
'main',
|
||||
),
|
||||
)(DashboardTopbar);
|
||||
|
||||
@@ -8,15 +8,21 @@ import {
|
||||
Popover,
|
||||
Position,
|
||||
} from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import { If, FormattedMessage as T } from 'components';
|
||||
|
||||
import { firstLettersArgs } from 'utils';
|
||||
import { useAuthActions, useAuthUser } from 'hooks/state';
|
||||
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import { compose } from 'utils';
|
||||
import withSubscriptions from '../../containers/Subscriptions/withSubscriptions';
|
||||
|
||||
function DashboardTopbarUser({ openDialog }) {
|
||||
function DashboardTopbarUser({
|
||||
openDialog,
|
||||
|
||||
// #withSubscriptions
|
||||
isSubscriptionActive
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const { setLogout } = useAuthActions();
|
||||
const user = useAuthUser();
|
||||
@@ -48,14 +54,16 @@ function DashboardTopbarUser({ openDialog }) {
|
||||
}
|
||||
/>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
text={<T id={'keyboard_shortcuts'} />}
|
||||
onClick={onKeyboardShortcut}
|
||||
/>
|
||||
<MenuItem
|
||||
text={<T id={'preferences'} />}
|
||||
onClick={() => history.push('/preferences')}
|
||||
/>
|
||||
<If condition={isSubscriptionActive}>
|
||||
<MenuItem
|
||||
text={<T id={'keyboard_shortcuts'} />}
|
||||
onClick={onKeyboardShortcut}
|
||||
/>
|
||||
<MenuItem
|
||||
text={<T id={'preferences'} />}
|
||||
onClick={() => history.push('/preferences')}
|
||||
/>
|
||||
</If>
|
||||
<MenuItem text={<T id={'logout'} />} onClick={onClickLogout} />
|
||||
</Menu>
|
||||
}
|
||||
@@ -69,4 +77,10 @@ function DashboardTopbarUser({ openDialog }) {
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
export default compose(withDialogActions)(DashboardTopbarUser);
|
||||
export default compose(
|
||||
withDialogActions,
|
||||
withSubscriptions(
|
||||
({ isSubscriptionActive }) => ({ isSubscriptionActive }),
|
||||
'main',
|
||||
),
|
||||
)(DashboardTopbarUser);
|
||||
|
||||
31
client/src/components/Guards/EnsureSubscriptionIsActive.js
Normal file
31
client/src/components/Guards/EnsureSubscriptionIsActive.js
Normal 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);
|
||||
31
client/src/components/Guards/EnsureSubscriptionsIsActive.js
Normal file
31
client/src/components/Guards/EnsureSubscriptionsIsActive.js
Normal 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);
|
||||
@@ -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);
|
||||
@@ -1,22 +1,20 @@
|
||||
import React from 'react';
|
||||
import { Route, Switch, Redirect } from 'react-router-dom';
|
||||
import preferencesRoutes from 'routes/preferences'
|
||||
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
import preferencesRoutes from 'routes/preferences';
|
||||
|
||||
export default function DashboardContentRoute() {
|
||||
|
||||
return (
|
||||
<Route pathname="/preferences">
|
||||
<Switch>
|
||||
{ preferencesRoutes.map((route, index) => (
|
||||
{preferencesRoutes.map((route, index) => (
|
||||
<Route
|
||||
key={index}
|
||||
path={`${route.path}`}
|
||||
exact={route.exact}
|
||||
component={route.component}
|
||||
/>
|
||||
/>
|
||||
))}
|
||||
</Switch>
|
||||
</Route>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withDashboard from 'containers/Dashboard/withDashboard';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import withSubscriptions from '../../containers/Subscriptions/withSubscriptions';
|
||||
|
||||
function SidebarContainer({
|
||||
// #ownProps
|
||||
@@ -15,6 +16,9 @@ function SidebarContainer({
|
||||
|
||||
// #withDashboard
|
||||
sidebarExpended,
|
||||
|
||||
// #withSubscription
|
||||
isSubscriptionActive,
|
||||
}) {
|
||||
const sidebarScrollerRef = React.useRef();
|
||||
|
||||
@@ -30,8 +34,8 @@ function SidebarContainer({
|
||||
}, [sidebarExpended]);
|
||||
|
||||
const handleSidebarMouseLeave = () => {
|
||||
if (!sidebarExpended && sidebarScrollerRef.current) {
|
||||
sidebarScrollerRef.current.scrollTo({ top: 0, left: 0, });
|
||||
if (!sidebarExpended && sidebarScrollerRef.current) {
|
||||
sidebarScrollerRef.current.scrollTo({ top: 0, left: 0 });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -43,6 +47,7 @@ function SidebarContainer({
|
||||
<div
|
||||
className={classNames('sidebar', {
|
||||
'sidebar--mini-sidebar': !sidebarExpended,
|
||||
'is-subscription-inactive': !isSubscriptionActive,
|
||||
})}
|
||||
id="sidebar"
|
||||
onMouseLeave={handleSidebarMouseLeave}
|
||||
@@ -64,4 +69,8 @@ export default compose(
|
||||
withDashboard(({ sidebarExpended }) => ({
|
||||
sidebarExpended,
|
||||
})),
|
||||
withSubscriptions(
|
||||
({ isSubscriptionActive }) => ({ isSubscriptionActive }),
|
||||
'main',
|
||||
),
|
||||
)(SidebarContainer);
|
||||
|
||||
@@ -8,6 +8,8 @@ import MenuItem from 'components/MenuItem';
|
||||
import { MenuItemLabel } from 'components';
|
||||
import classNames from 'classnames';
|
||||
import SidebarOverlay from 'components/SidebarOverlay';
|
||||
import { compose } from 'redux';
|
||||
import withSubscriptions from '../../containers/Subscriptions/withSubscriptions';
|
||||
|
||||
const DEFAULT_ITEM = {
|
||||
text: '',
|
||||
@@ -19,12 +21,10 @@ function matchPath(pathname, path, matchExact) {
|
||||
}
|
||||
|
||||
function SidebarMenuItemSpace({ space }) {
|
||||
return (
|
||||
<div class="bp3-menu-spacer" style={{ height: `${space}px` }} />
|
||||
)
|
||||
return <div class="bp3-menu-spacer" style={{ height: `${space}px` }} />;
|
||||
}
|
||||
|
||||
export default function SidebarMenu() {
|
||||
function SidebarMenu({ isSubscriptionActive }) {
|
||||
const history = useHistory();
|
||||
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 = () => {
|
||||
setIsOpen(false);
|
||||
@@ -110,3 +114,10 @@ export default function SidebarMenu() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withSubscriptions(
|
||||
({ isSubscriptionActive }) => ({ isSubscriptionActive }),
|
||||
'main',
|
||||
),
|
||||
)(SidebarMenu);
|
||||
|
||||
135
client/src/components/Subscriptions/index.js
Normal file
135
client/src/components/Subscriptions/index.js
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -72,6 +72,8 @@ export * from './Details';
|
||||
export * from './Drawer/DrawerInsider';
|
||||
export * from './Drawer/DrawerMainTabs';
|
||||
export * from './TotalLines/index'
|
||||
export * from './Alert';
|
||||
export * from './Subscriptions';
|
||||
|
||||
const Hint = FieldHint;
|
||||
|
||||
|
||||
@@ -298,6 +298,7 @@ export default [
|
||||
},
|
||||
{
|
||||
text: <T id={'system'} />,
|
||||
enableBilling: true,
|
||||
label: true,
|
||||
},
|
||||
{
|
||||
@@ -307,5 +308,6 @@ export default [
|
||||
{
|
||||
text: <T id={'billing'} />,
|
||||
href: '/billing',
|
||||
enableBilling: true,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React from 'react';
|
||||
import { Formik } from 'formik';
|
||||
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
import * as Yup from 'yup';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import Toaster from 'components/AppToaster';
|
||||
|
||||
import 'style/pages/Setup/PaymentViaVoucherDialog.scss';
|
||||
@@ -14,7 +15,6 @@ import PaymentViaLicenseForm from './PaymentViaVoucherForm';
|
||||
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import { compose } from 'utils';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
|
||||
/**
|
||||
* Payment via license dialog content.
|
||||
@@ -35,10 +35,11 @@ function PaymentViaLicenseDialogContent({
|
||||
const handleSubmit = (values, { setSubmitting, setErrors }) => {
|
||||
setSubmitting(true);
|
||||
|
||||
// Payment via voucher mutate.
|
||||
paymentViaVoucherMutate({ ...values })
|
||||
.then(() => {
|
||||
Toaster.show({
|
||||
message: intl.get('payment_has_been_done_successfully'),
|
||||
message: intl.get('payment_via_voucher.success_message'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
return closeDialog('payment-via-voucher');
|
||||
@@ -54,7 +55,7 @@ function PaymentViaLicenseDialogContent({
|
||||
}) => {
|
||||
if (errors.find((e) => e.type === 'LICENSE.CODE.IS.INVALID')) {
|
||||
setErrors({
|
||||
license_code: 'The license code is not valid, please try agin.',
|
||||
license_code: 'payment_via_voucher.license_code_not_valid',
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -31,7 +31,7 @@ function PaymentViaLicenseForm({
|
||||
<Form>
|
||||
<div className={CLASSES.DIALOG_BODY}>
|
||||
<p>
|
||||
<T id={'Pleasse enter your voucher number that you received from reseller.'} />
|
||||
<T id={'payment_via_voucher.dialog.description'} />
|
||||
</p>
|
||||
|
||||
<FastField name="license_code">
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import AppToaster from 'components/AppToaster';
|
||||
|
||||
import withGlobalErrors from './withGlobalErrors';
|
||||
@@ -17,29 +19,31 @@ function GlobalErrors({
|
||||
globalErrorsSet,
|
||||
}) {
|
||||
if (globalErrors.something_wrong) {
|
||||
toastKeySessionExpired = AppToaster.show({
|
||||
message: intl.get('ops_something_went_wrong'),
|
||||
intent: Intent.DANGER,
|
||||
onDismiss: () => {
|
||||
globalErrorsSet({ something_wrong: false });
|
||||
}
|
||||
}, toastKeySessionExpired);
|
||||
toastKeySessionExpired = AppToaster.show(
|
||||
{
|
||||
message: intl.get('ops_something_went_wrong'),
|
||||
intent: Intent.DANGER,
|
||||
onDismiss: () => {
|
||||
globalErrorsSet({ something_wrong: false });
|
||||
},
|
||||
},
|
||||
toastKeySessionExpired,
|
||||
);
|
||||
}
|
||||
|
||||
if (globalErrors.session_expired) {
|
||||
toastKeySomethingWrong = AppToaster.show({
|
||||
message: intl.get('session_expired'),
|
||||
intent: Intent.DANGER,
|
||||
onDismiss: () => {
|
||||
globalErrorsSet({ session_expired: false });
|
||||
}
|
||||
}, toastKeySomethingWrong);
|
||||
toastKeySomethingWrong = AppToaster.show(
|
||||
{
|
||||
message: intl.get('session_expired'),
|
||||
intent: Intent.DANGER,
|
||||
onDismiss: () => {
|
||||
globalErrorsSet({ session_expired: false });
|
||||
},
|
||||
},
|
||||
toastKeySomethingWrong,
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withGlobalErrors,
|
||||
withGlobalErrorsActions,
|
||||
)(GlobalErrors);
|
||||
export default compose(withGlobalErrors, withGlobalErrorsActions)(GlobalErrors);
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { Button, Intent } from '@blueprintjs/core';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import WorkflowIcon from './WorkflowIcon';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
|
||||
import withOrganizationActions from 'containers/Organization/withOrganizationActions';
|
||||
import { compose } from 'utils';
|
||||
|
||||
import 'style/pages/Setup/Congrats.scss';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Setup congrats page.
|
||||
@@ -28,15 +30,15 @@ function SetupCongratsPage({ setOrganizationSetupCompleted }) {
|
||||
|
||||
<div class="setup-congrats__text">
|
||||
<h1>
|
||||
<T id={'congrats_you_are_ready_to_go'} />
|
||||
<T id={'setup.congrats.title'} />
|
||||
</h1>
|
||||
|
||||
<p class="paragraph">
|
||||
<T id={'it_is_a_long_established_fact_that_a_reader'} />
|
||||
<T id={'setup.congrats.description'} />
|
||||
</p>
|
||||
|
||||
<Button intent={Intent.PRIMARY} type="submit" onClick={handleBtnClick}>
|
||||
<T id={'go_to_dashboard'} />
|
||||
<T id={'setup.congrats.go_to_dashboard'} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@ export default function SetupInitializingForm() {
|
||||
isError,
|
||||
} = useBuildTenant();
|
||||
|
||||
useEffect(() => {
|
||||
React.useEffect(() => {
|
||||
buildTenantMutate();
|
||||
}, [buildTenantMutate]);
|
||||
|
||||
@@ -27,32 +27,32 @@ export default function SetupInitializingForm() {
|
||||
{isLoading ? (
|
||||
<>
|
||||
<h1>
|
||||
<T id={'it_s_time_to_make_your_accounting_really_simple'} />
|
||||
<T id={'setup.initializing.title'} />
|
||||
</h1>
|
||||
<p className={'paragraph'}>
|
||||
<T
|
||||
id={
|
||||
'while_we_set_up_your_account_please_remember_to_verify_your_account'
|
||||
}
|
||||
/>
|
||||
<T id={'setup.initializing.description'} />
|
||||
</p>
|
||||
</>
|
||||
) : isError ? (
|
||||
<>
|
||||
<h1>
|
||||
<T id={'something_went_wrong'} />
|
||||
<T id={'setup.initializing.something_went_wrong'} />
|
||||
</h1>
|
||||
<p class="paragraph">
|
||||
<T id={'please_refresh_the_page'} />
|
||||
<T id={'setup.initializing.please_refresh_the_page'} />
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<h1>
|
||||
<T id={'waiting_to_redirect'} />
|
||||
<T id={'setup.initializing.waiting_to_redirect'} />
|
||||
</h1>
|
||||
<p class="paragraph">
|
||||
<T id={'refresh_the_page_if_redirect_not_worked'} />
|
||||
<T
|
||||
id={
|
||||
'setup.initializing.refresh_the_page_if_redirect_not_worked'
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -3,11 +3,14 @@ import { Icon, For } from 'components';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
|
||||
import { getFooterLinks } from 'config/footerLinks';
|
||||
import { useAuthActions, useAuthOrganizationId } from 'hooks/state';
|
||||
import { useAuthActions } from 'hooks/state';
|
||||
|
||||
/**
|
||||
* Footer item link.
|
||||
*/
|
||||
function FooterLinkItem({ title, link }) {
|
||||
return (
|
||||
<div class="">
|
||||
<div class="content__links-item">
|
||||
<a href={link} target="_blank">
|
||||
{title}
|
||||
</a>
|
||||
@@ -16,20 +19,65 @@ function FooterLinkItem({ title, link }) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Wizard setup left section.
|
||||
* Setup left section footer.
|
||||
*/
|
||||
export default function SetupLeftSection() {
|
||||
const { setLogout } = useAuthActions();
|
||||
const organizationId = useAuthOrganizationId();
|
||||
|
||||
function SetupLeftSectionFooter() {
|
||||
// Retrieve the footer links.
|
||||
const footerLinks = getFooterLinks();
|
||||
|
||||
return (
|
||||
<div className={'content__footer'}>
|
||||
<div className={'content__contact-info'}>
|
||||
<p>
|
||||
<T id={'setup.left_side.footer_help'} />{' '}
|
||||
<span>{'+21892-738-1987'}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={'content__links'}>
|
||||
<For render={FooterLinkItem} of={footerLinks} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup left section header.
|
||||
*/
|
||||
function SetupLeftSectionHeader() {
|
||||
const { setLogout } = useAuthActions();
|
||||
|
||||
// Handle logout link click.
|
||||
const onClickLogout = () => {
|
||||
setLogout();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={'content__header'}>
|
||||
<h1 className={'content__title'}>
|
||||
<T id={'setup.left_side.title'} />
|
||||
</h1>
|
||||
|
||||
<p className={'content__text'}>
|
||||
<T id={'setup.left_side.description'} />
|
||||
</p>
|
||||
<div class="content__divider"></div>
|
||||
|
||||
<div className={'content__organization'}>
|
||||
<span class="signout">
|
||||
<a onClick={onClickLogout} href="#">
|
||||
<T id={'sign_out'} />
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wizard setup left section.
|
||||
*/
|
||||
export default function SetupLeftSection() {
|
||||
return (
|
||||
<section className={'setup-page__left-section'}>
|
||||
<div className={'content'}>
|
||||
@@ -41,40 +89,8 @@ export default function SetupLeftSection() {
|
||||
width={190}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h1 className={'content__title'}>
|
||||
<T id={'register_a_new_organization_now'} />
|
||||
</h1>
|
||||
|
||||
<p className={'content__text'}>
|
||||
<T id={'you_have_a_bigcapital_account'} />
|
||||
</p>
|
||||
<span class="content__divider"></span>
|
||||
|
||||
<div className={'content__organization'}>
|
||||
<span class="organization-id">
|
||||
<T id={'organization_id'} />:{' '}
|
||||
<span class="id">{organizationId}</span>,
|
||||
</span>
|
||||
<br />
|
||||
<span class="signout">
|
||||
<a onClick={onClickLogout} href="#">
|
||||
<T id={'sign_out'} />
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className={'content__footer'}>
|
||||
<div className={'content__contact-info'}>
|
||||
<p>
|
||||
<T id={'we_re_here_to_help'} /> <span>{'+21892-738-1987'}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={'content__links'}>
|
||||
<For render={FooterLinkItem} of={footerLinks} />
|
||||
</div>
|
||||
</div>
|
||||
<SetupLeftSectionHeader />
|
||||
<SetupLeftSectionFooter />
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
15
client/src/containers/Setup/SetupOrganization.schema.js
Normal file
15
client/src/containers/Setup/SetupOrganization.schema.js
Normal 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_')),
|
||||
});
|
||||
@@ -14,20 +14,18 @@ import classNames from 'classnames';
|
||||
import { TimezonePicker } from '@blueprintjs/timezone';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
|
||||
import { FieldRequiredHint, Col, Row, ListSelect } from 'components';
|
||||
import { Col, Row, ListSelect } from 'components';
|
||||
import {
|
||||
momentFormatter,
|
||||
tansformDateValue,
|
||||
inputIntent,
|
||||
handleDateChange
|
||||
handleDateChange,
|
||||
} from 'utils';
|
||||
|
||||
import { getFiscalYear } from 'common/fiscalYearOptions';
|
||||
import { getLanguages } from 'common/languagesOptions';
|
||||
import { getCurrencies } from 'common/currencies';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Setup organization form.
|
||||
*/
|
||||
@@ -46,22 +44,24 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
|
||||
<FastField name={'organization_name'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
label={<T id={'legal_organization_name'} />}
|
||||
className={'form-group--name'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'organization_name'} />}
|
||||
>
|
||||
<InputGroup {...field} />
|
||||
<InputGroup {...field} intent={inputIntent({ error, touched })} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/* ---------- Financial starting date ---------- */}
|
||||
<FastField name={'financialDateStart'}>
|
||||
{({ form: { setFieldValue }, field: { value }, meta: { error, touched } }) => (
|
||||
{({
|
||||
form: { setFieldValue },
|
||||
field: { value },
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<FormGroup
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
label={<T id={'financial_starting_date'} />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="financialDateStart" />}
|
||||
@@ -74,6 +74,7 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
|
||||
onChange={handleDateChange((formattedDate) => {
|
||||
setFieldValue('financialDateStart', formattedDate);
|
||||
})}
|
||||
intent={inputIntent({ error, touched })}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
@@ -89,7 +90,6 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<FormGroup
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
label={<T id={'base_currency'} />}
|
||||
className={classNames(
|
||||
'form-group--base-currency',
|
||||
@@ -101,7 +101,9 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
|
||||
>
|
||||
<ListSelect
|
||||
items={Currencies}
|
||||
noResults={<MenuItem disabled={true} text={<T id={'no_results'} />} />}
|
||||
noResults={
|
||||
<MenuItem disabled={true} text={<T id={'no_results'} />} />
|
||||
}
|
||||
popoverProps={{ minimal: true }}
|
||||
onItemSelect={(item) => {
|
||||
setFieldValue('baseCurrency', item.code);
|
||||
@@ -110,6 +112,7 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
|
||||
textProp={'name'}
|
||||
defaultText={<T id={'select_base_currency'} />}
|
||||
selectedItem={value}
|
||||
intent={inputIntent({ error, touched })}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
@@ -136,7 +139,9 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
|
||||
>
|
||||
<ListSelect
|
||||
items={Languages}
|
||||
noResults={<MenuItem disabled={true} text={<T id={'no_results'} />} />}
|
||||
noResults={
|
||||
<MenuItem disabled={true} text={<T id={'no_results'} />} />
|
||||
}
|
||||
onItemSelect={(item) => {
|
||||
setFieldValue('language', item.value);
|
||||
}}
|
||||
@@ -146,6 +151,7 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
|
||||
defaultText={<T id={'select_language'} />}
|
||||
popoverProps={{ minimal: true }}
|
||||
filterable={false}
|
||||
intent={inputIntent({ error, touched })}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
@@ -154,9 +160,12 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
|
||||
</Row>
|
||||
{/* --------- Fiscal Year ----------- */}
|
||||
<FastField name={'fiscalYear'}>
|
||||
{({ form: { setFieldValue }, field: { value }, meta: { error, touched } }) => (
|
||||
{({
|
||||
form: { setFieldValue },
|
||||
field: { value },
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<FormGroup
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
label={<T id={'fiscal_year'} />}
|
||||
className={classNames(
|
||||
'form-group--fiscal_year',
|
||||
@@ -168,14 +177,16 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
|
||||
>
|
||||
<ListSelect
|
||||
items={FiscalYear}
|
||||
noResults={<MenuItem disabled={true} text={<T id={'no_results'} />} />}
|
||||
noResults={
|
||||
<MenuItem disabled={true} text={<T id={'no_results'} />} />
|
||||
}
|
||||
selectedItem={value}
|
||||
selectedItemProp={'value'}
|
||||
textProp={'name'}
|
||||
defaultText={<T id={'select_fiscal_year'} />}
|
||||
popoverProps={{ minimal: true }}
|
||||
onItemSelect={(item) => {
|
||||
setFieldValue('fiscalYear', item.value)
|
||||
setFieldValue('fiscalYear', item.value);
|
||||
}}
|
||||
filterable={false}
|
||||
/>
|
||||
@@ -191,7 +202,6 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<FormGroup
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
label={<T id={'time_zone'} />}
|
||||
className={classNames(
|
||||
'form-group--time-zone',
|
||||
@@ -216,20 +226,11 @@ export default function SetupOrganizationForm({ isSubmitting, values }) {
|
||||
</FastField>
|
||||
|
||||
<p className={'register-org-note'}>
|
||||
<T
|
||||
id={
|
||||
'note_you_can_change_your_preferences_later_in_dashboard_if_needed'
|
||||
}
|
||||
/>
|
||||
<T id={'setup.organization.note_you_can_change_your_preferences'} />
|
||||
</p>
|
||||
|
||||
<div className={'register-org-button'}>
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
disabled={isSubmitting}
|
||||
loading={isSubmitting}
|
||||
type="submit"
|
||||
>
|
||||
<Button intent={Intent.PRIMARY} loading={isSubmitting} type="submit">
|
||||
<T id={'save_continue'} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { Formik } from 'formik';
|
||||
import moment from 'moment';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
|
||||
import 'style/pages/Setup/Organization.scss';
|
||||
|
||||
@@ -13,53 +12,30 @@ import { useOrganizationSetup } from 'hooks/query';
|
||||
import withSettingsActions from 'containers/Settings/withSettingsActions';
|
||||
import withOrganizationActions from 'containers/Organization/withOrganizationActions';
|
||||
|
||||
import {
|
||||
compose,
|
||||
transfromToSnakeCase,
|
||||
} from 'utils';
|
||||
import { compose, transfromToSnakeCase } from 'utils';
|
||||
import { getSetupOrganizationValidation } from './SetupOrganization.schema';
|
||||
|
||||
|
||||
// Initial values.
|
||||
const defaultValues = {
|
||||
organization_name: '',
|
||||
financialDateStart: moment(new Date()).format('YYYY-MM-DD'),
|
||||
baseCurrency: '',
|
||||
language: 'en',
|
||||
fiscalYear: '',
|
||||
timeZone: '',
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup organization form.
|
||||
*/
|
||||
function SetupOrganizationPage({
|
||||
wizard,
|
||||
setOrganizationSetupCompleted,
|
||||
}) {
|
||||
|
||||
function SetupOrganizationPage({ wizard, setOrganizationSetupCompleted }) {
|
||||
const { mutateAsync: organizationSetupMutate } = useOrganizationSetup();
|
||||
|
||||
// Validation schema.
|
||||
const validationSchema = Yup.object().shape({
|
||||
organization_name: Yup.string()
|
||||
.required()
|
||||
.label(intl.get('organization_name_')),
|
||||
financialDateStart: Yup.date()
|
||||
.required()
|
||||
.label(intl.get('date_start_')),
|
||||
baseCurrency: Yup.string()
|
||||
.required()
|
||||
.label(intl.get('base_currency_')),
|
||||
language: Yup.string()
|
||||
.required()
|
||||
.label(intl.get('language')),
|
||||
fiscalYear: Yup.string()
|
||||
.required()
|
||||
.label(intl.get('fiscal_year_')),
|
||||
timeZone: Yup.string()
|
||||
.required()
|
||||
.label(intl.get('time_zone_')),
|
||||
});
|
||||
|
||||
// Initial values.
|
||||
const defaultValues = {
|
||||
organization_name: '',
|
||||
financialDateStart: moment(new Date()).format('YYYY-MM-DD'),
|
||||
baseCurrency: '',
|
||||
language: 'en',
|
||||
fiscalYear: '',
|
||||
timeZone: '',
|
||||
};
|
||||
const validationSchema = getSetupOrganizationValidation();
|
||||
|
||||
// Initialize values.
|
||||
const initialValues = {
|
||||
...defaultValues,
|
||||
};
|
||||
@@ -83,10 +59,10 @@ function SetupOrganizationPage({
|
||||
<div className={'setup-organization'}>
|
||||
<div className={'setup-organization__title-wrap'}>
|
||||
<h1>
|
||||
<T id={'let_s_get_started'} />
|
||||
<T id={'setup.organization.title'} />
|
||||
</h1>
|
||||
<p class="paragraph">
|
||||
<T id={'tell_the_system_a_little_bit_about_your_organization'} />
|
||||
<T id={'setup.organization.description'} />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as R from 'ramda';
|
||||
|
||||
import 'style/pages/Setup/Subscription.scss';
|
||||
|
||||
import SetupSubscriptionForm from './SetupSubscriptionForm';
|
||||
import SetupSubscriptionForm from './SetupSubscription/SetupSubscriptionForm';
|
||||
import { getSubscriptionFormSchema } from './SubscriptionForm.schema';
|
||||
import withSubscriptionPlansActions from '../Subscriptions/withSubscriptionPlansActions';
|
||||
|
||||
@@ -13,13 +13,11 @@ import withSubscriptionPlansActions from '../Subscriptions/withSubscriptionPlans
|
||||
*/
|
||||
function SetupSubscription({
|
||||
// #withSubscriptionPlansActions
|
||||
initSubscriptionPlans
|
||||
initSubscriptionPlans,
|
||||
}) {
|
||||
React.useEffect(() => {
|
||||
initSubscriptionPlans();
|
||||
}, [
|
||||
initSubscriptionPlans
|
||||
]);
|
||||
}, [initSubscriptionPlans]);
|
||||
|
||||
// Initial values.
|
||||
const initialValues = {
|
||||
@@ -30,10 +28,14 @@ function SetupSubscription({
|
||||
// Handle form submit.
|
||||
const handleSubmit = () => {};
|
||||
|
||||
const SubscriptionFormSchema = getSubscriptionFormSchema();
|
||||
// Retrieve momerized subscription form schema.
|
||||
const SubscriptionFormSchema = React.useMemo(
|
||||
() => getSubscriptionFormSchema(),
|
||||
[],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={'setup-subscription-form'}>
|
||||
<div className={'setup-subscription-form'}>
|
||||
<Formik
|
||||
validationSchema={SubscriptionFormSchema}
|
||||
initialValues={initialValues}
|
||||
@@ -44,6 +46,4 @@ function SetupSubscription({
|
||||
);
|
||||
}
|
||||
|
||||
export default R.compose(
|
||||
withSubscriptionPlansActions,
|
||||
)(SetupSubscription);
|
||||
export default R.compose(withSubscriptionPlansActions)(SetupSubscription);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -5,9 +5,7 @@ import { getSetupWizardSteps } from 'common/registerWizard';
|
||||
function WizardSetupStep({ label, isActive = false }) {
|
||||
return (
|
||||
<li className={classNames({ 'is-active': isActive })}>
|
||||
<p className={'wizard-info'}>
|
||||
{ label }
|
||||
</p>
|
||||
<p className={'wizard-info'}>{label}</p>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { Formik, Form } from 'formik';
|
||||
import intl from 'react-intl-universal';
|
||||
import { If, Alert, T } from 'components';
|
||||
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
|
||||
import 'style/pages/Billing/BillingPage.scss';
|
||||
@@ -10,8 +11,11 @@ import { MasterBillingTabs } from './SubscriptionTabs';
|
||||
|
||||
import withBillingActions from './withBillingActions';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withSubscriptionPlansActions from './withSubscriptionPlansActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import { getBillingFormValidationSchema } from './utils';
|
||||
import withSubscriptions from './withSubscriptions';
|
||||
|
||||
/**
|
||||
* Billing form.
|
||||
@@ -20,26 +24,30 @@ function BillingForm({
|
||||
// #withDashboardActions
|
||||
changePageTitle,
|
||||
|
||||
//#withBillingActions
|
||||
// #withBillingActions
|
||||
requestSubmitBilling,
|
||||
|
||||
initSubscriptionPlans,
|
||||
|
||||
// #withSubscriptions
|
||||
isSubscriptionInactive,
|
||||
}) {
|
||||
useEffect(() => {
|
||||
changePageTitle(intl.get('billing'));
|
||||
}, [changePageTitle]);
|
||||
|
||||
const validationSchema = Yup.object().shape({
|
||||
plan_slug: Yup.string()
|
||||
.required(),
|
||||
period: Yup.string().required(),
|
||||
license_code: Yup.string().trim(),
|
||||
});
|
||||
React.useEffect(() => {
|
||||
initSubscriptionPlans();
|
||||
}, [initSubscriptionPlans]);
|
||||
|
||||
// Initial values.
|
||||
const initialValues = {
|
||||
plan_slug: 'free',
|
||||
period: 'month',
|
||||
license_code: '',
|
||||
};
|
||||
|
||||
// Handle form submitting.
|
||||
const handleSubmit = (values, { setSubmitting }) => {
|
||||
requestSubmitBilling(values)
|
||||
.then((response) => {
|
||||
@@ -53,20 +61,34 @@ function BillingForm({
|
||||
return (
|
||||
<DashboardInsider name={'billing-page'}>
|
||||
<div className={'billing-page'}>
|
||||
<If condition={isSubscriptionInactive}>
|
||||
<Alert
|
||||
intent={'danger'}
|
||||
title={<T id={'billing.suspend_message.title'} />}
|
||||
description={<T id={'billing.suspend_message.description'} />}
|
||||
/>
|
||||
</If>
|
||||
|
||||
<Formik
|
||||
validationSchema={validationSchema}
|
||||
validationSchema={getBillingFormValidationSchema()}
|
||||
onSubmit={handleSubmit}
|
||||
initialValues={initialValues}
|
||||
>
|
||||
{({ isSubmitting, handleSubmit }) => (
|
||||
<Form>
|
||||
<MasterBillingTabs />
|
||||
</Form>
|
||||
)}
|
||||
<Form>
|
||||
<MasterBillingTabs />
|
||||
</Form>
|
||||
</Formik>
|
||||
</div>
|
||||
</DashboardInsider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withDashboardActions, withBillingActions)(BillingForm);
|
||||
export default compose(
|
||||
withDashboardActions,
|
||||
withBillingActions,
|
||||
withSubscriptionPlansActions,
|
||||
withSubscriptions(
|
||||
({ isSubscriptionInactive }) => ({ isSubscriptionInactive }),
|
||||
'main',
|
||||
),
|
||||
)(BillingForm);
|
||||
|
||||
@@ -15,12 +15,14 @@ import { saveInvoke } from 'utils';
|
||||
function BillingPeriod({
|
||||
// #ownProps
|
||||
label,
|
||||
currencyCode,
|
||||
value,
|
||||
selectedOption,
|
||||
onSelected,
|
||||
period,
|
||||
|
||||
// #withPlan
|
||||
price,
|
||||
currencyCode,
|
||||
}) {
|
||||
const handlePeriodClick = () => {
|
||||
saveInvoke(onSelected, value);
|
||||
|
||||
@@ -1,42 +1,46 @@
|
||||
import React from 'react';
|
||||
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.
|
||||
*/
|
||||
function BillingPeriods({ title, description, plansPeriods }) {
|
||||
export default function BillingPeriods() {
|
||||
return (
|
||||
<section class="billing-plans__section">
|
||||
<h1 class="title">{title}</h1>
|
||||
<h1 class="title">
|
||||
<T id={'setup.plans.select_period.title'} />
|
||||
</h1>
|
||||
<div class="description">
|
||||
<p className="paragraph">{description}</p>
|
||||
<p className="paragraph">
|
||||
<T id={'setup.plans.select_period.description'} />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Field name={'period'}>
|
||||
{({ form: { setFieldValue, values } }) => (
|
||||
<div className={'plan-periods'}>
|
||||
{plansPeriods.map((period) => (
|
||||
<BillingPeriod
|
||||
planSlug={values.plan_slug}
|
||||
period={period.slug}
|
||||
label={period.label}
|
||||
value={period.slug}
|
||||
onSelected={(value) => setFieldValue('period', value)}
|
||||
selectedOption={values.period}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{({ field: { value }, form: { values, setFieldValue } }) => (
|
||||
<SubscriptionPeriodsEnhanced
|
||||
selectedPeriod={value}
|
||||
planSlug={values.plan_slug}
|
||||
onPeriodSelect={(period) => {
|
||||
setFieldValue('period', period);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withPlans(({ plansPeriods }) => ({ plansPeriods })))(
|
||||
BillingPeriods,
|
||||
);
|
||||
|
||||
@@ -3,8 +3,6 @@ import classNames from 'classnames';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
import 'style/pages/Subscription/PlanRadio.scss';
|
||||
|
||||
import { saveInvoke } from 'utils';
|
||||
|
||||
/**
|
||||
@@ -32,9 +30,7 @@ export default function BillingPlan({
|
||||
onClick={handlePlanClick}
|
||||
>
|
||||
<div className={'plan-radio__header'}>
|
||||
<div className={'plan-radio__name'}>
|
||||
{intl.get('plan_radio_name', { name: name })}
|
||||
</div>
|
||||
<div className={'plan-radio__name'}>{name}</div>
|
||||
</div>
|
||||
|
||||
<div className={'plan-radio__description'}>
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
import 'style/pages/Subscription/BillingPlans.scss';
|
||||
|
||||
import BillingPlansInput from 'containers/Subscriptions/BillingPlansInput';
|
||||
import BillingPeriodsInput from 'containers/Subscriptions/BillingPeriodsInput';
|
||||
import BillingPaymentMethod from 'containers/Subscriptions/BillingPaymentMethod';
|
||||
import BillingPlansInput from './BillingPlansInput';
|
||||
import BillingPeriodsInput from './BillingPeriodsInput';
|
||||
import BillingPaymentMethod from './BillingPaymentMethod';
|
||||
|
||||
/**
|
||||
* Billing plans form.
|
||||
@@ -14,18 +12,9 @@ import BillingPaymentMethod from 'containers/Subscriptions/BillingPaymentMethod'
|
||||
export default function BillingPlansForm() {
|
||||
return (
|
||||
<div class="billing-plans">
|
||||
<BillingPlansInput
|
||||
title={intl.get('select_a_plan')}
|
||||
description={<T id={'please_enter_your_preferred_payment_method'} />}
|
||||
/>
|
||||
<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'} />}
|
||||
/>
|
||||
<BillingPlansInput />
|
||||
<BillingPeriodsInput />
|
||||
<BillingPaymentMethod />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { FastField, Field } from 'formik';
|
||||
import BillingPlan from './BillingPlan';
|
||||
import { Field } from 'formik';
|
||||
import { T, SubscriptionPlans } from 'components';
|
||||
|
||||
import withPlans from './withPlans';
|
||||
import { compose } from 'utils';
|
||||
@@ -11,27 +11,24 @@ import { compose } from 'utils';
|
||||
function BillingPlans({ plans, title, description, selectedOption }) {
|
||||
return (
|
||||
<section class="billing-plans__section">
|
||||
<h1 class="title">{title}</h1>
|
||||
<h1 class="title">
|
||||
<T id={'setup.plans.select_plan.title'} />
|
||||
</h1>
|
||||
<div class="description">
|
||||
<p className="paragraph">{description}</p>
|
||||
<p className="paragraph">
|
||||
<T id={'setup.plans.select_plan.description'} />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Field name={'plan_slug'}>
|
||||
{({ form: { setFieldValue }, field: { value } }) => (
|
||||
<div className={'plan-radios'}>
|
||||
{plans.map((plan) => (
|
||||
<BillingPlan
|
||||
name={plan.name}
|
||||
description={plan.description}
|
||||
slug={plan.slug}
|
||||
price={plan.price.month}
|
||||
currencyCode={plan.currencyCode}
|
||||
value={plan.slug}
|
||||
onSelected={(value) => setFieldValue('plan_slug', value)}
|
||||
selectedOption={value}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<SubscriptionPlans
|
||||
plans={plans}
|
||||
value={value}
|
||||
onSelect={(value) => {
|
||||
setFieldValue('plan_slug', value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
</section>
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import React from 'react';
|
||||
import BillingPlansForm from 'containers/Subscriptions/BillingPlansForm';
|
||||
import BillingPlansForm from './BillingPlansForm';
|
||||
|
||||
export default function BillingTab() {
|
||||
return (
|
||||
<div>
|
||||
<BillingPlansForm />
|
||||
</div>
|
||||
);
|
||||
return (<BillingPlansForm />);
|
||||
}
|
||||
@@ -16,11 +16,7 @@ export const MasterBillingTabs = ({ formik }) => {
|
||||
id={'billing'}
|
||||
panel={<BillingTab formik={formik} />}
|
||||
/>
|
||||
<Tab
|
||||
title={intl.get('usage')}
|
||||
id={'usage'}
|
||||
disabled={true}
|
||||
/>
|
||||
<Tab title={intl.get('usage')} id={'usage'} disabled={true} />
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
@@ -43,11 +39,7 @@ export const PaymentMethodTabs = ({ formik }) => {
|
||||
id={'credit_card'}
|
||||
disabled={true}
|
||||
/>
|
||||
<Tab
|
||||
title={intl.get('paypal')}
|
||||
id={'paypal'}
|
||||
disabled={true}
|
||||
/>
|
||||
<Tab title={intl.get('paypal')} id={'paypal'} disabled={true} />
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import React from 'react';
|
||||
|
||||
import { T } from 'components';
|
||||
import { PaymentMethodTabs } from './SubscriptionTabs';
|
||||
|
||||
export default ({ formik, title, description }) => {
|
||||
return (
|
||||
<section class="billing-plans__section">
|
||||
<h1 className="title">{ title }</h1>
|
||||
<p className="paragraph">{ description }</p>
|
||||
<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>
|
||||
|
||||
8
client/src/containers/Subscriptions/utils.js
Normal file
8
client/src/containers/Subscriptions/utils.js
Normal 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(),
|
||||
});
|
||||
@@ -1,17 +1,14 @@
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
getPlansSelector,
|
||||
getPlansPeriodsSelector,
|
||||
} from 'store/plans/plans.selectors';
|
||||
|
||||
export default (mapState) => {
|
||||
const mapStateToProps = (state, props) => {
|
||||
const getPlans = getPlansSelector();
|
||||
const getPlansPeriods = getPlansPeriodsSelector();
|
||||
|
||||
const mapped = {
|
||||
plans: getPlans(state, props),
|
||||
plansPeriods: getPlansPeriods(state, props),
|
||||
};
|
||||
return mapState ? mapState(mapped, state, props) : mapped;
|
||||
};
|
||||
|
||||
19
client/src/containers/Subscriptions/withSubscriptionss.js
Normal file
19
client/src/containers/Subscriptions/withSubscriptionss.js
Normal 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);
|
||||
};
|
||||
@@ -23,23 +23,18 @@ export function useSaveSettings(props) {
|
||||
function useSettingsQuery(key, query, props) {
|
||||
const setSettings = useSetSettings();
|
||||
|
||||
const state = useRequestQuery(
|
||||
return useRequestQuery(
|
||||
key,
|
||||
{ method: 'get', url: 'settings', params: query },
|
||||
{
|
||||
select: (res) => res.data.settings,
|
||||
defaultData: [],
|
||||
onSuccess: (settings) => {
|
||||
setSettings(settings);
|
||||
},
|
||||
...props,
|
||||
},
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (state.isSuccess) {
|
||||
setSettings(state.data);
|
||||
}
|
||||
}, [state.data, state.isSuccess, setSettings]);
|
||||
|
||||
return state.data;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -584,7 +584,7 @@
|
||||
"payment_mades": "سندات الموردين",
|
||||
"subscription": "الاشتراك",
|
||||
"plan_slug": "سبيكة خطة",
|
||||
"billing": "الفواتير",
|
||||
"billing": "الاشتراك",
|
||||
"the_billing_has_been_created_successfully": "تم إنشاء الفواتير بنجاح.",
|
||||
"select_a_plan": "حدد الباقة",
|
||||
"choose_your_billing": "اختر مدة الدفع",
|
||||
@@ -1114,12 +1114,10 @@
|
||||
"filter_": "البحث...",
|
||||
"all_rights_reserved": "© {pre}-{current} كل الحقوق محفوظة.",
|
||||
"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": "أثناء قيامنا بإعداد حسابك ، يرجى تذكر التحقق من حسابك بالنقر فوق الارتباط الذي أرسلناه إلى عنوان بريدك الإلكتروني المسجل",
|
||||
"something_went_wrong": "هناك خطأ ما!",
|
||||
"please_refresh_the_page": "يرجى تحديث الصفحة",
|
||||
"waiting_to_redirect": "في انتظار إعادة التوجيه",
|
||||
"refresh_the_page_if_redirect_not_worked": "قم بتحديث الصفحة إذا لم تنجح إعادة التوجيه.",
|
||||
|
||||
|
||||
"blog": "المدونة",
|
||||
"support": "الدعم النفي",
|
||||
"service_status": "حالة الخدمة",
|
||||
@@ -1220,6 +1218,67 @@
|
||||
"pdf_preview.preview.button": "معاينة",
|
||||
"invoice_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": "انتهت فترة الاشتراك"
|
||||
}
|
||||
|
||||
|
||||
@@ -578,7 +578,6 @@
|
||||
"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",
|
||||
"payment_methods": "Payment methods",
|
||||
"usage": "Usage",
|
||||
@@ -591,7 +590,6 @@
|
||||
"yearly": "Yearly",
|
||||
"license_code": "License Code",
|
||||
"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.",
|
||||
"license_number": "License number",
|
||||
"subscribe": "Subscribe",
|
||||
@@ -616,20 +614,15 @@
|
||||
"purchasable": "Purchasable",
|
||||
"sell_account": "Sell 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",
|
||||
"let_s_get_started": "Let’s Get Started",
|
||||
"tell_the_system_a_little_bit_about_your_organization": "Tell the system a little bit about your organization.",
|
||||
"organization_details": "Organization details",
|
||||
"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",
|
||||
"organization_register": "Organization Register",
|
||||
"fiscal_year_": "Fiscal year",
|
||||
"welcome": "Welcome ",
|
||||
"sign_out": "Sign out",
|
||||
"we_re_here_to_help": "We’re Here to Help!",
|
||||
"date_start_": "Date start",
|
||||
"something_wentwrong": "Something went wrong.",
|
||||
"license_code_": "License code",
|
||||
@@ -906,7 +899,6 @@
|
||||
"accounting": "Accounting",
|
||||
"system": "SYSTEM",
|
||||
"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",
|
||||
"learn_more": "Learn more",
|
||||
"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_but_don_t_need": "<strong> Non-Inventory:</strong> Products you buy and/or sell but don’t need to (or can’t) 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.",
|
||||
"congrats_you_are_ready_to_go": "Congrats! You are ready to go",
|
||||
"go_to_dashboard": "Go to dashboard",
|
||||
|
||||
"mr": "Mr.",
|
||||
"mrs": "Mrs.",
|
||||
"ms": "Ms.",
|
||||
@@ -1101,25 +1092,13 @@
|
||||
"filter_": "Filter...",
|
||||
"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.",
|
||||
"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!",
|
||||
"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",
|
||||
"support": "Support",
|
||||
"service_status": "Service Status",
|
||||
"pricing": "Pricing",
|
||||
"reseller_partner": "Reseller Partner",
|
||||
"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.",
|
||||
"Purchase and sell orders.": "Purchase and sell orders.",
|
||||
"Inventory management.": "Inventory management.",
|
||||
@@ -1129,14 +1108,13 @@
|
||||
"Track multi-branches and locations.": "Track multi-branches and locations.",
|
||||
"Projects accounting and timesheets.": "Projects accounting and timesheets.",
|
||||
"Accounting dimensions.": "Accounting dimensions.",
|
||||
"For one user and accountant.": "For one user and accountant.",
|
||||
"Monthly": "Monthly",
|
||||
"Yearly": "Yearly",
|
||||
"Plans & Payment": "Plans & Payment",
|
||||
"Initializing": "Initializing",
|
||||
"Getting started": "Getting started",
|
||||
"Congratulations": "Congratulations",
|
||||
"payment_has_been_done_successfully": "Payment has been done successfully.",
|
||||
|
||||
"manual_journal_number": "Manual journal {number}",
|
||||
"conditions_and_terms": "Conditions and terms",
|
||||
"allocate_landed_coast": "Allocate landed cost",
|
||||
@@ -1167,12 +1145,12 @@
|
||||
"Landed": "Landed",
|
||||
"This options allows you to be able to add additional cost eg. freight then allocate cost to the items in your bills.": "This options allows you to be able to add additional cost eg. freight then allocate cost to the items in your bills.",
|
||||
"Once your delete this located landed cost, you won't be able to restore it later, Are your sure you want to delete this transaction?": "Once your delete this located landed cost, you won't be able to restore it later, Are your sure you want to delete this transaction?",
|
||||
"journal_entries":"Journal entries",
|
||||
"contact":"Contact",
|
||||
"invoice_details":"Invoice details",
|
||||
"receipt_details":"Receipt details",
|
||||
"payment_receive_details":"Payment receive details",
|
||||
"payment_made_details":"Payment made details",
|
||||
"journal_entries": "Journal entries",
|
||||
"contact": "Contact",
|
||||
"invoice_details": "Invoice details",
|
||||
"receipt_details": "Receipt details",
|
||||
"payment_receive_details": "Payment receive details",
|
||||
"payment_made_details": "Payment made details",
|
||||
"New item category": "New item category",
|
||||
"New service": "New service",
|
||||
"New inventory item": "New inventory item",
|
||||
@@ -1182,10 +1160,10 @@
|
||||
"New tasks": "New tasks",
|
||||
"sorry_about_that_something_went_wrong": "Sorry about that! Something went wrong",
|
||||
"if_the_problem_stuck_please_contact_us_as_soon_as_possible": "If the problem stuck, please contact us as soon as possible.",
|
||||
"non-inventory":"Non-Inventory",
|
||||
"terms_conditions":"Terms conditions",
|
||||
"your_invoice_numbers_are_set_on_auto_increment_mod_are_you_sure_changing_this_setting":"Your invoice numbers are set on auto-increment mod. Are you sure changing this setting?",
|
||||
"auto_incrementing_number":"Auto-incrementing number",
|
||||
"non-inventory": "Non-Inventory",
|
||||
"terms_conditions": "Terms conditions",
|
||||
"your_invoice_numbers_are_set_on_auto_increment_mod_are_you_sure_changing_this_setting": "Your invoice numbers are set on auto-increment mod. Are you sure changing this setting?",
|
||||
"auto_incrementing_number": "Auto-incrementing number",
|
||||
"inventory_adjustment.publish.success_message": "The inventory adjustment has been published successfully.",
|
||||
"inventory_adjustment.publish.alert_message": "Are you sure you want to publish this inventory adjustment?",
|
||||
"the_contact_has_been_activated_successfully": "The contact has been inactivated successfully.",
|
||||
@@ -1207,15 +1185,67 @@
|
||||
"pdf_preview.download.button": "Download",
|
||||
"pdf_preview.preview.button": "Preview",
|
||||
"invoice_preview.dialog.title": "Invoice PDF Preview",
|
||||
"estimate_preview.dialog.title":"Estimate PDF Preview",
|
||||
"receipt_preview.dialog.title":"Receipt PDF Preview",
|
||||
"edit_contact":"Edit {name}",
|
||||
"estimate_preview.dialog.title": "Estimate PDF Preview",
|
||||
"receipt_preview.dialog.title": "Receipt PDF Preview",
|
||||
"edit_contact": "Edit {name}",
|
||||
"item.sell_description": "Sell description",
|
||||
"item.purchase_description": "Purchase description",
|
||||
"closed_date":"Closed date",
|
||||
"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 #"
|
||||
"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": "We’re 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": "Let’s 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"
|
||||
}
|
||||
@@ -3,10 +3,14 @@ import { lazy } from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { RESOURCES_TYPES } from '../common/resourcesTypes';
|
||||
|
||||
|
||||
const SUBSCRIPTION_TYPE = {
|
||||
MAIN: 'main',
|
||||
}
|
||||
// const BASE_URL = '/dashboard';
|
||||
|
||||
export const getDashboardRoutes = () => [
|
||||
// // Accounts.
|
||||
// Accounts.
|
||||
{
|
||||
path: `/accounts`,
|
||||
component: lazy(() => import('containers/Accounts/AccountsChart')),
|
||||
@@ -14,6 +18,7 @@ export const getDashboardRoutes = () => [
|
||||
hotkey: 'shift+a',
|
||||
pageTitle: intl.get('accounts_chart'),
|
||||
defaultSearchResource: RESOURCES_TYPES.ACCOUNT,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
// Accounting.
|
||||
{
|
||||
@@ -27,6 +32,7 @@ export const getDashboardRoutes = () => [
|
||||
sidebarExpand: false,
|
||||
backLink: true,
|
||||
defaultSearchResource: RESOURCES_TYPES.MANUAL_JOURNAL,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/manual-journals/:id/edit`,
|
||||
@@ -38,6 +44,7 @@ export const getDashboardRoutes = () => [
|
||||
sidebarExpand: false,
|
||||
backLink: true,
|
||||
defaultSearchResource: RESOURCES_TYPES.MANUAL_JOURNAL,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/manual-journals`,
|
||||
@@ -48,6 +55,7 @@ export const getDashboardRoutes = () => [
|
||||
hotkey: 'shift+m',
|
||||
pageTitle: intl.get('manual_journals'),
|
||||
defaultSearchResource: RESOURCES_TYPES.MANUAL_JOURNAL,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/items/categories`,
|
||||
@@ -57,6 +65,7 @@ export const getDashboardRoutes = () => [
|
||||
breadcrumb: intl.get('categories'),
|
||||
pageTitle: intl.get('category_list'),
|
||||
defaultSearchResource: RESOURCES_TYPES.ITEM,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
// Items.
|
||||
{
|
||||
@@ -67,6 +76,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('edit_item'),
|
||||
backLink: true,
|
||||
defaultSearchResource: RESOURCES_TYPES.ITEM,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/items/new?duplicate=/:id`,
|
||||
@@ -75,6 +85,7 @@ export const getDashboardRoutes = () => [
|
||||
}),
|
||||
breadcrumb: intl.get('duplicate_item'),
|
||||
defaultSearchResource: RESOURCES_TYPES.ITEM,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/items/new`,
|
||||
@@ -85,6 +96,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('new_item'),
|
||||
backLink: true,
|
||||
defaultSearchResource: RESOURCES_TYPES.ITEM,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/items`,
|
||||
@@ -93,6 +105,7 @@ export const getDashboardRoutes = () => [
|
||||
hotkey: 'shift+w',
|
||||
pageTitle: intl.get('items_list'),
|
||||
defaultSearchResource: RESOURCES_TYPES.ITEM,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
|
||||
// Inventory adjustments.
|
||||
@@ -104,6 +117,7 @@ export const getDashboardRoutes = () => [
|
||||
breadcrumb: intl.get('inventory_adjustments'),
|
||||
pageTitle: intl.get('inventory_adjustment_list'),
|
||||
defaultSearchResource: RESOURCES_TYPES.ITEM,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
|
||||
// Financial Reports.
|
||||
@@ -119,6 +133,7 @@ export const getDashboardRoutes = () => [
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
defaultSearchResource: RESOURCES_TYPES.INVENTORY_ADJUSTMENT,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/financial-reports/balance-sheet`,
|
||||
@@ -131,6 +146,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('balance_sheet'),
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/financial-reports/trial-balance-sheet`,
|
||||
@@ -145,6 +161,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('trial_balance_sheet'),
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/financial-reports/profit-loss-sheet`,
|
||||
@@ -157,6 +174,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('profit_loss_sheet'),
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: '/financial-reports/receivable-aging-summary',
|
||||
@@ -168,6 +186,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('receivable_aging_summary'),
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: '/financial-reports/payable-aging-summary',
|
||||
@@ -179,6 +198,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('payable_aging_summary'),
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/financial-reports/journal-sheet`,
|
||||
@@ -191,6 +211,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('journal_sheet'),
|
||||
sidebarExpand: false,
|
||||
backLink: true,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/financial-reports/purchases-by-items`,
|
||||
@@ -204,6 +225,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('purchases_by_items'),
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/financial-reports/sales-by-items`,
|
||||
@@ -217,6 +239,7 @@ export const getDashboardRoutes = () => [
|
||||
),
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/financial-reports/inventory-valuation`,
|
||||
@@ -230,6 +253,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('inventory_valuation'),
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/financial-reports/customers-balance-summary`,
|
||||
@@ -243,6 +267,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('customers_balance_summary'),
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/financial-reports/vendors-balance-summary`,
|
||||
@@ -256,6 +281,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('vendors_balance_summary'),
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/financial-reports/transactions-by-customers`,
|
||||
@@ -269,6 +295,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('customers_transactions'),
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/financial-reports/transactions-by-vendors`,
|
||||
@@ -282,6 +309,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('vendors_transactions'),
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/financial-reports/cash-flow`,
|
||||
@@ -295,6 +323,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('cash_flow_statement'),
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/financial-reports/inventory-item-details`,
|
||||
@@ -308,6 +337,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('inventory_item_details'),
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: '/financial-reports',
|
||||
@@ -316,6 +346,7 @@ export const getDashboardRoutes = () => [
|
||||
),
|
||||
breadcrumb: intl.get('financial_reports'),
|
||||
pageTitle: intl.get('all_financial_reports'),
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
// Exchange Rates
|
||||
{
|
||||
@@ -323,6 +354,7 @@ export const getDashboardRoutes = () => [
|
||||
component: lazy(() => import('containers/ExchangeRates/ExchangeRatesList')),
|
||||
breadcrumb: intl.get('exchange_rates_list'),
|
||||
pageTitle: intl.get('exchange_rates_list'),
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
// Expenses.
|
||||
{
|
||||
@@ -336,6 +368,7 @@ export const getDashboardRoutes = () => [
|
||||
sidebarExpand: false,
|
||||
backLink: true,
|
||||
defaultSearchResource: RESOURCES_TYPES.EXPENSE,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/expenses/:id/edit`,
|
||||
@@ -347,6 +380,7 @@ export const getDashboardRoutes = () => [
|
||||
sidebarExpand: false,
|
||||
backLink: true,
|
||||
defaultSearchResource: RESOURCES_TYPES.EXPENSE,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/expenses`,
|
||||
@@ -357,6 +391,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('expenses_list'),
|
||||
hotkey: 'shift+x',
|
||||
defaultSearchResource: RESOURCES_TYPES.EXPENSE,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
|
||||
// Customers
|
||||
@@ -370,6 +405,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('edit_customer'),
|
||||
backLink: true,
|
||||
defaultSearchResource: RESOURCES_TYPES.CUSTOMER,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/customers/new`,
|
||||
@@ -382,6 +418,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('new_customer'),
|
||||
backLink: true,
|
||||
defaultSearchResource: RESOURCES_TYPES.CUSTOMER,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/customers`,
|
||||
@@ -392,6 +429,7 @@ export const getDashboardRoutes = () => [
|
||||
hotkey: 'shift+c',
|
||||
pageTitle: intl.get('customers_list'),
|
||||
defaultSearchResource: RESOURCES_TYPES.CUSTOMER,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/customers/contact_duplicate=/:id`,
|
||||
@@ -403,6 +441,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('new_customer'),
|
||||
backLink: true,
|
||||
defaultSearchResource: RESOURCES_TYPES.CUSTOMER,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
|
||||
// Vendors
|
||||
@@ -416,6 +455,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('edit_vendor'),
|
||||
backLink: true,
|
||||
defaultSearchResource: RESOURCES_TYPES.VENDOR,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/vendors/new`,
|
||||
@@ -428,6 +468,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('new_vendor'),
|
||||
backLink: true,
|
||||
defaultSearchResource: RESOURCES_TYPES.VENDOR,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/vendors`,
|
||||
@@ -438,6 +479,7 @@ export const getDashboardRoutes = () => [
|
||||
hotkey: 'shift+v',
|
||||
pageTitle: intl.get('vendors_list'),
|
||||
defaultSearchResource: RESOURCES_TYPES.VENDOR,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/vendors/contact_duplicate=/:id`,
|
||||
@@ -449,6 +491,7 @@ export const getDashboardRoutes = () => [
|
||||
pageTitle: intl.get('new_vendor'),
|
||||
backLink: true,
|
||||
defaultSearchResource: RESOURCES_TYPES.VENDOR,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
|
||||
// Estimates
|
||||
@@ -463,6 +506,7 @@ export const getDashboardRoutes = () => [
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
defaultSearchResource: RESOURCES_TYPES.ESTIMATE,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/invoices/new?from_estimate_id=/:id`,
|
||||
@@ -475,6 +519,7 @@ export const getDashboardRoutes = () => [
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
defaultSearchResource: RESOURCES_TYPES.INVOICE,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/estimates/new`,
|
||||
@@ -488,6 +533,7 @@ export const getDashboardRoutes = () => [
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
defaultSearchResource: RESOURCES_TYPES.ESTIMATE,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/estimates`,
|
||||
@@ -499,6 +545,7 @@ export const getDashboardRoutes = () => [
|
||||
hotkey: 'shift+e',
|
||||
pageTitle: intl.get('estimates_list'),
|
||||
defaultSearchResource: RESOURCES_TYPES.ESTIMATE,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
|
||||
// Invoices.
|
||||
@@ -513,6 +560,7 @@ export const getDashboardRoutes = () => [
|
||||
sidebarExpand: false,
|
||||
backLink: true,
|
||||
defaultSearchResource: RESOURCES_TYPES.INVOICE,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/invoices/new`,
|
||||
@@ -526,6 +574,7 @@ export const getDashboardRoutes = () => [
|
||||
sidebarExpand: false,
|
||||
backLink: true,
|
||||
defaultSearchResource: RESOURCES_TYPES.INVOICE,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/invoices`,
|
||||
@@ -536,6 +585,7 @@ export const getDashboardRoutes = () => [
|
||||
hotkey: 'shift+i',
|
||||
pageTitle: intl.get('invoices_list'),
|
||||
defaultSearchResource: RESOURCES_TYPES.INVOICE,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
|
||||
// Sales Receipts.
|
||||
@@ -550,6 +600,7 @@ export const getDashboardRoutes = () => [
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
defaultSearchResource: RESOURCES_TYPES.RECEIPT,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/receipts/new`,
|
||||
@@ -563,6 +614,7 @@ export const getDashboardRoutes = () => [
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
defaultSearchResource: RESOURCES_TYPES.RECEIPT,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/receipts`,
|
||||
@@ -573,6 +625,7 @@ export const getDashboardRoutes = () => [
|
||||
hotkey: 'shift+r',
|
||||
pageTitle: intl.get('receipts_list'),
|
||||
defaultSearchResource: RESOURCES_TYPES.RECEIPT,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
|
||||
// Payment receives
|
||||
@@ -589,6 +642,7 @@ export const getDashboardRoutes = () => [
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
defaultSearchResource: RESOURCES_TYPES.PAYMENT_RECEIVE,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/payment-receives/new`,
|
||||
@@ -603,6 +657,7 @@ export const getDashboardRoutes = () => [
|
||||
backLink: true,
|
||||
sidebarExpand: false,
|
||||
defaultSearchResource: RESOURCES_TYPES.PAYMENT_RECEIVE,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/payment-receives`,
|
||||
@@ -614,6 +669,7 @@ export const getDashboardRoutes = () => [
|
||||
breadcrumb: intl.get('payment_receives_list'),
|
||||
pageTitle: intl.get('payment_receives_list'),
|
||||
defaultSearchResource: RESOURCES_TYPES.PAYMENT_RECEIVE,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
|
||||
// Bills
|
||||
@@ -628,6 +684,7 @@ export const getDashboardRoutes = () => [
|
||||
sidebarExpand: false,
|
||||
backLink: true,
|
||||
defaultSearchResource: RESOURCES_TYPES.BILL,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/bills/new`,
|
||||
@@ -641,6 +698,7 @@ export const getDashboardRoutes = () => [
|
||||
sidebarExpand: false,
|
||||
backLink: true,
|
||||
defaultSearchResource: RESOURCES_TYPES.BILL,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/bills`,
|
||||
@@ -651,6 +709,7 @@ export const getDashboardRoutes = () => [
|
||||
hotkey: 'shift+b',
|
||||
pageTitle: intl.get('bills_list'),
|
||||
defaultSearchResource: RESOURCES_TYPES.BILL,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
|
||||
// Subscription billing.
|
||||
@@ -658,6 +717,7 @@ export const getDashboardRoutes = () => [
|
||||
path: `/billing`,
|
||||
component: lazy(() => import('containers/Subscriptions/BillingForm')),
|
||||
breadcrumb: intl.get('new_billing'),
|
||||
subscriptionInactive: [SUBSCRIPTION_TYPE.MAIN]
|
||||
},
|
||||
// Payment modes.
|
||||
{
|
||||
@@ -673,6 +733,7 @@ export const getDashboardRoutes = () => [
|
||||
sidebarExpand: false,
|
||||
backLink: true,
|
||||
defaultSearchResource: RESOURCES_TYPES.PAYMENT_MADE,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/payment-mades/new`,
|
||||
@@ -687,6 +748,7 @@ export const getDashboardRoutes = () => [
|
||||
sidebarExpand: false,
|
||||
backLink: true,
|
||||
defaultSearchResource: RESOURCES_TYPES.PAYMENT_MADE,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
{
|
||||
path: `/payment-mades`,
|
||||
@@ -698,11 +760,13 @@ export const getDashboardRoutes = () => [
|
||||
breadcrumb: intl.get('payment_made_list'),
|
||||
pageTitle: intl.get('payment_made_list'),
|
||||
defaultSearchResource: RESOURCES_TYPES.PAYMENT_MADE,
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
// Homepage
|
||||
{
|
||||
path: `/`,
|
||||
component: lazy(() => import('containers/Homepage/Homepage')),
|
||||
breadcrumb: intl.get('homepage'),
|
||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,69 +1,95 @@
|
||||
import React from 'react';
|
||||
import { createReducer } from '@reduxjs/toolkit';
|
||||
import intl from 'react-intl-universal';
|
||||
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 = () => [
|
||||
{
|
||||
slug: 'month',
|
||||
label: intl.get('Monthly'),
|
||||
label: intl.get('plan.monthly'),
|
||||
},
|
||||
{
|
||||
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',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@ const plansSelector = (state) => state.plans.plans;
|
||||
const planSelector = (state, props) => state.plans.plans
|
||||
.find((plan) => plan.slug === props.planSlug);
|
||||
|
||||
const plansPeriodsSelector = (state) => state.plans.periods;
|
||||
|
||||
// Retrieve manual jounral current page results.
|
||||
export const getPlansSelector = () => createSelector(
|
||||
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.
|
||||
export const getPlanSelector = () => createSelector(
|
||||
planSelector,
|
||||
|
||||
@@ -1,23 +1,45 @@
|
||||
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);
|
||||
return subscriptions.find((subscription) => subscription.slug === slug);
|
||||
return subscriptions.find(
|
||||
(subscription) => subscription.slug === (slug || props.subscriptionType),
|
||||
);
|
||||
};
|
||||
|
||||
export const isSubscriptionOnTrialFactory = (slug) => createSelector(
|
||||
subscriptionSelector(slug),
|
||||
(subscription) => !!subscription?.on_trial,
|
||||
);
|
||||
const subscriptionsSelector = (state, props) => {
|
||||
const subscriptions = Object.values(state.subscriptions.data);
|
||||
return subscriptions.filter(
|
||||
(subscription) =>
|
||||
includes(props.subscriptionTypes, subscription.slug) ||
|
||||
!props.subscriptionTypes,
|
||||
);
|
||||
};
|
||||
|
||||
export const isSubscriptionActiveFactory = (slug) => createSelector(
|
||||
subscriptionSelector(slug),
|
||||
(subscription) => {
|
||||
export const isSubscriptionOnTrialFactory = (slug) =>
|
||||
createSelector(
|
||||
subscriptionSelector(slug),
|
||||
(subscription) => !!subscription?.on_trial,
|
||||
);
|
||||
|
||||
export const isSubscriptionActiveFactory = (slug) =>
|
||||
createSelector(subscriptionSelector(slug), (subscription) => {
|
||||
return !!subscription?.active;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
export const isSubscriptionInactiveFactory = (slug) => createSelector(
|
||||
subscriptionSelector(slug),
|
||||
(subscription) => !!subscription?.inactive,
|
||||
);
|
||||
export const isSubscriptionInactiveFactory = (slug) =>
|
||||
createSelector(
|
||||
subscriptionSelector(slug),
|
||||
(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),
|
||||
);
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.ScrollbarsCustom-Content{
|
||||
.ScrollbarsCustom-Content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
@@ -50,7 +50,7 @@
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
|
||||
svg{
|
||||
svg {
|
||||
opacity: $sidebar-logo-opacity;
|
||||
}
|
||||
}
|
||||
@@ -69,12 +69,14 @@
|
||||
&:not([class*="bp3-intent-"]):not(.bp3-minimal) {
|
||||
color: rgb(255, 255, 255);
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active,
|
||||
&.bp3-active {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.bp3-button-text {
|
||||
margin-right: 4px;
|
||||
text-overflow: ellipsis;
|
||||
@@ -82,13 +84,14 @@
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.bp3-popover-wrapper,
|
||||
.bp3-popover-target{
|
||||
.bp3-popover-target {
|
||||
max-width: 100%;
|
||||
display: inline-block;
|
||||
}
|
||||
@@ -104,20 +107,24 @@
|
||||
.sidebar__head-logo {
|
||||
transition: transform 0.05s ease-in-out;
|
||||
}
|
||||
|
||||
.is-subscription-inactive:not(.sidebar--mini-sidebar) & {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
&__scroll-wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&__version{
|
||||
|
||||
&__version {
|
||||
margin-top: auto;
|
||||
padding: 0 20px 20px;
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
&__inner{
|
||||
&__inner {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -170,7 +177,7 @@
|
||||
font-weight: 500;
|
||||
letter-spacing: 1px;
|
||||
|
||||
html[lang^="ar"] &{
|
||||
html[lang^="ar"] & {
|
||||
font-size: 12px;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
@@ -188,6 +195,7 @@
|
||||
padding-bottom: 6px;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.#{$ns}-menu-item {
|
||||
padding: 8px 20px;
|
||||
font-size: 15px;
|
||||
@@ -242,11 +250,11 @@
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
// Hide text of bigcapital logo.
|
||||
&-logo {
|
||||
|
||||
}
|
||||
&-organization{
|
||||
// Hide text of bigcapital logo.
|
||||
&-logo {}
|
||||
|
||||
&-organization {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
}
|
||||
@@ -263,7 +271,7 @@
|
||||
transition: min-width 0.15s ease-in-out;
|
||||
min-width: 50px;
|
||||
|
||||
.ScrollbarsCustom-Scroller{
|
||||
.ScrollbarsCustom-Scroller {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
@@ -274,14 +282,14 @@
|
||||
opacity: 0;
|
||||
transition-delay: 0s;
|
||||
}
|
||||
|
||||
|
||||
.sidebar__head-organization,
|
||||
.sidebar__menu,
|
||||
.sidebar__version {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.ScrollbarsCustom-Scroller{
|
||||
.ScrollbarsCustom-Scroller {
|
||||
overflow: scroll !important;
|
||||
}
|
||||
}
|
||||
@@ -326,12 +334,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.menu--dashboard-organization{
|
||||
|
||||
.menu--dashboard-organization {
|
||||
padding: 10px;
|
||||
|
||||
.org-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
|
||||
&__logo {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
@@ -343,16 +353,16 @@
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
||||
&__name {
|
||||
margin-left: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&__divider{
|
||||
&__divider {
|
||||
margin: 4px 0;
|
||||
height: 1px;
|
||||
background: #ebebeb;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
.billing-page{
|
||||
padding: 0 60px;
|
||||
margin-top: 20px;
|
||||
margin-top: 40px;
|
||||
max-width: 820px;
|
||||
|
||||
.bp3-tab-list{
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.setup-congrats {
|
||||
width: 600px;
|
||||
width: 500px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
padding-top: 80px;
|
||||
@@ -15,7 +15,7 @@
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.paragraph {
|
||||
font-size: 15px;
|
||||
font-size: 16px;
|
||||
opacity: 0.85;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
|
||||
form {
|
||||
h3 {
|
||||
margin-bottom: 1rem;
|
||||
color: #4e5764;
|
||||
margin-bottom: 1.2rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@import "../../Base.scss";
|
||||
|
||||
.setup-page {
|
||||
max-width: 1600px;
|
||||
|
||||
@@ -9,15 +11,21 @@
|
||||
&:before {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 30%;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
min-width: 300px;
|
||||
max-width: 350px;
|
||||
min-width: 350px;
|
||||
|
||||
@media only screen and (max-width: 1200px) {
|
||||
min-width: 300px;
|
||||
max-width: 350px;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
h1,
|
||||
h3 {
|
||||
font-weight: 500;
|
||||
@@ -36,11 +44,14 @@
|
||||
overflow: auto;
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
width: 30%;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
max-width: 350px;
|
||||
min-width: 300px;
|
||||
width: 350px;
|
||||
|
||||
@media only screen and (max-width: 1200px) {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
@@ -76,7 +87,7 @@
|
||||
font-size: 16px;
|
||||
opacity: 0.75;
|
||||
|
||||
span > a {
|
||||
span>a {
|
||||
text-decoration: underline;
|
||||
color: #ffffff;
|
||||
margin-top: 6px;
|
||||
@@ -101,7 +112,8 @@
|
||||
opacity: 0.75;
|
||||
padding-bottom: 5px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.15);
|
||||
p > span {
|
||||
|
||||
p>span {
|
||||
unicode-bidi: plaintext;
|
||||
}
|
||||
}
|
||||
@@ -110,7 +122,7 @@
|
||||
text-align: left;
|
||||
opacity: 0.55;
|
||||
|
||||
> div {
|
||||
>div {
|
||||
font-size: 13px;
|
||||
margin-right: 15px;
|
||||
display: inline;
|
||||
@@ -133,9 +145,11 @@
|
||||
margin: 0 auto;
|
||||
padding: 50px 0 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
list-style-type: none;
|
||||
@@ -155,6 +169,7 @@
|
||||
border-radius: 50%;
|
||||
background-color: #75859c;
|
||||
}
|
||||
|
||||
&::after {
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
@@ -165,24 +180,29 @@
|
||||
left: -50%;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
&:first-child::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
text-decoration: underline;
|
||||
|
||||
&::before {
|
||||
background-color: #75859c;
|
||||
}
|
||||
~ li {
|
||||
|
||||
~li {
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
background: #e0e0e0;
|
||||
}
|
||||
}
|
||||
|
||||
p.wizard-info {
|
||||
color: #004dd0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user