feat: account drawer.

This commit is contained in:
elforjani3
2021-04-27 15:58:37 +02:00
parent 5bb31f783c
commit 5b62410afa
14 changed files with 437 additions and 12 deletions

View File

@@ -13,6 +13,7 @@ import { useAccountsChartContext } from './AccountsChartProvider';
import withAlertsActions from 'containers/Alert/withAlertActions';
import withDialogActions from 'containers/Dialog/withDialogActions';
import withDrawerActions from 'containers/Drawer/withDrawerActions';
/**
* Accounts data-table.
@@ -23,6 +24,9 @@ function AccountsDataTable({
// #withDial
openDialog,
// #withDrawerActions
openDrawer,
}) {
const {
isAccountsLoading,
@@ -59,6 +63,11 @@ function AccountsDataTable({
openDialog('account-form', { action: 'edit', id: account.id });
};
// Handle view detail account.
const handleViewDetailAccount = ({ id, name, code }) => {
openDrawer('account-drawer', { accountId: id, title: `${name} ${code}` });
};
// Handle new child button click.
const handleNewChildAccount = (account) => {
openDialog('account-form', {
@@ -76,40 +85,38 @@ function AccountsDataTable({
selectionColumn={true}
expandable={true}
sticky={true}
loading={isAccountsLoading}
headerLoading={isAccountsLoading}
progressBarLoading={isAccountsFetching}
rowClassNames={rowClassNames}
autoResetExpanded={false}
autoResetSortBy={false}
autoResetSelectedRows={false}
expandColumnSpace={1}
expandToggleColumn={2}
selectionColumnWidth={50}
TableCellRenderer={TableFastCell}
TableRowsRenderer={TableVirtualizedListRows}
TableLoadingRenderer={TableSkeletonRows}
TableHeaderSkeletonRenderer={TableSkeletonHeader}
ContextMenu={ActionsMenu}
// #TableVirtualizedListRows props.
vListrowHeight={42}
vListOverscanRowCount={0}
payload={{
onEdit: handleEditAccount,
onDelete: handleDeleteAccount,
onActivate: handleActivateAccount,
onInactivate: handleInactivateAccount,
onNewChild: handleNewChildAccount
onNewChild: handleNewChildAccount,
onViewDetails: handleViewDetailAccount,
}}
/>
);
}
export default compose(withAlertsActions, withDialogActions)(AccountsDataTable);
export default compose(
withAlertsActions,
withDrawerActions,
withDialogActions,
)(AccountsDataTable);

View File

@@ -27,6 +27,7 @@ export function ActionsMenu({
onNewChild,
onActivate,
onInactivate,
// onDrawer,
},
}) {
return (

View File

@@ -0,0 +1,85 @@
import React from 'react';
import Icon from 'components/Icon';
import { Button, Classes, NavbarGroup, Intent } from '@blueprintjs/core';
import { FormattedMessage as T } from 'react-intl';
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
import withDialogActions from 'containers/Dialog/withDialogActions';
import withAlertsActions from 'containers/Alert/withAlertActions';
import withDrawerActions from 'containers/Drawer/withDrawerActions';
import { safeCallback } from 'utils';
import { compose } from 'utils';
/**
* Account drawer action bar.
*/
function AccountDrawerActionBar({
// #withDialog
openDialog,
// #withAlertsDialog
openAlert,
// #withDrawerActions
closeDrawer,
// #ownProps
account,
}) {
// Handle new child button click.
const onNewChildAccount = () => {
openDialog('account-form', {
action: 'new_child',
parentAccountId: account.id,
accountType: account.account_type,
});
};
// Handle edit account action.
const onEditAccount = () => {
openDialog('account-form', { action: 'edit', id: account.id });
};
// Handle delete action account.
const onDeleteAccount = () => {
if (account) {
openAlert('account-delete', { accountId: account.id });
closeDrawer('account-drawer');
}
};
return (
<DashboardActionsBar>
<NavbarGroup>
<Button
className={Classes.MINIMAL}
icon={<Icon icon="pen-18" />}
text={<T id={'edit_account'} />}
onClick={safeCallback(onEditAccount)}
/>
<Button
className={Classes.MINIMAL}
icon={<Icon icon="plus" />}
text={<T id={'new_child_account'} />}
onClick={safeCallback(onNewChildAccount)}
/>
<Button
className={Classes.MINIMAL}
icon={<Icon style={{ color: 'red' }} icon="trash-18" iconSize={18} />}
text={<T id={'delete'} />}
// intent={Intent.DANGER}
onClick={safeCallback(onDeleteAccount)}
/>
</NavbarGroup>
</DashboardActionsBar>
);
}
export default compose(
withDialogActions,
withAlertsActions,
withDrawerActions,
)(AccountDrawerActionBar);

