From 361aab89e63e5fe145994ae980bc87c1db2b9f95 Mon Sep 17 00:00:00 2001 From: "a.bouhuolia" Date: Wed, 8 Sep 2021 16:27:16 +0200 Subject: [PATCH] feat: application booting. --- client/src/common/classes.js | 1 + client/src/components/App.js | 61 +++++--------- client/src/components/AppIntlLoader.js | 36 +++++--- client/src/components/Authentication.js | 3 +- .../components/Dashboard/AuthenticatedUser.js | 28 +++++++ .../src/components/Dashboard/DashboardBoot.js | 76 +++++++++++++++++ .../components/Dashboard/DashboardProvider.js | 9 +- .../src/components/Dashboard/PrivatePages.js | 7 +- .../Dashboard/PrivatePagesProvider.js | 14 +--- .../src/components/Dashboard/SplashScreen.js | 15 ++++ client/src/components/Dashboard/TopbarUser.js | 7 +- client/src/components/Dashboard/index.js | 4 + client/src/components/EmptyStatus.js | 21 ++--- client/src/components/Sidebar/SidebarHead.js | 5 +- client/src/components/index.js | 1 + .../ManualJournalsDataTable.js | 58 ++++++------- .../JournalsLanding/ManualJournalsList.js | 19 ++--- .../Authentication/AuthenticationBoot.js | 15 ++++ client/src/containers/Authentication/Login.js | 16 ++-- .../src/containers/Authentication/Register.js | 54 ++++++------ .../Authentication/withAuthentication.js | 7 +- .../CustomersLanding/CustomersList.js | 5 +- .../CustomersLanding/CustomersTable.js | 66 ++++++++------- .../src/containers/Dashboard/withDashboard.js | 2 + .../Dashboard/withDashboardActions.js | 4 + .../PaymentViaVoucherDialogContent.js | 6 +- .../ExpensesLanding/ExpenseDataTable.js | 55 ++++++------ .../Expenses/ExpensesLanding/ExpensesList.js | 5 +- .../APAgingSummary/APAgingSummary.js | 19 +++-- .../ARAgingSummary/ARAgingSummary.js | 6 +- .../BalanceSheet/BalanceSheet.js | 17 ++-- .../CashFlowStatement/CashFlowStatement.js | 6 +- .../CustomersBalanceSummary.js | 7 +- .../CustomersTransactions.js | 6 +- .../GeneralLedger/GeneralLedger.js | 6 +- .../InventoryItemDetails.js | 6 +- .../InventoryValuation/InventoryValuation.js | 6 +- .../FinancialStatements/Journal/Journal.js | 6 +- .../ProfitLossSheet/ProfitLossSheet.js | 6 +- .../PurchasesByItems/PurchasesByItems.js | 6 +- .../SalesByItems/SalesByItems.js | 6 +- .../TrialBalanceSheet/TrialBalanceSheet.js | 6 +- .../VendorsBalanceSummary.js | 6 +- .../VendorsTransactions.js | 6 +- client/src/containers/Items/ItemsDataTable.js | 74 +++++++++-------- .../src/containers/Items/ItemsEmptyStatus.js | 2 +- client/src/containers/Items/ItemsList.js | 7 +- .../Preferences/General/GeneralFormPage.js | 5 ++ .../Purchases/Bills/BillsLanding/BillsList.js | 5 +- .../Bills/BillsLanding/BillsTable.js | 3 + .../PaymentsLanding/PaymentMadeList.js | 5 +- .../PaymentsLanding/PaymentMadesTable.js | 55 ++++++------ .../EstimatesLanding/EstimatesDataTable.js | 62 +++++++------- .../EstimatesLanding/EstimatesList.js | 5 +- .../InvoicesLanding/InvoicesDataTable.js | 66 ++++++++------- .../Invoices/InvoicesLanding/InvoicesList.js | 5 +- .../PaymentsLanding/PaymentReceivesList.js | 5 +- .../PaymentsLanding/PaymentReceivesTable.js | 56 +++++++------ .../Receipts/ReceiptsLanding/ReceiptsList.js | 5 +- .../Receipts/ReceiptsLanding/ReceiptsTable.js | 64 +++++++------- .../src/containers/Setup/SetupCongratsPage.js | 21 +++-- .../containers/Setup/SetupInitializingForm.js | 83 ++++++++++++------- .../containers/Setup/SetupOrganizationForm.js | 9 +- .../containers/Setup/SetupOrganizationPage.js | 5 +- .../src/containers/Setup/SetupSubscription.js | 4 +- .../SubscriptionPeriodsSection.js | 4 + .../containers/Subscriptions/BillingForm.js | 2 +- .../Subscriptions/BillingPeriodsInput.js | 2 + .../Vendors/VendorsLanding/VendorsList.js | 8 +- .../Vendors/VendorsLanding/VendorsTable.js | 64 +++++++------- client/src/hooks/query/authentication.js | 49 ++++++----- client/src/hooks/query/base.js | 9 ++ client/src/hooks/query/users.js | 15 ++-- client/src/hooks/state/authentication.js | 21 ++++- client/src/lang/{ar-ly => ar}/index.json | 0 client/src/lang/{ar-ly => ar}/locale.js | 0 .../authentication/authentication.actions.js | 6 +- .../authentication/authentication.reducer.js | 37 +++------ .../authentication.selectors.js | 4 +- client/src/store/createStore.js | 5 +- .../src/store/dashboard/dashboard.actions.js | 15 ++++ .../src/store/dashboard/dashboard.reducer.js | 12 +++ client/src/store/dashboard/dashboard.types.js | 2 + .../organizations/organizations.actions.js | 4 +- .../organizations/organizations.reducers.js | 6 +- .../organizations/organizations.selectors.js | 59 ++++++------- .../organizations/organizations.types.js | 1 + client/src/store/plans/plans.reducer.js | 2 +- .../style/components/BigcapitalLoading.scss | 2 + ....scss => DataTableEmptyStatus.module.scss} | 20 ++--- .../src/style/pages/Dashboard/Dashboard.scss | 12 +++ client/src/utils.js | 14 ++++ .../models/Subscriptions/PlanSubscription.js | 42 ++++------ 93 files changed, 961 insertions(+), 723 deletions(-) create mode 100644 client/src/components/Dashboard/AuthenticatedUser.js create mode 100644 client/src/components/Dashboard/DashboardBoot.js create mode 100644 client/src/components/Dashboard/SplashScreen.js create mode 100644 client/src/components/Dashboard/index.js create mode 100644 client/src/containers/Authentication/AuthenticationBoot.js create mode 100644 client/src/hooks/query/base.js rename client/src/lang/{ar-ly => ar}/index.json (100%) rename client/src/lang/{ar-ly => ar}/locale.js (100%) rename client/src/style/components/DataTable/{DataTableEmptyStatus.scss => DataTableEmptyStatus.module.scss} (70%) diff --git a/client/src/common/classes.js b/client/src/common/classes.js index 7a3af3936..5df7cb855 100644 --- a/client/src/common/classes.js +++ b/client/src/common/classes.js @@ -14,6 +14,7 @@ const CLASSES = { DASHBOARD_CONTENT: 'dashboard-content', DASHBOARD_CONTENT_PREFERENCES: 'dashboard-content--preferences', DASHBOARD_CONTENT_PANE: 'Pane2', + DASHBOARD_CENTERED_EMPTY_STATUS: 'dashboard__centered-empty-status', PAGE_FORM: 'page-form', PAGE_FORM_HEADER: 'page-form__header', diff --git a/client/src/components/App.js b/client/src/components/App.js index aa79941ae..e656550e4 100644 --- a/client/src/components/App.js +++ b/client/src/components/App.js @@ -13,29 +13,14 @@ import PrivateRoute from 'components/Guards/PrivateRoute'; import GlobalErrors from 'containers/GlobalErrors/GlobalErrors'; import DashboardPrivatePages from 'components/Dashboard/PrivatePages'; import Authentication from 'components/Authentication'; - -// Query client config. -const queryConfig = { - defaultOptions: { - queries: { - refetchOnWindowFocus: true, - staleTime: 30000, - }, - }, -}; - -// Global fetch query. -function GlobalFetchQuery({ - children -}) { - window.localStorage.setItem('lang', 'ar-ly'); - return children -} +import { SplashScreen } from '../components'; +import { queryConfig } from '../hooks/query/base' /** * Core application. */ -function App({ locale }) { +export default function App() { + // Browser history. const history = createBrowserHistory(); // Query client. @@ -43,30 +28,24 @@ function App({ locale }) { return ( - - -
- - - - - - - - + - -
-
-
+ +
+ + + + + + + + + + +
+
); -} - -App.defaultProps = { - locale: 'en', -}; - -export default App; +} \ No newline at end of file diff --git a/client/src/components/AppIntlLoader.js b/client/src/components/AppIntlLoader.js index e6f85f46e..d32da21e8 100644 --- a/client/src/components/AppIntlLoader.js +++ b/client/src/components/AppIntlLoader.js @@ -4,12 +4,14 @@ import { setLocale } from 'yup'; import intl from 'react-intl-universal'; import { find } from 'lodash'; import rtlDetect from 'rtl-detect'; +import * as R from 'ramda'; import { AppIntlProvider } from './AppIntlProvider'; -import DashboardLoadingIndicator from 'components/Dashboard/DashboardLoadingIndicator'; +import withDashboardActions from '../containers/Dashboard/withDashboardActions'; +import withDashboard from '../containers/Dashboard/withDashboard'; const SUPPORTED_LOCALES = [ { name: 'English', value: 'en' }, - { name: 'العربية', value: 'ar-ly' }, + { name: 'العربية', value: 'ar' }, ]; /** @@ -18,7 +20,7 @@ const SUPPORTED_LOCALES = [ function getCurrentLocal() { let currentLocale = intl.determineLocale({ urlLocaleKey: 'lang', - cookieLocaleKey: 'lang', + cookieLocaleKey: 'locale', localStorageLocaleKey: 'lang', }); if (!find(SUPPORTED_LOCALES, { value: currentLocale })) { @@ -57,10 +59,14 @@ function useDocumentDirectionModifier(locale, isRTL) { /** * Application Intl loader. */ -export default function AppIntlLoader({ children }) { - const [isLoading, setIsLoading] = React.useState(true); +function AppIntlLoader({ appIntlIsLoading, setAppIntlIsLoading, children }) { + const [isLocalsLoading, setIsLocalsLoading] = React.useState(true); + const [isYupLoading, setIsYupLoading] = React.useState(true); + + // Retrieve the current locale. const currentLocale = getCurrentLocal(); + // Detarmines the document direction based on the given locale. const isRTL = rtlDetect.isRtlLang(currentLocale); // Modifies the html document direction @@ -79,23 +85,33 @@ export default function AppIntlLoader({ children }) { }) .then(() => { moment.locale(currentLocale); - setIsLoading(false); + setIsLocalsLoading(false); }); - }, [currentLocale, setIsLoading]); + }, [currentLocale, setIsLocalsLoading]); React.useEffect(() => { loadYupLocales(currentLocale) .then(({ locale }) => { setLocale(locale); + setIsYupLoading(false); }) .then(() => {}); }, [currentLocale]); + React.useEffect(() => { + if (!isLocalsLoading && !isYupLoading) { + setAppIntlIsLoading(false); + } + }); + return ( - - {children} - + {appIntlIsLoading ? null : children} ); } + +export default R.compose( + withDashboardActions, + withDashboard(({ appIntlIsLoading }) => ({ appIntlIsLoading })), +)(AppIntlLoader); diff --git a/client/src/components/Authentication.js b/client/src/components/Authentication.js index 8bbb29515..a9b8575c2 100644 --- a/client/src/components/Authentication.js +++ b/client/src/components/Authentication.js @@ -6,7 +6,7 @@ import authenticationRoutes from 'routes/authentication'; import { FormattedMessage as T } from 'components'; import Icon from 'components/Icon'; import { useIsAuthenticated } from 'hooks/state'; - +import {AuthenticationBoot} from '../containers/Authentication/AuthenticationBoot'; import 'style/pages/Authentication/Auth.scss'; function PageFade(props) { @@ -26,6 +26,7 @@ export default function AuthenticationWrapper({ ...rest }) { ) : (
+ + ); +} + +export const AuthenticatedUser = withAuthentication( + ({ authenticatedUserId }) => ({ + authenticatedUserId, + }), +)(AuthenticatedUserComponent); + +export const useAuthenticatedUser = () => + React.useContext(AuthenticatedUserContext); diff --git a/client/src/components/Dashboard/DashboardBoot.js b/client/src/components/Dashboard/DashboardBoot.js new file mode 100644 index 000000000..69351ae6b --- /dev/null +++ b/client/src/components/Dashboard/DashboardBoot.js @@ -0,0 +1,76 @@ +import React from 'react'; +import * as R from 'ramda'; + +import { useUser, useCurrentOrganization } from 'hooks/query'; +import withAuthentication from '../../containers/Authentication/withAuthentication'; +import withDashboardActions from '../../containers/Dashboard/withDashboardActions'; + +import { setCookie, getCookie } from '../../utils'; + +/** + * Dashboard async booting. + */ +function DashboardBootJSX({ setAppIsLoading, authenticatedUserId }) { + // Fetches the current user's organization. + const { isSuccess: isCurrentOrganizationSuccess, data: organization } = + useCurrentOrganization(); + + // Authenticated user. + const { isSuccess: isAuthUserSuccess, data: authUser } = + useUser(authenticatedUserId); + + // Initial locale cookie value. + const localeCookie = getCookie('locale'); + + // Is the dashboard booted. + const isBooted = React.useRef(false); + + // Syns the organization language with locale cookie. + React.useEffect(() => { + if (organization?.metadata?.language) { + setCookie('locale', organization.metadata.language); + } + }, [organization]); + + React.useEffect(() => { + // Can't continue if the organization metadata is not loaded yet. + if (!organization?.metadata?.language) { + return; + } + // Can't continue if the organization is already booted. + if (isBooted.current) { + return; + } + // Reboot the application in case the initial locale not equal + // the current organization language. + if (localeCookie !== organization.metadata.language) { + window.location.reload(); + } + }, [localeCookie, organization]); + + React.useEffect(() => { + // Once the all requests complete change the app loading state. + if ( + isAuthUserSuccess && + isCurrentOrganizationSuccess && + localeCookie === organization?.metadata?.language + ) { + setAppIsLoading(false); + isBooted.current = true; + } + }, [ + isAuthUserSuccess, + isCurrentOrganizationSuccess, + organization, + setAppIsLoading, + localeCookie, + ]); + return null; +} + +export const DashboardBoot = R.compose( + withAuthentication(({ authenticatedUserId }) => ({ + authenticatedUserId, + })), + withDashboardActions, +)(DashboardBootJSX); diff --git a/client/src/components/Dashboard/DashboardProvider.js b/client/src/components/Dashboard/DashboardProvider.js index 6f454214f..5a5fce966 100644 --- a/client/src/components/Dashboard/DashboardProvider.js +++ b/client/src/components/Dashboard/DashboardProvider.js @@ -1,13 +1,8 @@ import React from 'react'; -import DashboardLoadingIndicator from './DashboardLoadingIndicator'; /** * Dashboard provider. */ export default function DashboardProvider({ children }) { - return ( - - { children } - - ) -} \ No newline at end of file + return children; +} diff --git a/client/src/components/Dashboard/PrivatePages.js b/client/src/components/Dashboard/PrivatePages.js index 0d0b5d06c..8c2a2d421 100644 --- a/client/src/components/Dashboard/PrivatePages.js +++ b/client/src/components/Dashboard/PrivatePages.js @@ -7,6 +7,7 @@ import SetupWizardPage from 'containers/Setup/WizardSetupPage'; import EnsureOrganizationIsReady from 'components/Guards/EnsureOrganizationIsReady'; import EnsureOrganizationIsNotReady from 'components/Guards/EnsureOrganizationIsNotReady'; import { PrivatePagesProvider } from './PrivatePagesProvider'; +import { DashboardBoot } from '../../components'; import 'style/pages/Dashboard/Dashboard.scss'; @@ -16,6 +17,8 @@ import 'style/pages/Dashboard/Dashboard.scss'; export default function DashboardPrivatePages() { return ( + + @@ -23,7 +26,7 @@ export default function DashboardPrivatePages() { - + @@ -31,4 +34,4 @@ export default function DashboardPrivatePages() { ); -} \ No newline at end of file +} diff --git a/client/src/components/Dashboard/PrivatePagesProvider.js b/client/src/components/Dashboard/PrivatePagesProvider.js index 34db3a813..956c6063a 100644 --- a/client/src/components/Dashboard/PrivatePagesProvider.js +++ b/client/src/components/Dashboard/PrivatePagesProvider.js @@ -1,17 +1,9 @@ import React from 'react'; -import DashboardLoadingIndicator from 'components/Dashboard/DashboardLoadingIndicator'; -import { useCurrentOrganization } from '../../hooks/query/organization'; +import { AuthenticatedUser } from './AuthenticatedUser'; /** * Private pages provider. */ export function PrivatePagesProvider({ children }) { - // Fetches the current user's organization. - const { isLoading } = useCurrentOrganization(); - - return ( - - {children} - - ) -} \ No newline at end of file + return {children}; +} diff --git a/client/src/components/Dashboard/SplashScreen.js b/client/src/components/Dashboard/SplashScreen.js new file mode 100644 index 000000000..fc32548e4 --- /dev/null +++ b/client/src/components/Dashboard/SplashScreen.js @@ -0,0 +1,15 @@ +import React from 'react'; +import * as R from 'ramda'; +import BigcapitalLoading from './BigcapitalLoading'; +import withDashboard from '../../containers/Dashboard/withDashboard'; + +function SplashScreenComponent({ appIsLoading, appIntlIsLoading }) { + return appIsLoading || appIntlIsLoading ? : null; +} + +export const SplashScreen = R.compose( + withDashboard(({ appIsLoading, appIntlIsLoading }) => ({ + appIsLoading, + appIntlIsLoading, + })), +)(SplashScreenComponent); diff --git a/client/src/components/Dashboard/TopbarUser.js b/client/src/components/Dashboard/TopbarUser.js index bfd1272e8..80c0f0adb 100644 --- a/client/src/components/Dashboard/TopbarUser.js +++ b/client/src/components/Dashboard/TopbarUser.js @@ -11,11 +11,12 @@ import { import { If, FormattedMessage as T } from 'components'; import { firstLettersArgs } from 'utils'; -import { useAuthActions, useAuthUser } from 'hooks/state'; +import { useAuthActions } from 'hooks/state'; import withDialogActions from 'containers/Dialog/withDialogActions'; import { compose } from 'utils'; import withSubscriptions from '../../containers/Subscriptions/withSubscriptions'; +import { useAuthenticatedUser } from './AuthenticatedUser'; function DashboardTopbarUser({ openDialog, @@ -25,7 +26,9 @@ function DashboardTopbarUser({ }) { const history = useHistory(); const { setLogout } = useAuthActions(); - const user = useAuthUser(); + + // Retrieve authenticated user information. + const { user } = useAuthenticatedUser(); const onClickLogout = () => { setLogout(); diff --git a/client/src/components/Dashboard/index.js b/client/src/components/Dashboard/index.js new file mode 100644 index 000000000..143e73dcb --- /dev/null +++ b/client/src/components/Dashboard/index.js @@ -0,0 +1,4 @@ + + +export * from './SplashScreen'; +export * from './DashboardBoot'; \ No newline at end of file diff --git a/client/src/components/EmptyStatus.js b/client/src/components/EmptyStatus.js index 5a7bb79ef..e95fac008 100644 --- a/client/src/components/EmptyStatus.js +++ b/client/src/components/EmptyStatus.js @@ -1,26 +1,17 @@ import React from 'react'; import classNames from 'classnames'; -import { CLASSES } from 'common/classes'; -import 'style/components/DataTable/DataTableEmptyStatus.scss'; +import Style from 'style/components/DataTable/DataTableEmptyStatus.module.scss'; /** * Datatable empty status. */ -export default function EmptyStatuts({ title, description, action, children }) { +export default function EmptyStatus({ title, description, action, children }) { return ( -
-

- {title} -

- -
- {description} -
- -
- {action} -
+
+

{title}

+
{description}
+
{action}
{children}
); diff --git a/client/src/components/Sidebar/SidebarHead.js b/client/src/components/Sidebar/SidebarHead.js index 3955c9c40..bc5c11c22 100644 --- a/client/src/components/Sidebar/SidebarHead.js +++ b/client/src/components/Sidebar/SidebarHead.js @@ -1,9 +1,9 @@ import React from 'react'; import { Button, Popover, Menu, Position } from '@blueprintjs/core'; import Icon from 'components/Icon'; -import { useAuthUser } from 'hooks/state'; import { compose, firstLettersArgs } from 'utils'; import withCurrentOrganization from '../../containers/Organization/withCurrentOrganization'; +import { useAuthenticatedUser } from '../Dashboard/AuthenticatedUser'; // Popover modifiers. const POPOVER_MODIFIERS = { @@ -17,7 +17,8 @@ function SidebarHead({ // #withCurrentOrganization organization, }) { - const user = useAuthUser(); + // Retrieve authenticated user information. + const { user } = useAuthenticatedUser(); return (
diff --git a/client/src/components/index.js b/client/src/components/index.js index 197e79cb9..68db361f5 100644 --- a/client/src/components/index.js +++ b/client/src/components/index.js @@ -74,6 +74,7 @@ export * from './Drawer/DrawerMainTabs'; export * from './TotalLines/index' export * from './Alert'; export * from './Subscriptions'; +export * from './Dashboard'; const Hint = FieldHint; diff --git a/client/src/containers/Accounting/JournalsLanding/ManualJournalsDataTable.js b/client/src/containers/Accounting/JournalsLanding/ManualJournalsDataTable.js index 645e3fcce..0b8e2c793 100644 --- a/client/src/containers/Accounting/JournalsLanding/ManualJournalsDataTable.js +++ b/client/src/containers/Accounting/JournalsLanding/ManualJournalsDataTable.js @@ -1,7 +1,7 @@ import React from 'react'; import { useHistory } from 'react-router-dom'; -import { DataTable } from 'components'; +import { DataTable, DashboardContentTable } from 'components'; import ManualJournalsEmptyStatus from './ManualJournalsEmptyStatus'; import TableSkeletonRows from 'components/Datatable/TableSkeletonRows'; @@ -91,33 +91,35 @@ function ManualJournalsDataTable({ } return ( - + + + ); } diff --git a/client/src/containers/Accounting/JournalsLanding/ManualJournalsList.js b/client/src/containers/Accounting/JournalsLanding/ManualJournalsList.js index 22c2cb542..c1e6020cc 100644 --- a/client/src/containers/Accounting/JournalsLanding/ManualJournalsList.js +++ b/client/src/containers/Accounting/JournalsLanding/ManualJournalsList.js @@ -30,20 +30,19 @@ function ManualJournalsTable({ - - - - - - + + + ); } export default compose( - withManualJournals(({ manualJournalsTableState, manualJournalTableStateChanged }) => ({ - journalsTableState: manualJournalsTableState, - journalsTableStateChanged: manualJournalTableStateChanged, - })), + withManualJournals( + ({ manualJournalsTableState, manualJournalTableStateChanged }) => ({ + journalsTableState: manualJournalsTableState, + journalsTableStateChanged: manualJournalTableStateChanged, + }), + ), )(ManualJournalsTable); diff --git a/client/src/containers/Authentication/AuthenticationBoot.js b/client/src/containers/Authentication/AuthenticationBoot.js new file mode 100644 index 000000000..4ac6c9899 --- /dev/null +++ b/client/src/containers/Authentication/AuthenticationBoot.js @@ -0,0 +1,15 @@ +import React from 'react'; +import * as R from 'ramda'; + +import withDashboardActions from '../../containers/Dashboard/withDashboardActions'; + +function AuthenticationBootJSX({ setAppIsLoading }) { + React.useEffect(() => { + setAppIsLoading(false); + }, [setAppIsLoading]); + + return null; +} +export const AuthenticationBoot = R.compose(withDashboardActions)( + AuthenticationBootJSX, +); diff --git a/client/src/containers/Authentication/Login.js b/client/src/containers/Authentication/Login.js index afa1006f8..5f2b38de6 100644 --- a/client/src/containers/Authentication/Login.js +++ b/client/src/containers/Authentication/Login.js @@ -20,18 +20,20 @@ export default function Login() { loginMutate({ crediential: values.crediential, password: values.password, - }) - .then(() => { - setSubmitting(false); - }) - .catch(({ response: { data: { errors } } }) => { + }).catch( + ({ + response: { + data: { errors }, + }, + }) => { const toastBuilders = transformLoginErrorsToToasts(errors); toastBuilders.forEach((builder) => { Toaster.show(builder); }); setSubmitting(false); - }); + }, + ); }; return ( @@ -66,4 +68,4 @@ export default function Login() {
); -} \ No newline at end of file +} diff --git a/client/src/containers/Authentication/Register.js b/client/src/containers/Authentication/Register.js index 864f8f9a6..9e56dc997 100644 --- a/client/src/containers/Authentication/Register.js +++ b/client/src/containers/Authentication/Register.js @@ -1,15 +1,16 @@ -import React, { useMemo } from 'react'; +import React, { useMemo } from 'react'; import { Formik } from 'formik'; -import { Link, useHistory } from 'react-router-dom'; -import { - Intent, -} from '@blueprintjs/core'; +import { Link } from 'react-router-dom'; +import { Intent } from '@blueprintjs/core'; import intl from 'react-intl-universal'; import { FormattedMessage as T } from 'components'; import AppToaster from 'components/AppToaster'; import AuthInsider from 'containers/Authentication/AuthInsider'; -import { useAuthLogin, useAuthRegister } from '../../hooks/query/authentication'; +import { + useAuthLogin, + useAuthRegister, +} from '../../hooks/query/authentication'; import RegisterForm from './RegisterForm'; import { RegisterSchema, transformRegisterErrorsToForm } from './utils'; @@ -18,11 +19,9 @@ import { RegisterSchema, transformRegisterErrorsToForm } from './utils'; * Register form. */ export default function RegisterUserForm() { - const history = useHistory(); + const { mutateAsync: authLoginMutate } = useAuthLogin(); + const { mutateAsync: authRegisterMutate } = useAuthRegister(); - const { mutateAsync: authLoginMutate } = useAuthLogin(); - const { mutateAsync: authRegisterMutate } = useAuthRegister(); - const initialValues = useMemo( () => ({ first_name: '', @@ -41,26 +40,33 @@ export default function RegisterUserForm() { authLoginMutate({ crediential: values.email, password: values.password, - }) - .then(() => { - history.push('/register/subscription'); - setSubmitting(false); - }) - .catch(({ response: { data: { errors } } }) => { + }).catch( + ({ + response: { + data: { errors }, + }, + }) => { AppToaster.show({ message: intl.get('something_wentwrong'), intent: Intent.SUCCESS, }); - }); + }, + ); }) - .catch(({ response: { data: { errors } } }) => { - const formErrors = transformRegisterErrorsToForm(errors); + .catch( + ({ + response: { + data: { errors }, + }, + }) => { + const formErrors = transformRegisterErrorsToForm(errors); - setErrors(formErrors); - setSubmitting(false); - }); + setErrors(formErrors); + setSubmitting(false); + }, + ); }; - + return (
@@ -83,4 +89,4 @@ export default function RegisterUserForm() {
); -} \ No newline at end of file +} diff --git a/client/src/containers/Authentication/withAuthentication.js b/client/src/containers/Authentication/withAuthentication.js index 94d846f28..3d6fe3b80 100644 --- a/client/src/containers/Authentication/withAuthentication.js +++ b/client/src/containers/Authentication/withAuthentication.js @@ -1,14 +1,15 @@ -import { isAuthenticated } from 'store/authentication/authentication.reducer' +import { isAuthenticated } from 'store/authentication/authentication.reducer'; import { connect } from 'react-redux'; export default (mapState) => { const mapStateToProps = (state, props) => { const mapped = { isAuthorized: isAuthenticated(state), - user: state.authentication.user, + authenticatedUserId: state.authentication.userId, currentOrganizationId: state.authentication?.organizationId, + currentTenantId: state.authentication?.tenantId, }; return mapState ? mapState(mapped, state, props) : mapped; }; return connect(mapStateToProps); -}; \ No newline at end of file +}; diff --git a/client/src/containers/Customers/CustomersLanding/CustomersList.js b/client/src/containers/Customers/CustomersLanding/CustomersList.js index cae241ab7..f4cb9d428 100644 --- a/client/src/containers/Customers/CustomersLanding/CustomersList.js +++ b/client/src/containers/Customers/CustomersLanding/CustomersList.js @@ -47,10 +47,7 @@ function CustomersList({ - - - - + diff --git a/client/src/containers/Customers/CustomersLanding/CustomersTable.js b/client/src/containers/Customers/CustomersLanding/CustomersTable.js index 741570929..39cfb1fed 100644 --- a/client/src/containers/Customers/CustomersLanding/CustomersTable.js +++ b/client/src/containers/Customers/CustomersLanding/CustomersTable.js @@ -5,7 +5,7 @@ import CustomersEmptyStatus from './CustomersEmptyStatus'; import TableSkeletonRows from 'components/Datatable/TableSkeletonRows'; import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton'; -import { DataTable } from 'components'; +import { DataTable, DashboardContentTable } from 'components'; import withCustomers from './withCustomers'; import withCustomersActions from './withCustomersActions'; @@ -100,37 +100,39 @@ function CustomersTable({ } return ( - + + + ); } diff --git a/client/src/containers/Dashboard/withDashboard.js b/client/src/containers/Dashboard/withDashboard.js index 5400d48f1..d4303c6dc 100644 --- a/client/src/containers/Dashboard/withDashboard.js +++ b/client/src/containers/Dashboard/withDashboard.js @@ -11,6 +11,8 @@ export default (mapState) => { sidebarExpended: state.dashboard.sidebarExpended, preferencesPageTitle: state.dashboard.preferencesPageTitle, dashboardBackLink: state.dashboard.backLink, + appIsLoading: state.dashboard.appIsLoading, + appIntlIsLoading: state.dashboard.appIntlIsLoading }; return mapState ? mapState(mapped, state, props) : mapped; }; diff --git a/client/src/containers/Dashboard/withDashboardActions.js b/client/src/containers/Dashboard/withDashboardActions.js index 472bbc486..8c188539e 100644 --- a/client/src/containers/Dashboard/withDashboardActions.js +++ b/client/src/containers/Dashboard/withDashboardActions.js @@ -2,6 +2,8 @@ import { connect } from 'react-redux'; import t from 'store/types'; import { toggleExpendSidebar, + appIsLoading, + appIntlIsLoading } from 'store/dashboard/dashboard.actions'; const mapActionsToProps = (dispatch) => ({ @@ -55,6 +57,8 @@ const mapActionsToProps = (dispatch) => ({ type: t.SET_DASHBOARD_BACK_LINK, payload: { backLink }, }), + setAppIsLoading: (isLoading) => dispatch(appIsLoading(isLoading)), + setAppIntlIsLoading: (isLoading) => dispatch(appIntlIsLoading(isLoading)), }); export default connect(null, mapActionsToProps); diff --git a/client/src/containers/Dialogs/PaymentViaVoucherDialog/PaymentViaVoucherDialogContent.js b/client/src/containers/Dialogs/PaymentViaVoucherDialog/PaymentViaVoucherDialogContent.js index 872b45452..22770dd06 100644 --- a/client/src/containers/Dialogs/PaymentViaVoucherDialog/PaymentViaVoucherDialogContent.js +++ b/client/src/containers/Dialogs/PaymentViaVoucherDialog/PaymentViaVoucherDialogContent.js @@ -35,8 +35,12 @@ function PaymentViaLicenseDialogContent({ const handleSubmit = (values, { setSubmitting, setErrors }) => { setSubmitting(true); + const mutateValues = { + plan_slug: `${values.plan_slug}-${values.period}ly`, + license_code: values.license_code, + }; // Payment via voucher mutate. - paymentViaVoucherMutate({ ...values }) + paymentViaVoucherMutate({ ...mutateValues }) .then(() => { Toaster.show({ message: intl.get('payment_via_voucher.success_message'), diff --git a/client/src/containers/Expenses/ExpensesLanding/ExpenseDataTable.js b/client/src/containers/Expenses/ExpensesLanding/ExpenseDataTable.js index 2c42c9957..ddd4b0300 100644 --- a/client/src/containers/Expenses/ExpensesLanding/ExpenseDataTable.js +++ b/client/src/containers/Expenses/ExpensesLanding/ExpenseDataTable.js @@ -4,6 +4,7 @@ import { useHistory } from 'react-router-dom'; import { compose } from 'utils'; import { useExpensesListContext } from './ExpensesListProvider'; +import { DashboardContentTable } from 'components'; import DataTable from 'components/DataTable'; import ExpensesEmptyStatus from './ExpensesEmptyStatus'; import TableSkeletonRows from 'components/Datatable/TableSkeletonRows'; @@ -84,32 +85,34 @@ function ExpensesDataTable({ } return ( - + + + ); } diff --git a/client/src/containers/Expenses/ExpensesLanding/ExpensesList.js b/client/src/containers/Expenses/ExpensesLanding/ExpensesList.js index 815b01684..5bbbc1948 100644 --- a/client/src/containers/Expenses/ExpensesLanding/ExpensesList.js +++ b/client/src/containers/Expenses/ExpensesLanding/ExpensesList.js @@ -47,10 +47,7 @@ function ExpensesList({ - - - - + diff --git a/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummary.js b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummary.js index 0f281a38d..dda5b2cc0 100644 --- a/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummary.js +++ b/client/src/containers/FinancialStatements/APAgingSummary/APAgingSummary.js @@ -12,8 +12,8 @@ import DashboardPageContent from 'components/Dashboard/DashboardPageContent'; import { APAgingSummaryProvider } from './APAgingSummaryProvider'; import { APAgingSummarySheetLoadingBar } from './components'; -import withSettings from 'containers/Settings/withSettings'; -import withAPAgingSummaryActions from './withAPAgingSummaryActions' +import withCurrentOrganization from '../../Organization/withCurrentOrganization'; +import withAPAgingSummaryActions from './withAPAgingSummaryActions'; import { compose } from 'utils'; /** @@ -51,9 +51,12 @@ function APAgingSummary({ }; // Hide the report filter drawer once the page unmount. - useEffect(() => () => { - toggleDisplayFilterDrawer(false); - }, [toggleDisplayFilterDrawer]) + useEffect( + () => () => { + toggleDisplayFilterDrawer(false); + }, + [toggleDisplayFilterDrawer], + ); return ( @@ -79,8 +82,8 @@ function APAgingSummary({ } export default compose( - withSettings(({ organizationSettings }) => ({ - organizationName: organizationSettings?.name, + withCurrentOrganization(({ organization }) => ({ + organizationName: organization?.name, })), - withAPAgingSummaryActions + withAPAgingSummaryActions, )(APAgingSummary); diff --git a/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummary.js b/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummary.js index 0a112eb49..fdb0def8f 100644 --- a/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummary.js +++ b/client/src/containers/FinancialStatements/ARAgingSummary/ARAgingSummary.js @@ -13,7 +13,7 @@ import { ARAgingSummaryProvider } from './ARAgingSummaryProvider'; import { ARAgingSummarySheetLoadingBar } from './components'; import withARAgingSummaryActions from './withARAgingSummaryActions' -import withSettings from 'containers/Settings/withSettings'; +import withCurrentOrganization from '../../../containers/Organization/withCurrentOrganization'; import { compose } from 'utils'; @@ -77,8 +77,8 @@ function ReceivableAgingSummarySheet({ } export default compose( - withSettings(({ organizationSettings }) => ({ - organizationName: organizationSettings.name, + withCurrentOrganization(({ organization }) => ({ + organizationName: organization.name, })), withARAgingSummaryActions )(ReceivableAgingSummarySheet); diff --git a/client/src/containers/FinancialStatements/BalanceSheet/BalanceSheet.js b/client/src/containers/FinancialStatements/BalanceSheet/BalanceSheet.js index e21a53dee..a95c39752 100644 --- a/client/src/containers/FinancialStatements/BalanceSheet/BalanceSheet.js +++ b/client/src/containers/FinancialStatements/BalanceSheet/BalanceSheet.js @@ -10,7 +10,7 @@ import { BalanceSheetAlerts, BalanceSheetLoadingBar } from './components'; import { FinancialStatement } from 'components'; import withBalanceSheetActions from './withBalanceSheetActions'; -import withSettings from 'containers/Settings/withSettings'; +import withCurrentOrganization from '../../../containers/Organization/withCurrentOrganization'; import { BalanceSheetProvider } from './BalanceSheetProvider'; import { compose } from 'utils'; @@ -23,7 +23,7 @@ function BalanceSheet({ organizationName, // #withBalanceSheetActions - toggleBalanceSheetFilterDrawer + toggleBalanceSheetFilterDrawer, }) { const [filter, setFilter] = useState({ fromDate: moment().startOf('year').format('YYYY-MM-DD'), @@ -52,9 +52,12 @@ function BalanceSheet({ }; // Hides the balance sheet filter drawer once the page unmount. - useEffect(() => () => { - toggleBalanceSheetFilterDrawer(false); - }, [toggleBalanceSheetFilterDrawer]) + useEffect( + () => () => { + toggleBalanceSheetFilterDrawer(false); + }, + [toggleBalanceSheetFilterDrawer], + ); return ( @@ -81,8 +84,8 @@ function BalanceSheet({ } export default compose( - withSettings(({ organizationSettings }) => ({ - organizationName: organizationSettings.name, + withCurrentOrganization(({ organization }) => ({ + organizationName: organization.name, })), withBalanceSheetActions, )(BalanceSheet); diff --git a/client/src/containers/FinancialStatements/CashFlowStatement/CashFlowStatement.js b/client/src/containers/FinancialStatements/CashFlowStatement/CashFlowStatement.js index 514e4838d..2b1071276 100644 --- a/client/src/containers/FinancialStatements/CashFlowStatement/CashFlowStatement.js +++ b/client/src/containers/FinancialStatements/CashFlowStatement/CashFlowStatement.js @@ -9,7 +9,7 @@ import CashFlowStatementHeader from './CashFlowStatementHeader'; import CashFlowStatementTable from './CashFlowStatementTable'; import CashFlowStatementActionsBar from './CashFlowStatementActionsBar'; -import withSettings from 'containers/Settings/withSettings'; +import withCurrentOrganization from '../../Organization/withCurrentOrganization'; import withCashFlowStatementActions from './withCashFlowStatementActions'; import { CashFlowStatementProvider } from './CashFlowStatementProvider'; import { @@ -85,8 +85,8 @@ function CashFlowStatement({ } export default compose( - withSettings(({ organizationSettings }) => ({ - organizationName: organizationSettings?.name, + withCurrentOrganization(({ organization }) => ({ + organizationName: organization.name, })), withCashFlowStatementActions, )(CashFlowStatement); diff --git a/client/src/containers/FinancialStatements/CustomersBalanceSummary/CustomersBalanceSummary.js b/client/src/containers/FinancialStatements/CustomersBalanceSummary/CustomersBalanceSummary.js index 939a519e6..a5681ce9e 100644 --- a/client/src/containers/FinancialStatements/CustomersBalanceSummary/CustomersBalanceSummary.js +++ b/client/src/containers/FinancialStatements/CustomersBalanceSummary/CustomersBalanceSummary.js @@ -13,8 +13,7 @@ import CustomersBalanceSummaryTable from './CustomersBalanceSummaryTable'; import { CustomersBalanceLoadingBar } from './components'; import { CustomersBalanceSummaryProvider } from './CustomersBalanceSummaryProvider'; import withCustomersBalanceSummaryActions from './withCustomersBalanceSummaryActions'; - -import withSettings from 'containers/Settings/withSettings'; +import withCurrentOrganization from '../../Organization/withCurrentOrganization'; import { compose } from 'redux'; @@ -81,8 +80,8 @@ function CustomersBalanceSummary({ ); } export default compose( - withSettings(({ organizationSettings }) => ({ - organizationName: organizationSettings.name, + withCurrentOrganization(({ organization }) => ({ + organizationName: organization.name, })), withCustomersBalanceSummaryActions, )(CustomersBalanceSummary); diff --git a/client/src/containers/FinancialStatements/CustomersTransactions/CustomersTransactions.js b/client/src/containers/FinancialStatements/CustomersTransactions/CustomersTransactions.js index c919da40f..bfe6de920 100644 --- a/client/src/containers/FinancialStatements/CustomersTransactions/CustomersTransactions.js +++ b/client/src/containers/FinancialStatements/CustomersTransactions/CustomersTransactions.js @@ -10,7 +10,7 @@ import CustomersTransactionsTable from './CustomersTransactionsTable'; import CustomersTransactionsActionsBar from './CustomersTransactionsActionsBar'; import withCustomersTransactionsActions from './withCustomersTransactionsActions'; -import withSettings from 'containers/Settings/withSettings'; +import withCurrentOrganization from '../../../containers/Organization/withCurrentOrganization'; import { CustomersTransactionsLoadingBar } from './components'; import { CustomersTransactionsProvider } from './CustomersTransactionsProvider'; @@ -81,8 +81,8 @@ function CustomersTransactions({ ); } export default compose( - withSettings(({ organizationSettings }) => ({ - organizationName: organizationSettings.name, + withCurrentOrganization(({ organization }) => ({ + organizationName: organization.name, })), withCustomersTransactionsActions, )(CustomersTransactions); diff --git a/client/src/containers/FinancialStatements/GeneralLedger/GeneralLedger.js b/client/src/containers/FinancialStatements/GeneralLedger/GeneralLedger.js index 8e52874d8..3cdee521d 100644 --- a/client/src/containers/FinancialStatements/GeneralLedger/GeneralLedger.js +++ b/client/src/containers/FinancialStatements/GeneralLedger/GeneralLedger.js @@ -15,7 +15,7 @@ import { } from './components'; import withGeneralLedgerActions from './withGeneralLedgerActions'; -import withSettings from 'containers/Settings/withSettings'; +import withCurrentOrganization from '../../Organization/withCurrentOrganization'; import { transformFilterFormToQuery } from 'containers/FinancialStatements/common'; import { compose } from 'utils'; @@ -85,7 +85,7 @@ function GeneralLedger({ export default compose( withGeneralLedgerActions, - withSettings(({ organizationSettings }) => ({ - organizationName: organizationSettings.name, + withCurrentOrganization(({ organization }) => ({ + organizationName: organization.name, })), )(GeneralLedger); diff --git a/client/src/containers/FinancialStatements/InventoryItemDetails/InventoryItemDetails.js b/client/src/containers/FinancialStatements/InventoryItemDetails/InventoryItemDetails.js index 802cc4188..0c25f6de0 100644 --- a/client/src/containers/FinancialStatements/InventoryItemDetails/InventoryItemDetails.js +++ b/client/src/containers/FinancialStatements/InventoryItemDetails/InventoryItemDetails.js @@ -10,7 +10,7 @@ import InventoryItemDetailsHeader from './InventoryItemDetailsHeader'; import InventoryItemDetailsTable from './InventoryItemDetailsTable'; import withInventoryItemDetailsActions from './withInventoryItemDetailsActions'; -import withSettings from 'containers/Settings/withSettings'; +import withCurrentOrganization from '../../../containers/Organization/withCurrentOrganization'; import { InventoryItemDetailsProvider } from './InventoryItemDetailsProvider'; import { InventoryItemDetailsLoadingBar, @@ -84,8 +84,8 @@ function InventoryItemDetails({ } export default compose( - withSettings(({ organizationSettings }) => ({ - organizationName: organizationSettings?.name, + withCurrentOrganization(({ organization }) => ({ + organizationName: organization.name, })), withInventoryItemDetailsActions, )(InventoryItemDetails); diff --git a/client/src/containers/FinancialStatements/InventoryValuation/InventoryValuation.js b/client/src/containers/FinancialStatements/InventoryValuation/InventoryValuation.js index cea4716b1..6317b9fff 100644 --- a/client/src/containers/FinancialStatements/InventoryValuation/InventoryValuation.js +++ b/client/src/containers/FinancialStatements/InventoryValuation/InventoryValuation.js @@ -11,7 +11,7 @@ import InventoryValuationTable from './InventoryValuationTable'; import DashboardPageContent from 'components/Dashboard/DashboardPageContent'; import { InventoryValuationLoadingBar } from './components'; import withInventoryValuationActions from './withInventoryValuationActions'; -import withSettings from 'containers/Settings/withSettings'; +import withCurrentOrganization from '../../../containers/Organization/withCurrentOrganization'; import { compose } from 'utils'; @@ -80,7 +80,7 @@ function InventoryValuation({ export default compose( withInventoryValuationActions, - withSettings(({ organizationSettings }) => ({ - organizationName: organizationSettings?.name, + withCurrentOrganization(({ organization }) => ({ + organizationName: organization.name, })), )(InventoryValuation); diff --git a/client/src/containers/FinancialStatements/Journal/Journal.js b/client/src/containers/FinancialStatements/Journal/Journal.js index 5eeca928d..f951e79c6 100644 --- a/client/src/containers/FinancialStatements/Journal/Journal.js +++ b/client/src/containers/FinancialStatements/Journal/Journal.js @@ -11,7 +11,7 @@ import JournalActionsBar from './JournalActionsBar'; import { JournalSheetProvider } from './JournalProvider'; import { JournalSheetLoadingBar, JournalSheetAlerts } from './components'; -import withSettings from 'containers/Settings/withSettings'; +import withCurrentOrganization from '../../Organization/withCurrentOrganization'; import withDashboardActions from 'containers/Dashboard/withDashboardActions'; import withJournalActions from './withJournalActions'; @@ -79,7 +79,7 @@ function Journal({ export default compose( withDashboardActions, withJournalActions, - withSettings(({ organizationSettings }) => ({ - organizationName: organizationSettings.name, + withCurrentOrganization(({ organization }) => ({ + organizationName: organization.name, })), )(Journal); diff --git a/client/src/containers/FinancialStatements/ProfitLossSheet/ProfitLossSheet.js b/client/src/containers/FinancialStatements/ProfitLossSheet/ProfitLossSheet.js index e65eba735..1c966ab60 100644 --- a/client/src/containers/FinancialStatements/ProfitLossSheet/ProfitLossSheet.js +++ b/client/src/containers/FinancialStatements/ProfitLossSheet/ProfitLossSheet.js @@ -10,7 +10,7 @@ import DashboardPageContent from 'components/Dashboard/DashboardPageContent'; import withDashboardActions from 'containers/Dashboard/withDashboardActions'; import withProfitLossActions from './withProfitLossActions'; -import withSettings from 'containers/Settings/withSettings'; +import withCurrentOrganization from '../../Organization/withCurrentOrganization'; import 'style/pages/FinancialStatements/ProfitLossSheet.scss'; import { ProfitLossSheetProvider } from './ProfitLossProvider'; @@ -91,7 +91,7 @@ function ProfitLossSheet({ export default compose( withDashboardActions, withProfitLossActions, - withSettings(({ organizationSettings }) => ({ - organizationName: organizationSettings.name, + withCurrentOrganization(({ organization }) => ({ + organizationName: organization.name, })), )(ProfitLossSheet); diff --git a/client/src/containers/FinancialStatements/PurchasesByItems/PurchasesByItems.js b/client/src/containers/FinancialStatements/PurchasesByItems/PurchasesByItems.js index f0afb8566..368cb0f62 100644 --- a/client/src/containers/FinancialStatements/PurchasesByItems/PurchasesByItems.js +++ b/client/src/containers/FinancialStatements/PurchasesByItems/PurchasesByItems.js @@ -11,7 +11,7 @@ import DashboardPageContent from 'components/Dashboard/DashboardPageContent'; import { PurchasesByItemsLoadingBar } from './components'; import withPurchasesByItemsActions from './withPurchasesByItemsActions'; -import withSettings from 'containers/Settings/withSettings'; +import withCurrentOrganization from '../../../containers/Organization/withCurrentOrganization'; import { compose } from 'utils'; /** @@ -82,7 +82,7 @@ function PurchasesByItems({ export default compose( withPurchasesByItemsActions, - withSettings(({ organizationSettings }) => ({ - organizationName: organizationSettings.name, + withCurrentOrganization(({ organization }) => ({ + organizationName: organization.name, })), )(PurchasesByItems); diff --git a/client/src/containers/FinancialStatements/SalesByItems/SalesByItems.js b/client/src/containers/FinancialStatements/SalesByItems/SalesByItems.js index 73c159396..459f61af7 100644 --- a/client/src/containers/FinancialStatements/SalesByItems/SalesByItems.js +++ b/client/src/containers/FinancialStatements/SalesByItems/SalesByItems.js @@ -13,7 +13,7 @@ import DashboardPageContent from 'components/Dashboard/DashboardPageContent'; import { SalesByItemsLoadingBar } from './components'; import withSalesByItemsActions from './withSalesByItemsActions'; -import withSettings from 'containers/Settings/withSettings'; +import withCurrentOrganization from '../../../containers/Organization/withCurrentOrganization'; import { compose } from 'utils'; @@ -84,7 +84,7 @@ function SalesByItems({ export default compose( withSalesByItemsActions, - withSettings(({ organizationSettings }) => ({ - organizationName: organizationSettings.name, + withCurrentOrganization(({ organization }) => ({ + organizationName: organization.name, })), )(SalesByItems); diff --git a/client/src/containers/FinancialStatements/TrialBalanceSheet/TrialBalanceSheet.js b/client/src/containers/FinancialStatements/TrialBalanceSheet/TrialBalanceSheet.js index 6bb6fce25..0025b933a 100644 --- a/client/src/containers/FinancialStatements/TrialBalanceSheet/TrialBalanceSheet.js +++ b/client/src/containers/FinancialStatements/TrialBalanceSheet/TrialBalanceSheet.js @@ -15,7 +15,7 @@ import { } from './components'; import withTrialBalanceActions from './withTrialBalanceActions'; -import withSettings from 'containers/Settings/withSettings'; +import withCurrentOrganization from '../../Organization/withCurrentOrganization'; import { compose } from 'utils'; @@ -91,7 +91,7 @@ function TrialBalanceSheet({ export default compose( withTrialBalanceActions, - withSettings(({ organizationSettings }) => ({ - organizationName: organizationSettings.name, + withCurrentOrganization(({ organization }) => ({ + organizationName: organization.name, })), )(TrialBalanceSheet); diff --git a/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummary.js b/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummary.js index bc350c942..72a443e65 100644 --- a/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummary.js +++ b/client/src/containers/FinancialStatements/VendorsBalanceSummary/VendorsBalanceSummary.js @@ -14,7 +14,7 @@ import { VendorsBalanceSummaryProvider } from './VendorsBalanceSummaryProvider'; import { VendorsSummarySheetLoadingBar } from './components'; import withVendorsBalanceSummaryActions from './withVendorsBalanceSummaryActions'; -import withSettings from 'containers/Settings/withSettings'; +import withCurrentOrganization from '../../../containers/Organization/withCurrentOrganization'; import { compose } from 'utils'; @@ -82,8 +82,8 @@ function VendorsBalanceSummary({ } export default compose( - withSettings(({ organizationSettings }) => ({ - organizationName: organizationSettings?.name, + withCurrentOrganization(({ organization }) => ({ + organizationName: organization.name, })), withVendorsBalanceSummaryActions, )(VendorsBalanceSummary); diff --git a/client/src/containers/FinancialStatements/VendorsTransactions/VendorsTransactions.js b/client/src/containers/FinancialStatements/VendorsTransactions/VendorsTransactions.js index 74ed07d60..e6cb8ee64 100644 --- a/client/src/containers/FinancialStatements/VendorsTransactions/VendorsTransactions.js +++ b/client/src/containers/FinancialStatements/VendorsTransactions/VendorsTransactions.js @@ -10,7 +10,7 @@ import VendorsTransactionsActionsBar from './VendorsTransactionsActionsBar'; import VendorsTransactionsTable from './VendorsTransactionsTable'; import withVendorsTransactionsActions from './withVendorsTransactionsActions'; -import withSettings from 'containers/Settings/withSettings'; +import withCurrentOrganization from '../../../containers/Organization/withCurrentOrganization'; import { VendorsTransactionsProvider } from './VendorsTransactionsProvider'; import { VendorsTransactionsLoadingBar } from './components'; @@ -81,8 +81,8 @@ function VendorsTransactions({ ); } export default compose( - withSettings(({ organizationSettings }) => ({ - organizationName: organizationSettings.name, + withCurrentOrganization(({ organization }) => ({ + organizationName: organization.name, })), withVendorsTransactionsActions, )(VendorsTransactions); diff --git a/client/src/containers/Items/ItemsDataTable.js b/client/src/containers/Items/ItemsDataTable.js index 62847e127..3c4205dcf 100644 --- a/client/src/containers/Items/ItemsDataTable.js +++ b/client/src/containers/Items/ItemsDataTable.js @@ -2,7 +2,7 @@ import React from 'react'; import { useHistory } from 'react-router-dom'; import { FormattedMessage as T } from 'components'; -import { DataTable } from 'components'; +import { DashboardContentTable, DataTable } from 'components'; import ItemsEmptyStatus from './ItemsEmptyStatus'; import TableSkeletonRows from 'components/Datatable/TableSkeletonRows'; @@ -108,41 +108,43 @@ function ItemsDataTable({ } return ( - } - {...tableProps} - /> + + } + {...tableProps} + /> + ); } diff --git a/client/src/containers/Items/ItemsEmptyStatus.js b/client/src/containers/Items/ItemsEmptyStatus.js index e4340a696..3a0c53fe3 100644 --- a/client/src/containers/Items/ItemsEmptyStatus.js +++ b/client/src/containers/Items/ItemsEmptyStatus.js @@ -28,7 +28,7 @@ export default function ItemsEmptyStatus() { } diff --git a/client/src/containers/Items/ItemsList.js b/client/src/containers/Items/ItemsList.js index 167e4ad04..6af0dddfb 100644 --- a/client/src/containers/Items/ItemsList.js +++ b/client/src/containers/Items/ItemsList.js @@ -3,7 +3,7 @@ import { compose } from 'utils'; import 'style/pages/Items/List.scss'; -import { DashboardContentTable, DashboardPageContent } from 'components'; +import { DashboardPageContent } from 'components'; import ItemsActionsBar from './ItemsActionsBar'; import ItemsAlerts from './ItemsAlerts'; @@ -43,10 +43,7 @@ function ItemsList({ - - - - + diff --git a/client/src/containers/Preferences/General/GeneralFormPage.js b/client/src/containers/Preferences/General/GeneralFormPage.js index c6b0e271d..da5125bef 100644 --- a/client/src/containers/Preferences/General/GeneralFormPage.js +++ b/client/src/containers/Preferences/General/GeneralFormPage.js @@ -51,6 +51,11 @@ function GeneralFormPage({ intent: Intent.SUCCESS, }); setSubmitting(false); + + // Reboot the application if the application's language is mutated. + if (organization.language !== values.language) { + window.location.reload(); + } }; // Handle request error. const onError = (errors) => { diff --git a/client/src/containers/Purchases/Bills/BillsLanding/BillsList.js b/client/src/containers/Purchases/Bills/BillsLanding/BillsList.js index 42f8834c0..90d0e4964 100644 --- a/client/src/containers/Purchases/Bills/BillsLanding/BillsList.js +++ b/client/src/containers/Purchases/Bills/BillsLanding/BillsList.js @@ -47,10 +47,7 @@ function BillsList({ - - - - + diff --git a/client/src/containers/Purchases/Bills/BillsLanding/BillsTable.js b/client/src/containers/Purchases/Bills/BillsLanding/BillsTable.js index 0bb5de39b..449c93131 100644 --- a/client/src/containers/Purchases/Bills/BillsLanding/BillsTable.js +++ b/client/src/containers/Purchases/Bills/BillsLanding/BillsTable.js @@ -4,6 +4,7 @@ import { useHistory } from 'react-router-dom'; import { compose } from 'utils'; import DataTable from 'components/DataTable'; +import { DashboardContentTable } from 'components'; import TableSkeletonRows from 'components/Datatable/TableSkeletonRows'; import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton'; @@ -92,6 +93,7 @@ function BillsDataTable({ } return ( + + ); } diff --git a/client/src/containers/Purchases/PaymentMades/PaymentsLanding/PaymentMadeList.js b/client/src/containers/Purchases/PaymentMades/PaymentsLanding/PaymentMadeList.js index 566e93095..48ac5e0fd 100644 --- a/client/src/containers/Purchases/PaymentMades/PaymentsLanding/PaymentMadeList.js +++ b/client/src/containers/Purchases/PaymentMades/PaymentsLanding/PaymentMadeList.js @@ -42,10 +42,7 @@ function PaymentMadeList({ - - - - + diff --git a/client/src/containers/Purchases/PaymentMades/PaymentsLanding/PaymentMadesTable.js b/client/src/containers/Purchases/PaymentMades/PaymentsLanding/PaymentMadesTable.js index 7c720c02b..6844c77e5 100644 --- a/client/src/containers/Purchases/PaymentMades/PaymentsLanding/PaymentMadesTable.js +++ b/client/src/containers/Purchases/PaymentMades/PaymentsLanding/PaymentMadesTable.js @@ -3,7 +3,8 @@ import { useHistory } from 'react-router-dom'; import { compose } from 'utils'; -import { DataTable } from 'components'; +import { DataTable, DashboardContentTable } from 'components'; + import PaymentMadesEmptyStatus from './PaymentMadesEmptyStatus'; import TableSkeletonRows from 'components/Datatable/TableSkeletonRows'; import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton'; @@ -76,31 +77,33 @@ function PaymentMadesTable({ } return ( - + + + ); } diff --git a/client/src/containers/Sales/Estimates/EstimatesLanding/EstimatesDataTable.js b/client/src/containers/Sales/Estimates/EstimatesLanding/EstimatesDataTable.js index 891aabff0..fd1572478 100644 --- a/client/src/containers/Sales/Estimates/EstimatesLanding/EstimatesDataTable.js +++ b/client/src/containers/Sales/Estimates/EstimatesLanding/EstimatesDataTable.js @@ -3,7 +3,7 @@ import { useHistory } from 'react-router-dom'; import { compose } from 'utils'; -import { DataTable } from 'components'; +import { DataTable, DashboardContentTable } from 'components'; import EstimatesEmptyStatus from './EstimatesEmptyStatus'; import TableSkeletonRows from 'components/Datatable/TableSkeletonRows'; import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton'; @@ -109,35 +109,37 @@ function EstimatesDataTable({ } return ( - + + + ); } diff --git a/client/src/containers/Sales/Estimates/EstimatesLanding/EstimatesList.js b/client/src/containers/Sales/Estimates/EstimatesLanding/EstimatesList.js index c94f80753..7b32bd85f 100644 --- a/client/src/containers/Sales/Estimates/EstimatesLanding/EstimatesList.js +++ b/client/src/containers/Sales/Estimates/EstimatesLanding/EstimatesList.js @@ -42,10 +42,7 @@ function EstimatesList({ - - - - + diff --git a/client/src/containers/Sales/Invoices/InvoicesLanding/InvoicesDataTable.js b/client/src/containers/Sales/Invoices/InvoicesLanding/InvoicesDataTable.js index d804e490d..8fbcd47c8 100644 --- a/client/src/containers/Sales/Invoices/InvoicesLanding/InvoicesDataTable.js +++ b/client/src/containers/Sales/Invoices/InvoicesLanding/InvoicesDataTable.js @@ -4,7 +4,7 @@ import { useHistory } from 'react-router-dom'; import InvoicesEmptyStatus from './InvoicesEmptyStatus'; import { compose } from 'utils'; -import { DataTable } from 'components'; +import { DataTable, DashboardContentTable } from 'components'; import TableSkeletonRows from 'components/Datatable/TableSkeletonRows'; import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton'; @@ -108,37 +108,39 @@ function InvoicesDataTable({ } return ( - + + + ); } diff --git a/client/src/containers/Sales/Invoices/InvoicesLanding/InvoicesList.js b/client/src/containers/Sales/Invoices/InvoicesLanding/InvoicesList.js index d440253b9..1af3314a3 100644 --- a/client/src/containers/Sales/Invoices/InvoicesLanding/InvoicesList.js +++ b/client/src/containers/Sales/Invoices/InvoicesLanding/InvoicesList.js @@ -44,10 +44,7 @@ function InvoicesList({ - - - - + diff --git a/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceivesList.js b/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceivesList.js index 0dbb0c556..352a8ef9c 100644 --- a/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceivesList.js +++ b/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceivesList.js @@ -42,10 +42,7 @@ function PaymentReceiveList({ - - - - + diff --git a/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceivesTable.js b/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceivesTable.js index 89b1b2047..af2227528 100644 --- a/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceivesTable.js +++ b/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceivesTable.js @@ -3,8 +3,8 @@ import { useHistory } from 'react-router-dom'; import { compose } from 'utils'; +import { DataTable, DashboardContentTable } from 'components'; import PaymentReceivesEmptyStatus from './PaymentReceivesEmptyStatus'; -import { DataTable } from 'components'; import TableSkeletonRows from 'components/Datatable/TableSkeletonRows'; import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton'; @@ -85,32 +85,34 @@ function PaymentReceivesDataTable({ } return ( - + + + ); } diff --git a/client/src/containers/Sales/Receipts/ReceiptsLanding/ReceiptsList.js b/client/src/containers/Sales/Receipts/ReceiptsLanding/ReceiptsList.js index 749450e07..65fcf60c6 100644 --- a/client/src/containers/Sales/Receipts/ReceiptsLanding/ReceiptsList.js +++ b/client/src/containers/Sales/Receipts/ReceiptsLanding/ReceiptsList.js @@ -43,10 +43,7 @@ function ReceiptsList({ - - - - + diff --git a/client/src/containers/Sales/Receipts/ReceiptsLanding/ReceiptsTable.js b/client/src/containers/Sales/Receipts/ReceiptsLanding/ReceiptsTable.js index e535e2f3e..4f7cd2f59 100644 --- a/client/src/containers/Sales/Receipts/ReceiptsLanding/ReceiptsTable.js +++ b/client/src/containers/Sales/Receipts/ReceiptsLanding/ReceiptsTable.js @@ -2,7 +2,7 @@ import React, { useCallback } from 'react'; import { useHistory } from 'react-router-dom'; import { compose } from 'utils'; -import { DataTable } from 'components'; +import { DataTable, DashboardContentTable } from 'components'; import ReceiptsEmptyStatus from './ReceiptsEmptyStatus'; import TableSkeletonRows from 'components/Datatable/TableSkeletonRows'; @@ -101,36 +101,38 @@ function ReceiptsDataTable({ } return ( - + + + ); } diff --git a/client/src/containers/Setup/SetupCongratsPage.js b/client/src/containers/Setup/SetupCongratsPage.js index 335d8dbbd..56c2f8a35 100644 --- a/client/src/containers/Setup/SetupCongratsPage.js +++ b/client/src/containers/Setup/SetupCongratsPage.js @@ -1,6 +1,5 @@ -import React, { useCallback } from 'react'; +import React from 'react'; import { Button, Intent } from '@blueprintjs/core'; -import { useHistory } from 'react-router-dom'; import WorkflowIcon from './WorkflowIcon'; import { FormattedMessage as T } from 'components'; @@ -10,17 +9,16 @@ import { compose } from 'utils'; import 'style/pages/Setup/Congrats.scss'; - /** * Setup congrats page. */ function SetupCongratsPage({ setOrganizationSetupCompleted }) { - const history = useHistory(); + const [isReloading, setIsReloading] = React.useState(false); - const handleBtnClick = useCallback(() => { - setOrganizationSetupCompleted(false); - history.push('/homepage'); - }, [setOrganizationSetupCompleted, history]); + const handleBtnClick = () => { + setIsReloading(true); + window.location.reload(); + }; return (
@@ -37,7 +35,12 @@ function SetupCongratsPage({ setOrganizationSetupCompleted }) {

-
diff --git a/client/src/containers/Setup/SetupInitializingForm.js b/client/src/containers/Setup/SetupInitializingForm.js index 3545caa15..0bb1fbcbe 100644 --- a/client/src/containers/Setup/SetupInitializingForm.js +++ b/client/src/containers/Setup/SetupInitializingForm.js @@ -20,10 +20,12 @@ function SetupInitializingForm({ }) { const { refetch, isSuccess } = useCurrentOrganization({ enabled: false }); + // Job done state. const [isJobDone, setIsJobDone] = React.useState(false); const { data: { running, queued, failed, completed }, + isFetching: isJobFetching, } = useJob(organization?.build_job_id, { refetchInterval: 2000, enabled: !!organization?.build_job_id, @@ -45,17 +47,15 @@ function SetupInitializingForm({ return (
- - -
- {failed ? ( - - ) : running || queued ? ( - - ) : completed ? ( - - ) : null} -
+ {failed ? ( + + ) : running || queued || isJobFetching ? ( + + ) : completed ? ( + + ) : ( + + )}
); } @@ -68,41 +68,60 @@ export default R.compose( withOrganization(({ organization }) => ({ organization })), )(SetupInitializingForm); +/** + * State initializing failed state. + */ function SetupInitializingFailed() { return ( -
-

- -

-

- -

+
+
+

+ +

+

+ +

+
); } +/** + * Setup initializing running state. + */ function SetupInitializingRunning() { return ( -
-

- -

-

- -

+
+ + +
+

+ +

+

+ +

+
); } +/** + * Setup initializing completed state. + */ function SetupInitializingCompleted() { return ( -
-

- -

-

- -

+
+
+

+ +

+

+ +

+
); } diff --git a/client/src/containers/Setup/SetupOrganizationForm.js b/client/src/containers/Setup/SetupOrganizationForm.js index b7b26e37c..f320dbead 100644 --- a/client/src/containers/Setup/SetupOrganizationForm.js +++ b/client/src/containers/Setup/SetupOrganizationForm.js @@ -10,6 +10,7 @@ import { } from '@blueprintjs/core'; import classNames from 'classnames'; import { TimezonePicker } from '@blueprintjs/timezone'; +import useAutofocus from 'hooks/useAutofocus' import { FormattedMessage as T } from 'components'; import { getCountries } from 'common/countries'; @@ -29,6 +30,8 @@ export default function SetupOrganizationForm({ isSubmitting, values }) { const currencies = getAllCurrenciesOptions(); const countries = getCountries(); + const accountRef = useAutofocus(); + return (

@@ -44,7 +47,11 @@ export default function SetupOrganizationForm({ isSubmitting, values }) { intent={inputIntent({ error, touched })} helperText={} > - + )} diff --git a/client/src/containers/Setup/SetupOrganizationPage.js b/client/src/containers/Setup/SetupOrganizationPage.js index 2d16c0836..1bc7c6593 100644 --- a/client/src/containers/Setup/SetupOrganizationPage.js +++ b/client/src/containers/Setup/SetupOrganizationPage.js @@ -9,7 +9,7 @@ import SetupOrganizationForm from './SetupOrganizationForm'; import { useOrganizationSetup } from 'hooks/query'; import withSettingsActions from 'containers/Settings/withSettingsActions'; -import { compose, transfromToSnakeCase } from 'utils'; +import { setCookie, compose, transfromToSnakeCase } from 'utils'; import { getSetupOrganizationValidation } from './SetupOrganization.schema'; // Initial values. @@ -41,6 +41,9 @@ function SetupOrganizationPage({ wizard }) { organizationSetupMutate({ ...transfromToSnakeCase(values) }) .then((response) => { setSubmitting(false); + + // Sets locale cookie to next boot cycle. + setCookie('locale', values.language); wizard.next(); }) .catch((erros) => { diff --git a/client/src/containers/Setup/SetupSubscription.js b/client/src/containers/Setup/SetupSubscription.js index 00974e056..1fdd60015 100644 --- a/client/src/containers/Setup/SetupSubscription.js +++ b/client/src/containers/Setup/SetupSubscription.js @@ -21,12 +21,12 @@ function SetupSubscription({ // Initial values. const initialValues = { - plan_slug: 'starter', + plan_slug: 'essentials', period: 'month', license_code: '', }; // Handle form submit. - const handleSubmit = () => {}; + const handleSubmit = (values) => {}; // Retrieve momerized subscription form schema. const SubscriptionFormSchema = React.useMemo( diff --git a/client/src/containers/Setup/SetupSubscription/SubscriptionPeriodsSection.js b/client/src/containers/Setup/SetupSubscription/SubscriptionPeriodsSection.js index b07731eb7..3ac065eed 100644 --- a/client/src/containers/Setup/SetupSubscription/SubscriptionPeriodsSection.js +++ b/client/src/containers/Setup/SetupSubscription/SubscriptionPeriodsSection.js @@ -9,6 +9,10 @@ import withPlan from '../../Subscriptions/withPlan'; const SubscriptionPeriodsEnhanced = R.compose( withPlan(({ plan }) => ({ plan })), )(({ plan, ...restProps }) => { + // Can't continue if the current plan of the form not selected. + if (!plan) { + return null; + } return ; }); diff --git a/client/src/containers/Subscriptions/BillingForm.js b/client/src/containers/Subscriptions/BillingForm.js index f20a8898a..7861f7e7c 100644 --- a/client/src/containers/Subscriptions/BillingForm.js +++ b/client/src/containers/Subscriptions/BillingForm.js @@ -42,7 +42,7 @@ function BillingForm({ // Initial values. const initialValues = { - plan_slug: 'free', + plan_slug: 'essentials', period: 'month', license_code: '', }; diff --git a/client/src/containers/Subscriptions/BillingPeriodsInput.js b/client/src/containers/Subscriptions/BillingPeriodsInput.js index 29ace1f48..55b39d792 100644 --- a/client/src/containers/Subscriptions/BillingPeriodsInput.js +++ b/client/src/containers/Subscriptions/BillingPeriodsInput.js @@ -12,6 +12,8 @@ import withPlan from './withPlan'; const SubscriptionPeriodsEnhanced = R.compose( withPlan(({ plan }) => ({ plan })), )(({ plan, ...restProps }) => { + if (!plan) return null; + return ; }); diff --git a/client/src/containers/Vendors/VendorsLanding/VendorsList.js b/client/src/containers/Vendors/VendorsLanding/VendorsList.js index 61352ba73..4799ffc6e 100644 --- a/client/src/containers/Vendors/VendorsLanding/VendorsList.js +++ b/client/src/containers/Vendors/VendorsLanding/VendorsList.js @@ -47,13 +47,9 @@ function VendorsList({ - - - - - - + + ); } diff --git a/client/src/containers/Vendors/VendorsLanding/VendorsTable.js b/client/src/containers/Vendors/VendorsLanding/VendorsTable.js index ed9b6e8dd..2b0723974 100644 --- a/client/src/containers/Vendors/VendorsLanding/VendorsTable.js +++ b/client/src/containers/Vendors/VendorsLanding/VendorsTable.js @@ -1,7 +1,7 @@ import React from 'react'; import { useHistory } from 'react-router'; -import { DataTable } from 'components'; +import { DataTable, DashboardContentTable } from 'components'; import TableSkeletonRows from 'components/Datatable/TableSkeletonRows'; import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton'; @@ -85,7 +85,7 @@ function VendorsTable({ const handleViewDetailVendor = ({ id }) => { openDrawer('contact-detail-drawer', { contactId: id }); }; - + // Handle fetch data once the page index, size or sort by of the table change. const handleFetchData = React.useCallback( ({ pageSize, pageIndex, sortBy }) => { @@ -104,35 +104,37 @@ function VendorsTable({ } return ( - + + + ); } diff --git a/client/src/hooks/query/authentication.js b/client/src/hooks/query/authentication.js index 37926b64d..e038ae477 100644 --- a/client/src/hooks/query/authentication.js +++ b/client/src/hooks/query/authentication.js @@ -1,28 +1,37 @@ import { useMutation } from 'react-query'; import useApiRequest from '../useRequest'; -import { useAuthActions } from '../state'; -import { persistor } from 'store/createStore'; +import { setCookie } from '../../utils'; + +/** + * Saves the response data to cookies. + */ +function setAuthLoginCookies(data) { + setCookie('token', data.token); + setCookie('authenticated_user_id', data.user.id); + setCookie('organization_id', data.tenant.organization_id); + setCookie('tenant_id', data.tenant.id); + + if (data?.tenant?.metadata?.language) + setCookie('locale', data.tenant.metadata.language); +} /** * Authentication login. */ export const useAuthLogin = (props) => { - const { setLogin } = useAuthActions(); const apiRequest = useApiRequest(); - return useMutation( - (values) => apiRequest.post('auth/login', values), - { - select: (res) => res.data, - onSuccess: (data) => { - setLogin(data.data); + return useMutation((values) => apiRequest.post('auth/login', values), { + select: (res) => res.data, + onSuccess: (data) => { + // Set authentication cookies. + setAuthLoginCookies(data.data); - // Run the store persist. - persistor.persist(); - }, - ...props - } - ); + // Reboot the application. + window.location.reload(); + }, + ...props, + }); }; /** @@ -34,7 +43,7 @@ export const useAuthRegister = (props) => { return useMutation( (values) => apiRequest.post('auth/register', values), props, - ) + ); }; /** @@ -45,9 +54,9 @@ export const useAuthSendResetPassword = (props) => { return useMutation( (email) => apiRequest.post('auth/send_reset_password', email), - props + props, ); -} +}; /** * Authentication reset password. @@ -58,5 +67,5 @@ export const useAuthResetPassword = (props) => { return useMutation( ([token, values]) => apiRequest.post(`auth/reset/${token}`, values), props, - ) -} + ); +}; diff --git a/client/src/hooks/query/base.js b/client/src/hooks/query/base.js new file mode 100644 index 000000000..091447f76 --- /dev/null +++ b/client/src/hooks/query/base.js @@ -0,0 +1,9 @@ +// Query client config. +export const queryConfig = { + defaultOptions: { + queries: { + refetchOnWindowFocus: true, + staleTime: 30000, + }, + }, +}; diff --git a/client/src/hooks/query/users.js b/client/src/hooks/query/users.js index 4b98ce945..7dabd191a 100644 --- a/client/src/hooks/query/users.js +++ b/client/src/hooks/query/users.js @@ -112,11 +112,16 @@ export function useUsers(props) { * Retrieve details of the given user. */ export function useUser(id, props) { - const apiRequest = useApiRequest(); - - return useQueryTenant( + return useRequestQuery( [t.USER, id], - () => apiRequest.get(`users/${id}`).then((response) => response.data.user), - props, + { + method: 'get', + url: `users/${id}`, + }, + { + select: (response) => response.data.user, + defaultData: {}, + ...props, + }, ); } diff --git a/client/src/hooks/state/authentication.js b/client/src/hooks/state/authentication.js index 995195b87..6116988b7 100644 --- a/client/src/hooks/state/authentication.js +++ b/client/src/hooks/state/authentication.js @@ -6,6 +6,15 @@ import { setStoreReset, } from 'store/authentication/authentication.actions'; import { useQueryClient } from 'react-query'; +import { removeCookie } from '../../utils'; + +function removeAuthenticationCookies() { + removeCookie('token'); + removeCookie('organization_id'); + removeCookie('tenant_id'); + removeCookie('authenticated_user_id'); + removeCookie('locale'); +} export const useAuthActions = () => { const dispatch = useDispatch(); @@ -15,11 +24,15 @@ export const useAuthActions = () => { setLogin: useCallback((login) => dispatch(setLogin(login)), [dispatch]), setLogout: useCallback(() => { // Resets store state. - dispatch(setStoreReset()); + // dispatch(setStoreReset()); // Remove all cached queries. queryClient.removeQueries(); - }, [dispatch, queryClient]), + + removeAuthenticationCookies(); + + window.location.reload(); + }, [queryClient]), }; }; @@ -41,12 +54,12 @@ export const useAuthToken = () => { * Retrieve the authentication user. */ export const useAuthUser = () => { - return useSelector((state) => state.authentication.user); + return useSelector((state) => ({})); }; /** * Retrieve the authenticated organization id. */ export const useAuthOrganizationId = () => { - return useSelector((state) => state.authentication.organization); + return useSelector((state) => state.authentication.organizationId); }; diff --git a/client/src/lang/ar-ly/index.json b/client/src/lang/ar/index.json similarity index 100% rename from client/src/lang/ar-ly/index.json rename to client/src/lang/ar/index.json diff --git a/client/src/lang/ar-ly/locale.js b/client/src/lang/ar/locale.js similarity index 100% rename from client/src/lang/ar-ly/locale.js rename to client/src/lang/ar/locale.js diff --git a/client/src/store/authentication/authentication.actions.js b/client/src/store/authentication/authentication.actions.js index 4e12a6f2b..7d269e41f 100644 --- a/client/src/store/authentication/authentication.actions.js +++ b/client/src/store/authentication/authentication.actions.js @@ -1,9 +1,5 @@ import t from 'store/types'; -export const setLogin = ({ user, token, tenant }) => ({ - type: t.LOGIN_SUCCESS, - payload: { user, token, tenant, }, -}); - +export const setLogin = () => ({ type: t.LOGIN_SUCCESS }); export const setLogout = () => ({ type: t.LOGOUT }); export const setStoreReset = () => ({ type: t.RESET }); \ No newline at end of file diff --git a/client/src/store/authentication/authentication.reducer.js b/client/src/store/authentication/authentication.reducer.js index 0e5b9fde3..ea9ed59e5 100644 --- a/client/src/store/authentication/authentication.reducer.js +++ b/client/src/store/authentication/authentication.reducer.js @@ -2,36 +2,28 @@ import { createReducer } from '@reduxjs/toolkit'; import { persistReducer } from 'redux-persist'; import purgeStoredState from 'redux-persist/es/purgeStoredState'; import storage from 'redux-persist/lib/storage'; +import { getCookie, setCookie } from 'utils'; import t from 'store/types'; +import { removeCookie } from '../../utils'; +// Read stored data in cookies and merge it with the initial state. const initialState = { - token: '', - organization: '', - organizationId: null, - user: '', - tenant: {}, - locale: '', + token: getCookie('token'), + organizationId: getCookie('organization_id'), + tenantId: getCookie('tenant_id'), + userId: getCookie('authenticated_user_id'), + locale: getCookie('locale'), errors: [], }; const STORAGE_KEY = 'bigcapital:authentication'; const CONFIG = { key: STORAGE_KEY, - blacklist: ['errors'], + whitelist: [], storage, }; const reducerInstance = createReducer(initialState, { - [t.LOGIN_SUCCESS]: (state, action) => { - const { token, user, tenant } = action.payload; - - state.token = token; - state.user = user; - state.organization = tenant.organization_id; - state.organizationId = tenant.id; - state.tenant = tenant; - }, - [t.LOGIN_FAILURE]: (state, action) => { state.errors = action.errors; }, @@ -40,15 +32,12 @@ const reducerInstance = createReducer(initialState, { state.errors = []; }, - [t.RESET]: () => { + [t.RESET]: (state) => { purgeStoredState(CONFIG); - } + }, }); -export default persistReducer( - CONFIG, - reducerInstance, -); +export default persistReducer(CONFIG, reducerInstance); export const isAuthenticated = (state) => !!state.authentication.token; export const hasErrorType = (state, errorType) => { @@ -59,4 +48,4 @@ export const isTenantSeeded = (state) => !!state.tenant.seeded_at; export const isTenantBuilt = (state) => !!state.tenant.initialized_at; export const isTenantHasSubscription = () => false; -export const isTenantSubscriptionExpired = () => false; \ No newline at end of file +export const isTenantSubscriptionExpired = () => false; diff --git a/client/src/store/authentication/authentication.selectors.js b/client/src/store/authentication/authentication.selectors.js index b7d06f030..38a0b9989 100644 --- a/client/src/store/authentication/authentication.selectors.js +++ b/client/src/store/authentication/authentication.selectors.js @@ -1,8 +1,8 @@ import { defaultTo } from 'lodash'; import { createSelector } from '@reduxjs/toolkit'; -const getCurrentOrganizationId = (state) => state.authentication.organization; -const getCurrentTenantId = (state) => state.authentication.organizationId; +const getCurrentOrganizationId = (state) => state.authentication.organizationId; +const getCurrentTenantId = (state) => state.authentication.tenantId; const getOrganizationsMap = (state) => state.organizations.data; // Retrieve organization tenant id. diff --git a/client/src/store/createStore.js b/client/src/store/createStore.js index a91594850..6f34e58a5 100644 --- a/client/src/store/createStore.js +++ b/client/src/store/createStore.js @@ -10,13 +10,14 @@ import loggerMiddleware from 'middleware/logger'; import rootReducer from 'store/reducers'; import ResetMiddleware from './ResetMiddleware'; + const createStoreFactory = (initialState = {}) => { /** |-------------------------------------------------- | Middleware Configuration |-------------------------------------------------- */ - const middleware = [thunkMiddleware, loggerMiddleware ]; + const middleware = [thunkMiddleware, loggerMiddleware]; /** |-------------------------------------------------- @@ -41,8 +42,6 @@ const createStoreFactory = (initialState = {}) => { rootReducer, initialState, composeEnhancers(applyMiddleware(...middleware), ...enhancers), - - ); store.asyncReducers = {}; return store; diff --git a/client/src/store/dashboard/dashboard.actions.js b/client/src/store/dashboard/dashboard.actions.js index 9810a23ce..f7df3e671 100644 --- a/client/src/store/dashboard/dashboard.actions.js +++ b/client/src/store/dashboard/dashboard.actions.js @@ -69,4 +69,19 @@ export function toggleExpendSidebar(toggle) { type: t.SIDEBAR_EXPEND_TOGGLE, payload: { toggle } }; +} + + +export function appIsLoading(toggle) { + return { + type: t.APP_IS_LOADING, + payload: { isLoading: toggle }, + }; +} + +export function appIntlIsLoading(toggle) { + return { + type: t.APP_INTL_IS_LOADING, + payload: { isLoading: toggle }, + }; } \ No newline at end of file diff --git a/client/src/store/dashboard/dashboard.reducer.js b/client/src/store/dashboard/dashboard.reducer.js index 3045336cb..2107975fa 100644 --- a/client/src/store/dashboard/dashboard.reducer.js +++ b/client/src/store/dashboard/dashboard.reducer.js @@ -16,6 +16,8 @@ const initialState = { topbarEditViewId: null, requestsLoading: 0, backLink: false, + appIsLoading: true, + appIntlIsLoading: true, }; const STORAGE_KEY = 'bigcapital:dashboard'; @@ -100,6 +102,16 @@ const reducerInstance = createReducer(initialState, { state.backLink = backLink; }, + [t.APP_IS_LOADING]: (state, action) => { + const { isLoading } = action.payload; + state.appIsLoading = isLoading; + }, + + [t.APP_INTL_IS_LOADING]: (state, action) => { + const { isLoading } = action.payload; + state.appIntlIsLoading = isLoading; + }, + [t.RESET]: () => { purgeStoredState(CONFIG); }, diff --git a/client/src/store/dashboard/dashboard.types.js b/client/src/store/dashboard/dashboard.types.js index e1ddc7425..cc8885a30 100644 --- a/client/src/store/dashboard/dashboard.types.js +++ b/client/src/store/dashboard/dashboard.types.js @@ -14,4 +14,6 @@ export default { SET_TOPBAR_EDIT_VIEW: 'SET_TOPBAR_EDIT_VIEW', SIDEBAR_EXPEND_TOGGLE: 'SIDEBAR_EXPEND_TOGGLE', SET_DASHBOARD_BACK_LINK: 'SET_DASHBOARD_BACK_LINK', + APP_IS_LOADING: 'APP_IS_LOADING', + APP_INTL_IS_LOADING: 'APP_INTL_IS_LOADING' }; diff --git a/client/src/store/organizations/organizations.actions.js b/client/src/store/organizations/organizations.actions.js index 602a36b22..808e24933 100644 --- a/client/src/store/organizations/organizations.actions.js +++ b/client/src/store/organizations/organizations.actions.js @@ -29,12 +29,12 @@ export const fetchOrganizations = () => (dispatch) => export const setOrganizationSetupCompleted = (congrats) => (dispatch, getState) => { - const organizationId = getState().authentication.organizationId; + const tenantId = getState().authentication.tenantId; dispatch({ type: t.SET_ORGANIZATION_CONGRATS, payload: { - organizationId, + tenantId, congrats, }, }); diff --git a/client/src/store/organizations/organizations.reducers.js b/client/src/store/organizations/organizations.reducers.js index f192208a9..d466f4df3 100644 --- a/client/src/store/organizations/organizations.reducers.js +++ b/client/src/store/organizations/organizations.reducers.js @@ -27,10 +27,10 @@ const reducer = createReducer(initialState, { }, [t.SET_ORGANIZATION_CONGRATS]: (state, action) => { - const { organizationId, congrats } = action.payload; + const { tenantId, congrats } = action.payload; - state.data[organizationId] = { - ...(state.data[organizationId] || {}), + state.data[tenantId] = { + ...(state.data[tenantId] || {}), is_congrats: !!congrats, }; } diff --git a/client/src/store/organizations/organizations.selectors.js b/client/src/store/organizations/organizations.selectors.js index 63716bdf9..4abc98475 100644 --- a/client/src/store/organizations/organizations.selectors.js +++ b/client/src/store/organizations/organizations.selectors.js @@ -1,50 +1,39 @@ import { createSelector } from '@reduxjs/toolkit'; -const organizationSelector = (state, props) => state.organizations.data[props.organizationId]; +const organizationSelector = (state, props) => { + const tenantId = state.organizations.byOrganizationId[props.organizationId]; + return state.organizations.data[tenantId]; +}; -export const getOrganizationByIdFactory = () => createSelector( - organizationSelector, - (organization) => organization -); +export const getOrganizationByIdFactory = () => + createSelector(organizationSelector, (organization) => organization); -export const isOrganizationSeededFactory = () => createSelector( - organizationSelector, - (organization) => { +export const isOrganizationSeededFactory = () => + createSelector(organizationSelector, (organization) => { return !!organization?.seeded_at; - }, -); + }); -export const isOrganizationBuiltFactory = () => createSelector( - organizationSelector, - (organization) => { +export const isOrganizationBuiltFactory = () => + createSelector(organizationSelector, (organization) => { return !!organization?.initialized_at; - }, -); + }); -export const isOrganizationReadyFactory = () => createSelector( - organizationSelector, - (organization) => { +export const isOrganizationReadyFactory = () => + createSelector(organizationSelector, (organization) => { return organization?.is_ready; - }, -); + }); -export const isOrganizationSubscribedFactory = () => createSelector( - organizationSelector, - (organization) => { +export const isOrganizationSubscribedFactory = () => + createSelector(organizationSelector, (organization) => { return organization?.subscriptions?.length > 0; - } -); + }); -export const isOrganizationCongratsFactory = () => createSelector( - organizationSelector, - (organization) => { +export const isOrganizationCongratsFactory = () => + createSelector(organizationSelector, (organization) => { return !!organization?.is_congrats; - } -); + }); -export const isOrganizationBuildRunningFactory = () => createSelector( - organizationSelector, - (organization) => { +export const isOrganizationBuildRunningFactory = () => + createSelector(organizationSelector, (organization) => { return !!organization?.is_build_running; - } -) \ No newline at end of file + }); diff --git a/client/src/store/organizations/organizations.types.js b/client/src/store/organizations/organizations.types.js index c7efea8a7..7f7c90eb6 100644 --- a/client/src/store/organizations/organizations.types.js +++ b/client/src/store/organizations/organizations.types.js @@ -1,5 +1,6 @@ export default { + ORGANIZATION_SET: 'ORGANIZATION_SET', ORGANIZATIONS_LIST_SET: 'ORGANIZATIONS_LIST_SET', SET_ORGANIZATION_CONGRATS: 'SET_ORGANIZATION_CONGRATS' }; \ No newline at end of file diff --git a/client/src/store/plans/plans.reducer.js b/client/src/store/plans/plans.reducer.js index e127cfbcf..154baf721 100644 --- a/client/src/store/plans/plans.reducer.js +++ b/client/src/store/plans/plans.reducer.js @@ -17,7 +17,7 @@ const getSubscriptionPeriods = () => [ const getSubscriptionPlans = () => [ { name: intl.get('plan.essential.title'), - slug: 'free', + slug: 'essentials', description: [ intl.get('plan.feature.sale_purchase_invoice'), intl.get('plan.feature.receivable_payable_accounts'), diff --git a/client/src/style/components/BigcapitalLoading.scss b/client/src/style/components/BigcapitalLoading.scss index b44be05a3..51eca004c 100644 --- a/client/src/style/components/BigcapitalLoading.scss +++ b/client/src/style/components/BigcapitalLoading.scss @@ -3,6 +3,8 @@ width: 100%; position: absolute; display: flex; + background: #fff; + z-index: 999999; .center { width: auto; diff --git a/client/src/style/components/DataTable/DataTableEmptyStatus.scss b/client/src/style/components/DataTable/DataTableEmptyStatus.module.scss similarity index 70% rename from client/src/style/components/DataTable/DataTableEmptyStatus.scss rename to client/src/style/components/DataTable/DataTableEmptyStatus.module.scss index c2c3ddbeb..d2dc44c16 100644 --- a/client/src/style/components/DataTable/DataTableEmptyStatus.scss +++ b/client/src/style/components/DataTable/DataTableEmptyStatus.module.scss @@ -1,11 +1,11 @@ -.datatable-empty-status { - max-width: 550px; +.root { + max-width: 500px; width: 100%; - margin: 0 auto; + margin: auto; + padding-bottom: 40px; text-align: center; - margin-top: 200px; - &__title { + &_title { font-size: 20px; color: #2c3a5d; font-weight: 600; @@ -19,20 +19,16 @@ font-size: 22px; } } - &__desc { + &_desc { font-size: 16px; color: #1f3255; opacity: 0.8; line-height: 1.6; - - html[lang='ar'] & { - font-size: 18px; - } } - &__actions { + &_actions { margin-top: 26px; - .bp3-button { + :global .bp3-button { min-height: 36px; & + .bp3-button { diff --git a/client/src/style/pages/Dashboard/Dashboard.scss b/client/src/style/pages/Dashboard/Dashboard.scss index 628356ac0..0039c1e8e 100644 --- a/client/src/style/pages/Dashboard/Dashboard.scss +++ b/client/src/style/pages/Dashboard/Dashboard.scss @@ -371,6 +371,18 @@ $dashboard-views-bar-height: 44px; } } + &__centered-empty-status{ + display: flex; + flex: 1 0 0; + flex-direction: column; + margin: auto; + + .datatable-empty-status{ + margin: auto; + padding-bottom: 40px; + } + } + &__datatable { display: flex; flex: 1 0 0; diff --git a/client/src/utils.js b/client/src/utils.js index 088adbae9..3d1d0cc1b 100644 --- a/client/src/utils.js +++ b/client/src/utils.js @@ -11,6 +11,19 @@ import deepMapKeys from 'deep-map-keys'; import { createSelectorCreator, defaultMemoize } from 'reselect'; import { isEqual } from 'lodash'; +import jsCookie from 'js-cookie'; + +export const getCookie = (name, defaultValue) => _.defaultTo(jsCookie.get(name), defaultValue); + +export const setCookie = (name, value, expiry = 365, secure = false) => { + jsCookie.set(name, value, { expires: expiry, path: '/', secure }); +}; + +export const removeCookie = (name) => { + return jsCookie.remove(name, { path: '/' }); +} + + export function removeEmptyFromObject(obj) { obj = Object.assign({}, obj); var keys = Object.keys(obj); @@ -227,6 +240,7 @@ export const firstLettersArgs = (...args) => { return letters.join('').toUpperCase(); }; + export const uniqueMultiProps = (items, props) => { return _.uniqBy(items, (item) => { return JSON.stringify(_.pick(item, props)); diff --git a/server/src/system/models/Subscriptions/PlanSubscription.js b/server/src/system/models/Subscriptions/PlanSubscription.js index 7d50889f4..7b8e53f07 100644 --- a/server/src/system/models/Subscriptions/PlanSubscription.js +++ b/server/src/system/models/Subscriptions/PlanSubscription.js @@ -59,7 +59,7 @@ export default class PlanSubscription extends mixin(SystemModel) { const endDate = moment().format(dateFormat); builder.where('trial_ends_at', '<=', endDate); - } + }, }; } @@ -79,7 +79,7 @@ export default class PlanSubscription extends mixin(SystemModel) { modelClass: Tenant.default, join: { from: 'subscription_plan_subscriptions.tenantId', - to: 'tenants.id' + to: 'tenants.id', }, }, @@ -131,23 +131,18 @@ export default class PlanSubscription extends mixin(SystemModel) { /** * Set new period from the given details. - * @param {string} invoiceInterval - * @param {number} invoicePeriod - * @param {string} start - * + * @param {string} invoiceInterval + * @param {number} invoicePeriod + * @param {string} start + * * @return {Object} */ - setNewPeriod(invoiceInterval, invoicePeriod, start) { - let _invoiceInterval = invoiceInterval; - let _invoicePeriod = invoicePeriod; - - if (!invoiceInterval) { - _invoiceInterval = this.plan.invoiceInterval; - } - if (!invoicePeriod) { - _invoicePeriod = this.plan.invoicePeriod; - } - const period = new SubscriptionPeriod(_invoiceInterval, _invoicePeriod, start); + static setNewPeriod(invoiceInterval, invoicePeriod, start) { + const period = new SubscriptionPeriod( + invoiceInterval, + invoicePeriod, + start, + ); const startsAt = period.getStartDate(); const endsAt = period.getEndDate(); @@ -159,12 +154,11 @@ export default class PlanSubscription extends mixin(SystemModel) { * Renews subscription period. * @Promise */ - renew(plan) { - const { invoicePeriod, invoiceInterval } = plan; - const patch = { ...this.setNewPeriod(invoiceInterval, invoicePeriod) }; - patch.cancelsAt = null; - patch.planId = plan.id; - - return this.$query().patch(patch); + renew(invoiceInterval, invoicePeriod) { + const { startsAt, endsAt } = PlanSubscription.setNewPeriod( + invoiceInterval, + invoicePeriod, + ); + return this.$query().update({ startsAt, endsAt }); } }