re-structure to monorepo.

This commit is contained in:
a.bouhuolia
2023-02-03 01:02:31 +02:00
parent 8242ec64ba
commit 7a0a13f9d5
10400 changed files with 46966 additions and 17223 deletions

View File

@@ -0,0 +1,135 @@
// @ts-nocheck
import React, { useMemo } from 'react';
import {
Button,
NavbarGroup,
Classes,
NavbarDivider,
Alignment,
} from '@blueprintjs/core';
import {
Icon,
DashboardActionsBar,
DashboardRowsHeightButton,
FormattedMessage as T,
} from '@/components';
import { CashFlowMenuItems } from './utils';
import {
getAddMoneyOutOptions,
getAddMoneyInOptions,
} from '@/constants/cashflowOptions';
import { useRefreshCashflowTransactionsInfinity } from '@/hooks/query';
import { useAccountTransactionsContext } from './AccountTransactionsProvider';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import withSettings from '@/containers/Settings/withSettings';
import withSettingsActions from '@/containers/Settings/withSettingsActions';
import { compose } from '@/utils';
function AccountTransactionsActionsBar({
// #withDialogActions
openDialog,
// #withSettings
cashflowTansactionsTableSize,
// #withSettingsActions
addSetting,
}) {
// Handle table row size change.
const handleTableRowSizeChange = (size) => {
addSetting('cashflowTransactions', 'tableSize', size);
};
const { accountId } = useAccountTransactionsContext();
// Retrieves the money in/out buttons options.
const addMoneyInOptions = useMemo(() => getAddMoneyInOptions(), []);
const addMoneyOutOptions = useMemo(() => getAddMoneyOutOptions(), []);
// Handle money in form
const handleMoneyInFormTransaction = (account) => {
openDialog('money-in', {
account_id: accountId,
account_type: account.value,
account_name: account.name,
});
};
// Handle money out form
const handlMoneyOutFormTransaction = (account) => {
openDialog('money-out', {
account_id: accountId,
account_type: account.value,
account_name: account.name,
});
};
// Refresh cashflow infinity transactions hook.
const { refresh } = useRefreshCashflowTransactionsInfinity();
// Handle the refresh button click.
const handleRefreshBtnClick = () => {
refresh();
};
return (
<DashboardActionsBar>
<NavbarGroup>
<CashFlowMenuItems
items={addMoneyInOptions}
onItemSelect={handleMoneyInFormTransaction}
text={<T id={'cash_flow.label.add_money_in'} />}
buttonProps={{
icon: <Icon icon={'arrow-downward'} iconSize={20} />,
}}
/>
<CashFlowMenuItems
items={addMoneyOutOptions}
onItemSelect={handlMoneyOutFormTransaction}
text={<T id={'cash_flow.label.add_money_out'} />}
buttonProps={{
icon: <Icon icon={'arrow-upward'} iconSize={20} />,
}}
/>
<NavbarDivider />
<Button
className={Classes.MINIMAL}
icon={<Icon icon="print-16" iconSize={16} />}
text={<T id={'print'} />}
/>
<Button
className={Classes.MINIMAL}
icon={<Icon icon="file-export-16" iconSize={16} />}
text={<T id={'export'} />}
/>
<Button
className={Classes.MINIMAL}
icon={<Icon icon="file-import-16" iconSize={16} />}
text={<T id={'import'} />}
/>
<NavbarDivider />
<DashboardRowsHeightButton
initialValue={cashflowTansactionsTableSize}
onChange={handleTableRowSizeChange}
/>
<NavbarDivider />
</NavbarGroup>
<NavbarGroup align={Alignment.RIGHT}>
<Button
className={Classes.MINIMAL}
icon={<Icon icon="refresh-16" iconSize={14} />}
onClick={handleRefreshBtnClick}
/>
</NavbarGroup>
</DashboardActionsBar>
);
}
export default compose(
withDialogActions,
withSettingsActions,
withSettings(({ cashflowTransactionsSettings }) => ({
cashflowTansactionsTableSize: cashflowTransactionsSettings?.tableSize,
})),
)(AccountTransactionsActionsBar);

View File

@@ -0,0 +1,16 @@
// @ts-nocheck
import React from 'react';
const AccountDeleteTransactionAlert = React.lazy(
() => import('@/containers/Alerts/CashFlow/AccountDeleteTransactionAlert'),
);
/**
* Account transaction alert.
*/
export default [
{
name: 'account-transaction-delete',
component: AccountDeleteTransactionAlert,
},
];

View File

