- feat: Highlight inactive accounts in data-table.

- feat: Separate accounts list and table order.
This commit is contained in:
Ahmed Bouhuolia
2020-07-04 15:30:24 +02:00
parent 273834b13e
commit 3fc390652d
13 changed files with 116 additions and 49 deletions

View File

@@ -95,7 +95,7 @@ function MakeJournalEntriesTable({
customers, customers,
// #withAccounts // #withAccounts
accounts, accountsList,
// #ownPorps // #ownPorps
onClickRemoveRow, onClickRemoveRow,
@@ -251,7 +251,7 @@ function MakeJournalEntriesTable({
rowClassNames={rowClassNames} rowClassNames={rowClassNames}
sticky={true} sticky={true}
payload={{ payload={{
accounts, accounts: accountsList,
errors: errors.entries || [], errors: errors.entries || [],
updateData: handleUpdateData, updateData: handleUpdateData,
removeRow: handleRemoveRow, removeRow: handleRemoveRow,
@@ -286,8 +286,8 @@ function MakeJournalEntriesTable({
} }
export default compose( export default compose(
withAccounts(({ accounts }) => ({ withAccounts(({ accountsList }) => ({
accounts, accountsList,
})), })),
withCustomers(({ customersItems }) => ({ withCustomers(({ customersItems }) => ({
customers: customersItems, customers: customersItems,

View File

@@ -12,11 +12,13 @@ import {
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { withRouter } from 'react-router'; import { withRouter } from 'react-router';
import { FormattedMessage as T, useIntl } from 'react-intl'; import { FormattedMessage as T, useIntl } from 'react-intl';
import classnames from 'classnames';
import { import {
Icon, Icon,
DataTable, DataTable,
Money, Money,
If, If,
Choose,
} from 'components'; } from 'components';
import { compose } from 'utils'; import { compose } from 'utils';
import { useUpdateEffect } from 'hooks'; import { useUpdateEffect } from 'hooks';
@@ -60,24 +62,54 @@ function BalanceCell({ cell }) {
); );
} }
function AccountNameAccessor(row) { function InactiveSemafro() {
return row.description ? ( return (
<Tooltip <Tooltip
className={Classes.TOOLTIP_INDICATOR} content={<T id='inactive' />}
content={row.description} className={classnames(
position={Position.RIGHT_TOP} Classes.TOOLTIP_INDICATOR,
hoverOpenDelay={500} 'bp3-popover-wrapper--inactive-semafro'
> )}
{row.name} position={Position.TOP}
hoverOpenDelay={250}>
<div className="inactive-semafro"></div>
</Tooltip> </Tooltip>
) : ( );
row.name }
function AccountNameAccessor(row) {
return (
<>
<Choose>
<Choose.When condition={!!row.description}>
<Tooltip
className={classnames(
Classes.TOOLTIP_INDICATOR,
'bp3-popover-wrapper--account-desc',
)}
content={row.description}
position={Position.RIGHT_TOP}
hoverOpenDelay={500}
>
{row.name}
</Tooltip>
</Choose.When>
<Choose.Otherwise>
{ row.name }
</Choose.Otherwise>
</Choose>
<If condition={!row.active}>
<InactiveSemafro />
</If>
</>
); );
} }
function AccountsDataTable({ function AccountsDataTable({
// #withDashboardActions // #withDashboardActions
accounts, accountsTable,
accountsLoading, accountsLoading,
// #withDialog. // #withDialog.
@@ -256,7 +288,7 @@ function AccountsDataTable({
<DataTable <DataTable
noInitialFetch={true} noInitialFetch={true}
columns={columns} columns={columns}
data={accounts} data={accountsTable}
onFetchData={handleDatatableFetchData} onFetchData={handleDatatableFetchData}
manualSortBy={true} manualSortBy={true}
selectionColumn={selectionColumn} selectionColumn={selectionColumn}
@@ -275,8 +307,8 @@ export default compose(
withDialogActions, withDialogActions,
withDashboardActions, withDashboardActions,
withAccountsActions, withAccountsActions,
withAccounts(({ accountsLoading, accounts }) => ({ withAccounts(({ accountsLoading, accountsTable }) => ({
accountsLoading, accountsLoading,
accounts, accountsTable,
})), })),
)(AccountsDataTable); )(AccountsDataTable);

View File

@@ -1,6 +1,6 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { import {
getAccountsItems, getAccountsItems, getAccountsListFactory,
} from 'store/accounts/accounts.selectors'; } from 'store/accounts/accounts.selectors';
import { import {
getResourceViews, getResourceViews,
@@ -8,10 +8,13 @@ import {
export default (mapState) => { export default (mapState) => {
const getAccountsList = getAccountsListFactory();
const mapStateToProps = (state, props) => { const mapStateToProps = (state, props) => {
const mapped = { const mapped = {
accountsViews: getResourceViews(state, props, 'accounts'), accountsViews: getResourceViews(state, props, 'accounts'),
accounts: getAccountsItems(state, props), accountsTable: getAccountsItems(state, props),
accountsList: getAccountsList(state, props),
accountsTypes: state.accounts.accountsTypes, accountsTypes: state.accounts.accountsTypes,
accountsTableQuery: state.accounts.tableQuery, accountsTableQuery: state.accounts.tableQuery,

View File

@@ -20,9 +20,9 @@ export default compose(
AccountFormDialogConnect, AccountFormDialogConnect,
withAccountsActions, withAccountsActions,
withAccountDetail, withAccountDetail,
withAccounts(({ accountsTypes, accounts }) => ({ withAccounts(({ accountsTypes, accountsList }) => ({
accountsTypes, accountsTypes,
accounts, accounts: accountsList,
})), })),
withDialogActions, withDialogActions,
); );

View File

@@ -26,7 +26,7 @@ import withAccounts from 'containers/Accounts/withAccounts';
function ExpenseFormHeader({ function ExpenseFormHeader({
formik: { errors, touched, setFieldValue, getFieldProps, values }, formik: { errors, touched, setFieldValue, getFieldProps, values },
currenciesList, currenciesList,
accounts, accountsList,
accountsTypes, accountsTypes,
}) { }) {
const [selectedItems, setSelectedItems] = useState({}); const [selectedItems, setSelectedItems] = useState({});
@@ -165,7 +165,7 @@ function ExpenseFormHeader({
} }
> >
<AccountsSelectList <AccountsSelectList
accounts={accounts} accounts={accountsList}
onAccountSelected={onChangeAccount} onAccountSelected={onChangeAccount}
defaultSelectText={<T id={'select_payment_account'} />} defaultSelectText={<T id={'select_payment_account'} />}
selectedAccountId={values.payment_account_id} selectedAccountId={values.payment_account_id}
@@ -252,8 +252,8 @@ function ExpenseFormHeader({
} }
export default compose( export default compose(
withAccounts(({ accounts, accountsTypes }) => ({ withAccounts(({ accountsList, accountsTypes }) => ({
accounts, accountsList,
accountsTypes, accountsTypes,
})), })),
withCurrencies(({ currenciesList }) => ({ withCurrencies(({ currenciesList }) => ({

View File

@@ -87,7 +87,7 @@ const TotalAmountCellRenderer = (chainedComponent, type) => (props) => {
function ExpenseTable({ function ExpenseTable({
// #withAccounts // #withAccounts
accounts, accountsList,
// #ownPorps // #ownPorps
onClickRemoveRow, onClickRemoveRow,
@@ -228,7 +228,7 @@ function ExpenseTable({
rowClassNames={rowClassNames} rowClassNames={rowClassNames}
sticky={true} sticky={true}
payload={{ payload={{
accounts, accounts: accountsList,
errors: errors.categories || [], errors: errors.categories || [],
updateData: handleUpdateData, updateData: handleUpdateData,
removeRow: handleRemoveRow, removeRow: handleRemoveRow,
@@ -256,7 +256,7 @@ function ExpenseTable({
} }
export default compose( export default compose(
withAccounts(({ accounts }) => ({ withAccounts(({ accountsList }) => ({
accounts, accountsList,
})), })),
)(ExpenseTable); )(ExpenseTable);

View File

@@ -25,7 +25,7 @@ function GeneralLedgerHeader({
pageFilter, pageFilter,
// #withAccounts // #withAccounts
accounts, accountsList,
// #withGeneralLedgerActions // #withGeneralLedgerActions
refreshGeneralLedgerSheet, refreshGeneralLedgerSheet,
@@ -83,7 +83,7 @@ function GeneralLedgerHeader({
className={classNames('form-group--select-list', Classes.FILL)} className={classNames('form-group--select-list', Classes.FILL)}
> >
<AccountsMultiSelect <AccountsMultiSelect
accounts={accounts} accounts={accountsList}
onAccountSelected={onAccountSelected} onAccountSelected={onAccountSelected}
/> />
</FormGroup> </FormGroup>
@@ -102,8 +102,8 @@ function GeneralLedgerHeader({
} }
export default compose( export default compose(
withAccounts(({ accounts }) => ({ withAccounts(({ accountsList }) => ({
accounts, accountsList,
})), })),
withGeneralLedger(({ generalLedgerSheetFilter, generalLedgerSheetRefresh }) => ({ withGeneralLedger(({ generalLedgerSheetFilter, generalLedgerSheetRefresh }) => ({
generalLedgerSheetFilter, generalLedgerSheetFilter,

View File

@@ -528,4 +528,5 @@ export default {
'A unique code/number for this account (limited to 10 characters)', 'A unique code/number for this account (limited to 10 characters)',
logic_expression: 'logic expression', logic_expression: 'logic expression',
assign_to_customer: 'Assign to Customer', assign_to_customer: 'Assign to Customer',
inactive: 'Inactive',
}; };

View File

@@ -19,32 +19,29 @@ export const fetchAccountTypes = () => {
}); });
}; };
export const fetchAccountsList = ({ query } = {}) => { export const fetchAccountsList = () => {
return (dispatch) => return (dispatch) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
dispatch({ const query = { column_sort_by: 'name', sort_order: 'asc' };
type: t.SET_DASHBOARD_REQUEST_LOADING,
});
ApiService.get('accounts', { params: query }) ApiService.get('accounts', { params: query })
.then((response) => { .then((response) => {
dispatch({
type: t.ACCOUNTS_PAGE_SET,
accounts: response.data.accounts,
customViewId: response.data.customViewId,
});
dispatch({ dispatch({
type: t.ACCOUNTS_ITEMS_SET, type: t.ACCOUNTS_ITEMS_SET,
accounts: response.data.accounts, accounts: response.data.accounts,
}); });
dispatch({
type: t.ACCOUNTS_LIST_SET,
payload: {
accounts: response.data.accounts,
}
})
dispatch({ dispatch({
type: t.SET_DASHBOARD_REQUEST_COMPLETED, type: t.SET_DASHBOARD_REQUEST_COMPLETED,
}); });
resolve(response); resolve(response);
}) })
.catch((error) => { .catch((error) => {
dispatch({
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
});
reject(error); reject(error);
}); });
}); });

View File

@@ -5,6 +5,7 @@ import { createTableQueryReducers } from 'store/queryReducers';
const initialState = { const initialState = {
items: {}, items: {},
views: {}, views: {},
list: [],
accountsTypes: [], accountsTypes: [],
accountsById: {}, accountsById: {},
tableQuery: { tableQuery: {
@@ -40,6 +41,11 @@ const accountsReducer = createReducer(initialState, {
}; };
}, },
[t.ACCOUNTS_LIST_SET]: (state, action) => {
const { accounts } = action.payload;
state.list = accounts.map(account => account.id);
},
[t.ACCOUNT_TYPES_LIST_SET]: (state, action) => { [t.ACCOUNT_TYPES_LIST_SET]: (state, action) => {
state.accountsTypes = action.account_types; state.accountsTypes = action.account_types;
}, },

View File

@@ -6,6 +6,9 @@ const accountsDataSelector = (state) => state.accounts.items;
const accountsCurrentViewSelector = (state) => state.accounts.currentViewId; const accountsCurrentViewSelector = (state) => state.accounts.currentViewId;
const accountIdPropSelector = (state, props) => props.accountId; const accountIdPropSelector = (state, props) => props.accountId;
const accountsListSelector = state => state.accounts.list;
export const getAccountsItems = createSelector( export const getAccountsItems = createSelector(
accountsViewsSelector, accountsViewsSelector,
accountsDataSelector, accountsDataSelector,
@@ -19,6 +22,14 @@ export const getAccountsItems = createSelector(
}, },
); );
export const getAccountsListFactory = () => createSelector(
accountsListSelector,
accountsDataSelector,
(accounts, accountsItems) => {
return pickItemsFromIds(accountsItems, accounts);
},
)
export const getAccountById = createSelector( export const getAccountById = createSelector(
accountsDataSelector, accountsDataSelector,
accountIdPropSelector, accountIdPropSelector,

View File

@@ -3,6 +3,7 @@
export default { export default {
ACCOUNT_TYPES_LIST_SET: 'ACCOUNT_TYPES_LIST_SET', ACCOUNT_TYPES_LIST_SET: 'ACCOUNT_TYPES_LIST_SET',
ACCOUNTS_PAGE_SET: 'ACCOUNTS_PAGE_SET', ACCOUNTS_PAGE_SET: 'ACCOUNTS_PAGE_SET',
ACCOUNTS_LIST_SET: 'ACCOUNTS_LIST_SET',
ACCOUNTS_ITEMS_SET: 'ACCOUNTS_ITEMS_SET', ACCOUNTS_ITEMS_SET: 'ACCOUNTS_ITEMS_SET',
ACCOUNT_SET: 'ACCOUNT_SET', ACCOUNT_SET: 'ACCOUNT_SET',
ACCOUNT_DELETE: 'ACCOUNT_DELETE', ACCOUNT_DELETE: 'ACCOUNT_DELETE',

View File

@@ -16,10 +16,26 @@
padding-bottom: 0.36rem; padding-bottom: 0.36rem;
} }
.account_name{ .account_name{
> div{
width: 100%;
}
.bp3-popover-wrapper--inactive-semafro{
margin-left: 8px;
margin-right: 6px;
float: right;
border: 0;
}
.bp3-popover-wrapper--account-desc{
border-bottom-color: #BBB;
}
.inactive-semafro{
height: 7px;
width: 7px;
background: #BBB;
display: inline-block;
border-radius: 8px;
.bp3-tooltip-indicator{
cursor: default;
border-bottom-color: #c6c6c6;
} }
} }
.normal{ .normal{