@@ -110,5 +116,5 @@ function SendResetPassword({
}
export default compose(
- AuthenticationConnect,
+ withAuthenticationActions,
)(SendResetPassword);
diff --git a/client/src/connectors/Authentication.connect.js b/client/src/containers/Authentication/withAuthenticationActions.js
similarity index 86%
rename from client/src/connectors/Authentication.connect.js
rename to client/src/containers/Authentication/withAuthenticationActions.js
index 5cbb943b4..a9e148a04 100644
--- a/client/src/connectors/Authentication.connect.js
+++ b/client/src/containers/Authentication/withAuthenticationActions.js
@@ -8,9 +8,6 @@ import {
} from 'store/authentication/authentication.actions';
import { connect } from 'react-redux';
-const mapStateToProps = (state) => ({
-
-});
const mapDispatchToProps = (dispatch) => ({
requestLogin: (form) => dispatch(login({ form })),
@@ -21,4 +18,4 @@ const mapDispatchToProps = (dispatch) => ({
requestInviteMetaByToken: (token) => dispatch(inviteMetaByToken({ token })),
});
-export default connect(mapStateToProps, mapDispatchToProps);
\ No newline at end of file
+export default connect(null, mapDispatchToProps);
\ No newline at end of file
diff --git a/client/src/containers/Dashboard/Items/ItemCategoryList.js b/client/src/containers/Dashboard/Items/ItemCategoryList.js
deleted file mode 100644
index 0f57a6910..000000000
--- a/client/src/containers/Dashboard/Items/ItemCategoryList.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import React, { useEffect } from 'react';
-import { useParams } from 'react-router-dom';
-import DashboardConnect from 'connectors/Dashboard.connector';
-import DashboardInsider from 'components/Dashboard/DashboardInsider';
-import CategoryList from 'components/Items/categoryList';
-import ItemFormDialog from 'connectors/ItemFormDialog.connect';
-import { compose } from 'utils';
-
-const ItemCategoryList = ({ changePageTitle }) => {
- const { id } = useParams();
-
- useEffect(() => {
- id
- ? changePageTitle('Edit Category Details')
- : changePageTitle('Category List');
- }, []);
- return (
-
-
-
- );
-};
-
-export default compose(DashboardConnect, ItemFormDialog)(ItemCategoryList);
diff --git a/client/src/containers/Dashboard/Items/ItemForm.js b/client/src/containers/Dashboard/Items/ItemForm.js
deleted file mode 100644
index 8ab7d8bc7..000000000
--- a/client/src/containers/Dashboard/Items/ItemForm.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import React, { useEffect } from 'react';
-import { useParams } from 'react-router-dom';
-import { useAsync } from 'react-use';
-import DashboardConnect from 'connectors/Dashboard.connector';
-import ItemForm from 'components/Items/ItemForm';
-import DashboardInsider from 'components/Dashboard/DashboardInsider';
-import ItemsConnect from 'connectors/Items.connect';
-import AccountsConnect from 'connectors/Accounts.connector';
-import ItemCategoryConnect from 'connectors/ItemsCategory.connect';
-import { compose } from 'utils';
-
-const ItemFormContainer = ({
- changePageTitle,
- requestFetchAccounts,
- requestFetchItemCategories,
-}) => {
- const { id } = useParams();
-
- useEffect(() => {
- id ?
- changePageTitle('Edit Item Details') :
- changePageTitle('New Item');
- }, [id, changePageTitle]);
-
- const fetchHook = useAsync(async () => {
- await Promise.all([
- requestFetchAccounts(),
- requestFetchItemCategories(),
- ]);
- });
- return (
-
-
-
- );
-};
-
-export default compose(
- DashboardConnect,
- ItemsConnect,
- AccountsConnect,
- ItemCategoryConnect,
-)(ItemFormContainer);
diff --git a/client/src/containers/Dashboard/Items/ItemsCategoryList.js b/client/src/containers/Dashboard/Items/ItemsCategoryList.js
deleted file mode 100644
index b0b30883e..000000000
--- a/client/src/containers/Dashboard/Items/ItemsCategoryList.js
+++ /dev/null
@@ -1,102 +0,0 @@
-import React, { useEffect, useState, useCallback } from 'react';
-import DashboardInsider from 'components/Dashboard/DashboardInsider';
-import useAsync from 'hooks/async';
-import { useParams } from 'react-router-dom';
-import DashboardConnect from 'connectors/Dashboard.connector';
-import ItemsCategoryConnect from 'connectors/ItemsCategory.connect';
-import { compose } from 'utils';
-import ItemsCategoryList from 'components/Items/ItemsCategoryList';
-import ItemsCategoryActionsBar from './ItemsCategoryActionsBar';
-import { Alert, Intent } from '@blueprintjs/core';
-import AppToaster from 'components/AppToaster';
-import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
-
-const ItemCategoriesList = ({
- changePageTitle,
- views,
- requestFetchItemCategories,
- requestEditItemCategory,
- requestDeleteItemCategory,
-}) => {
- const { id } = useParams();
- const [deleteCategory, setDeleteCategory] = useState(false);
- const [selectedRows, setSelectedRows] = useState([]);
-
- useEffect(() => {
- id
- ? changePageTitle('Edit Item Details')
- : changePageTitle('Categories List');
- }, [id, changePageTitle]);
-
- const fetchHook = useAsync(async () => {
- await Promise.all([
- requestFetchItemCategories(),
- ]);
- }, false);
-
- const handelDeleteCategory = useCallback((category) => {
- setDeleteCategory(category);
- }, [setDeleteCategory]);
-
- const handelEditCategory = category => {};
-
- const handelCancelCategoryDelete = useCallback(() => {
- setDeleteCategory(false);
- }, [setDeleteCategory]);
-
- const handelConfirmCategoryDelete = useCallback(() => {
- requestDeleteItemCategory(deleteCategory.id).then(() => {
- setDeleteCategory(false);
- AppToaster.show({
- message: 'the_category_has_been_delete'
- });
- });
- }, [deleteCategory, requestDeleteItemCategory, setDeleteCategory]);
-
- const handleFetchData = useCallback(() => {
- fetchHook.execute();
- }, []);
-
- // Handle selected rows change.
- const handleSelectedRowsChange = useCallback((accounts) => {
- setSelectedRows(accounts);
- }, [setSelectedRows]);
-
- return (
-
-
-
-
-
-
-
- Are you sure you want to move filename to Trash? You will be
- able to restore it later, but it will become private to you.
-
-
-
-
- );
-};
-
-export default compose(
- DashboardConnect,
- ItemsCategoryConnect
-)(ItemCategoriesList);
diff --git a/client/src/containers/Dashboard/Views/ViewFormPage.js b/client/src/containers/Dashboard/Views/ViewFormPage.js
deleted file mode 100644
index fdfb1993e..000000000
--- a/client/src/containers/Dashboard/Views/ViewFormPage.js
+++ /dev/null
@@ -1,101 +0,0 @@
-import React, {useEffect, useState, useCallback} from 'react';
-import { useAsync } from 'react-use';
-import { useParams, useHistory } from 'react-router-dom';
-import { Intent, Alert } from '@blueprintjs/core';
-import DashboardInsider from 'components/Dashboard/DashboardInsider';
-import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
-import ViewForm from 'components/Views/ViewForm';
-import DashboardConnect from 'connectors/Dashboard.connector';
-import ResourceConnect from 'connectors/Resource.connector';
-import ViewConnect from 'connectors/View.connector';
-import {compose} from 'utils';
-import AppToaster from 'components/AppToaster';
-
-function ViewFormPage({
- changePageTitle,
- fetchResourceFields,
- fetchResourceColumns,
- fetchView,
- getResourceColumns,
- getResourceFields,
- submitView,
- getViewMeta,
- deleteView,
-}) {
- const { resource_slug: resourceSlug, view_id: viewId } = useParams();
-
- const columns = getResourceColumns('accounts');
- const fields = getResourceFields('accounts');
-
- const viewForm = (viewId) ? getViewMeta(viewId) : null;
-
- const [stateDeleteView, setStateDeleteView] = useState(null);
-
- useEffect(() => {
- if (viewId) {
- changePageTitle('Edit Custom View');
- } else {
- changePageTitle('New Custom View');
- }
- }, [viewId, changePageTitle]);
-
- const fetchHook = useAsync(async () => {
- await Promise.all([
- fetchResourceColumns('accounts'),
- fetchResourceFields('accounts'),
- ...(viewId) ? [
- fetchView(viewId),
- ] : [],
- ]);
- }, []);
-
- const handleDeleteView = useCallback((view) => {
- setStateDeleteView(view);
- }, []);
-
- const handleCancelDeleteView = useCallback(() => {
- setStateDeleteView(null);
- }, []);
-
- const handleConfirmDeleteView = useCallback(() => {
- deleteView(stateDeleteView.id).then((response) => {
- setStateDeleteView(null);
- AppToaster.show({
- message: 'the_custom_view_has_been_deleted',
- });
- })
- }, [deleteView, stateDeleteView]);
-
- return (
-
-
-
-
-
-
- Are you sure you want to move filename to Trash? You will be able to restore it later,
- but it will become private to you.
-
-
-
-
- );
-}
-
-export default compose(
- DashboardConnect,
- ResourceConnect,
- ViewConnect,
-)(ViewFormPage);
\ No newline at end of file
diff --git a/client/src/containers/Dashboard/withDashboard.js b/client/src/containers/Dashboard/withDashboard.js
new file mode 100644
index 000000000..6c3ce2bf9
--- /dev/null
+++ b/client/src/containers/Dashboard/withDashboard.js
@@ -0,0 +1,27 @@
+
+import { connect } from 'react-redux';
+import t from 'store/types';
+
+const mapActionsToProps = (dispatch) => ({
+ changePageTitle: (pageTitle) => dispatch({
+ type: t.CHANGE_DASHBOARD_PAGE_TITLE, pageTitle
+ }),
+
+ changePageSubtitle: (pageSubtitle) => dispatch({
+ type: t.ALTER_DASHBOARD_PAGE_SUBTITLE, pageSubtitle,
+ }),
+
+ setTopbarEditView: (id) => dispatch({
+ type: t.SET_TOPBAR_EDIT_VIEW, id,
+ }),
+
+ setDashboardRequestLoading: () => dispatch({
+ type: t.SET_DASHBOARD_REQUEST_LOADING,
+ }),
+
+ setDashboardRequestCompleted: () => dispatch({
+ type: t.SET_DASHBOARD_REQUEST_COMPLETED,
+ }),
+});
+
+export default connect(null, mapActionsToProps);
\ No newline at end of file
diff --git a/client/src/containers/Dialogs/AccountFormDialog.container.js b/client/src/containers/Dialogs/AccountFormDialog.container.js
new file mode 100644
index 000000000..6766f19cc
--- /dev/null
+++ b/client/src/containers/Dialogs/AccountFormDialog.container.js
@@ -0,0 +1,30 @@
+import {connect} from 'react-redux';
+import { compose } from 'utils';
+import DialogConnect from 'connectors/Dialog.connector';
+import DialogReduxConnect from 'components/DialogReduxConnect';
+import {getDialogPayload} from 'store/dashboard/dashboard.reducer';
+import withAccountsActions from 'containers/Accounts/withAccountsActions';
+import withAccountDetail from 'containers/Accounts/withAccountDetail';
+import withAccounts from 'containers/Accounts/withAccounts';
+
+export const mapStateToProps = (state, props) => {
+ const dialogPayload = getDialogPayload(state, 'account-form');
+
+ return {
+ name: 'account-form',
+ payload: {action: 'new', id: null, ...dialogPayload},
+
+ accountId: dialogPayload?.id || null,
+ };
+};
+
+const AccountFormDialogConnect = connect(mapStateToProps);
+
+export default compose(
+ AccountFormDialogConnect,
+ withAccountsActions,
+ withAccountDetail,
+ withAccounts,
+ DialogReduxConnect,
+ DialogConnect,
+);
diff --git a/client/src/containers/Dashboard/Dialogs/AccountFormDialog.js b/client/src/containers/Dialogs/AccountFormDialog.js
similarity index 86%
rename from client/src/containers/Dashboard/Dialogs/AccountFormDialog.js
rename to client/src/containers/Dialogs/AccountFormDialog.js
index 87a16237f..c85594a42 100644
--- a/client/src/containers/Dashboard/Dialogs/AccountFormDialog.js
+++ b/client/src/containers/Dialogs/AccountFormDialog.js
@@ -15,31 +15,40 @@ import * as Yup from 'yup';
import { useFormik } from 'formik';
import { useIntl } from 'react-intl';
import { omit } from 'lodash';
-import { compose } from 'utils';
-import useAsync from 'hooks/async';
+import { useQuery, queryCache } from 'react-query';
+
import Dialog from 'components/Dialog';
import AppToaster from 'components/AppToaster';
-import DialogConnect from 'connectors/Dialog.connector';
-import DialogReduxConnect from 'components/DialogReduxConnect';
-import AccountFormDialogConnect from 'connectors/AccountFormDialog.connector';
-import AccountsConnect from 'connectors/Accounts.connector';
+
+import AccountFormDialogContainer from 'containers/Dialogs/AccountFormDialog.container';
+
import classNames from 'classnames';
import Icon from 'components/Icon';
import ErrorMessage from 'components/ErrorMessage';
+import { fetchAccountTypes } from 'store/accounts/accounts.actions';
+
function AccountFormDialog({
name,
payload,
isOpen,
+
+ // #withAccounts
accountsTypes,
accounts,
+
+ // #withAccountDetail
+ account,
+
+ // #withAccountsActions
requestFetchAccounts,
requestFetchAccountTypes,
requestFetchAccount,
- closeDialog,
requestSubmitAccount,
requestEditAccount,
- getAccountById,
+
+ // #withDialog
+ closeDialog,
}) {
const intl = useIntl();
const accountFormValidationSchema = Yup.object().shape({
@@ -63,10 +72,6 @@ function AccountFormDialog({
accounts.find(a => a.id === payload.id) : null,
);
- const editAccount = useMemo(() =>
- payload.action === 'edit' ? getAccountById(payload.id) : null,
- [payload, getAccountById]);
-
const transformApiErrors = (errors) => {
const fields = {};
if (errors.find(e => e.type === 'NOT_UNIQUE_CODE')) {
@@ -79,8 +84,7 @@ function AccountFormDialog({
const formik = useFormik({
enableReinitialize: true,
initialValues: {
- ...(payload.action === 'edit' && editAccount)
- ? editAccount : initialValues,
+ ...(payload.action === 'edit' && account) ? account : initialValues,
},
validationSchema: accountFormValidationSchema,
onSubmit: (values, { setSubmitting, setErrors }) => {
@@ -97,12 +101,13 @@ function AccountFormDialog({
intent: Intent.SUCCESS,
});
setSubmitting(false);
+ queryCache.refetchQueries('accounts-table', { force: true });
}).catch((errors) => {
setSubmitting(false);
setErrors(transformApiErrors(errors));
});
} else {
- requestSubmitAccount({ form: { ...omit(values, exclude) } }).then(response => {
+ requestSubmitAccount({ form: { ...omit(values, exclude) } }).then((response) => {
closeDialog(name);
AppToaster.show({
message: 'the_account_has_been_submit',
@@ -110,6 +115,7 @@ function AccountFormDialog({
position: Position.BOTTOM,
});
setSubmitting(false);
+ queryCache.refetchQueries('accounts-table', { force: true });
}).catch((errors) => {
setSubmitting(false);
setErrors(transformApiErrors(errors));
@@ -121,13 +127,13 @@ function AccountFormDialog({
// Set default account type.
useEffect(() => {
- if (editAccount && editAccount.account_type_id) {
+ if (account && account.account_type_id) {
const defaultType = accountsTypes.find((t) =>
- t.id === editAccount.account_type_id);
+ t.id === account.account_type_id);
defaultType && setSelectedAccountType(defaultType);
}
- }, [editAccount, accountsTypes]);
+ }, [account, accountsTypes]);
// Filters accounts types items.
const filterAccountTypeItems = (query, accountType, _index, exactMatch) => {
@@ -168,18 +174,32 @@ function AccountFormDialog({
// Handles dialog close.
const handleClose = useCallback(() => { closeDialog(name); }, [closeDialog, name]);
- const fetchHook = useAsync(async () => {
- await Promise.all([
- requestFetchAccounts(),
- requestFetchAccountTypes(),
- // Fetch the target in case edit mode.
- ...(payload.action === 'edit' ?
- [requestFetchAccount(payload.id)] : [])
- ]);
- }, false);
+ // Fetches accounts list.
+ const fetchAccountsList = useQuery('accounts-list',
+ () => requestFetchAccounts(), { manual: true });
+
+ // Fetches accounts types.
+ const fetchAccountsTypes = useQuery('accounts-types-list', async () => {
+ await requestFetchAccountTypes();
+ }, { manual: true });
+
+ // Fetch the given account id on edit mode.
+ const fetchAccount = useQuery(
+ payload.action === 'edit' && ['account', payload.id],
+ (key, id) => requestFetchAccount(id),
+ { manual: true });
+
+ const isFetching = (
+ fetchAccountsList.isFetching ||
+ fetchAccountTypes.isFetching ||
+ fetchAccount.isFetching);
// Fetch requests on dialog opening.
- const onDialogOpening = useCallback(() => { fetchHook.execute(); }, [fetchHook]);
+ const onDialogOpening = useCallback(() => {
+ fetchAccountsList.refetch();
+ fetchAccountsTypes.refetch();
+ fetchAccount.refetch();
+ }, []);
const onChangeAccountType = useCallback((accountType) => {
setSelectedAccountType(accountType);
@@ -211,7 +231,7 @@ function AccountFormDialog({
name={name}
title={payload.action === 'edit' ? 'Edit Account' : 'New Account'}
className={{
- 'dialog--loading': fetchHook.pending,
+ 'dialog--loading': isFetching,
'dialog--account-form': true
}}
autoFocus={true}
@@ -219,7 +239,7 @@ function AccountFormDialog({
onClosed={onDialogClosed}
onOpening={onDialogOpening}
isOpen={isOpen}
- isLoading={fetchHook.pending}
+ isLoading={isFetching}
onClose={handleClose}
>
diff --git a/client/src/containers/Items/ItemFormPage.js b/client/src/containers/Items/ItemFormPage.js
new file mode 100644
index 000000000..9927ef219
--- /dev/null
+++ b/client/src/containers/Items/ItemFormPage.js
@@ -0,0 +1,52 @@
+import React, { useEffect } from 'react';
+import { useParams } from 'react-router-dom';
+import { useQuery } from 'react-query';
+
+import ItemForm from 'containers/Items/ItemForm';
+import DashboardInsider from 'components/Dashboard/DashboardInsider';
+
+import withDashboard from 'containers/Dashboard/withDashboard';
+import withAccountsActions from 'containers/Accounts/withAccountsActions';
+import withItemCategoriesActions from 'containers/Items/withItemCategoriesActions';
+
+import { compose } from 'utils';
+
+
+const ItemFormContainer = ({
+ // #withDashboard
+ changePageTitle,
+
+ // #withAccountsActions
+ requestFetchAccounts,
+
+ // #withItemCategoriesActions
+ requestFetchItemCategories,
+}) => {
+ const { id } = useParams();
+
+ useEffect(() => {
+ id ?
+ changePageTitle('Edit Item Details') :
+ changePageTitle('New Item');
+ }, [id, changePageTitle]);
+
+ const fetchAccounts = useQuery('accounts-list',
+ (key) => requestFetchAccounts());
+
+ const fetchCategories = useQuery('item-categories-list',
+ (key) => requestFetchItemCategories());
+
+ return (
+