mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-22 15:50:32 +00:00
feat: cashflow account transactions infinity scroll loading.
This commit is contained in:
@@ -2,6 +2,8 @@ import React from 'react';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Classes } from '@blueprintjs/core';
|
import { Classes } from '@blueprintjs/core';
|
||||||
import clsx from 'classnames';
|
import clsx from 'classnames';
|
||||||
|
import useContextMenu from 'react-use-context-menu';
|
||||||
|
import ContextMenu from '../ContextMenu';
|
||||||
import Icon from '../Icon';
|
import Icon from '../Icon';
|
||||||
|
|
||||||
const BankAccountWrap = styled.div`
|
const BankAccountWrap = styled.div`
|
||||||
@@ -20,21 +22,6 @@ const BankAccountWrap = styled.div`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const BankAccountAnchor = styled.a`
|
|
||||||
text-decoration: none;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex: 1;
|
|
||||||
color: inherit;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus,
|
|
||||||
&:active {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const BankAccountHeader = styled.div`
|
const BankAccountHeader = styled.div`
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
padding-top: 16px;
|
padding-top: 16px;
|
||||||
@@ -179,43 +166,65 @@ function BankAccountTypeIcon({ type }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function BankAccount({
|
export function BankAccount({
|
||||||
title,
|
title,
|
||||||
code,
|
code,
|
||||||
type,
|
type,
|
||||||
balance,
|
balance,
|
||||||
loading = false,
|
loading = false,
|
||||||
to
|
updatedBeforeText,
|
||||||
|
to,
|
||||||
|
contextMenuContent,
|
||||||
}) {
|
}) {
|
||||||
|
const [
|
||||||
|
bindMenu,
|
||||||
|
bindMenuItem,
|
||||||
|
useContextTrigger,
|
||||||
|
{ coords, setVisible, isVisible },
|
||||||
|
] = useContextMenu();
|
||||||
|
|
||||||
|
const [bindTrigger] = useContextTrigger({
|
||||||
|
collect: () => 'Title',
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleClose = React.useCallback(() => {
|
||||||
|
setVisible(false);
|
||||||
|
}, [setVisible]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BankAccountWrap>
|
<BankAccountWrap {...bindTrigger}>
|
||||||
<BankAccountAnchor href="#">
|
<BankAccountHeader>
|
||||||
<BankAccountHeader>
|
<BankAccountTitle className={clsx({ [Classes.SKELETON]: loading })}>
|
||||||
<BankAccountTitle className={clsx({ [Classes.SKELETON]: loading })}>
|
{title}
|
||||||
{title}
|
</BankAccountTitle>
|
||||||
</BankAccountTitle>
|
<BnakAccountCode className={clsx({ [Classes.SKELETON]: loading })}>
|
||||||
<BnakAccountCode className={clsx({ [Classes.SKELETON]: loading })}>
|
{code}
|
||||||
{code}
|
</BnakAccountCode>
|
||||||
</BnakAccountCode>
|
{!loading && <BankAccountTypeIcon type={type} />}
|
||||||
{!loading && <BankAccountTypeIcon type={type} />}
|
</BankAccountHeader>
|
||||||
</BankAccountHeader>
|
|
||||||
|
|
||||||
<BankAccountMeta>
|
<BankAccountMeta>
|
||||||
<BankAccountMetaLine
|
<BankAccountMetaLine
|
||||||
title={'Account transactions'}
|
title={'Account transactions'}
|
||||||
value={2}
|
value={2}
|
||||||
className={clsx({ [Classes.SKELETON]: loading })}
|
className={clsx({ [Classes.SKELETON]: loading })}
|
||||||
/>
|
/>
|
||||||
<BankAccountMetaLine
|
<BankAccountMetaLine
|
||||||
title={'Updated 2 days ago'}
|
title={updatedBeforeText}
|
||||||
className={clsx({ [Classes.SKELETON]: loading })}
|
className={clsx({ [Classes.SKELETON]: loading })}
|
||||||
/>
|
/>
|
||||||
</BankAccountMeta>
|
</BankAccountMeta>
|
||||||
|
|
||||||
<BankAccountBalance amount={balance} loading={loading} />
|
<BankAccountBalance amount={balance} loading={loading} />
|
||||||
</BankAccountAnchor>
|
|
||||||
|
<ContextMenu
|
||||||
|
bindMenu={bindMenu}
|
||||||
|
isOpen={isVisible}
|
||||||
|
coords={coords}
|
||||||
|
onClosed={handleClose}
|
||||||
|
>
|
||||||
|
<contextMenuContent />
|
||||||
|
</ContextMenu>
|
||||||
</BankAccountWrap>
|
</BankAccountWrap>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
23
src/components/IntersectionObserver/index.js
Normal file
23
src/components/IntersectionObserver/index.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useIntersectionObserver } from 'hooks/utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intersection observer.
|
||||||
|
*/
|
||||||
|
export function IntersectionObserver({ onIntersect }) {
|
||||||
|
const loadMoreButtonRef = React.useRef();
|
||||||
|
|
||||||
|
useIntersectionObserver({
|
||||||
|
// enabled: !isItemsLoading && !isResourceLoading,
|
||||||
|
target: loadMoreButtonRef,
|
||||||
|
onIntersect: () => {
|
||||||
|
onIntersect && onIntersect();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={loadMoreButtonRef} style={{ opacity: 0 }}>
|
||||||
|
Load Newer
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -82,6 +82,7 @@ export * from './MultiSelectTaggable';
|
|||||||
export * from './Utils/FormatNumber';
|
export * from './Utils/FormatNumber';
|
||||||
export * from './Utils/FormatDate';
|
export * from './Utils/FormatDate';
|
||||||
export * from './BankAccounts';
|
export * from './BankAccounts';
|
||||||
|
export * from './IntersectionObserver'
|
||||||
|
|
||||||
const Hint = FieldHint;
|
const Hint = FieldHint;
|
||||||
|
|
||||||
|
|||||||
@@ -52,14 +52,13 @@ function AccountTransactionsDataTable({
|
|||||||
TableRowsRenderer={TableVirtualizedListRows}
|
TableRowsRenderer={TableVirtualizedListRows}
|
||||||
TableHeaderSkeletonRenderer={TableSkeletonHeader}
|
TableHeaderSkeletonRenderer={TableSkeletonHeader}
|
||||||
// #TableVirtualizedListRows props.
|
// #TableVirtualizedListRows props.
|
||||||
vListrowHeight={cashflowTansactionsTableSize == 'small' ? 40 : 42}
|
vListrowHeight={cashflowTansactionsTableSize == 'small' ? 32 : 40}
|
||||||
vListOverscanRowCount={0}
|
vListOverscanRowCount={0}
|
||||||
TableHeaderSkeletonRenderer={TableSkeletonHeader}
|
TableHeaderSkeletonRenderer={TableSkeletonHeader}
|
||||||
initialColumnsWidths={initialColumnsWidths}
|
initialColumnsWidths={initialColumnsWidths}
|
||||||
onColumnResizing={handleColumnResizing}
|
onColumnResizing={handleColumnResizing}
|
||||||
size={cashflowTansactionsTableSize}
|
size={cashflowTansactionsTableSize}
|
||||||
noResults={'There is deposit/withdrawal transactions on the current account.'}
|
noResults={'There is deposit/withdrawal transactions on the current account.'}
|
||||||
|
|
||||||
className="table-constrant"
|
className="table-constrant"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ function AccountSwitchButton() {
|
|||||||
|
|
||||||
function AccountSwitchItem() {
|
function AccountSwitchItem() {
|
||||||
const { push } = useHistory();
|
const { push } = useHistory();
|
||||||
const { cashflowAccounts } = useAccountTransactionsContext();
|
const { cashflowAccounts, accountId } = useAccountTransactionsContext();
|
||||||
|
|
||||||
// Handle item click.
|
// Handle item click.
|
||||||
const handleItemClick = curry((account, event) => {
|
const handleItemClick = curry((account, event) => {
|
||||||
@@ -39,7 +39,9 @@ function AccountSwitchItem() {
|
|||||||
const items = cashflowAccounts.map((account) => (
|
const items = cashflowAccounts.map((account) => (
|
||||||
<AccountSwitchMenuItem
|
<AccountSwitchMenuItem
|
||||||
name={account.name}
|
name={account.name}
|
||||||
|
balance={account.formatted_amount}
|
||||||
onClick={handleItemClick(account)}
|
onClick={handleItemClick(account)}
|
||||||
|
active={account.id === accountId}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -111,7 +113,7 @@ function AccountSwitchMenuItem({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
label={'LYD100,000'}
|
label={balance}
|
||||||
text={
|
text={
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<AccountSwitchItemName>{name}</AccountSwitchItemName>
|
<AccountSwitchItemName>{name}</AccountSwitchItemName>
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { flatten, map } from 'lodash';
|
||||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
|
import { IntersectionObserver } from 'components';
|
||||||
import {
|
import {
|
||||||
useCashflowTransactions,
|
useAccountTransactionsInfinity,
|
||||||
useCashflowAccounts,
|
useCashflowAccounts,
|
||||||
useAccount,
|
useAccount,
|
||||||
} from 'hooks/query';
|
} from 'hooks/query';
|
||||||
|
|
||||||
const AccountTransactionsContext = React.createContext();
|
const AccountTransactionsContext = React.createContext();
|
||||||
|
|
||||||
|
function flattenInfinityPages(data) {
|
||||||
|
return flatten(map(data.pages, (page) => page.cashflow_transactions));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Account transctions provider.
|
* Account transctions provider.
|
||||||
*/
|
*/
|
||||||
@@ -18,26 +24,44 @@ function AccountTransactionsProvider({ query, ...props }) {
|
|||||||
|
|
||||||
// Fetch cashflow account transactions list
|
// Fetch cashflow account transactions list
|
||||||
const {
|
const {
|
||||||
data: cashflowTransactions,
|
data: cashflowTransactionsPages,
|
||||||
isFetching: isCashFlowTransactionsFetching,
|
isFetching: isCashFlowTransactionsFetching,
|
||||||
isLoading: isCashFlowTransactionsLoading,
|
isLoading: isCashFlowTransactionsLoading,
|
||||||
} = useCashflowTransactions(accountId, {
|
isSuccess: isCashflowTransactionsSuccess,
|
||||||
enabled: !!accountId,
|
fetchNextPage: fetchNextTransactionsPage,
|
||||||
|
isFetchingNextPage,
|
||||||
|
} = useAccountTransactionsInfinity(accountId, {
|
||||||
|
page_size: 50,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch cashflow accounts .
|
const cashflowTransactions = React.useMemo(
|
||||||
|
() =>
|
||||||
|
isCashflowTransactionsSuccess
|
||||||
|
? flattenInfinityPages(cashflowTransactionsPages)
|
||||||
|
: [],
|
||||||
|
[cashflowTransactionsPages, isCashflowTransactionsSuccess],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Fetch cashflow accounts.
|
||||||
const {
|
const {
|
||||||
data: cashflowAccounts,
|
data: cashflowAccounts,
|
||||||
isFetching: isCashFlowAccountsFetching,
|
isFetching: isCashFlowAccountsFetching,
|
||||||
isLoading: isCashFlowAccountsLoading,
|
isLoading: isCashFlowAccountsLoading,
|
||||||
} = useCashflowAccounts(query, { keepPreviousData: true });
|
} = useCashflowAccounts(query, { keepPreviousData: true });
|
||||||
|
|
||||||
|
// Retrieve specific account details.
|
||||||
const {
|
const {
|
||||||
data: currentAccount,
|
data: currentAccount,
|
||||||
isFetching: isCurrentAccountFetching,
|
isFetching: isCurrentAccountFetching,
|
||||||
isLoading: isCurrentAccountLoading,
|
isLoading: isCurrentAccountLoading,
|
||||||
} = useAccount(accountId, { keepPreviousData: true });
|
} = useAccount(accountId, { keepPreviousData: true });
|
||||||
|
|
||||||
|
const handleObserverInteract = React.useCallback(() => {
|
||||||
|
if (!isFetchingNextPage) {
|
||||||
|
fetchNextTransactionsPage();
|
||||||
|
}
|
||||||
|
}, [isFetchingNextPage, fetchNextTransactionsPage]);
|
||||||
|
|
||||||
// Provider payload.
|
// Provider payload.
|
||||||
const provider = {
|
const provider = {
|
||||||
accountId,
|
accountId,
|
||||||
@@ -55,6 +79,10 @@ function AccountTransactionsProvider({ query, ...props }) {
|
|||||||
return (
|
return (
|
||||||
<DashboardInsider name={'account-transactions'}>
|
<DashboardInsider name={'account-transactions'}>
|
||||||
<AccountTransactionsContext.Provider value={provider} {...props} />
|
<AccountTransactionsContext.Provider value={provider} {...props} />
|
||||||
|
<IntersectionObserver
|
||||||
|
onIntersect={handleObserverInteract}
|
||||||
|
// enabled={!isFetchingNextPage}
|
||||||
|
/>
|
||||||
</DashboardInsider>
|
</DashboardInsider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,32 +21,41 @@ export function useAccountTransactionsColumns() {
|
|||||||
{
|
{
|
||||||
id: 'type',
|
id: 'type',
|
||||||
Header: intl.get('type'),
|
Header: intl.get('type'),
|
||||||
accessor: 'type',
|
accessor: 'reference_type_formatted',
|
||||||
className: 'type',
|
className: 'type',
|
||||||
width: 140,
|
width: 140,
|
||||||
textOverview: true,
|
textOverview: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'status',
|
id: 'transaction_number',
|
||||||
Header: intl.get('status'),
|
Header: intl.get('transaction_number'),
|
||||||
// accessor:
|
accessor: 'transaction_number',
|
||||||
width: 160,
|
width: 160,
|
||||||
className: 'status',
|
className: 'transaction_number',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'reference_number',
|
||||||
|
Header: intl.get('reference_no'),
|
||||||
|
accessor: 'reference_number',
|
||||||
|
width: 160,
|
||||||
|
className: 'reference_number',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'deposit',
|
id: 'deposit',
|
||||||
Header: intl.get('cash_flow.label.deposit'),
|
Header: intl.get('cash_flow.label.deposit'),
|
||||||
accessor: 'formattedDeposit',
|
accessor: 'formatted_deposit',
|
||||||
width: 110,
|
width: 110,
|
||||||
className: 'deposit',
|
className: 'deposit',
|
||||||
align: 'right',
|
textOverview: true,
|
||||||
|
align: 'right'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'withdrawal',
|
id: 'withdrawal',
|
||||||
Header: intl.get('cash_flow.label.withdrawal'),
|
Header: intl.get('cash_flow.label.withdrawal'),
|
||||||
accessor: 'formattedWithdrawal',
|
accessor: 'formatted_withdrawal',
|
||||||
className: 'withdrawal',
|
className: 'withdrawal',
|
||||||
width: 150,
|
width: 150,
|
||||||
|
textOverview: true,
|
||||||
align: 'right',
|
align: 'right',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -55,7 +64,8 @@ export function useAccountTransactionsColumns() {
|
|||||||
accessor: 'running_balance',
|
accessor: 'running_balance',
|
||||||
className: 'running_balance',
|
className: 'running_balance',
|
||||||
width: 150,
|
width: 150,
|
||||||
align: 'right',
|
textOverview: true,
|
||||||
|
align: 'right'
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[],
|
[],
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ import React from 'react';
|
|||||||
import { isNull } from 'lodash';
|
import { isNull } from 'lodash';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { BankAccountsList, BankAccount } from '../../../components';
|
import { Menu, MenuItem, MenuDivider, Intent } from '@blueprintjs/core';
|
||||||
|
import intl from 'react-intl-universal';
|
||||||
|
import { BankAccountsList, BankAccount, If, Icon } from '../../../components';
|
||||||
import { useCashFlowAccountsContext } from './CashFlowAccountsProvider';
|
import { useCashFlowAccountsContext } from './CashFlowAccountsProvider';
|
||||||
|
|
||||||
const CashflowAccountsGridWrap = styled.div`
|
const CashflowAccountsGridWrap = styled.div`
|
||||||
@@ -22,16 +24,22 @@ function CashflowAccountsSkeleton() {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getUpdatedBeforeText(createdAt) {
|
||||||
|
return 'Updated before 2 years.';
|
||||||
|
}
|
||||||
|
|
||||||
function CashflowAccountsGridItems({ accounts }) {
|
function CashflowAccountsGridItems({ accounts }) {
|
||||||
return accounts.map((account) => (
|
return accounts.map((account) => (
|
||||||
<Link to={`/cashflow-accounts/${account.id}/transactions`}>
|
<CashflowAccountAnchor to={`/cashflow-accounts/${account.id}/transactions`}>
|
||||||
<BankAccount
|
<BankAccount
|
||||||
title={account.name}
|
title={account.name}
|
||||||
code={account.code}
|
code={account.code}
|
||||||
balance={!isNull(account.amount) ? account.formattedAmount : '-'}
|
balance={!isNull(account.amount) ? account.formatted_amount : '-'}
|
||||||
type={'cash'}
|
type={'cash'}
|
||||||
|
contextMenuContent={CashflowAccountContextMenu}
|
||||||
|
updatedBeforeText={getUpdatedBeforeText(account.createdAt)}
|
||||||
/>
|
/>
|
||||||
</Link>
|
</CashflowAccountAnchor>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,3 +60,44 @@ export default function CashflowAccountsGrid() {
|
|||||||
</CashflowAccountsGridWrap>
|
</CashflowAccountsGridWrap>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function CashflowAccountContextMenu() {
|
||||||
|
return (
|
||||||
|
<Menu>
|
||||||
|
<MenuItem
|
||||||
|
icon={<Icon icon="reader-18" />}
|
||||||
|
text={intl.get('view_details')}
|
||||||
|
/>
|
||||||
|
<MenuDivider />
|
||||||
|
<MenuItem icon={<Icon icon="pen-18" />} text={intl.get('edit_account')} />
|
||||||
|
<MenuDivider />
|
||||||
|
<If condition={false}>
|
||||||
|
<MenuItem
|
||||||
|
text={intl.get('inactivate_account')}
|
||||||
|
icon={<Icon icon="pause-16" iconSize={16} />}
|
||||||
|
/>
|
||||||
|
</If>
|
||||||
|
<If condition={!false}>
|
||||||
|
<MenuItem
|
||||||
|
text={intl.get('activate_account')}
|
||||||
|
icon={<Icon icon="play-16" iconSize={16} />}
|
||||||
|
/>
|
||||||
|
</If>
|
||||||
|
<MenuItem
|
||||||
|
text={intl.get('delete_account')}
|
||||||
|
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||||
|
intent={Intent.DANGER}
|
||||||
|
/>
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const CashflowAccountAnchor = styled(Link)`
|
||||||
|
&,
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { useMutation, useQueryClient } from 'react-query';
|
import { useMutation, useQueryClient, useInfiniteQuery } from 'react-query';
|
||||||
import { useRequestQuery } from '../useQueryRequest';
|
import { useRequestQuery } from '../useQueryRequest';
|
||||||
import useApiRequest from '../useRequest';
|
import useApiRequest from '../useRequest';
|
||||||
import t from './types';
|
import t from './types';
|
||||||
|
|
||||||
const commonInvalidateQueries = (queryClient) => {
|
const commonInvalidateQueries = (queryClient) => {
|
||||||
// queryClient.invalidateQueries(t.CASH_FLOW_TRANSACTIONS);
|
// Invalidate accounts.
|
||||||
|
queryClient.invalidateQueries(t.ACCOUNTS);
|
||||||
|
queryClient.invalidateQueries(t.ACCOUNT);
|
||||||
queryClient.invalidateQueries(t.CASH_FLOW_TRANSACTION);
|
queryClient.invalidateQueries(t.CASH_FLOW_TRANSACTION);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -22,13 +24,14 @@ export function useCashflowAccounts(query, props) {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve account transactions list.
|
* Retrieve account transactions list.
|
||||||
*/
|
*/
|
||||||
export function useCashflowTransactions(id, props) {
|
export function useCashflowTransactions(id, props) {
|
||||||
return useRequestQuery(
|
return useRequestQuery(
|
||||||
[t.CASH_FLOW_TRANSACTIONS, id],
|
[t.CASH_FLOW_TRANSACTIONS, id],
|
||||||
{ method: 'get', url: `cashflow/account/${1000}/transactions` },
|
{ method: 'get', url: `cashflow/account/${id}/transactions` },
|
||||||
{
|
{
|
||||||
select: (res) => res.data.cashflow_transactions,
|
select: (res) => res.data.cashflow_transactions,
|
||||||
defaultData: [],
|
defaultData: [],
|
||||||
@@ -73,3 +76,42 @@ export function useDeleteCashflowTransaction(props) {
|
|||||||
...props,
|
...props,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve account transactions infinity scrolling.
|
||||||
|
* @param {number} accountId
|
||||||
|
* @param {*} axios
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function useAccountTransactionsInfinity(
|
||||||
|
accountId,
|
||||||
|
query,
|
||||||
|
axios,
|
||||||
|
infinityProps,
|
||||||
|
) {
|
||||||
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
|
return useInfiniteQuery(
|
||||||
|
['CASHFLOW_ACCOUNT_TRANSACTIONS_INFINITY', accountId],
|
||||||
|
async ({ pageParam = 1 }) => {
|
||||||
|
const response = await apiRequest.http({
|
||||||
|
...axios,
|
||||||
|
method: 'get',
|
||||||
|
url: `/api/cashflow/account/${accountId}/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,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,3 +7,4 @@ export * from './useWatch';
|
|||||||
export * from './useWhen';
|
export * from './useWhen';
|
||||||
export * from './useRequestPdf';
|
export * from './useRequestPdf';
|
||||||
export * from './useAsync';
|
export * from './useAsync';
|
||||||
|
export * from './useIntersectionObserver';
|
||||||
36
src/hooks/utils/useIntersectionObserver.js
Normal file
36
src/hooks/utils/useIntersectionObserver.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function useIntersectionObserver({
|
||||||
|
root,
|
||||||
|
target,
|
||||||
|
onIntersect,
|
||||||
|
threshold = 1.0,
|
||||||
|
rootMargin = '0px',
|
||||||
|
enabled = true,
|
||||||
|
}) {
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
(entries) =>
|
||||||
|
entries.forEach((entry) => entry.isIntersecting && onIntersect()),
|
||||||
|
{
|
||||||
|
root: root && root.current,
|
||||||
|
rootMargin,
|
||||||
|
// threshold,
|
||||||
|
threshold: 0.25,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const el = target && target.current;
|
||||||
|
|
||||||
|
if (!el) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
observer.observe(el);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
observer.unobserve(el);
|
||||||
|
};
|
||||||
|
}, [target.current, enabled, onIntersect, root]);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user