@@ -0,0 +1,139 @@
// @ts-nocheck
import React from 'react';
import styled from 'styled-components';
import {
DataTable,
TableFastCell,
TableSkeletonRows,
TableSkeletonHeader,
TableVirtualizedListRows,
FormattedMessage as T,
} from '@/components';
import { TABLES } from '@/constants/tables';
import withSettings from '@/containers/Settings/withSettings';
import withAlertsActions from '@/containers/Alert/withAlertActions';
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
import { useMemorizedColumnsWidths } from '@/hooks';
import { useAccountTransactionsColumns, ActionsMenu } from './components';
import { useAccountTransactionsContext } from './AccountTransactionsProvider';
import { handleCashFlowTransactionType } from './utils';
import { compose } from '@/utils';
/**
* Account transactions data table.
*/
function AccountTransactionsDataTable({
// #withSettings
cashflowTansactionsTableSize,
// #withAlertsActions
openAlert,
// #withDrawerActions
openDrawer,
}) {
// Retrieve table columns.
const columns = useAccountTransactionsColumns();
// Retrieve list context.
const { cashflowTransactions, isCashFlowTransactionsLoading } =
useAccountTransactionsContext();
// Local storage memorizing columns widths.
const [initialColumnsWidths, , handleColumnResizing] =
useMemorizedColumnsWidths(TABLES.CASHFLOW_Transactions);
// handle delete transaction
const handleDeleteTransaction = ({ reference_id }) => {
openAlert('account-transaction-delete', { referenceId: reference_id });
};
const handleViewDetailCashflowTransaction = (referenceType) => {
handleCashFlowTransactionType(referenceType, openDrawer);
};
// Handle cell click.
const handleCellClick = (cell, event) => {
const referenceType = cell.row.original;
handleCashFlowTransactionType(referenceType, openDrawer);
};
return (
<CashflowTransactionsTable
noInitialFetch={true}
columns={columns}
data={cashflowTransactions}
sticky={true}
loading={isCashFlowTransactionsLoading}
headerLoading={isCashFlowTransactionsLoading}
expandColumnSpace={1}
expandToggleColumn={2}
selectionColumnWidth={45}
TableCellRenderer={TableFastCell}
TableLoadingRenderer={TableSkeletonRows}
TableRowsRenderer={TableVirtualizedListRows}
TableHeaderSkeletonRenderer={TableSkeletonHeader}
ContextMenu={ActionsMenu}
onCellClick={handleCellClick}
// #TableVirtualizedListRows props.
vListrowHeight={cashflowTansactionsTableSize == 'small' ? 32 : 40}
vListOverscanRowCount={0}
initialColumnsWidths={initialColumnsWidths}
onColumnResizing={handleColumnResizing}
noResults={<T id={'cash_flow.account_transactions.no_results'} />}
className="table-constrant"
payload={{
onViewDetails: handleViewDetailCashflowTransaction,
onDelete: handleDeleteTransaction,
}}
/>
);
}
export default compose(
withSettings(({ cashflowTransactionsSettings }) => ({
cashflowTansactionsTableSize: cashflowTransactionsSettings?.tableSize,
})),
withAlertsActions,
withDrawerActions,
)(AccountTransactionsDataTable);
const DashboardConstrantTable = styled(DataTable)`
.table {
.thead {
.th {
background: #fff;
}
}
.tbody {
.tr:last-child .td {
border-bottom: 0;
}
}
}
`;
const CashflowTransactionsTable = styled(DashboardConstrantTable)`
.table .tbody {
.tbody-inner .tr.no-results {
.td {
padding: 2rem 0;
font-size: 14px;
color: #888;
font-weight: 400;
border-bottom: 0;
}
}
.tbody-inner {
.tr .td:not(:first-child) {
border-left: 1px solid #e6e6e6;
}
}
}
`;

View File

@@ -0,0 +1,184 @@
// @ts-nocheck
import React from 'react';
import intl from 'react-intl-universal';
import styled from 'styled-components';
import {
Popover,
Menu,
Position,
Button,
MenuItem,
Classes,
} from '@blueprintjs/core';
import { Icon } from '@/components';
import { useHistory } from 'react-router-dom';
import { curry } from 'lodash/fp';
import { useAccountTransactionsContext } from './AccountTransactionsProvider';
function AccountSwitchButton() {
const { currentAccount } = useAccountTransactionsContext();
return (
<AccountSwitchButtonBase
minimal={true}
rightIcon={<Icon icon={'arrow-drop-down'} iconSize={24} />}
>
<AccountSwitchText>{currentAccount.name}</AccountSwitchText>
</AccountSwitchButtonBase>
);
}
function AccountSwitchItem() {
const { push } = useHistory();
const { cashflowAccounts, accountId } = useAccountTransactionsContext();
// Handle item click.
const handleItemClick = curry((account, event) => {
push(`/cashflow-accounts/${account.id}/transactions`);
});
const items = cashflowAccounts.map((account) => (
<AccountSwitchMenuItem
name={account.name}
balance={account.formatted_amount}
onClick={handleItemClick(account)}
active={account.id === accountId}
/>
));
return (
<Popover
content={<Menu>{items}</Menu>}
position={Position.BOTTOM_LEFT}
minimal={true}
>
<AccountSwitchButton />
</Popover>
);
}
function AccountBalanceItem() {
const { currentAccount } = useAccountTransactionsContext();
return (
<AccountBalanceItemWrap>
{intl.get('cash_flow_transaction.balance_in_bigcapital')} {''}
<AccountBalanceAmount>
{currentAccount.formatted_amount}
</AccountBalanceAmount>
</AccountBalanceItemWrap>
);
}
function AccountTransactionsDetailsBarSkeleton() {
return (
<React.Fragment>
<DetailsBarSkeletonBase className={Classes.SKELETON}>
X
</DetailsBarSkeletonBase>
<DetailsBarSkeletonBase className={Classes.SKELETON}>
X
</DetailsBarSkeletonBase>
</React.Fragment>
);
}
function AccountTransactionsDetailsContent() {
return (
<React.Fragment>
<AccountSwitchItem />
<AccountBalanceItem />
</React.Fragment>
);
}
export function AccountTransactionsDetailsBar() {
const { isCurrentAccountLoading } = useAccountTransactionsContext();
return (
<AccountTransactionDetailsWrap>
{isCurrentAccountLoading ? (
<AccountTransactionsDetailsBarSkeleton />
) : (
<AccountTransactionsDetailsContent />
)}
</AccountTransactionDetailsWrap>
);
}
function AccountSwitchMenuItem({
name,
balance,
transactionsNumber,
...restProps
}) {
return (
<MenuItem
label={balance}
text={
<React.Fragment>
<AccountSwitchItemName>{name}</AccountSwitchItemName>
<AccountSwitchItemTranscations>
{intl.get('cash_flow_transaction.switch_item', { value: '25' })}
</AccountSwitchItemTranscations>
<AccountSwitchItemUpdatedAt></AccountSwitchItemUpdatedAt>
</React.Fragment>
}
{...restProps}
/>
);
}
const DetailsBarSkeletonBase = styled.div`
letter-spacing: 10px;
margin-right: 10px;
margin-left: 10px;
font-size: 8px;
width: 140px;
`;
const AccountBalanceItemWrap = styled.div`
margin-left: 18px;
color: #5f6d86;
`;
const AccountTransactionDetailsWrap = styled.div`
display: flex;
background: #fff;
border-bottom: 1px solid #d2dce2;
padding: 0 22px;
height: 42px;
align-items: center;
`;
const AccountSwitchText = styled.div`
font-weight: 600;
font-size: 14px;
`;
const AccountBalanceAmount = styled.span`
font-weight: 600;
display: inline-block;
color: rgb(31, 50, 85);
margin-left: 10px;
`;
const AccountSwitchItemName = styled.div`
font-weight: 600;
`;
const AccountSwitchItemTranscations = styled.div`
font-size: 12px;
opacity: 0.7;
`;
const AccountSwitchItemUpdatedAt = styled.div`
font-size: 12px;
opacity: 0.5;
`;
const AccountSwitchButtonBase = styled(Button)`
.bp3-button-text {
margin-right: 5px;
}
`;