View File

@@ -0,0 +1,17 @@
import React from 'react';
import { AccountDrawerProvider } from './AccountDrawerProvider';
import AccountDrawerDetails from './AccountDrawerDetails';
/**
* Account drawer content.
*/
export default function AccountDrawerContent({
// #ownProp
accountId,
}) {
return (
<AccountDrawerProvider accountId={accountId}>
<AccountDrawerDetails />
</AccountDrawerProvider>
);
}

View File

@@ -0,0 +1,23 @@
import React from 'react';
import AccountDrawerActionBar from './AccountDrawerActionBar';
import AccountDrawerHeader from './AccountDrawerHeader';
import AccountDrawerTable from './AccountDrawerTable';
import { useAccountDrawerContext } from './AccountDrawerProvider';
import 'style/components/Drawer/AccountDrawer.scss';
/**
* Account view details.
*/
export default function AccountDrawerDetails() {
const { account, accounts } = useAccountDrawerContext();
return (
<div className={'account-drawer'}>
<AccountDrawerActionBar account={account} />
<AccountDrawerHeader account={account} />
<AccountDrawerTable />
</div>
);
}

View File

@@ -0,0 +1,61 @@
import React from 'react';
import { FormattedMessage as T } from 'react-intl';
import { If, Money } from 'components';
/**
* Account drawer header.
*/
export default function AccountDrawerHeader({
account: {
account_normal,
account_type_label,
code,
amount,
currency_code,
description,
},
}) {
return (
<div className={'account-drawer__content'}>
<div>
<span>Closing Balance</span>
<p className={'balance'}>
{<Money amount={amount} currency={currency_code} />}
</p>
</div>
<div>
<span>
<T id={'account_type'} />
</span>
<p>{account_type_label}</p>
</div>
<div>
<span>
<T id={'account_normal'} />
</span>
<p> {account_normal}</p>
</div>
<div>
<span>
<T id={'code'} />
</span>
<p>{code}</p>
</div>
<div>
<span>
<T id={'currency'} />
</span>
<p>{currency_code}</p>
</div>
<p className={'account-drawer__content--desc'}>
<If condition={description}>
<b>
<T id={'description'} />
</b>
: {description}
</If>
</p>
</div>
);
}

View File

@@ -0,0 +1,40 @@
import React from 'react';
import { useAccount, useAccountTransactions } from 'hooks/query';
import DashboardInsider from 'components/Dashboard/DashboardInsider';
const AccountDrawerContext = React.createContext();
/**
* Account drawer provider.
*/
function AccountDrawerProvider({ accountId, ...props }) {
// Fetches the specific account details.
const { data: account, isLoading: isAccountLoading } = useAccount(accountId, {
enabled: !!accountId,
});
// Load the specific account transactions.
const {
data: accounts,
isLoading: isAccountsLoading,
} = useAccountTransactions(accountId, {
enabled: !!accountId,
});
// provider.
const provider = {
accountId,
account,
accounts,
};
return (
<DashboardInsider loading={isAccountLoading || isAccountsLoading}>
<AccountDrawerContext.Provider value={provider} {...props} />
</DashboardInsider>
);
}
const useAccountDrawerContext = () => React.useContext(AccountDrawerContext);
export { AccountDrawerProvider, useAccountDrawerContext };

View File

