diff --git a/packages/server/src/api/controllers/Banking/BankAccountsController.ts b/packages/server/src/api/controllers/Banking/BankAccountsController.ts
index 7f134fd75..e02aad096 100644
--- a/packages/server/src/api/controllers/Banking/BankAccountsController.ts
+++ b/packages/server/src/api/controllers/Banking/BankAccountsController.ts
@@ -29,7 +29,7 @@ export class BankAccountsController extends BaseController {
[
query('account_id').optional().isNumeric().toInt(),
query('page').optional().isNumeric().toInt(),
- query('page_size').optional().isNumeric().toInt(),
+ query('page_size').optional().isNumeric().toInt(),
],
this.validationResult,
this.getBankAccountsPendingTransactions.bind(this)
@@ -94,11 +94,13 @@ export class BankAccountsController extends BaseController {
next: NextFunction
) {
const { tenantId } = req;
+ const query = this.matchedQueryData(req);
try {
const data =
await this.getPendingTransactionsService.getPendingTransactions(
- tenantId
+ tenantId,
+ query
);
return res.status(200).send(data);
} catch (error) {
diff --git a/packages/server/src/services/Cashflow/GetPendingBankAccountTransaction.ts b/packages/server/src/services/Cashflow/GetPendingBankAccountTransaction.ts
index 34bd5ad31..11ad32576 100644
--- a/packages/server/src/services/Cashflow/GetPendingBankAccountTransaction.ts
+++ b/packages/server/src/services/Cashflow/GetPendingBankAccountTransaction.ts
@@ -37,7 +37,7 @@ export class GetPendingBankAccountTransactions {
})
.pagination(_filter.page - 1, _filter.pageSize);
- const data = this.transformer.transform(
+ const data = await this.transformer.transform(
tenantId,
results,
new GetPendingBankAccountTransactionTransformer()
diff --git a/packages/webapp/src/constants/query-keys/banking.ts b/packages/webapp/src/constants/query-keys/banking.ts
index 1a69341b0..26ec3135b 100644
--- a/packages/webapp/src/constants/query-keys/banking.ts
+++ b/packages/webapp/src/constants/query-keys/banking.ts
@@ -7,5 +7,7 @@ export const BANK_QUERY_KEY = {
'RECOGNIZED_BANK_TRANSACTIONS_INFINITY',
BANK_ACCOUNT_SUMMARY_META: 'BANK_ACCOUNT_SUMMARY_META',
AUTOFILL_CATEGORIZE_BANK_TRANSACTION: 'AUTOFILL_CATEGORIZE_BANK_TRANSACTION',
- PENDING_BANK_ACCOUNT_TRANSACTIONS: 'PENDING_BANK_ACCOUNT_TRANSACTIONS'
+ PENDING_BANK_ACCOUNT_TRANSACTIONS: 'PENDING_BANK_ACCOUNT_TRANSACTIONS',
+ PENDING_BANK_ACCOUNT_TRANSACTIONS_INFINITY:
+ 'PENDING_BANK_ACCOUNT_TRANSACTIONS_INFINITY',
};
diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/AllTransactionsUncategorized.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/AllTransactionsUncategorized.tsx
index 54996f1a3..5e77c38f9 100644
--- a/packages/webapp/src/containers/CashFlow/AccountTransactions/AllTransactionsUncategorized.tsx
+++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/AllTransactionsUncategorized.tsx
@@ -54,6 +54,12 @@ const AccountUncategorizedTransactions = lazy(() =>
).then((module) => ({ default: module.AccountUncategorizedTransactionsAll })),
);
+const PendingTransactions = lazy(() =>
+ import('./PendingTransactions/PendingTransactions').then((module) => ({
+ default: module.PendingTransactions,
+ })),
+);
+
/**
* Switches between the account transactions tables.
* @returns {React.ReactNode}
@@ -71,7 +77,7 @@ function AccountTransactionsSwitcher() {
default:
return ;
case 'pending':
- return null;
+ return ;
}
}
diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/PendingTransactions/PendingTransactions.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/PendingTransactions/PendingTransactions.tsx
new file mode 100644
index 000000000..34e813cc2
--- /dev/null
+++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/PendingTransactions/PendingTransactions.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import { AccountTransactionsCard } from '../UncategorizedTransactions/AccountTransactionsCard';
+import { PendingTransactionsBoot } from './PendingTransactionsTableBoot';
+import { PendingTransactionsDataTable } from './PendingTransactionsTable';
+
+export function PendingTransactions() {
+ return (
+
+
+
+
+
+ );
+}
diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/PendingTransactions/PendingTransactionsTable.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/PendingTransactions/PendingTransactionsTable.tsx
index e69de29bb..ae3ecf944 100644
--- a/packages/webapp/src/containers/CashFlow/AccountTransactions/PendingTransactions/PendingTransactionsTable.tsx
+++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/PendingTransactions/PendingTransactionsTable.tsx
@@ -0,0 +1,107 @@
+// @ts-nocheck
+import React from 'react';
+import clsx from 'classnames';
+import styled from 'styled-components';
+import {
+ DataTable,
+ TableFastCell,
+ TableSkeletonRows,
+ TableSkeletonHeader,
+ TableVirtualizedListRows,
+} from '@/components';
+import withSettings from '@/containers/Settings/withSettings';
+import { withBankingActions } from '../../withBankingActions';
+
+import { useAccountTransactionsContext } from '../AccountTransactionsProvider';
+import { usePendingTransactionsContext } from './PendingTransactionsTableBoot';
+import { usePendingTransactionsTableColumns } from './_hooks';
+
+import { compose } from '@/utils';
+
+/**
+ * Account transactions data table.
+ */
+function PendingTransactionsDataTableRoot({
+ // #withSettings
+ cashflowTansactionsTableSize,
+}) {
+ // Retrieve table columns.
+ const columns = usePendingTransactionsTableColumns();
+ const { scrollableRef } = useAccountTransactionsContext();
+
+ // Retrieve list context.
+ const { pendingTransactions, isPendingTransactionsLoading } =
+ usePendingTransactionsContext();
+
+ return (
+
+ );
+}
+
+export const PendingTransactionsDataTable = compose(
+ withSettings(({ cashflowTransactionsSettings }) => ({
+ cashflowTansactionsTableSize: cashflowTransactionsSettings?.tableSize,
+ })),
+ withBankingActions,
+)(PendingTransactionsDataTableRoot);
+
+const DashboardConstrantTable = styled(DataTable)`
+ .table {
+ .thead {
+ .th {
+ background: #fff;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ font-size: 13px;i
+ font-weight: 500;
+ }
+ }
+
+ .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;
+ }
+
+ .td-description {
+ color: #5f6b7c;
+ }
+ }
+ }
+`;
diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/PendingTransactions/PendingTransactionsTableBoot.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/PendingTransactions/PendingTransactionsTableBoot.tsx
index e69de29bb..9cd5eba44 100644
--- a/packages/webapp/src/containers/CashFlow/AccountTransactions/PendingTransactions/PendingTransactionsTableBoot.tsx
+++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/PendingTransactions/PendingTransactionsTableBoot.tsx
@@ -0,0 +1,72 @@
+// @ts-nocheck
+import React from 'react';
+import { flatten, map } from 'lodash';
+import { IntersectionObserver } from '@/components';
+import { useAccountTransactionsContext } from '../AccountTransactionsProvider';
+import { usePendingBankTransactionsInfinity } from '@/hooks/query/bank-rules';
+
+const PendingTransactionsContext = React.createContext();
+
+function flattenInfinityPagesData(data) {
+ return flatten(map(data.pages, (page) => page.data));
+}
+
+/**
+ * Account pending transctions provider.
+ */
+function PendingTransactionsBoot({ children }) {
+ const { accountId } = useAccountTransactionsContext();
+
+ // Fetches the pending transactions.
+ const {
+ data: pendingTransactionsPage,
+ isFetching: isPendingTransactionFetching,
+ isLoading: isPendingTransactionsLoading,
+ isSuccess: isPendingTransactionsSuccess,
+ isFetchingNextPage: isPendingTransactionFetchNextPage,
+ fetchNextPage: fetchNextPendingTransactionsPage,
+ hasNextPage: hasPendingTransactionsNextPage,
+ } = usePendingBankTransactionsInfinity({
+ account_id: accountId,
+ page_size: 50,
+ });
+ // Memorized the cashflow account transactions.
+ const pendingTransactions = React.useMemo(
+ () =>
+ isPendingTransactionsSuccess
+ ? flattenInfinityPagesData(pendingTransactionsPage)
+ : [],
+ [pendingTransactionsPage, isPendingTransactionsSuccess],
+ );
+ // Handle the observer ineraction.
+ const handleObserverInteract = React.useCallback(() => {
+ if (!isPendingTransactionFetching && hasPendingTransactionsNextPage) {
+ fetchNextPendingTransactionsPage();
+ }
+ }, [
+ isPendingTransactionFetching,
+ hasPendingTransactionsNextPage,
+ fetchNextPendingTransactionsPage,
+ ]);
+ // Provider payload.
+ const provider = {
+ pendingTransactions,
+ isPendingTransactionFetching,
+ isPendingTransactionsLoading,
+ };
+
+ return (
+
+ {children}
+
+
+ );
+}
+
+const usePendingTransactionsContext = () =>
+ React.useContext(PendingTransactionsContext);
+
+export { PendingTransactionsBoot, usePendingTransactionsContext };
diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/PendingTransactions/_hooks.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/PendingTransactions/_hooks.tsx
new file mode 100644
index 000000000..2ea2e20f3
--- /dev/null
+++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/PendingTransactions/_hooks.tsx
@@ -0,0 +1,65 @@
+import { useMemo } from 'react';
+import intl from 'react-intl-universal';
+
+/**
+ * Retrieve account pending transctions table columns.
+ */
+export function usePendingTransactionsTableColumns() {
+ return useMemo(
+ () => [
+ {
+ id: 'date',
+ Header: intl.get('date'),
+ accessor: 'formatted_date',
+ width: 40,
+ clickable: true,
+ textOverview: true,
+ },
+ {
+ id: 'description',
+ Header: 'Description',
+ accessor: 'description',
+ width: 160,
+ textOverview: true,
+ clickable: true,
+ },
+ {
+ id: 'payee',
+ Header: 'Payee',
+ accessor: 'payee',
+ width: 60,
+ clickable: true,
+ textOverview: true,
+ },
+ {
+ id: 'reference_number',
+ Header: 'Ref.#',
+ accessor: 'reference_no',
+ width: 50,
+ clickable: true,
+ textOverview: true,
+ },
+ {
+ id: 'deposit',
+ Header: intl.get('cash_flow.label.deposit'),
+ accessor: 'formatted_deposit_amount',
+ width: 40,
+ className: 'deposit',
+ textOverview: true,
+ align: 'right',
+ clickable: true,
+ },
+ {
+ id: 'withdrawal',
+ Header: intl.get('cash_flow.label.withdrawal'),
+ accessor: 'formatted_withdrawal_amount',
+ className: 'withdrawal',
+ width: 40,
+ textOverview: true,
+ align: 'right',
+ clickable: true,
+ },
+ ],
+ [],
+ );
+}
diff --git a/packages/webapp/src/hooks/query/bank-rules.ts b/packages/webapp/src/hooks/query/bank-rules.ts
index 71697ec7e..1f72e7137 100644
--- a/packages/webapp/src/hooks/query/bank-rules.ts
+++ b/packages/webapp/src/hooks/query/bank-rules.ts
@@ -686,3 +686,34 @@ export function useExcludedBankTransactionsInfinity(
},
);
}
+
+export function usePendingBankTransactionsInfinity(
+ query,
+ infinityProps,
+ axios,
+) {
+ const apiRequest = useApiRequest();
+
+ return useInfiniteQuery(
+ [BANK_QUERY_KEY.PENDING_BANK_ACCOUNT_TRANSACTIONS_INFINITY, query],
+ async ({ pageParam = 1 }) => {
+ const response = await apiRequest.http({
+ ...axios,
+ method: 'get',
+ url: `/api/banking/bank_accounts/pending_transactions`,
+ params: { page: pageParam, ...query },
+ });
+ return response.data;
+ },
+ {
+ getPreviousPageParam: (firstPage) => firstPage.pagination.page - 1,
+ getNextPageParam: (lastPage) => {
+ const { pagination } = lastPage;
+ return pagination.total > pagination.page_size * pagination.page
+ ? lastPage.pagination.page + 1
+ : undefined;
+ },
+ ...infinityProps,
+ },
+ );
+}