mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-14 11:50:31 +00:00
Compare commits
12 Commits
listen-pay
...
v0.19.9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58552c6c94 | ||
|
|
2072e35cfa | ||
|
|
1eaac9d691 | ||
|
|
a916e8a0cb | ||
|
|
a56f560036 | ||
|
|
0fb886936c | ||
|
|
670136916f | ||
|
|
768297f137 | ||
|
|
ef505a0a62 | ||
|
|
42d40620ec | ||
|
|
959ef7a691 | ||
|
|
e44ebb700a |
@@ -246,8 +246,11 @@ module.exports = {
|
||||
apiKey: process.env.LOOPS_API_KEY,
|
||||
},
|
||||
|
||||
oneClickDemoAccounts: parseBoolean(
|
||||
process.env.ONE_CLICK_DEMO_ACCOUNTS,
|
||||
false
|
||||
),
|
||||
/**
|
||||
* One-click demo accounts.
|
||||
*/
|
||||
oneClickDemoAccounts: {
|
||||
enable: parseBoolean(process.env.ONE_CLICK_DEMO_ACCOUNTS, false),
|
||||
demoUrl: process.env.ONE_CLICK_DEMO_ACCOUNTS_URL || '',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@ export default class SeedSettings extends TenantSeeder {
|
||||
|
||||
// Manual journals settings.
|
||||
{ group: 'manual_journals', key: 'next_number', value: '00001' },
|
||||
{ group: 'manual_journals', key: 'number_prefix', value: 'J-' },
|
||||
{ group: 'manual_journals', key: 'auto_increment', value: true },
|
||||
|
||||
// Sale invoices settings.
|
||||
|
||||
@@ -76,6 +76,10 @@ export interface IAuthSendedResetPassword {
|
||||
|
||||
export interface IAuthGetMetaPOJO {
|
||||
signupDisabled: boolean;
|
||||
oneClickDemo: {
|
||||
enable: boolean;
|
||||
demoUrl: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface IAuthSignUpVerifingEventPayload {
|
||||
|
||||
@@ -11,6 +11,10 @@ export class GetAuthMeta {
|
||||
public async getAuthMeta(): Promise<IAuthGetMetaPOJO> {
|
||||
return {
|
||||
signupDisabled: config.signupRestrictions.disabled,
|
||||
oneClickDemo: {
|
||||
enable: config.oneClickDemoAccounts.enable,
|
||||
demoUrl: config.oneClickDemoAccounts.demoUrl,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,10 @@ export class LemonResumeSubscription {
|
||||
* @param {string} subscriptionSlug - Subscription slug by default main subscription.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async resumeSubscription(tenantId: number, subscriptionSlug: string = 'main') {
|
||||
public async resumeSubscription(
|
||||
tenantId: number,
|
||||
subscriptionSlug: string = 'main'
|
||||
) {
|
||||
configureLemonSqueezy();
|
||||
|
||||
const subscription = await PlanSubscription.query().findOne({
|
||||
@@ -34,7 +37,7 @@ export class LemonResumeSubscription {
|
||||
cancelled: false,
|
||||
});
|
||||
if (returnedSub.error) {
|
||||
throw new ServiceError(ٌٌُERRORS.SOMETHING_WENT_WRONG_WITH_LS);
|
||||
throw new ServiceError(ERRORS.SOMETHING_WENT_WRONG_WITH_LS);
|
||||
}
|
||||
// Triggers `onSubscriptionResume` event.
|
||||
await this.eventPublisher.emitAsync(
|
||||
|
||||
@@ -20,7 +20,6 @@ import { queryConfig } from '../hooks/query/base';
|
||||
import { EnsureUserEmailVerified } from './Guards/EnsureUserEmailVerified';
|
||||
import { EnsureAuthNotAuthenticated } from './Guards/EnsureAuthNotAuthenticated';
|
||||
import { EnsureUserEmailNotVerified } from './Guards/EnsureUserEmailNotVerified';
|
||||
import { EnsureOneClickDemoAccountEnabled } from '@/containers/OneClickDemo/EnsureOneClickDemoAccountEnabled';
|
||||
|
||||
const EmailConfirmation = LazyLoader({
|
||||
loader: () => import('@/containers/Authentication/EmailConfirmation'),
|
||||
@@ -31,6 +30,7 @@ const RegisterVerify = LazyLoader({
|
||||
const OneClickDemoPage = LazyLoader({
|
||||
loader: () => import('@/containers/OneClickDemo/OneClickDemoPage'),
|
||||
});
|
||||
|
||||
/**
|
||||
* App inner.
|
||||
*/
|
||||
@@ -40,13 +40,7 @@ function AppInsider({ history }) {
|
||||
<DashboardThemeProvider>
|
||||
<Router history={history}>
|
||||
<Switch>
|
||||
<Route path={'/one_click_demo'}>
|
||||
<EnsureOneClickDemoAccountEnabled>
|
||||
<EnsureAuthNotAuthenticated>
|
||||
<OneClickDemoPage />
|
||||
</EnsureAuthNotAuthenticated>
|
||||
</EnsureOneClickDemoAccountEnabled>
|
||||
</Route>
|
||||
<Route path={'/one_click_demo'} children={<OneClickDemoPage />} />
|
||||
<Route path={'/auth/register/verify'}>
|
||||
<EnsureAuthenticated>
|
||||
<EnsureUserEmailNotVerified>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { useApplicationBoot } from '@/components';
|
||||
import { useAuthMetadata } from '@/hooks/query/authentication';
|
||||
|
||||
/**
|
||||
* Private pages provider.
|
||||
@@ -9,7 +10,10 @@ export function PrivatePagesProvider({
|
||||
// #ownProps
|
||||
children,
|
||||
}) {
|
||||
const { isLoading } = useApplicationBoot();
|
||||
const { isLoading: isAppBootLoading } = useApplicationBoot();
|
||||
const { isLoading: isAuthMetaLoading } = useAuthMetadata();
|
||||
|
||||
const isLoading = isAppBootLoading || isAuthMetaLoading;
|
||||
|
||||
return <React.Fragment>{!isLoading ? children : null}</React.Fragment>;
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
export const Config = {
|
||||
oneClickDemo: {
|
||||
enable: process.env.REACT_APP_ONE_CLICK_DEMO_ENABLE === 'true',
|
||||
demoUrl: process.env.REACT_APP_DEMO_ACCOUNT_URL || '',
|
||||
},
|
||||
};
|
||||
@@ -54,9 +54,9 @@ export default [
|
||||
disabled: false,
|
||||
href: '/preferences/items',
|
||||
},
|
||||
{
|
||||
text: <T id={'sms_integration.label'} />,
|
||||
disabled: false,
|
||||
href: '/preferences/sms-message',
|
||||
},
|
||||
// {
|
||||
// text: <T id={'sms_integration.label'} />,
|
||||
// disabled: false,
|
||||
// href: '/preferences/sms-message',
|
||||
// },
|
||||
];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { Config } from '@/config';
|
||||
import { useOneClickDemoBoot } from './OneClickDemoBoot';
|
||||
|
||||
interface EnsureOneClickDemoAccountEnabledProps {
|
||||
children: React.ReactNode;
|
||||
@@ -11,9 +11,10 @@ export const EnsureOneClickDemoAccountEnabled = ({
|
||||
children,
|
||||
redirectTo = '/',
|
||||
}: EnsureOneClickDemoAccountEnabledProps) => {
|
||||
const enabeld = Config.oneClickDemo.enable || false;
|
||||
const { authMeta } = useOneClickDemoBoot();
|
||||
const enabled = authMeta?.meta?.one_click_demo?.enable || false;
|
||||
|
||||
if (!enabeld) {
|
||||
if (!enabled) {
|
||||
return <Redirect to={{ pathname: redirectTo }} />;
|
||||
}
|
||||
return <>{children}</>;
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import React, { createContext, useContext, ReactNode } from 'react';
|
||||
import { useAuthMetadata } from '@/hooks/query/authentication';
|
||||
|
||||
interface OneClickDemoContextType {
|
||||
authMeta: any;
|
||||
}
|
||||
|
||||
const OneClickDemoContext = createContext<OneClickDemoContextType>(
|
||||
{} as OneClickDemoContextType,
|
||||
);
|
||||
|
||||
export const useOneClickDemoBoot = () => {
|
||||
const context = useContext(OneClickDemoContext);
|
||||
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
'useOneClickDemo must be used within a OneClickDemoProvider',
|
||||
);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
interface OneClickDemoBootProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const OneClickDemoBoot: React.FC<OneClickDemoBootProps> = ({
|
||||
children,
|
||||
}) => {
|
||||
const { isLoading: isAuthMetaLoading, data: authMeta } = useAuthMetadata();
|
||||
|
||||
const value = {
|
||||
isAuthMetaLoading,
|
||||
authMeta,
|
||||
};
|
||||
|
||||
if (isAuthMetaLoading) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<OneClickDemoContext.Provider value={value}>
|
||||
{children}
|
||||
</OneClickDemoContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -1,97 +1,16 @@
|
||||
// @ts-nocheck
|
||||
import { Button, Intent, ProgressBar, Text } from '@blueprintjs/core';
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
useCreateOneClickDemo,
|
||||
useOneClickDemoSignin,
|
||||
} from '@/hooks/query/oneclick-demo';
|
||||
import { Box, Icon, Stack } from '@/components';
|
||||
import { useJob } from '@/hooks/query';
|
||||
import style from './OneClickDemoPage.module.scss';
|
||||
import { EnsureAuthNotAuthenticated } from '@/components/Guards/EnsureAuthNotAuthenticated';
|
||||
import { EnsureOneClickDemoAccountEnabled } from './EnsureOneClickDemoAccountEnabled';
|
||||
import { OneClickDemoBoot } from './OneClickDemoBoot';
|
||||
import { OneClickDemoPageContent } from './OneClickDemoPageContent';
|
||||
|
||||
export default function OneClickDemoPage() {
|
||||
const {
|
||||
mutateAsync: createOneClickDemo,
|
||||
isLoading: isCreateOneClickLoading,
|
||||
} = useCreateOneClickDemo();
|
||||
const {
|
||||
mutateAsync: oneClickDemoSignIn,
|
||||
isLoading: isOneclickDemoSigningIn,
|
||||
} = useOneClickDemoSignin();
|
||||
|
||||
// Job states.
|
||||
const [demoId, setDemoId] = useState<string>('');
|
||||
const [buildJobId, setBuildJobId] = useState<string>('');
|
||||
const [isJobDone, setIsJobDone] = useState<boolean>(false);
|
||||
|
||||
const {
|
||||
data: { running, completed },
|
||||
} = useJob(buildJobId, {
|
||||
refetchInterval: 2000,
|
||||
enabled: !isJobDone && !!buildJobId,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (completed) {
|
||||
setIsJobDone(true);
|
||||
}
|
||||
}, [completed, setIsJobDone]);
|
||||
|
||||
// One the job done request sign-in using the demo id.
|
||||
useEffect(() => {
|
||||
if (isJobDone) {
|
||||
oneClickDemoSignIn({ demoId }).then((res) => {
|
||||
debugger;
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isJobDone]);
|
||||
|
||||
const handleCreateAccountBtnClick = () => {
|
||||
createOneClickDemo({})
|
||||
.then(({ data: { data } }) => {
|
||||
setBuildJobId(data?.build_job?.job_id);
|
||||
setDemoId(data?.demo_id);
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
const isLoading = running || isOneclickDemoSigningIn;
|
||||
|
||||
return (
|
||||
<Box className={style.root}>
|
||||
<Box className={style.inner}>
|
||||
<Stack align={'center'} spacing={40}>
|
||||
<Icon icon="bigcapital" height={37} width={228} />
|
||||
|
||||
{isLoading && (
|
||||
<Stack align={'center'} spacing={15}>
|
||||
<ProgressBar stripes value={null} className={style.progressBar} />
|
||||
{isOneclickDemoSigningIn && (
|
||||
<Text className={style.waitingText}>
|
||||
It's signin-in to your demo account, Just a second!
|
||||
</Text>
|
||||
)}
|
||||
{running && (
|
||||
<Text className={style.waitingText}>
|
||||
We're preparing temporary environment for trial, It typically
|
||||
take few seconds. Do not close or refresh the page.
|
||||
</Text>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
{!isLoading && (
|
||||
<Button
|
||||
className={style.oneClickBtn}
|
||||
intent={Intent.NONE}
|
||||
onClick={handleCreateAccountBtnClick}
|
||||
loading={isCreateOneClickLoading}
|
||||
>
|
||||
Create Demo Account
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
<EnsureAuthNotAuthenticated>
|
||||
<OneClickDemoBoot>
|
||||
<EnsureOneClickDemoAccountEnabled>
|
||||
<OneClickDemoPageContent />
|
||||
</EnsureOneClickDemoAccountEnabled>
|
||||
</OneClickDemoBoot>
|
||||
</EnsureAuthNotAuthenticated>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
// @ts-nocheck
|
||||
import { Button, Intent, ProgressBar, Text } from '@blueprintjs/core';
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
useCreateOneClickDemo,
|
||||
useOneClickDemoSignin,
|
||||
} from '@/hooks/query/oneclick-demo';
|
||||
import { Box, Icon, Stack } from '@/components';
|
||||
import { useJob } from '@/hooks/query';
|
||||
import style from './OneClickDemoPage.module.scss';
|
||||
|
||||
export function OneClickDemoPageContent() {
|
||||
const {
|
||||
mutateAsync: createOneClickDemo,
|
||||
isLoading: isCreateOneClickLoading,
|
||||
} = useCreateOneClickDemo();
|
||||
const {
|
||||
mutateAsync: oneClickDemoSignIn,
|
||||
isLoading: isOneclickDemoSigningIn,
|
||||
} = useOneClickDemoSignin();
|
||||
|
||||
// Job states.
|
||||
const [demoId, setDemoId] = useState<string>('');
|
||||
const [buildJobId, setBuildJobId] = useState<string>('');
|
||||
const [isJobDone, setIsJobDone] = useState<boolean>(false);
|
||||
|
||||
const {
|
||||
data: { running, completed },
|
||||
} = useJob(buildJobId, {
|
||||
refetchInterval: 2000,
|
||||
enabled: !isJobDone && !!buildJobId,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (completed) {
|
||||
setIsJobDone(true);
|
||||
}
|
||||
}, [completed, setIsJobDone]);
|
||||
|
||||
// One the job done request sign-in using the demo id.
|
||||
useEffect(() => {
|
||||
if (isJobDone) {
|
||||
oneClickDemoSignIn({ demoId }).then((res) => {
|
||||
debugger;
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isJobDone]);
|
||||
|
||||
const handleCreateAccountBtnClick = () => {
|
||||
createOneClickDemo({})
|
||||
.then(({ data: { data } }) => {
|
||||
setBuildJobId(data?.build_job?.job_id);
|
||||
setDemoId(data?.demo_id);
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
const isLoading = running || isOneclickDemoSigningIn;
|
||||
|
||||
return (
|
||||
<Box className={style.root}>
|
||||
<Box className={style.inner}>
|
||||
<Stack align={'center'} spacing={40}>
|
||||
<Icon icon="bigcapital" height={37} width={228} />
|
||||
|
||||
{isLoading && (
|
||||
<Stack align={'center'} spacing={15}>
|
||||
<ProgressBar stripes value={null} className={style.progressBar} />
|
||||
{isOneclickDemoSigningIn && (
|
||||
<Text className={style.waitingText}>
|
||||
It's signin-in to your demo account, Just a second!
|
||||
</Text>
|
||||
)}
|
||||
{running && (
|
||||
<Text className={style.waitingText}>
|
||||
We're preparing temporary environment for trial, It typically
|
||||
take few seconds. Do not close or refresh the page.
|
||||
</Text>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
{!isLoading && (
|
||||
<Button
|
||||
className={style.oneClickBtn}
|
||||
intent={Intent.NONE}
|
||||
onClick={handleCreateAccountBtnClick}
|
||||
loading={isCreateOneClickLoading}
|
||||
>
|
||||
Create Demo Account
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
width: 100%;
|
||||
margin-bottom: 2rem;
|
||||
background: transparent;
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
border: 1px solid rgb(255, 255, 255, 0.3);
|
||||
padding: 10px;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
border: 1px solid rgb(255, 255, 255, 0.2);
|
||||
padding: 8px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
border-radius: 5px;
|
||||
@@ -12,11 +12,11 @@
|
||||
transition: border 0.15s ease-in-out, color 0.15s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
border: 1px solid rgb(255, 255, 255, 0.6);
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
border: 1px solid rgb(255, 255, 255, 0.3);
|
||||
}
|
||||
}
|
||||
.demoButtonLabel{
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-size: 13px;
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import { Icon, For, FormattedMessage as T, Stack } from '@/components';
|
||||
import { getFooterLinks } from '@/constants/footerLinks';
|
||||
import { useAuthActions } from '@/hooks/state';
|
||||
import style from './SetupLeftSection.module.scss';
|
||||
import { Config } from '@/config';
|
||||
import { useAuthMetadata } from '@/hooks/query';
|
||||
|
||||
/**
|
||||
* Footer item link.
|
||||
@@ -28,13 +28,16 @@ function SetupLeftSectionFooter() {
|
||||
// Retrieve the footer links.
|
||||
const footerLinks = getFooterLinks();
|
||||
|
||||
const { data: authMeta } = useAuthMetadata();
|
||||
const demoUrl = authMeta?.meta?.one_click_demo?.demo_url;
|
||||
|
||||
const handleDemoBtnClick = () => {
|
||||
window.open(Config.oneClickDemo.demoUrl);
|
||||
window.open(demoUrl);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={'content__footer'}>
|
||||
{Config.oneClickDemo.demoUrl && (
|
||||
{demoUrl && (
|
||||
<Stack spacing={16}>
|
||||
<Text className={style.demoButtonLabel}>Not Now?</Text>
|
||||
<button className={style.demoButton} onClick={handleDemoBtnClick}>
|
||||
|
||||
@@ -100,7 +100,7 @@ export const useAuthResetPassword = (props) => {
|
||||
/**
|
||||
* Fetches the authentication page metadata.
|
||||
*/
|
||||
export const useAuthMetadata = (props) => {
|
||||
export const useAuthMetadata = (props = {}) => {
|
||||
return useRequestQuery(
|
||||
[t.AUTH_METADATA_PAGE],
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ import Roles from '../containers/Preferences/Users/Roles/RolesForm/RolesFormPage
|
||||
import Accountant from '@/containers/Preferences/Accountant/Accountant';
|
||||
import Currencies from '@/containers/Preferences/Currencies/Currencies';
|
||||
import Item from '@/containers/Preferences/Item';
|
||||
import SMSIntegration from '../containers/Preferences/SMSIntegration';
|
||||
// import SMSIntegration from '../containers/Preferences/SMSIntegration';
|
||||
import DefaultRoute from '../containers/Preferences/DefaultRoute';
|
||||
import Warehouses from '../containers/Preferences/Warehouses';
|
||||
import Branches from '../containers/Preferences/Branches';
|
||||
@@ -85,11 +85,11 @@ export default [
|
||||
component: Item,
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: `${BASE_URL}/sms-message`,
|
||||
component: SMSIntegration,
|
||||
exact: true,
|
||||
},
|
||||
// {
|
||||
// path: `${BASE_URL}/sms-message`,
|
||||
// component: SMSIntegration,
|
||||
// exact: true,
|
||||
// },
|
||||
{
|
||||
path: `${BASE_URL}/billing`,
|
||||
component: BillingPage,
|
||||
|
||||
@@ -72,10 +72,10 @@
|
||||
|
||||
&__text {
|
||||
font-size: 18px;
|
||||
opacity: 0.75;
|
||||
opacity: 0.55;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 200;
|
||||
}
|
||||
}
|
||||
|
||||
&__organization {
|
||||
font-size: 16px;
|
||||
|
||||
Reference in New Issue
Block a user