feat: optimize accounts performance.

feat: optimize alerts architecture.
feat: optimize datatable architecture.
feat: optimize datatable style.
This commit is contained in:
a.bouhuolia
2021-01-26 08:44:11 +02:00
parent 0655963607
commit b26f6c937c
70 changed files with 1607 additions and 1012 deletions

View File

@@ -137,7 +137,8 @@ function ManualJournalsDataTable({
accessor: (r) => (
<Tooltip
content={<AmountPopoverContent journalEntries={r.entries} />}
position={Position.RIGHT_BOTTOM}
position={Position.RIGHT_TOP}
boundary={'viewport'}
>
<Money amount={r.amount} currency={'USD'} />
</Tooltip>

View File

@@ -1,4 +1,4 @@
import React, { useMemo, useState, useCallback } from 'react';
import React, { memo, useState } from 'react';
import Icon from 'components/Icon';
import {
Button,
@@ -22,9 +22,13 @@ import withDialogActions from 'containers/Dialog/withDialogActions';
import withResourceDetail from 'containers/Resources/withResourceDetails';
import withAccountsTableActions from 'containers/Accounts/withAccountsTableActions';
import withAccounts from 'containers/Accounts/withAccounts';
import withAlertActions from 'containers/Alert/withAlertActions';
import { compose } from 'utils';
/**
* Accounts actions bar.
*/
function AccountsActionsBar({
openDialog,
accountsViews,
@@ -32,18 +36,18 @@ function AccountsActionsBar({
// #withResourceDetail
resourceFields,
// #withAccountsActions
// #withAccountsTableActions
addAccountsTableQueries,
setAccountsBulkAction,
// #withAccounts
accountsTableQuery,
accountsSelectedRows,
// #withAlertActions
openAlert,
selectedRows = [],
onFilterChanged,
onBulkDelete,
onBulkArchive,
onBulkActivate,
onBulkInactive,
}) {
const [filterCount, setFilterCount] = useState(
accountsTableQuery?.filter_roles?.length || 0,
@@ -53,10 +57,7 @@ function AccountsActionsBar({
openDialog('account-form', {});
};
const hasSelectedRows = useMemo(() => selectedRows.length > 0, [
selectedRows,
]);
// Filter dropdown.
const filterDropdown = FilterDropdown({
fields: resourceFields,
initialConditions: accountsTableQuery.filter_roles,
@@ -74,17 +75,17 @@ function AccountsActionsBar({
},
});
const handleBulkDelete = useCallback(() => {
onBulkDelete && onBulkDelete(selectedRows.map((r) => r.id));
}, [onBulkDelete, selectedRows]);
const handleBulkDelete = () => {
openAlert('accounts-bulk-delete', { accountsIds: accountsSelectedRows });
};
const handelBulkActivate = useCallback(() => {
onBulkActivate && onBulkActivate(selectedRows.map((r) => r.id));
}, [onBulkActivate, selectedRows]);
const handelBulkActivate = () => {
openAlert('accounts-bulk-activate', { accountsIds: accountsSelectedRows });
};
const handelBulkInactive = useCallback(() => {
onBulkInactive && onBulkInactive(selectedRows.map((r) => r.id));
}, [onBulkInactive, selectedRows]);
const handelBulkInactive = () => {
openAlert('accounts-bulk-inactivate', { accountsIds: accountsSelectedRows });
};
return (
<DashboardActionsBar>
@@ -113,7 +114,7 @@ function AccountsActionsBar({
'has-active-filters': filterCount > 0,
})}
text={
filterCount <= 0 ? (
(filterCount <= 0) ? (
<T id={'filter'} />
) : (
<T
@@ -126,7 +127,7 @@ function AccountsActionsBar({
/>
</Popover>
<If condition={hasSelectedRows}>
<If condition={accountsSelectedRows.length}>
<Button
className={Classes.MINIMAL}
icon={<Icon icon="play-16" iconSize={16} />}
@@ -168,21 +169,30 @@ function AccountsActionsBar({
);
}
// Momerize the component.
const AccountsActionsBarMemo = memo(AccountsActionsBar);
const mapStateToProps = (state, props) => ({
resourceName: 'accounts',
});
const withAccountsActionsBar = connect(mapStateToProps);
export default compose(
const comp = compose(
withAccountsActionsBar,
withDialogActions,
withAccounts(({ accountsViews, accountsTableQuery }) => ({
accountsViews,
accountsTableQuery,
})),
withAccounts(
({ accountsSelectedRows, accountsViews, accountsTableQuery }) => ({
accountsViews,
accountsTableQuery,
accountsSelectedRows,
}),
),
withResourceDetail(({ resourceFields }) => ({
resourceFields,
})),
withAccountsTableActions,
)(AccountsActionsBar);
withAlertActions
)(AccountsActionsBarMemo);
export default comp;

View File

@@ -0,0 +1,26 @@
import React from 'react';
import AccountDeleteAlert from 'containers/Alerts/AccountDeleteAlert';
import AccountInactivateAlert from 'containers/Alerts/AccountInactivateAlert';
import AccountActivateAlert from 'containers/Alerts/AccountActivateAlert';
import AccountBulkDeleteAlert from 'containers/Alerts/AccountBulkDeleteAlert';
import AccountBulkInactivateAlert from 'containers/Alerts/AccountBulkInactivateAlert';
import AccountBulkActivateAlert from 'containers/Alerts/AccountBulkActivateAlert';
/**
* Accounts alert.
*/
export default function AccountsAlerts({
}) {
return (
<div class="accounts-alerts">
<AccountDeleteAlert name={'account-delete'} />
<AccountInactivateAlert name={'account-inactivate'} />
<AccountActivateAlert name={'account-activate'} />
<AccountBulkDeleteAlert name={'accounts-bulk-delete'} />
<AccountBulkInactivateAlert name={'accounts-bulk-inactivate'} />
<AccountBulkActivateAlert name={'accounts-bulk-activate'} />
</div>
)
}

View File

@@ -1,20 +1,17 @@
import React, { useEffect, useState, useMemo, useCallback } from 'react';
import React, { useEffect, useState, useCallback } from 'react';
import { Route, Switch } from 'react-router-dom';
import { Alert, Intent } from '@blueprintjs/core';
import { useQuery, queryCache } from 'react-query';
import { useQuery } from 'react-query';
import {
FormattedMessage as T,
FormattedHTMLMessage,
useIntl,
} from 'react-intl';
import AppToaster from 'components/AppToaster';
import 'style/pages/Accounts/List.scss';
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
import DashboardInsider from 'components/Dashboard/DashboardInsider';
import AccountsViewsTabs from 'containers/Accounts/AccountsViewsTabs';
import AccountsDataTable from 'containers/Accounts/AccountsDataTable';
import DashboardActionsBar from 'containers/Accounts/AccountsActionsBar';
import AccountsViewPage from 'containers/Accounts/AccountsViewPage';
import AccountsActionsBar from 'containers/Accounts/AccountsActionsBar';
import AccountsAlerts from './AccountsAlerts';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withResourceActions from 'containers/Resources/withResourcesActions';
@@ -25,8 +22,6 @@ import withAccounts from 'containers/Accounts/withAccounts';
import { compose } from 'utils';
import 'style/pages/Accounts/List.scss';
/**
* Accounts chart list.
*/
@@ -34,11 +29,6 @@ function AccountsChart({
// #withDashboardActions
changePageTitle,
// #withAccountsActions
requestDeleteAccount,
requestInactiveAccount,
requestActivateAccount,
// #withViewsActions
requestFetchResourceViews,
@@ -47,35 +37,23 @@ function AccountsChart({
// #withAccountsTableActions
requestFetchAccountsTable,
requestDeleteBulkAccounts,
addAccountsTableQueries,
requestBulkActivateAccounts,
requestBulkInactiveAccounts,
// #withAccounts
accountsTableQuery,
}) {
const { formatMessage } = useIntl();
const [deleteAccount, setDeleteAccount] = useState(false);
const [inactiveAccount, setInactiveAccount] = useState(false);
const [activateAccount, setActivateAccount] = useState(false);
const [bulkDelete, setBulkDelete] = useState(false);
const [selectedRows, setSelectedRows] = useState([]);
const [bulkActivate, setBulkActivate] = useState(false);
const [bulkInactiveAccounts, setBulkInactiveAccounts] = useState(false);
// Fetch accounts resource views and fields.
const fetchResourceViews = useQuery(
['resource-views', 'accounts'],
(key, resourceName) => requestFetchResourceViews(resourceName),
);
// Fetch the accounts resource fields.
const fetchResourceFields = useQuery(
['resource-fields', 'accounts'],
(key, resourceName) => requestFetchResourceFields(resourceName),
);
// Fetch accounts list according to the given custom view id.
const fetchAccountsHook = useQuery(
['accounts-table', accountsTableQuery],
@@ -86,162 +64,10 @@ function AccountsChart({
changePageTitle(formatMessage({ id: 'chart_of_accounts' }));
}, [changePageTitle, formatMessage]);
// Handle click and cancel/confirm account delete
const handleDeleteAccount = (account) => {
setDeleteAccount(account);
};
// handle cancel delete account alert.
const handleCancelAccountDelete = useCallback(() => {
setDeleteAccount(false);
}, []);
// Handle delete errors in bulk and singular.
const handleDeleteErrors = (errors) => {
if (errors.find((e) => e.type === 'ACCOUNT.PREDEFINED')) {
AppToaster.show({
message: formatMessage({
id: 'you_could_not_delete_predefined_accounts',
}),
intent: Intent.DANGER,
});
}
if (errors.find((e) => e.type === 'ACCOUNT.HAS.ASSOCIATED.TRANSACTIONS')) {
AppToaster.show({
message: formatMessage({
id: 'cannot_delete_account_has_associated_transactions',
}),
intent: Intent.DANGER,
});
}
};
// Handle confirm account delete
const handleConfirmAccountDelete = useCallback(() => {
requestDeleteAccount(deleteAccount.id)
.then(() => {
setDeleteAccount(false);
AppToaster.show({
message: formatMessage({
id: 'the_account_has_been_successfully_deleted',
}),
intent: Intent.SUCCESS,
});
queryCache.invalidateQueries('accounts-table');
})
.catch((errors) => {
setDeleteAccount(false);
handleDeleteErrors(errors);
});
}, [deleteAccount, requestDeleteAccount, formatMessage]);
// Handle cancel/confirm account inactive.
const handleInactiveAccount = useCallback((account) => {
setInactiveAccount(account);
}, []);
// Handle cancel inactive account alert.
const handleCancelInactiveAccount = useCallback(() => {
setInactiveAccount(false);
}, []);
// Handle confirm account activation.
const handleConfirmAccountActive = useCallback(() => {
requestInactiveAccount(inactiveAccount.id)
.then(() => {
setInactiveAccount(false);
AppToaster.show({
message: formatMessage({
id: 'the_account_has_been_successfully_inactivated',
}),
intent: Intent.SUCCESS,
});
queryCache.invalidateQueries('accounts-table');
})
.catch((error) => {
setInactiveAccount(false);
});
}, [inactiveAccount, requestInactiveAccount, formatMessage]);
// Handle activate account click.
const handleActivateAccount = useCallback((account) => {
setActivateAccount(account);
});
// Handle activate account alert cancel.
const handleCancelActivateAccount = useCallback(() => {
setActivateAccount(false);
});
// Handle activate account confirm.
const handleConfirmAccountActivate = useCallback(() => {
requestActivateAccount(activateAccount.id)
.then(() => {
setActivateAccount(false);
AppToaster.show({
message: formatMessage({
id: 'the_account_has_been_successfully_activated',
}),
intent: Intent.SUCCESS,
});
queryCache.invalidateQueries('accounts-table');
})
.catch((error) => {
setActivateAccount(false);
});
}, [activateAccount, requestActivateAccount, formatMessage]);
const handleRestoreAccount = (account) => {};
// Handle accounts bulk delete button click.,
const handleBulkDelete = useCallback(
(accountsIds) => {
setBulkDelete(accountsIds);
},
[setBulkDelete],
);
// Handle confirm accounts bulk delete.
const handleConfirmBulkDelete = useCallback(() => {
requestDeleteBulkAccounts(bulkDelete)
.then(() => {
setBulkDelete(false);
AppToaster.show({
message: formatMessage({
id: 'the_accounts_has_been_successfully_deleted',
}),
intent: Intent.SUCCESS,
});
queryCache.invalidateQueries('accounts-table');
})
.catch((errors) => {
setBulkDelete(false);
handleDeleteErrors(errors);
});
}, [requestDeleteBulkAccounts, bulkDelete, formatMessage]);
// Handle cancel accounts bulk delete.
const handleCancelBulkDelete = useCallback(() => {
setBulkDelete(false);
}, []);
const handleBulkArchive = useCallback((accounts) => {}, []);
const handleEditAccount = useCallback(() => {}, []);
// Handle selected rows change.
const handleSelectedRowsChange = useCallback(
(accounts) => {
setSelectedRows(accounts);
},
[setSelectedRows],
);
// Refetches accounts data table when current custom view changed.
const handleFilterChanged = useCallback(() => {
fetchAccountsHook.refetch();
}, [fetchAccountsHook]);
}, []);
// Handle fetch data of accounts datatable.
const handleFetchData = useCallback(
@@ -255,198 +81,22 @@ function AccountsChart({
: {}),
});
},
[fetchAccountsHook, addAccountsTableQueries],
[addAccountsTableQueries],
);
// Calculates the data table selected rows count.
const selectedRowsCount = useMemo(() => Object.values(selectedRows).length, [
selectedRows,
]);
// Handle bulk Activate accounts button click.,
const handleBulkActivate = useCallback(
(bulkActivateIds) => {
setBulkActivate(bulkActivateIds);
},
[setBulkActivate],
);
// Handle cancel Bulk Activate accounts bulk delete.
const handleCancelBulkActivate = useCallback(() => {
setBulkActivate(false);
}, []);
// Handle Bulk activate account confirm.
const handleConfirmBulkActivate = useCallback(() => {
requestBulkActivateAccounts(bulkActivate)
.then(() => {
setBulkActivate(false);
AppToaster.show({
message: formatMessage({
id: 'the_accounts_has_been_successfully_activated',
}),
intent: Intent.SUCCESS,
});
queryCache.invalidateQueries('accounts-table');
})
.catch((errors) => {
setBulkActivate(false);
});
}, [requestBulkActivateAccounts, bulkActivate, formatMessage]);
// Handle bulk Inactive accounts button click.,
const handleBulkInactive = useCallback(
(bulkInactiveIds) => {
setBulkInactiveAccounts(bulkInactiveIds);
},
[setBulkInactiveAccounts],
);
// Handle cancel Bulk Inactive accounts bulk delete.
const handleCancelBulkInactive = useCallback(() => {
setBulkInactiveAccounts(false);
}, []);
// Handle Bulk Inactive accounts confirm.
const handleConfirmBulkInactive = useCallback(() => {
requestBulkInactiveAccounts(bulkInactiveAccounts)
.then(() => {
setBulkInactiveAccounts(false);
AppToaster.show({
message: formatMessage({
id: 'the_accounts_have_been_successfully_inactivated',
}),
intent: Intent.SUCCESS,
});
queryCache.invalidateQueries('accounts-table');
})
.catch((errors) => {
setBulkInactiveAccounts(false);
});
}, [requestBulkInactiveAccounts, bulkInactiveAccounts]);
return (
<DashboardInsider
loading={fetchResourceFields.isFetching || fetchResourceViews.isFetching}
name={'accounts-chart'}
>
<DashboardActionsBar
selectedRows={selectedRows}
<AccountsActionsBar
onFilterChanged={handleFilterChanged}
onBulkDelete={handleBulkDelete}
onBulkArchive={handleBulkArchive}
onBulkActivate={handleBulkActivate}
onBulkInactive={handleBulkInactive}
/>
<DashboardPageContent>
<Switch>
<Route
exact={true}
path={['/accounts/:custom_view_id/custom_view', '/accounts']}
>
<AccountsViewsTabs />
<AccountsDataTable
onDeleteAccount={handleDeleteAccount}
onInactiveAccount={handleInactiveAccount}
onActivateAccount={handleActivateAccount}
onRestoreAccount={handleRestoreAccount}
onEditAccount={handleEditAccount}
onFetchData={handleFetchData}
onSelectedRowsChange={handleSelectedRowsChange}
/>
</Route>
</Switch>
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={<T id={'delete'} />}
icon="trash"
intent={Intent.DANGER}
isOpen={deleteAccount}
onCancel={handleCancelAccountDelete}
onConfirm={handleConfirmAccountDelete}
>
<p>
<FormattedHTMLMessage
id={'once_delete_this_account_you_will_able_to_restore_it'}
/>
</p>
</Alert>
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={<T id={'inactivate'} />}
intent={Intent.WARNING}
isOpen={inactiveAccount}
onCancel={handleCancelInactiveAccount}
onConfirm={handleConfirmAccountActive}
>
<p>
<T id={'are_sure_to_inactive_this_account'} />
</p>
</Alert>
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={<T id={'activate'} />}
intent={Intent.WARNING}
isOpen={activateAccount}
onCancel={handleCancelActivateAccount}
onConfirm={handleConfirmAccountActivate}
>
<p>
<T id={'are_sure_to_activate_this_account'} />
</p>
</Alert>
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={`${formatMessage({
id: 'delete',
})} (${selectedRowsCount})`}
icon="trash"
intent={Intent.DANGER}
isOpen={bulkDelete}
onCancel={handleCancelBulkDelete}
onConfirm={handleConfirmBulkDelete}
>
<p>
<T
id={'once_delete_these_accounts_you_will_not_able_restore_them'}
/>
</p>
</Alert>
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={`${formatMessage({
id: 'activate',
})} (${selectedRowsCount})`}
intent={Intent.WARNING}
isOpen={bulkActivate}
onCancel={handleCancelBulkActivate}
onConfirm={handleConfirmBulkActivate}
>
<p>
<T id={'are_sure_to_activate_this_accounts'} />
</p>
</Alert>
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={`${formatMessage({
id: 'inactivate',
})} (${selectedRowsCount})`}
intent={Intent.WARNING}
isOpen={bulkInactiveAccounts}
onCancel={handleCancelBulkInactive}
onConfirm={handleConfirmBulkInactive}
>
<p>
<T id={'are_sure_to_inactive_this_accounts'} />
</p>
</Alert>
<AccountsViewPage />
</DashboardPageContent>
<AccountsAlerts />
</DashboardInsider>
);
}

View File

@@ -2,47 +2,45 @@ import React, { useCallback, useState, useMemo, useEffect } from 'react';
import {
Button,
Popover,
Menu,
MenuItem,
MenuDivider,
Position,
Intent,
} from '@blueprintjs/core';
import { withRouter } from 'react-router';
import { FormattedMessage as T, useIntl } from 'react-intl';
import classNames from 'classnames';
import { Icon, DataTable, If } from 'components';
import { compose } from 'utils';
import { saveInvoke, compose } from 'utils';
import { useUpdateEffect } from 'hooks';
import { CLASSES } from 'common/classes';
import {
NormalCell,
BalanceCell,
} from './components';
import { NormalCell, BalanceCell, AccountActionsMenuList } from './components';
import { TableFastCell } from 'components';
import TableVirtualizedListRows from 'components/Datatable/TableVirtualizedRows';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withAccountsActions from 'containers/Accounts/withAccountsActions';
import withAccounts from 'containers/Accounts/withAccounts';
import withDialogActions from 'containers/Dialog/withDialogActions';
import withCurrentView from 'containers/Views/withCurrentView';
/**
* Accounts data-table.
*/
function AccountsDataTable({
// #withDashboardActions
accountsTable,
accountsLoading,
// #withDialog.
openDialog,
// #
currentViewId,
// own properties
// #ownProps
onFetchData,
onSelectedRowsChange,
onDeleteAccount,
onInactiveAccount,
onInactivateAccount,
onActivateAccount,
onEditAccount,
onNewChildAccount
}) {
const [isMounted, setIsMounted] = useState(false);
const { formatMessage } = useIntl();
@@ -57,77 +55,44 @@ function AccountsDataTable({
}
}, [accountsLoading, setIsMounted]);
const handleEditAccount = useCallback(
(account) => () => {
openDialog('account-form', { action: 'edit', id: account.id });
},
[openDialog],
);
const handleNewParentAccount = useCallback(
(account) => {
openDialog('account-form', {
action: 'new_child',
parentAccountId: account.id,
accountTypeId: account.account_type_id,
});
},
[openDialog],
);
const ActionsCell = useMemo(() =>
({ row }) => (
<Popover
content={<AccountActionsMenuList
account={row.original}
onDeleteAccount={onDeleteAccount}
onInactivateAccount={onInactivateAccount}
onActivateAccount={onActivateAccount}
onEditAccount={onEditAccount}
/>}
position={Position.RIGHT_TOP}
>
<Button icon={<Icon icon="more-h-16" iconSize={16} />} />
</Popover>
), [
onDeleteAccount,
onInactivateAccount,
onActivateAccount,
onEditAccount
]);
const actionMenuList = useCallback(
(account) => (
<Menu>
<MenuItem
icon={<Icon icon="reader-18" />}
text={formatMessage({ id: 'view_details' })}
/>
<MenuDivider />
<MenuItem
icon={<Icon icon="pen-18" />}
text={formatMessage({ id: 'edit_account' })}
onClick={handleEditAccount(account)}
/>
<MenuItem
icon={<Icon icon="plus" />}
text={formatMessage({ id: 'new_child_account' })}
onClick={() => handleNewParentAccount(account)}
/>
<MenuDivider />
<If condition={account.active}>
<MenuItem
text={formatMessage({ id: 'inactivate_account' })}
icon={<Icon icon="pause-16" iconSize={16} />}
onClick={() => onInactiveAccount(account)}
/>
</If>
<If condition={!account.active}>
<MenuItem
text={formatMessage({ id: 'activate_account' })}
icon={<Icon icon="play-16" iconSize={16} />}
onClick={() => onActivateAccount(account)}
/>
</If>
<MenuItem
text={formatMessage({ id: 'delete_account' })}
icon={<Icon icon="trash-16" iconSize={16} />}
intent={Intent.DANGER}
onClick={() => onDeleteAccount(account)}
/>
</Menu>
),
[
handleEditAccount,
onDeleteAccount,
onInactiveAccount,
handleNewParentAccount,
formatMessage,
],
);
const rowContextMenu = (cell) => {
return actionMenuList(cell.row.original);
};
const RowContextMenu = useMemo(() => ({ row }) => (
<AccountActionsMenuList
account={row.original}
onDeleteAccount={onDeleteAccount}
onInactivateAccount={onInactivateAccount}
onActivateAccount={onActivateAccount}
onEditAccount={onEditAccount}
onNewChildAccount={onNewChildAccount}
/>
), [
onDeleteAccount,
onInactivateAccount,
onActivateAccount,
onEditAccount,
onNewChildAccount
]);
const columns = useMemo(
() => [
@@ -143,7 +108,7 @@ function AccountsDataTable({
Header: formatMessage({ id: 'code' }),
accessor: 'code',
className: 'code',
width: 70,
width: 80,
},
{
id: 'type',
@@ -158,7 +123,7 @@ function AccountsDataTable({
Cell: NormalCell,
accessor: 'account_normal',
className: 'normal',
width: 65,
width: 80,
},
{
id: 'currency',
@@ -176,40 +141,31 @@ function AccountsDataTable({
{
id: 'actions',
Header: '',
// Cell: ({ cell }) => (
// <Popover
// content={actionMenuList(cell.row.original)}
// position={Position.RIGHT_TOP}
// >
// <Button icon={<Icon icon="more-h-16" iconSize={16} />} />
// </Popover>
// ),
Cell: ActionsCell,
className: 'actions',
width: 50,
},
],
[actionMenuList, formatMessage],
[ActionsCell, formatMessage],
);
const handleDatatableFetchData = useCallback((...params) => {
onFetchData && onFetchData(...params);
}, []);
const handleDatatableFetchData = useCallback(
(...params) => {
saveInvoke(onFetchData, params);
},
[onFetchData],
);
const handleSelectedRowsChange = useCallback(
(selectedRows) => {
onSelectedRowsChange &&
onSelectedRowsChange(selectedRows.map((s) => s.original));
saveInvoke(onSelectedRowsChange, selectedRows);
},
[onSelectedRowsChange],
);
const rowClassNames = (row) => {
return {
'inactive': !row.original.active,
};
};
const rowClassNames = (row) => ({
inactive: !row.original.active,
});
return (
<div className={classNames(CLASSES.DASHBOARD_DATATABLE)}>
@@ -223,14 +179,19 @@ function AccountsDataTable({
sticky={true}
onSelectedRowsChange={handleSelectedRowsChange}
loading={accountsLoading && !isMounted}
rowContextMenu={rowContextMenu}
rowContextMenu={RowContextMenu}
rowClassNames={rowClassNames}
expandColumnSpace={1}
autoResetExpanded={false}
autoResetSortBy={false}
autoResetSelectedRows={false}
expandColumnSpace={1}
expandToggleColumn={2}
selectionColumnWidth={50}
virtualizedRows={true}
fixedSizeHeight={1000}
TableCellRenderer={TableFastCell}
TableRowsRenderer={TableVirtualizedListRows}
// #TableVirtualizedListRows props.
vListrowHeight={42}
vListOverscanRowCount={10}
/>
</div>
);
@@ -239,7 +200,6 @@ function AccountsDataTable({
export default compose(
withRouter,
withCurrentView,
withDialogActions,
withDashboardActions,
withAccountsActions,
withAccounts(({ accountsLoading, accountsTable }) => ({

View File

@@ -0,0 +1,84 @@
import React, { memo } from 'react';
import { Switch, Route } from 'react-router-dom';
import AccountsViewsTabs from 'containers/Accounts/AccountsViewsTabs';
import AccountsDataTable from 'containers/Accounts/AccountsDataTable';
import withAccountsTableActions from 'containers/Accounts/withAccountsTableActions';
import withAlertsActions from 'containers/Alert/withAlertActions';
import withDialogActions from 'containers/Dialog/withDialogActions';
import { compose } from 'utils';
/**
* Accounts view page.
*/
function AccountsViewPage({
openAlert,
// #withDialog.
openDialog,
// #withAccountsTableActions
setSelectedRowsAccounts
}) {
// Handle delete action account.
const handleDeleteAccount = (account) => {
openAlert('account-delete', { accountId: account.id })
};
// Handle activate action account.
const handleActivateAccount = (account) => {
openAlert('account-activate', { accountId: account.id });
};
// Handle inactivate action account.
const handleInactivateAccount = (account) => {
openAlert('account-inactivate', { accountId: account.id });
};
// Handle select accounts datatable rows.
const handleSelectedRowsChange = (selectedRows) => {
const selectedRowsIds = selectedRows.map(r => r.id);
setSelectedRowsAccounts(selectedRowsIds);
};
const handleEditAccount = (account) => {
openDialog('account-form', { action: 'edit', id: account.id });
}
const handleNewChildAccount = (account) => {
openDialog('account-form', {
action: 'new_child',
parentAccountId: account.id,
accountType: account.account_type,
});
};
return (
<Switch>
<Route
exact={true}
path={['/accounts/:custom_view_id/custom_view', '/accounts']}
>
<AccountsViewsTabs />
<AccountsDataTable
onDeleteAccount={handleDeleteAccount}
onInactivateAccount={handleInactivateAccount}
onActivateAccount={handleActivateAccount}
onSelectedRowsChange={handleSelectedRowsChange}
onEditAccount={handleEditAccount}
onNewChildAccount={handleNewChildAccount}
/>
</Route>
</Switch>
);
}
const AccountsViewPageMemo = memo(AccountsViewPage);
export default compose(
withAlertsActions,
withAccountsTableActions,
withDialogActions
)(AccountsViewPageMemo);

View File

@@ -1,11 +1,10 @@
import React, { useEffect } from 'react';
import React, { useEffect, useMemo, memo, useCallback } from 'react';
import { useHistory } from 'react-router';
import { connect } from 'react-redux';
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
import { useParams, withRouter } from 'react-router-dom';
import { pick } from 'lodash';
import { useUpdateEffect } from 'hooks';
import { DashboardViewsTabs } from 'components';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withAccounts from 'containers/Accounts/withAccounts';
@@ -14,6 +13,9 @@ import withViewDetail from 'containers/Views/withViewDetails';
import { compose } from 'utils';
/**
* Accounts views tabs.
*/
function AccountsViewsTabs({
// #withViewDetail
viewId,
@@ -23,16 +25,11 @@ function AccountsViewsTabs({
accountsViews,
// #withAccountsTableActions
addAccountsTableQueries,
changeAccountsCurrentView,
// #withDashboardActions
setTopbarEditView,
changePageSubtitle,
// props
customViewChanged,
onViewChanged,
}) {
const history = useHistory();
const { custom_view_id: customViewId = null } = useParams();
@@ -40,28 +37,27 @@ function AccountsViewsTabs({
useEffect(() => {
setTopbarEditView(customViewId);
changePageSubtitle(customViewId && viewItem ? viewItem.name : '');
}, [customViewId]);
}, [customViewId, viewItem, changePageSubtitle, setTopbarEditView]);
// Handle click a new view tab.
const handleClickNewView = () => {
const handleClickNewView = useCallback(() => {
setTopbarEditView(null);
history.push('/custom_views/accounts/new');
};
}, [setTopbarEditView]);
const handleTabChange = (viewId) => {
const handleTabChange = useCallback((viewId) => {
changeAccountsCurrentView(viewId || -1);
// addAccountsTableQueries({
// custom_view_id: viewId || null,
// });
};
}, [changeAccountsCurrentView]);
const tabs = accountsViews.map((view) => ({
const tabs = useMemo(() => accountsViews.map((view) => ({
...pick(view, ['name', 'id']),
}));
})), [accountsViews]);;
return (
<Navbar className="navbar--dashboard-views">
<NavbarGroup align={Alignment.LEFT}>
<DashboardViewsTabs
defaultTabText={'All Accounts'}
initialViewId={customViewId}
resourceName={'accounts'}
onChange={handleTabChange}
@@ -72,8 +68,10 @@ function AccountsViewsTabs({
);
}
const AccountsViewsTabsMemo = memo(AccountsViewsTabs);
const mapStateToProps = (state, ownProps) => ({
viewId: ownProps.match.params.custom_view_id,
viewId: -1,
});
const withAccountsViewsTabs = connect(mapStateToProps);
@@ -87,4 +85,4 @@ export default compose(
})),
withAccountsTableActions,
withViewDetail(),
)(AccountsViewsTabs);
)(AccountsViewsTabsMemo);

View File

@@ -3,15 +3,76 @@ import {
Position,
Classes,
Tooltip,
MenuItem,
Menu,
MenuDivider,
Intent
} from '@blueprintjs/core';
import { FormattedMessage as T, useIntl } from 'react-intl';
import classNames from 'classnames';
import { Icon, Money, If, Choose } from 'components';
import { Icon, Money, If } from 'components';
import { saveInvoke } from 'utils';
import { formatMessage } from 'services/intl';
import { POPOVER_CONTENT_SIZING } from '@blueprintjs/core/lib/esm/common/classes';
export function AccountActionsMenuList({
account,
onNewChildAccount,
onEditAccount,
onActivateAccount,
onInactivateAccount,
onDeleteAccount,
}) {
return (
<Menu>
<MenuItem
icon={<Icon icon="reader-18" />}
text={formatMessage({ id: 'view_details' })}
/>
<MenuDivider />
<MenuItem
icon={<Icon icon="pen-18" />}
text={formatMessage({ id: 'edit_account' })}
onClick={() => saveInvoke(onEditAccount, account)}
/>
<MenuItem
icon={<Icon icon="plus" />}
text={formatMessage({ id: 'new_child_account' })}
onClick={() => saveInvoke(onNewChildAccount, account)}
/>
<MenuDivider />
<If condition={account.active}>
<MenuItem
text={formatMessage({ id: 'inactivate_account' })}
icon={<Icon icon="pause-16" iconSize={16} />}
onClick={() => saveInvoke(onInactivateAccount, account)}
/>
</If>
<If condition={!account.active}>
<MenuItem
text={formatMessage({ id: 'activate_account' })}
icon={<Icon icon="play-16" iconSize={16} />}
onClick={() => saveInvoke(onActivateAccount, account)}
/>
</If>
<MenuItem
text={formatMessage({ id: 'delete_account' })}
icon={<Icon icon="trash-16" iconSize={16} />}
intent={Intent.DANGER}
onClick={() => saveInvoke(onDeleteAccount, account)}
/>
</Menu>
);
}
export function NormalCell({ cell: { value } }) {
const { formatMessage } = useIntl();
const arrowDirection = value === 'credit' ? 'down' : 'up';
// if (value !== 'credit' || value !== 'debit') {
// return '';
// }
return (
<Tooltip
className={Classes.TOOLTIP_INDICATOR}

View File

@@ -1,5 +1,7 @@
import React from 'react';
import { If } from 'components';
import { Intent } from '@blueprintjs/core';
import { If, AppToaster } from 'components';
import { formatMessage } from 'services/intl';
export const accountNameAccessor = (account) => {
return (
@@ -11,3 +13,23 @@ export const accountNameAccessor = (account) => {
</span>
);
};
// Handle delete errors in bulk and singular.
export const handleDeleteErrors = (errors) => {
if (errors.find((e) => e.type === 'ACCOUNT.PREDEFINED')) {
AppToaster.show({
message: formatMessage({
id: 'you_could_not_delete_predefined_accounts',
}),
intent: Intent.DANGER,
});
}
if (errors.find((e) => e.type === 'ACCOUNT.HAS.ASSOCIATED.TRANSACTIONS')) {
AppToaster.show({
message: formatMessage({
id: 'cannot_delete_account_has_associated_transactions',
}),
intent: Intent.DANGER,
});
}
};

View File

@@ -18,6 +18,7 @@ export default (mapState) => {
accountsTableQuery: state.accounts.tableQuery,
accountsLoading: state.accounts.loading,
accountErrors: state.accounts.errors,
accountsSelectedRows: state.accounts.selectedRows,
};
return mapState ? mapState(mapped, state, props) : mapped;
};

View File

@@ -1,6 +1,6 @@
import { connect } from 'react-redux';
import t from 'store/types';
import { fetchAccountsTable } from 'store/accounts/accounts.actions';
import { fetchAccountsTable, setBulkAction } from 'store/accounts/accounts.actions';
const mapActionsToProps = (dispatch) => ({
requestFetchAccountsTable: (query = {}) =>
@@ -21,11 +21,12 @@ const mapActionsToProps = (dispatch) => ({
type: t.ACCOUNTS_TABLE_QUERIES_ADD,
queries,
}),
setSelectedRowsAccounts: (ids) =>
setSelectedRowsAccounts: (selectedRows) =>
dispatch({
type: t.ACCOUNTS_SELECTED_ROWS_SET,
payload: { ids },
payload: { selectedRows },
}),
setAccountsBulkAction: (actionName) => setBulkAction(actionName),
});
export default connect(null, mapActionsToProps);

View File

@@ -0,0 +1,13 @@
import { connect } from 'react-redux';
import t from 'store/types';
export const mapStateToProps = (state, props) => {
return {};
};
export const mapDispatchToProps = (dispatch) => ({
openAlert: (name, payload) => dispatch({ type: t.OPEN_ALERT, name, payload }),
closeAlert: (name, payload) => dispatch({ type: t.CLOSE_ALERT, name, payload }),
});
export default connect(null, mapDispatchToProps);

View File

@@ -0,0 +1,19 @@
import { connect } from 'react-redux';
import {
isAlertOpenFactory,
getAlertPayloadFactory,
} from 'store/dashboard/dashboard.selectors';
export default (mapState) => {
const isAlertOpen = isAlertOpenFactory();
const getAlertPayload = getAlertPayloadFactory();
const mapStateToProps = (state, props) => {
const mapped = {
isOpen: isAlertOpen(state, props),
payload: getAlertPayload(state, props),
};
return mapState ? mapState(mapped) : mapped;
};
return connect(mapStateToProps);
}

View File

@@ -0,0 +1,74 @@
import React from 'react';
import {
FormattedMessage as T,
useIntl,
} from 'react-intl';
import { Intent, Alert } from '@blueprintjs/core';
import { queryCache } from 'react-query';
import { AppToaster } from 'components';
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
import withAlertActions from 'containers/Alert/withAlertActions';
import withAccountsActions from 'containers/Accounts/withAccountsActions';
import { compose } from 'utils';
/**
* Account activate alert.
*/
function AccountActivateAlert({
name,
isOpen,
payload: { accountId },
// #withAlertActions
closeAlert,
requestActivateAccount
}) {
const { formatMessage } = useIntl();
// Handle alert cancel.
const handleCancel = () => {
closeAlert('account-activate');
};
// Handle activate account confirm.
const handleConfirmAccountActivate = () => {
requestActivateAccount(accountId)
.then(() => {
closeAlert('account-activate');
AppToaster.show({
message: formatMessage({
id: 'the_account_has_been_successfully_activated',
}),
intent: Intent.SUCCESS,
});
queryCache.invalidateQueries('accounts-table');
})
.catch((error) => {
closeAlert('account-activate');
});
};
return (
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={<T id={'activate'} />}
intent={Intent.WARNING}
isOpen={isOpen}
onCancel={handleCancel}
onConfirm={handleConfirmAccountActivate}
>
<p>
<T id={'are_sure_to_activate_this_account'} />
</p>
</Alert>
);
}
export default compose(
withAlertStoreConnect(),
withAlertActions,
withAccountsActions
)(AccountActivateAlert);

View File

@@ -0,0 +1,76 @@
import React from 'react';
import {
FormattedMessage as T,
FormattedHTMLMessage,
useIntl
} from 'react-intl';
import { Intent, Alert } from '@blueprintjs/core';
import { queryCache } from 'react-query';
import { AppToaster } from 'components';
import withAccountsActions from 'containers/Accounts/withAccountsActions';
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
import withAlertActions from 'containers/Alert/withAlertActions';
import { compose } from 'utils';
function AccountBulkActivateAlert({
name,
isOpen,
payload: { accountsIds },
// #withAlertActions
closeAlert,
requestBulkActivateAccounts
}) {
const { formatMessage } = useIntl();
const selectedRowsCount = 0;
// Handle alert cancel.
const handleClose = () => {
closeAlert(name);
}
// Handle Bulk activate account confirm.
const handleConfirmBulkActivate = () => {
requestBulkActivateAccounts(accountsIds)
.then(() => {
closeAlert(name);
AppToaster.show({
message: formatMessage({
id: 'the_accounts_has_been_successfully_activated',
}),
intent: Intent.SUCCESS,
});
queryCache.invalidateQueries('accounts-table');
})
.catch((errors) => {
closeAlert(name);
});
};
return (
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={`${formatMessage({
id: 'activate',
})} (${selectedRowsCount})`}
intent={Intent.WARNING}
isOpen={isOpen}
onCancel={handleClose}
onConfirm={handleConfirmBulkActivate}
>
<p>
<T id={'are_sure_to_activate_this_accounts'} />
</p>
</Alert>
);
}
export default compose(
withAlertStoreConnect(),
withAlertActions,
withAccountsActions
)(AccountBulkActivateAlert);

View File

@@ -0,0 +1,80 @@
import React from 'react';
import {
FormattedMessage as T,
useIntl
} from 'react-intl';
import { Intent, Alert } from '@blueprintjs/core';
import { queryCache } from 'react-query';
import { AppToaster } from 'components';
import { handleDeleteErrors } from 'containers/Accounts/utils';
import withAccountsActions from 'containers/Accounts/withAccountsActions';
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
import withAlertActions from 'containers/Alert/withAlertActions';
import { compose } from 'utils';
function AccountBulkDeleteAlert({
// #ownProps
name,
// #withAlertStoreConnect
isOpen,
payload: { accountsIds },
// #withAlertActions
closeAlert,
// #withAccountsActions
requestDeleteBulkAccounts
}) {
const { formatMessage } = useIntl();
const selectedRowsCount = 0;
const handleCancel = () => {
closeAlert(name);
};
// Handle confirm accounts bulk delete.
const handleConfirmBulkDelete = () => {
requestDeleteBulkAccounts(accountsIds)
.then(() => {
closeAlert(name);
AppToaster.show({
message: formatMessage({
id: 'the_accounts_has_been_successfully_deleted',
}),
intent: Intent.SUCCESS,
});
queryCache.invalidateQueries('accounts-table');
})
.catch((errors) => {
closeAlert(name);
handleDeleteErrors(errors);
});
};
return (
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={`${formatMessage({
id: 'delete',
})} (${selectedRowsCount})`}
icon="trash"
intent={Intent.DANGER}
isOpen={isOpen}
onCancel={handleCancel}
onConfirm={handleConfirmBulkDelete}
>
<p>
<T id={'once_delete_these_accounts_you_will_not_able_restore_them'} />
</p>
</Alert>
);
}
export default compose(
withAlertStoreConnect(),
withAlertActions,
withAccountsActions
)(AccountBulkDeleteAlert);

View File

@@ -0,0 +1,71 @@
import React from 'react';
import { FormattedMessage as T, useIntl } from 'react-intl';
import { Intent, Alert } from '@blueprintjs/core';
import { queryCache } from 'react-query';
import { AppToaster } from 'components';
import withAccountsActions from 'containers/Accounts/withAccountsActions';
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
import withAlertActions from 'containers/Alert/withAlertActions';
import { compose } from 'utils';
function AccountBulkInactivateAlert({
name,
isOpen,
payload: { accountsIds },
// #withAccountsActions
requestBulkInactiveAccounts,
closeAlert,
}) {
const { formatMessage } = useIntl();
const selectedRowsCount = 0;
// Handle alert cancel.
const handleCancel = () => {
closeAlert(name);
};
// Handle Bulk Inactive accounts confirm.
const handleConfirmBulkInactive = () => {
requestBulkInactiveAccounts(accountsIds)
.then(() => {
closeAlert(name);
AppToaster.show({
message: formatMessage({
id: 'the_accounts_have_been_successfully_inactivated',
}),
intent: Intent.SUCCESS,
});
queryCache.invalidateQueries('accounts-table');
})
.catch((errors) => {
closeAlert(name);
});
};
return (
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={`${formatMessage({
id: 'inactivate',
})} (${selectedRowsCount})`}
intent={Intent.WARNING}
isOpen={isOpen}
onCancel={handleCancel}
onConfirm={handleConfirmBulkInactive}
>
<p>
<T id={'are_sure_to_inactive_this_accounts'} />
</p>
</Alert>
);
}
export default compose(
withAlertStoreConnect(),
withAlertActions,
withAccountsActions,
)(AccountBulkInactivateAlert);

View File

@@ -0,0 +1,84 @@
import React from 'react';
import {
FormattedMessage as T,
FormattedHTMLMessage,
useIntl
} from 'react-intl';
import { Intent, Alert } from '@blueprintjs/core';
import { queryCache } from 'react-query';
import { AppToaster } from 'components';
import { handleDeleteErrors } from 'containers/Accounts/utils';
import withAccountsActions from 'containers/Accounts/withAccountsActions';
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
import withAlertActions from 'containers/Alert/withAlertActions';
import { compose } from 'utils';
/**
* Account delete alerts.
*/
function AccountDeleteAlert({
name,
// #withAlertStoreConnect
isOpen,
payload: { accountId },
// #withAccountsActions
requestDeleteAccount,
// #withAlertActions
closeAlert
}) {
const { formatMessage } = useIntl();
// handle cancel delete account alert.
const handleCancelAccountDelete = () => {
closeAlert(name);
};
// Handle confirm account delete.
const handleConfirmAccountDelete = () => {
requestDeleteAccount(accountId)
.then(() => {
closeAlert(name);
AppToaster.show({
message: formatMessage({
id: 'the_account_has_been_successfully_deleted',
}),
intent: Intent.SUCCESS,
});
queryCache.invalidateQueries('accounts-table');
})
.catch((errors) => {
handleDeleteErrors(errors);
closeAlert(name);
});
};
return (
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={<T id={'delete'} />}
icon="trash"
intent={Intent.DANGER}
isOpen={isOpen}
onCancel={handleCancelAccountDelete}
onConfirm={handleConfirmAccountDelete}
>
<p>
<FormattedHTMLMessage
id={'once_delete_this_account_you_will_able_to_restore_it'}
/>
</p>
</Alert>
)
}
export default compose(
withAlertStoreConnect(),
withAlertActions,
withAccountsActions
)(AccountDeleteAlert);

View File

@@ -0,0 +1,71 @@
import React from 'react';
import {
FormattedMessage as T,
useIntl,
} from 'react-intl';
import { Intent, Alert } from '@blueprintjs/core';
import { queryCache } from 'react-query';
import { AppToaster } from 'components';
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
import withAlertActions from 'containers/Alert/withAlertActions';
import withAccountsActions from 'containers/Accounts/withAccountsActions';
import { compose } from 'utils';
function AccountInactivateAlert({
name,
isOpen,
payload: { accountId },
// #withAlertActions
closeAlert,
// #withAccountsActions
requestInactiveAccount,
}) {
const { formatMessage } = useIntl();
const handleCancelInactiveAccount = () => {
closeAlert('account-inactivate');
};
const handleConfirmAccountActive = () => {
requestInactiveAccount(accountId)
.then(() => {
closeAlert('account-inactivate');
AppToaster.show({
message: formatMessage({
id: 'the_account_has_been_successfully_inactivated',
}),
intent: Intent.SUCCESS,
});
queryCache.invalidateQueries('accounts-table');
})
.catch((error) => {
closeAlert('account-inactivate');
});
};
return (
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={<T id={'inactivate'} />}
intent={Intent.WARNING}
isOpen={isOpen}
onCancel={handleCancelInactiveAccount}
onConfirm={handleConfirmAccountActive}
>
<p>
<T id={'are_sure_to_inactive_this_account'} />
</p>
</Alert>
);
}
export default compose(
withAlertStoreConnect(),
withAlertActions,
withAccountsActions
)(AccountInactivateAlert);

View File

@@ -0,0 +1,5 @@
import AccountDeleteAlert from './AccountDeleteAlert';
export default {
AccountDeleteAlert,
};

View File

@@ -22,7 +22,7 @@ import { transformApiErrors, transformAccountToForm } from './utils';
import 'style/pages/Accounts/AccountFormDialog.scss';
const defaultInitialValues = {
account_type_id: '',
account_type: '',
parent_account_id: '',
name: '',
code: '',
@@ -51,7 +51,7 @@ function AccountFormDialogContent({
accountId,
action,
parentAccountId,
accountTypeId,
accountType,
}) {
const { formatMessage } = useIntl();
const isNewMode = !accountId;
@@ -72,7 +72,10 @@ function AccountFormDialogContent({
const handleSuccess = () => {
closeDialog(dialogName);
queryCache.invalidateQueries('accounts-table');
queryCache.invalidateQueries('accounts-list');
setTimeout(() => {
queryCache.invalidateQueries('accounts-list');
}, 1000);
AppToaster.show({
message: formatMessage(
@@ -116,7 +119,7 @@ function AccountFormDialogContent({
transformAccountToForm(account, {
action,
parentAccountId,
accountTypeId,
accountType,
}),
defaultInitialValues,
),
@@ -158,7 +161,7 @@ function AccountFormDialogContent({
>
<AccountFormDialogFields
dialogName={dialogName}
isNewMode={isNewMode}
action={action}
onClose={handleClose}
/>
</Formik>

View File

@@ -30,7 +30,7 @@ import { useAutofocus } from 'hooks';
function AccountFormDialogFields({
// #ownPropscl
onClose,
isNewMode,
action,
// #withAccounts
accounts,
@@ -42,7 +42,7 @@ function AccountFormDialogFields({
return (
<Form>
<div className={Classes.DIALOG_BODY}>
<FastField name={'account_type'}>
<Field name={'account_type'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'account_type'} />}
@@ -59,13 +59,13 @@ function AccountFormDialogFields({
onTypeSelected={(accountType) => {
form.setFieldValue('account_type', accountType.key);
}}
disabled={!isNewMode}
disabled={action === 'edit' || action === 'new_child'}
popoverProps={{ minimal: true }}
popoverFill={true}
/>
</FormGroup>
)}
</FastField>
</Field>
<FastField name={'name'}>
{({ field, meta: { error, touched } }) => (
@@ -126,7 +126,11 @@ function AccountFormDialogFields({
<If condition={values.subaccount}>
<FastField name={'parent_account_id'}>
{({ form, field: { value }, meta: { error, touched } }) => (
{({
form: { values, setFieldValue },
field: { value },
meta: { error, touched },
}) => (
<FormGroup
label={<T id={'parent_account'} />}
className={classNames(
@@ -139,11 +143,12 @@ function AccountFormDialogFields({
<AccountsSelectList
accounts={accounts}
onAccountSelected={(account) => {
form.setFieldValue('parent_account_id', account.id);
setFieldValue('parent_account_id', account.id);
}}
defaultSelectText={<T id={'select_parent_account'} />}
selectedAccountId={value}
popoverFill={true}
filterByTypes={values.account_type}
/>
</FormGroup>
)}
@@ -177,7 +182,7 @@ function AccountFormDialogFields({
style={{ minWidth: '75px' }}
type="submit"
>
{!isNewMode ? <T id={'edit'} /> : <T id={'submit'} />}
{action === 'edit' ? <T id={'edit'} /> : <T id={'submit'} />}
</Button>
</div>
</div>

View File

@@ -33,7 +33,7 @@ function AccountFormDialog({
accountId={payload.id}
action={payload.action}
parentAccountId={payload.parentAccountId}
accountTypeId={payload.accountTypeId}
accountType={payload.accountType}
/>
</DialogSuspense>
</Dialog>

View File

@@ -14,11 +14,11 @@ export const transformApiErrors = (errors) => {
export const transformAccountToForm = (account, {
action,
parentAccountId,
accountTypeId
accountType
}) => {
return {
parent_account_id: action === 'new_child' ? parentAccountId : '',
account_type_id: action === 'new_child'? accountTypeId : '',
account_type: action === 'new_child'? accountType : '',
subaccount: action === 'new_child' ? true : false,
...account,
}

View File

@@ -0,0 +1,6 @@
import { connect } from "react-redux";
import { withRouter } from "react-router-dom"
export default (mapState) => {
return () => withRouter ;
};

View File

@@ -28,4 +28,4 @@ const mapDispatchToProps = (dispatch, props) => {
}
}
export default connect(null, mapDispatchToProps)
export default connect(null, mapDispatchToProps)