mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 13:20:31 +00:00
feat: excluded bank transactions
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import {
|
||||
DataTable,
|
||||
TableFastCell,
|
||||
TableSkeletonRows,
|
||||
TableSkeletonHeader,
|
||||
TableVirtualizedListRows,
|
||||
AppToaster,
|
||||
} from '@/components';
|
||||
import { TABLES } from '@/constants/tables';
|
||||
|
||||
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
|
||||
|
||||
import { useMemorizedColumnsWidths } from '@/hooks';
|
||||
import { useExcludedTransactionsColumns } from './_utils';
|
||||
import { useExcludedTransactionsBoot } from './ExcludedTransactionsTableBoot';
|
||||
|
||||
import { compose } from '@/utils';
|
||||
import { ActionsMenu } from './_components';
|
||||
import { useUnexcludeUncategorizedTransaction } from '@/hooks/query/bank-rules';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
|
||||
interface ExcludedTransactionsTableProps {}
|
||||
|
||||
/**
|
||||
* Renders the recognized account transactions datatable.
|
||||
*/
|
||||
function ExcludedTransactionsTableRoot({}: ExcludedTransactionsTableProps) {
|
||||
const { excludedBankTransactions } = useExcludedTransactionsBoot();
|
||||
const { mutateAsync: unexcludeBankTransaction } =
|
||||
useUnexcludeUncategorizedTransaction();
|
||||
|
||||
// Retrieve table columns.
|
||||
const columns = useExcludedTransactionsColumns();
|
||||
|
||||
// Local storage memorizing columns widths.
|
||||
const [initialColumnsWidths, , handleColumnResizing] =
|
||||
useMemorizedColumnsWidths(TABLES.UNCATEGORIZED_ACCOUNT_TRANSACTIONS);
|
||||
|
||||
// Handle cell click.
|
||||
const handleCellClick = (cell, event) => {};
|
||||
|
||||
// Handle restore button click.
|
||||
const handleRestoreClick = (transaction) => {
|
||||
unexcludeBankTransaction(transaction.id)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: 'The excluded bank transaction has been restored.',
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
AppToaster.show({
|
||||
message: 'Something went wrong.',
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<CashflowTransactionsTable
|
||||
noInitialFetch={true}
|
||||
columns={columns}
|
||||
data={excludedBankTransactions}
|
||||
sticky={true}
|
||||
loading={false}
|
||||
headerLoading={false}
|
||||
expandColumnSpace={1}
|
||||
expandToggleColumn={2}
|
||||
selectionColumnWidth={45}
|
||||
TableCellRenderer={TableFastCell}
|
||||
TableLoadingRenderer={TableSkeletonRows}
|
||||
TableRowsRenderer={TableVirtualizedListRows}
|
||||
TableHeaderSkeletonRenderer={TableSkeletonHeader}
|
||||
ContextMenu={ActionsMenu}
|
||||
onCellClick={handleCellClick}
|
||||
// #TableVirtualizedListRows props.
|
||||
vListrowHeight={'small' == 'small' ? 32 : 40}
|
||||
vListrowHeight={40}
|
||||
vListOverscanRowCount={0}
|
||||
initialColumnsWidths={initialColumnsWidths}
|
||||
onColumnResizing={handleColumnResizing}
|
||||
// noResults={<T id={'cash_flow.account_transactions.no_results'} />}
|
||||
className="table-constrant"
|
||||
payload={{
|
||||
onRestore: handleRestoreClick,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const ExcludedTransactionsTable = compose(withDrawerActions)(
|
||||
ExcludedTransactionsTableRoot,
|
||||
);
|
||||
|
||||
const DashboardConstrantTable = styled(DataTable)`
|
||||
.table {
|
||||
.thead {
|
||||
.th {
|
||||
background: #fff;
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.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 {
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,90 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { flatten, map } from 'lodash';
|
||||
import { IntersectionObserver } from '@/components';
|
||||
import { useAccountTransactionsContext } from '../AccountTransactionsProvider';
|
||||
import { useExcludedBankTransactionsInfinity } from '@/hooks/query/bank-rules';
|
||||
|
||||
interface ExcludedBankTransactionsContextValue {
|
||||
isExcludedTransactionsLoading: boolean;
|
||||
isExcludedTransactionsFetching: boolean;
|
||||
excludedBankTransactions: Array<any>;
|
||||
}
|
||||
|
||||
const ExcludedTransactionsContext =
|
||||
React.createContext<ExcludedBankTransactionsContextValue>(
|
||||
{} as ExcludedBankTransactionsContextValue,
|
||||
);
|
||||
|
||||
function flattenInfinityPagesData(data) {
|
||||
return flatten(map(data.pages, (page) => page.data));
|
||||
}
|
||||
|
||||
interface ExcludedBankTransactionsTableBootProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Account uncategorized transctions provider.
|
||||
*/
|
||||
function ExcludedBankTransactionsTableBoot({
|
||||
children,
|
||||
}: ExcludedBankTransactionsTableBootProps) {
|
||||
const { accountId } = useAccountTransactionsContext();
|
||||
|
||||
// Fetches the uncategorized transactions.
|
||||
const {
|
||||
data: recognizedTransactionsPage,
|
||||
isFetching: isExcludedTransactionsFetching,
|
||||
isLoading: isExcludedTransactionsLoading,
|
||||
isSuccess: isRecognizedTransactionsSuccess,
|
||||
isFetchingNextPage: isUncategorizedTransactionFetchNextPage,
|
||||
fetchNextPage: fetchNextrecognizedTransactionsPage,
|
||||
hasNextPage: hasUncategorizedTransactionsNextPage,
|
||||
} = useExcludedBankTransactionsInfinity({
|
||||
page_size: 50,
|
||||
account_id: accountId,
|
||||
});
|
||||
// Memorized the cashflow account transactions.
|
||||
const excludedBankTransactions = React.useMemo(
|
||||
() =>
|
||||
isRecognizedTransactionsSuccess
|
||||
? flattenInfinityPagesData(recognizedTransactionsPage)
|
||||
: [],
|
||||
[recognizedTransactionsPage, isRecognizedTransactionsSuccess],
|
||||
);
|
||||
// Handle the observer ineraction.
|
||||
const handleObserverInteract = React.useCallback(() => {
|
||||
if (
|
||||
!isExcludedTransactionsFetching &&
|
||||
hasUncategorizedTransactionsNextPage
|
||||
) {
|
||||
fetchNextrecognizedTransactionsPage();
|
||||
}
|
||||
}, [
|
||||
isExcludedTransactionsFetching,
|
||||
hasUncategorizedTransactionsNextPage,
|
||||
fetchNextrecognizedTransactionsPage,
|
||||
]);
|
||||
// Provider payload.
|
||||
const provider = {
|
||||
excludedBankTransactions,
|
||||
isExcludedTransactionsFetching,
|
||||
isExcludedTransactionsLoading,
|
||||
};
|
||||
|
||||
return (
|
||||
<ExcludedTransactionsContext.Provider value={provider}>
|
||||
{children}
|
||||
<IntersectionObserver
|
||||
onIntersect={handleObserverInteract}
|
||||
enabled={!isUncategorizedTransactionFetchNextPage}
|
||||
/>
|
||||
</ExcludedTransactionsContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
const useExcludedTransactionsBoot = () =>
|
||||
React.useContext(ExcludedTransactionsContext);
|
||||
|
||||
export { ExcludedBankTransactionsTableBoot, useExcludedTransactionsBoot };
|
||||
@@ -0,0 +1,11 @@
|
||||
// @ts-nocheck
|
||||
import { Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
|
||||
import { safeCallback } from '@/utils';
|
||||
|
||||
export function ActionsMenu({ payload: { onRestore }, row: { original } }) {
|
||||
return (
|
||||
<Menu>
|
||||
<MenuItem text={'Restore'} onClick={safeCallback(onRestore, original)} />
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { getColumnWidth } from '@/utils';
|
||||
import { useExcludedTransactionsBoot } from './ExcludedTransactionsTableBoot';
|
||||
|
||||
const getReportColWidth = (data, accessor, headerText) => {
|
||||
return getColumnWidth(
|
||||
data,
|
||||
accessor,
|
||||
{ magicSpacing: 10, minWidth: 100 },
|
||||
headerText,
|
||||
);
|
||||
};
|
||||
|
||||
const descriptionAccessor = (transaction) => {
|
||||
return <span style={{ color: '#5F6B7C' }}>{transaction.description}</span>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve excluded transactions columns table.
|
||||
*/
|
||||
export function useExcludedTransactionsColumns() {
|
||||
const { excludedBankTransactions: data } = useExcludedTransactionsBoot();
|
||||
|
||||
const withdrawalWidth = getReportColWidth(
|
||||
data,
|
||||
'formatted_withdrawal_amount',
|
||||
'Withdrawal',
|
||||
);
|
||||
const depositWidth = getReportColWidth(
|
||||
data,
|
||||
'formatted_deposit_amount',
|
||||
'Deposit',
|
||||
);
|
||||
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: 'Date',
|
||||
accessor: 'formatted_date',
|
||||
width: 110,
|
||||
},
|
||||
{
|
||||
Header: 'Description',
|
||||
accessor: descriptionAccessor,
|
||||
},
|
||||
{
|
||||
Header: 'Payee',
|
||||
accessor: 'payee',
|
||||
},
|
||||
{
|
||||
Header: 'Deposit',
|
||||
accessor: 'formatted_deposit_amount',
|
||||
align: 'right',
|
||||
width: depositWidth,
|
||||
},
|
||||
{
|
||||
Header: 'Withdrawal',
|
||||
accessor: 'formatted_withdrawal_amount',
|
||||
align: 'right',
|
||||
width: withdrawalWidth,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user