View File

@@ -0,0 +1,43 @@
// @ts-nocheck
import React from 'react';
import styled from 'styled-components';
import '@/style/pages/CashFlow/AccountTransactions/List.scss';
import { DashboardPageContent } from '@/components';
import AccountTransactionsActionsBar from './AccountTransactionsActionsBar';
import AccountTransactionsDataTable from './AccountTransactionsDataTable';
import { AccountTransactionsProvider } from './AccountTransactionsProvider';
import { AccountTransactionsDetailsBar } from './AccountTransactionsDetailsBar';
import { AccountTransactionsProgressBar } from './components';
/**
* Account transactions list.
*/
function AccountTransactionsList() {
return (
<AccountTransactionsProvider>
<AccountTransactionsActionsBar />
<AccountTransactionsDetailsBar />
<AccountTransactionsProgressBar />
<DashboardPageContent>
<CashflowTransactionsTableCard>
<AccountTransactionsDataTable />
</CashflowTransactionsTableCard>
</DashboardPageContent>
</AccountTransactionsProvider>
);
}
export default AccountTransactionsList;
const CashflowTransactionsTableCard = styled.div`
border: 2px solid #f0f0f0;
border-radius: 10px;
padding: 30px 18px;
margin: 30px 15px;
background: #fff;
flex: 0 1;
`;

View File

@@ -0,0 +1,97 @@
// @ts-nocheck
import React from 'react';
import { useParams } from 'react-router-dom';
import { flatten, map } from 'lodash';
import { IntersectionObserver, DashboardInsider } from '@/components';
import {
useAccountTransactionsInfinity,
useCashflowAccounts,
useAccount,
} from '@/hooks/query';
const AccountTransactionsContext = React.createContext();
function flattenInfinityPages(data) {
return flatten(map(data.pages, (page) => page.transactions));
}
/**
* Account transctions provider.
*/
function AccountTransactionsProvider({ query, ...props }) {
const { id } = useParams();
const accountId = parseInt(id, 10);
// Fetch cashflow account transactions list
const {
data: cashflowTransactionsPages,
isFetching: isCashFlowTransactionsFetching,
isLoading: isCashFlowTransactionsLoading,
isSuccess: isCashflowTransactionsSuccess,
fetchNextPage: fetchNextTransactionsPage,
isFetchingNextPage,
hasNextPage,
} = useAccountTransactionsInfinity(accountId, {
page_size: 50,
account_id: accountId,
});
// Memorized the cashflow account transactions.
const cashflowTransactions = React.useMemo(
() =>
isCashflowTransactionsSuccess
? flattenInfinityPages(cashflowTransactionsPages)
: [],
[cashflowTransactionsPages, isCashflowTransactionsSuccess],
);
// Fetch cashflow accounts.
const {
data: cashflowAccounts,
isFetching: isCashFlowAccountsFetching,
isLoading: isCashFlowAccountsLoading,
} = useCashflowAccounts(query, { keepPreviousData: true });
// Retrieve specific account details.
const {
data: currentAccount,
isFetching: isCurrentAccountFetching,
isLoading: isCurrentAccountLoading,
} = useAccount(accountId, { keepPreviousData: true });
// Handle the observer ineraction.
const handleObserverInteract = React.useCallback(() => {
if (!isFetchingNextPage && hasNextPage) {
fetchNextTransactionsPage();
}
}, [isFetchingNextPage, hasNextPage, fetchNextTransactionsPage]);
// Provider payload.
const provider = {
accountId,
cashflowTransactions,
cashflowAccounts,
currentAccount,
isCashFlowTransactionsFetching,
isCashFlowTransactionsLoading,
isCashFlowAccountsFetching,
isCashFlowAccountsLoading,
isCurrentAccountFetching,
isCurrentAccountLoading,
};
return (
<DashboardInsider name={'account-transactions'}>
<AccountTransactionsContext.Provider value={provider} {...props} />
<IntersectionObserver
onIntersect={handleObserverInteract}
enabled={!isFetchingNextPage}
/>
</DashboardInsider>
);
}
const useAccountTransactionsContext = () =>
React.useContext(AccountTransactionsContext);
export { AccountTransactionsProvider, useAccountTransactionsContext };