@@ -0,0 +1,60 @@
import React from 'react';
import moment from 'moment';
import { useAccountDrawerContext } from './AccountDrawerProvider';
import { formatMessage } from 'services/intl';
import { DataTable, Money } from 'components';
import { isBlank } from 'utils';
/**
* account drawer table.
*/
export default function AccountDrawerTable() {
const {
account: { currency_code },
accounts,
} = useAccountDrawerContext();
const columns = React.useMemo(
() => [
{
Header: formatMessage({ id: 'transaction_date' }),
accessor: ({ date }) => moment(date).format('YYYY MMM DD'),
width: 110,
},
{
Header: formatMessage({ id: 'transaction_type' }),
accessor: 'reference_type_formatted',
width: 100,
},
{
Header: formatMessage({ id: 'credit' }),
accessor: ({ credit }) =>
!isBlank(credit) && credit !== 0 ? (
<Money amount={credit} currency={currency_code} />
) : null,
width: 80,
},
{
Header: formatMessage({ id: 'debit' }),
accessor: ({ debit }) =>
!isBlank(debit) && debit !== 0 ? (
<Money amount={debit} currency={currency_code} />
) : null,
width: 80,
},
{
Header: formatMessage({ id: 'running_balance' }),
accessor: 'balance',
width: 110,
},
],
[],
);
return (
<div className={'account-drawer__table'}>
<DataTable columns={columns} data={accounts} />
</div>
);
}

View File

@@ -0,0 +1,35 @@
import React, { lazy } from 'react';
import { Drawer, DrawerSuspense } from 'components';
import withDrawers from 'containers/Drawer/withDrawers';
import withDrawerActions from 'containers/Drawer/withDrawerActions';
import { compose } from 'utils';
const AccountDrawerContent = lazy(() => import('./AccountDrawerContent'));
/**
* Account drawer.
*/
function AccountDrawer({
name,
//#withDrawer
isOpen,
payload: { accountId, title },
closeDrawer,
}) {
// Handle close drawer.
const handleDrawerClose = () => {
closeDrawer(name);
};
return (
<Drawer isOpen={isOpen} title={title} isClose={handleDrawerClose}>
<DrawerSuspense>
<AccountDrawerContent accountId={accountId} />
</DrawerSuspense>
</Drawer>
);
}
export default compose(withDrawers(), withDrawerActions)(AccountDrawer);

View File

@@ -144,3 +144,19 @@ export function useInactivateAccount(props) {
...props,
});
}
/**
* Retrieve account transactions.
*/
export function useAccountTransactions(id, props) {
console.log(id, 'FF');
return useRequestQuery(
[t.ACCOUNT_TRANSACTION, id],
{ method: 'get', url: `accounts/transactions?account_id=${id}` },
{
select: (res) => res.data.transactions,
defaultData: [],
...props,
},
);
}

View File

@@ -1,5 +1,6 @@
const ACCOUNTS = {
ACCOUNT: 'ACCOUNT',
ACCOUNT_TRANSACTION: 'ACCOUNT_TRANSACTION',
ACCOUNTS: 'ACCOUNTS',
ACCOUNTS_TYPES: 'ACCOUNTS_TYPES',
};
@@ -126,5 +127,5 @@ export default {
...ORGANIZATIONS,
...SUBSCRIPTIONS,
...EXPENSES,
...MANUAL_JOURNALS
...MANUAL_JOURNALS,
};

View File

@@ -0,0 +1,79 @@
.account-drawer {
background-color: #fbfbfb;
&__content {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
margin: 18px;
padding: 15px;
background: white;
border: 1px solid #d2dce2;
color: #666666;
> div {
flex-grow: 1;
font-size: 14px;
margin: 15px 0;
.balance,
p {
text-transform: capitalize;
margin: 5px 0;
}
.balance {
font-size: 26px;
font-weight: 500;
color: #000;
margin: 10px 0;
}
}
&--desc {
flex-basis: 100%;
b {
color: #000;
}
}
}
&__table {
margin: 18px;
background: #fff;
border: 1px solid #d2dce2;
.table {
.thead .tr .th .resizer {
display: none;
}
.thead .th,
.tbody .tr .td {
padding: 0.8rem;
font-size: 14px;
font-weight: 400;
color: #666666;
}
}
}
}
.bp3-drawer.bp3-position-right {
bottom: 0;
right: 0;
top: 0;
overflow: auto;
height: 100%;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
.bp3-drawer-header {
margin-bottom: 2px;
box-shadow: (0, 0, 0);
background-color: #6a7993;
.bp3-heading,
.bp3-icon {
color: white;
}
}
}

View File

@@ -209,7 +209,7 @@ export default class AccountsController extends BaseController {
tenantId,
accountId
);
return res.status(200).send({ account });
return res.status(200).send({ account: this.transfromToResponse(account) });
} catch (error) {
next(error);
}

View File

@@ -665,7 +665,7 @@ export default class AccountsService {
key: 'base_currency',
});
return {
...account,
...account.toJSON(),
currencyCode: baseCurrency,
};
}