View File

@@ -0,0 +1,137 @@
// @ts-nocheck
import React from 'react';
import intl from 'react-intl-universal';
import { Intent, Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
import {
Can,
FormatDateCell,
If,
Icon,
MaterialProgressBar,
} from '@/components';
import { useAccountTransactionsContext } from './AccountTransactionsProvider';
import { TRANSACRIONS_TYPE } from '@/constants/cashflowOptions';
import { AbilitySubject, CashflowAction } from '@/constants/abilityOption';
import { safeCallback } from '@/utils';
export function ActionsMenu({
payload: { onDelete, onViewDetails },
row: { original },
}) {
return (
<Menu>
<MenuItem
icon={<Icon icon="reader-18" />}
text={intl.get('view_details')}
onClick={safeCallback(onViewDetails, original)}
/>
<Can I={CashflowAction.Delete} a={AbilitySubject.Cashflow}>
<If condition={TRANSACRIONS_TYPE.includes(original.reference_type)}>
<MenuDivider />
<MenuItem
text={intl.get('delete_transaction')}
intent={Intent.DANGER}
onClick={safeCallback(onDelete, original)}
icon={<Icon icon="trash-16" iconSize={16} />}
/>
</If>
</Can>
</Menu>
);
}
/**
* Retrieve account transctions table columns.
*/
export function useAccountTransactionsColumns() {
return React.useMemo(
() => [
{
id: 'date',
Header: intl.get('date'),
accessor: 'date',
Cell: FormatDateCell,
width: 110,
className: 'date',
clickable: true,
textOverview: true,
},
{
id: 'type',
Header: intl.get('type'),
accessor: 'formatted_transaction_type',
className: 'type',
width: 140,
textOverview: true,
clickable: true,
},
{
id: 'transaction_number',
Header: intl.get('transaction_number'),
accessor: 'transaction_number',
width: 160,
className: 'transaction_number',
clickable: true,
textOverview: true,
},
{
id: 'reference_number',
Header: intl.get('reference_no'),
accessor: 'reference_number',
width: 160,
className: 'reference_number',
clickable: true,
textOverview: true,
},
{
id: 'deposit',
Header: intl.get('cash_flow.label.deposit'),
accessor: 'formatted_deposit',
width: 110,
className: 'deposit',
textOverview: true,
align: 'right',
clickable: true,
},
{
id: 'withdrawal',
Header: intl.get('cash_flow.label.withdrawal'),
accessor: 'formatted_withdrawal',
className: 'withdrawal',
width: 150,
textOverview: true,
align: 'right',
clickable: true,
},
{
id: 'running_balance',
Header: intl.get('cash_flow.label.running_balance'),
accessor: 'formatted_running_balance',
className: 'running_balance',
width: 150,
textOverview: true,
align: 'right',
clickable: true,
},
{
id: 'balance',
Header: intl.get('balance'),
accessor: 'formatted_balance',
className: 'balance',
width: 150,
textOverview: true,
clickable: true,
align: 'right',
},
],
[],
);
}
/**
* Account transactions progress bar.
*/
export function AccountTransactionsProgressBar() {
const { isCashFlowTransactionsFetching } = useAccountTransactionsContext();
return isCashFlowTransactionsFetching ? <MaterialProgressBar /> : null;
}

View File

@@ -0,0 +1,93 @@
// @ts-nocheck
import React from 'react';
import {
Button,
PopoverInteractionKind,
MenuItem,
Position,
} from '@blueprintjs/core';
import { Select } from '@blueprintjs/select';
import { Icon } from '@/components';
export const CashFlowMenuItems = ({
text,
items,
onItemSelect,
buttonProps,
}) => {
// Menu items renderer.
const itemsRenderer = (item, { handleClick, modifiers, query }) => (
<MenuItem text={item.name} label={item.label} onClick={handleClick} />
);
const handleCashFlowMenuSelect = (type) => {
onItemSelect && onItemSelect(type);
};
return (
<Select
items={items}
itemRenderer={itemsRenderer}
onItemSelect={handleCashFlowMenuSelect}
popoverProps={{
minimal: true,
position: Position.BOTTOM_LEFT,
interactionKind: PopoverInteractionKind.CLICK,
modifiers: {
offset: { offset: '0, 4' },
},
}}
filterable={false}
>
<Button
text={text}
icon={<Icon icon={'plus-24'} iconSize={20} />}
minimal={true}
{...buttonProps}
/>
</Select>
);
};
export const handleCashFlowTransactionType = (reference, openDrawer) => {
switch (reference.reference_type) {
case 'SaleReceipt':
return openDrawer('receipt-detail-drawer', {
receiptId: reference.reference_id,
});
case 'Journal':
return openDrawer('journal-drawer', {
manualJournalId: reference.reference_id,
});
case 'Expense':
return openDrawer('expense-drawer', {
expenseId: reference.reference_id,
});
case 'PaymentReceive':
return openDrawer('payment-receive-detail-drawer', {
paymentReceiveId: reference.reference_id,
});
case 'BillPayment':
return openDrawer('payment-made-detail-drawer', {
paymentMadeId: reference.reference_id,
});
case 'RefundCreditNote':
return openDrawer('refund-credit-detail-drawer', {
refundTransactionId: reference.reference_id,
});
case 'RefundVendorCredit':
return openDrawer('refund-vendor-detail-drawer', {
refundTransactionId: reference.reference_id,
});
case 'InventoryAdjustment':
return openDrawer('inventory-adjustment-drawer', {
inventoryId: reference.reference_id,
});
default:
return openDrawer('cashflow-transaction-drawer', {
referenceId: reference.reference_id,
});
}
};

View File

@@ -0,0 +1,16 @@
// @ts-nocheck
import { connect } from 'react-redux';
import { getCashflowAccountsTableStateFactory } from '@/store/CashflowAccounts/CashflowAccounts.selectors';
export default (mapState) => {
const getCashflowAccountsTableState = getCashflowAccountsTableStateFactory();
const mapStateToProps = (state, props) => {
const mapped = {
cashflowAccountsTableState: getCashflowAccountsTableState(state, props),
};
return mapState ? mapState(mapped, state, props) : mapped;
};
return connect(mapStateToProps);
};

View File

@@ -0,0 +1,16 @@
// @ts-nocheck
import { connect } from 'react-redux';
import {
setCashflowAccountsTableState,
resetCashflowAccountsTableState,
} from '@/store/CashflowAccounts/CashflowAccounts.actions';
const mapActionsToProps = (dispatch) => ({
setCashflowAccountsTableState: (queries) =>
dispatch(setCashflowAccountsTableState(queries)),
resetCashflowAccountsTableState: () =>
dispatch(resetCashflowAccountsTableState()),
});
export default connect(null, mapActionsToProps);

View File

@@ -0,0 +1,122 @@
// @ts-nocheck
import React from 'react';
import {
Button,
NavbarGroup,
Classes,
NavbarDivider,
Alignment,
Switch,
} from '@blueprintjs/core';
import {
DashboardActionsBar,
Can,
Icon,
FormattedMessage as T,
} from '@/components';
import { useRefreshCashflowAccounts } from '@/hooks/query';
import { CashflowAction, AbilitySubject } from '@/constants/abilityOption';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import withCashflowAccountsTableActions from '../AccountTransactions/withCashflowAccountsTableActions';
import { AccountDialogAction } from '@/containers/Dialogs/AccountDialog/utils';
import { ACCOUNT_TYPE } from '@/constants';
import { DialogsName } from '@/constants/dialogs';
import { compose } from '@/utils';
/**
* Cash Flow accounts actions bar.
*/
function CashFlowAccountsActionsBar({
// #withDialogActions
openDialog,
// #withCashflowAccountsTableActions
setCashflowAccountsTableState,
}) {
const { refresh } = useRefreshCashflowAccounts();
// Handle refresh button click.
const handleRefreshBtnClick = () => {
refresh();
};
// Handle add bank account.
const handleAddBankAccount = () => {
openDialog(DialogsName.AccountForm, {
action: AccountDialogAction.NewDefinedType,
accountType: ACCOUNT_TYPE.CASH,
});
};
// Handle add cash account.
const handleAddCashAccount = () => {
openDialog(DialogsName.AccountForm, {
action: AccountDialogAction.NewDefinedType,
accountType: ACCOUNT_TYPE.BANK,
});
};
// Handle inactive switch changing.
const handleInactiveSwitchChange = (event) => {
const checked = event.target.checked;
setCashflowAccountsTableState({ inactiveMode: checked });
};
return (
<DashboardActionsBar>
<NavbarGroup>
<Can I={CashflowAction.Create} a={AbilitySubject.Cashflow}>
<Button
className={Classes.MINIMAL}
icon={<Icon icon={'plus-24'} iconSize={20} />}
text={<T id={'cash_flow.label.add_cash_account'} />}
onClick={handleAddBankAccount}
/>
<Button
className={Classes.MINIMAL}
icon={<Icon icon={'plus-24'} iconSize={20} />}
text={<T id={'cash_flow.label.add_bank_account'} />}
onClick={handleAddCashAccount}
/>
<NavbarDivider />
</Can>
<Button
className={Classes.MINIMAL}
icon={<Icon icon="print-16" iconSize={16} />}
text={<T id={'print'} />}
/>
<Button
className={Classes.MINIMAL}
icon={<Icon icon="file-export-16" iconSize={16} />}
text={<T id={'export'} />}
/>
<Button
className={Classes.MINIMAL}
icon={<Icon icon="file-import-16" iconSize={16} />}
text={<T id={'import'} />}
/>
<NavbarDivider />
<Can I={CashflowAction.Edit} a={AbilitySubject.Cashflow}>
<Switch
labelElement={<T id={'inactive'} />}
defaultChecked={false}
onChange={handleInactiveSwitchChange}
/>
</Can>
</NavbarGroup>
<NavbarGroup align={Alignment.RIGHT}>
<Button
className={Classes.MINIMAL}
icon={<Icon icon="refresh-16" iconSize={14} />}
onClick={handleRefreshBtnClick}
/>
</NavbarGroup>
</DashboardActionsBar>
);
}
export default compose(
withDialogActions,
withCashflowAccountsTableActions,
)(CashFlowAccountsActionsBar);

View File

@@ -0,0 +1,67 @@
// @ts-nocheck
import React from 'react';
import { TABLES } from '@/constants/tables';
import {
DataTable,
TableFastCell,
TableSkeletonRows,
TableSkeletonHeader,
} from '@/components';
import withSettings from '@/containers/Settings/withSettings';
import { useMemorizedColumnsWidths } from '@/hooks';
import { useCashFlowAccountsContext } from './CashFlowAccountsProvider';
import { useCashFlowAccountsTableColumns } from './components';
import { compose } from '@/utils';
/**
* Cash flow accounts data table.
*/
function CashFlowAccountsDataTable({
// #withSettings
cashflowTableSize,
}) {
// Retrieve list context.
const {
cashflowAccounts,
isCashFlowAccountsFetching,
isCashFlowAccountsLoading,
} = useCashFlowAccountsContext();
// Retrieve table columns.
const columns = useCashFlowAccountsTableColumns();
// Local storage memorizing columns widths.
const [initialColumnsWidths, , handleColumnResizing] =
useMemorizedColumnsWidths(TABLES.CASHFLOW_ACCOUNTS);
return (
<DataTable
noInitialFetch={true}
columns={columns}
data={cashflowAccounts}
selectionColumn={false}
sticky={true}
loading={isCashFlowAccountsLoading}
headerLoading={isCashFlowAccountsLoading}
progressBarLoading={isCashFlowAccountsFetching}
expandColumnSpace={1}
expandToggleColumn={2}
selectionColumnWidth={45}
TableCellRenderer={TableFastCell}
TableLoadingRenderer={TableSkeletonRows}
TableHeaderSkeletonRenderer={TableSkeletonHeader}
initialColumnsWidths={initialColumnsWidths}
onColumnResizing={handleColumnResizing}
size={cashflowTableSize}
/>
);
}
export default compose(
withSettings(({ cashflowSettings }) => ({
cashflowTableSize: cashflowSettings?.tableSize,
})),
)(CashFlowAccountsDataTable);

View File

@@ -0,0 +1,50 @@
// @ts-nocheck
import React, { useEffect } from 'react';
import { compose } from 'lodash/fp';
import '@/style/pages/CashFlow/CashFlowAccounts/List.scss';
import { DashboardPageContent } from '@/components';
import { CashFlowAccountsProvider } from './CashFlowAccountsProvider';
import CashflowAccountsGrid from './CashflowAccountsGrid';
import CashFlowAccountsActionsBar from './CashFlowAccountsActionsBar';
import withCashflowAccounts from '@/containers/CashFlow/AccountTransactions/withCashflowAccounts';
import withCashflowAccountsTableActions from '@/containers/CashFlow/AccountTransactions/withCashflowAccountsTableActions';
/**
* Cashflow accounts list.
*/
function CashFlowAccountsList({
// #withCashflowAccounts
cashflowAccountsTableState,
// #withCashflowAccountsTableActions
resetCashflowAccountsTableState,
}) {
// Resets the cashflow accounts table state.
useEffect(
() => () => {
resetCashflowAccountsTableState();
},
[resetCashflowAccountsTableState],
);
return (
<CashFlowAccountsProvider tableState={cashflowAccountsTableState}>
<CashFlowAccountsActionsBar />
<DashboardPageContent>
<CashflowAccountsGrid />
</DashboardPageContent>
</CashFlowAccountsProvider>
);
}
export default compose(
withCashflowAccounts(({ cashflowAccountsTableState }) => ({
cashflowAccountsTableState,
})),
withCashflowAccountsTableActions,
)(CashFlowAccountsList);

View File

@@ -0,0 +1,40 @@
// @ts-nocheck
import React from 'react';
import { DashboardInsider } from '@/components/Dashboard';
import { useCashflowAccounts } from '@/hooks/query';
import { transformAccountsStateToQuery } from './utils';
const CashFlowAccountsContext = React.createContext();
/**
* Cash Flow data provider.
*/
function CashFlowAccountsProvider({ tableState, ...props }) {
const query = transformAccountsStateToQuery(tableState);
// Fetch cash flow list .
const {
data: cashflowAccounts,
isFetching: isCashFlowAccountsFetching,
isLoading: isCashFlowAccountsLoading,
} = useCashflowAccounts(query, { keepPreviousData: true });
// Provider payload.
const provider = {
cashflowAccounts,
isCashFlowAccountsFetching,
isCashFlowAccountsLoading,
};
return (
<DashboardInsider name={'cashflow-accounts'}>
<CashFlowAccountsContext.Provider value={provider} {...props} />
</DashboardInsider>
);
}
const useCashFlowAccountsContext = () =>
React.useContext(CashFlowAccountsContext);
export { CashFlowAccountsProvider, useCashFlowAccountsContext };

View File

@@ -0,0 +1,319 @@
// @ts-nocheck
import React, { useMemo } from 'react';
import intl from 'react-intl-universal';
import styled from 'styled-components';
import { isNull, isEmpty } from 'lodash';
import { compose, curry } from 'lodash/fp';
import { Link } from 'react-router-dom';
import { ContextMenu2 } from '@blueprintjs/popover2';
import { Menu, MenuItem, MenuDivider, Intent } from '@blueprintjs/core';
import {
AccountAction,
CashflowAction,
AbilitySubject,
} from '@/constants/abilityOption';
import { DialogsName } from '@/constants/dialogs';
import {
getAddMoneyInOptions,
getAddMoneyOutOptions,
} from '@/constants/cashflowOptions';
import { BankAccountsList, BankAccount, If, Icon, T, Can } from '@/components';
import { useCashFlowAccountsContext } from './CashFlowAccountsProvider';
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
import withAlertsActions from '@/containers/Alert/withAlertActions';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import { AccountDialogAction } from '@/containers/Dialogs/AccountDialog/utils';
import { safeCallback } from '@/utils';
const CASHFLOW_SKELETON_N = 4;
/**
* Cashflow accounts skeleton for loading state.
*/
function CashflowAccountsSkeleton() {
return [...Array(CASHFLOW_SKELETON_N)].map((e, i) => (
<BankAccount
title={'XXXXX'}
code={'XXXXX'}
balance={'XXXXXX'}
cash={'cash'}
loading={true}
/>
));
}
/**
* Cashflow bank account.
*/
function CashflowBankAccount({
// #withAlertsDialog
openAlert,
// #withDial
openDialog,
// #withDrawerActions
openDrawer,
account,
}) {
// Handle view detail account.
const handleViewClick = () => {
openDrawer('account-drawer', { accountId: account.id });
};
// Handle delete action account.
const handleDeleteClick = () => {
openAlert('account-delete', { accountId: account.id });
};
// Handle inactivate action account.
const handleInactivateClick = () => {
openAlert('account-inactivate', { accountId: account.id });
};
// Handle activate action account.
const handleActivateClick = () => {
openAlert('account-activate', { accountId: account.id });
};
// Handle edit account action.
const handleEditAccount = () => {
openDialog(DialogsName.AccountForm, {
action: AccountDialogAction.Edit,
id: account.id,
});
};
// Handle money in menu item actions.
const handleMoneyInClick = (transactionType) => {
openDialog('money-in', {
account_type: transactionType,
account_id: account.id,
});
};
// Handle money out menu item actions.
const handleMoneyOutClick = (transactionType) => {
openDialog('money-out', {
account_type: transactionType,
account_id: account.id,
});
};
return (
<ContextMenu2
content={
<CashflowAccountContextMenu
account={account}
onViewClick={handleViewClick}
onDeleteClick={handleDeleteClick}
onActivateClick={handleActivateClick}
onInactivateClick={handleInactivateClick}
onEditClick={handleEditAccount}
onMoneyInClick={handleMoneyInClick}
onMoneyOutClick={handleMoneyOutClick}
/>
}
>
<CashflowAccountAnchor
to={`/cashflow-accounts/${account.id}/transactions`}
>
<BankAccount
title={account.name}
code={account.code}
balance={!isNull(account.amount) ? account.formatted_amount : '-'}
type={account.account_type}
updatedBeforeText={getUpdatedBeforeText(account.createdAt)}
/>
</CashflowAccountAnchor>
</ContextMenu2>
);
}
const CashflowBankAccountEnhanced = compose(
withAlertsActions,
withDrawerActions,
withDialogActions,
)(CashflowBankAccount);
function getUpdatedBeforeText(createdAt) {
return '';
}
/**
* Cashflow accounts grid items.
*/
function CashflowAccountsGridItems({ accounts }) {
return accounts.map((account) => (
<CashflowBankAccountEnhanced account={account} />
));
}
/**
* Cashflow accounts empty state.
*/
function CashflowAccountsEmptyState() {
return (
<AccountsEmptyStateBase>
<AccountsEmptyStateTitle>
<T id={'cash_flow.accounts.no_results'} />
</AccountsEmptyStateTitle>
</AccountsEmptyStateBase>
);
}
/**
* Cashflow accounts grid.
*/
export default function CashflowAccountsGrid() {
// Retrieve list context.
const { cashflowAccounts, isCashFlowAccountsLoading } =
useCashFlowAccountsContext();
return (
<CashflowAccountsGridWrap>
<BankAccountsList>
{isCashFlowAccountsLoading ? (
<CashflowAccountsSkeleton />
) : isEmpty(cashflowAccounts) ? (
<CashflowAccountsEmptyState />
) : (
<CashflowAccountsGridItems accounts={cashflowAccounts} />
)}
</BankAccountsList>
</CashflowAccountsGridWrap>
);
}
/**
* Cashflow account money out context menu.
*/
function CashflowAccountMoneyInContextMenu({ onClick }) {
const handleItemClick = curry((transactionType, event) => {
onClick && onClick(transactionType, event);
});
// Retreives the add money in button options.
const addMoneyInOptions = useMemo(() => getAddMoneyInOptions(), []);
return addMoneyInOptions.map((option) => (
<MenuItem text={option.name} onClick={handleItemClick(option.value)} />
));
}
/**
* Cashflow account money in context menu.
*/
function CashflowAccountMoneyOutContextMenu({ onClick }) {
const handleItemClick = curry((transactionType, event) => {
onClick && onClick(transactionType, event);
});
// Retreives the add money out button options.
const addMoneyOutOptions = useMemo(() => getAddMoneyOutOptions(), []);
return addMoneyOutOptions.map((option) => (
<MenuItem text={option.name} onClick={handleItemClick(option.value)} />
));
}
/**
* Cashflow account context menu.
*/
function CashflowAccountContextMenu({
account,
onViewClick,
onEditClick,
onInactivateClick,
onActivateClick,
onDeleteClick,
onMoneyInClick,
onMoneyOutClick,
}) {
return (
<Menu>
<MenuItem
icon={<Icon icon="reader-18" />}
text={intl.get('view_details')}
onClick={safeCallback(onViewClick)}
/>
<Can I={CashflowAction.Create} a={AbilitySubject.Cashflow}>
<MenuDivider />
<MenuItem
text={<T id={'cash_flow_money_in'} />}
icon={<Icon icon={'arrow-downward'} iconSize={16} />}
>
<CashflowAccountMoneyInContextMenu onClick={onMoneyInClick} />
</MenuItem>
<MenuItem
text={<T id={'cash_flow_money_out'} />}
icon={<Icon icon={'arrow-upward'} iconSize={16} />}
>
<CashflowAccountMoneyOutContextMenu onClick={onMoneyOutClick} />
</MenuItem>
</Can>
<Can I={CashflowAction.Edit} a={AbilitySubject.Cashflow}>
<MenuDivider />
<MenuItem
icon={<Icon icon="pen-18" />}
text={intl.get('edit_account')}
onClick={safeCallback(onEditClick)}
/>
</Can>
<Can I={AccountAction.Edit} a={AbilitySubject.Account}>
<MenuDivider />
<If condition={account.active}>
<MenuItem
text={intl.get('inactivate_account')}
icon={<Icon icon="pause-16" iconSize={16} />}
onClick={safeCallback(onInactivateClick)}
/>
</If>
<If condition={!account.active}>
<MenuItem
text={intl.get('activate_account')}
icon={<Icon icon="play-16" iconSize={16} />}
onClick={safeCallback(onActivateClick)}
/>
</If>
</Can>
<Can I={CashflowAction.Delete} a={AbilitySubject.Cashflow}>
<MenuDivider />
<MenuItem
text={intl.get('delete_account')}
icon={<Icon icon="trash-16" iconSize={16} />}
intent={Intent.DANGER}
onClick={safeCallback(onDeleteClick)}
/>
</Can>
</Menu>
);
}
const CashflowAccountAnchor = styled(Link)`
&,
&:hover,
&:focus,
&:active {
color: inherit;
text-decoration: none;
}
`;
const CashflowAccountsGridWrap = styled.div`
margin: 30px;
`;
const CashflowBankAccountWrap = styled.div``;
const AccountsEmptyStateBase = styled.div`
flex: 1;
text-align: center;
margin: 2rem 0;
`;
const AccountsEmptyStateTitle = styled.h1`
font-size: 16px;
color: #626b76;
opacity: 0.8;
line-height: 1.6;
font-weight: 500;
`;

View File

@@ -0,0 +1,93 @@
// @ts-nocheck
import React from 'react';
import intl from 'react-intl-universal';
import { Intent, Tag } from '@blueprintjs/core';
import { isBlank } from '@/utils';
import { Link } from 'react-router-dom';
/**
* Account code accessor.
*/
export const AccountCodeAccessor = (row) =>
!isBlank(row.code) ? (
<Tag minimal={true} round={true} intent={Intent.NONE}>
{row.code}
</Tag>
) : null;
/**
* Balance cell.
*/
export const BalanceCell = ({ cell }) => {
const account = cell.row.original;
return account.amount !== null ? (
<span>{account.formatted_amount}</span>
) : (
<span class="placeholder"></span>
);
};
/**
* Account cell.
*/
const AccountCell = ({ row }) => {
const account = row.original;
return (
<>
<div>X</div>
<Link to={`/account/${account.id}/transactions`}>{account.name}</Link>
</>
);
};
/**
* Retrieve Cash flow table columns.
*/
export function useCashFlowAccountsTableColumns() {
return React.useMemo(
() => [
{
id: 'name',
Header: intl.get('account_name'),
accessor: 'name',
Cell: AccountCell,
className: 'account_name',
width: 200,
textOverview: true,
},
{
id: 'code',
Header: intl.get('code'),
accessor: 'code',
className: 'code',
width: 80,
},
{
id: 'type',
Header: intl.get('type'),
accessor: 'account_type_label',
className: 'type',
width: 140,
textOverview: true,
},
{
id: 'currency',
Header: intl.get('currency'),
accessor: 'currency_code',
width: 75,
},
{
id: 'balance',
Header: intl.get('balance'),
accessor: 'amount',
className: 'balance',
Cell: BalanceCell,
width: 150,
align: 'right',
},
],
[],
);
}

View File

@@ -0,0 +1,12 @@
// @ts-nocheck
import { transformTableStateToQuery } from '@/utils';
/**
* Transformes the table state to list query.
*/
export const transformAccountsStateToQuery = (tableState) => {
return {
...transformTableStateToQuery(tableState),
inactive_mode: tableState.inactiveMode,
};
};