From 059316a87b0b811b8c381d40fbca30d86ec807ae Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Fri, 1 Nov 2024 15:47:22 -0600 Subject: [PATCH 01/17] add approve and pay bulk actions --- src/CONST.ts | 2 ++ src/components/Search/SearchPageHeader.tsx | 40 ++++++++++++++++++++++ src/languages/en.ts | 2 ++ src/languages/es.ts | 2 ++ 4 files changed, 46 insertions(+) diff --git a/src/CONST.ts b/src/CONST.ts index f01c8a72eb8e..4771c21f0f49 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -5849,6 +5849,8 @@ const CONST = { }, BULK_ACTION_TYPES: { EXPORT: 'export', + APPROVE: 'approve', + PAY: 'pay', HOLD: 'hold', UNHOLD: 'unhold', DELETE: 'delete', diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index 9fb19ff16288..a044339a2849 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -171,6 +171,46 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) { const options: Array> = []; + const shouldShowApproveOption = !isOffline && selectedTransactionsKeys.every((id) => selectedTransactions[id].action === CONST.SEARCH.ACTION_TYPES.APPROVE); + + if (shouldShowApproveOption) { + options.push({ + icon: Expensicons.ThumbsUp, + text: translate('search.bulkActions.approve'), + value: CONST.SEARCH.BULK_ACTION_TYPES.APPROVE, + shouldCloseModalOnSelect: true, + onSelected: () => { + if (isOffline) { + setIsOfflineModalVisible(true); + return; + } + + const reportIDList = selectedReports?.filter((report) => !!report) ?? []; + SearchActions.approveMoneyRequestOnSearch(hash, reportIDList); + }, + }); + } + + const shouldShowPayOption = !isOffline && selectedTransactionsKeys.every((id) => selectedTransactions[id].action === CONST.SEARCH.ACTION_TYPES.PAY); + + if (shouldShowPayOption) { + options.push({ + icon: Expensicons.MoneyBag, + text: translate('search.bulkActions.pay'), + value: CONST.SEARCH.BULK_ACTION_TYPES.PAY, + shouldCloseModalOnSelect: true, + onSelected: () => { + if (isOffline) { + setIsOfflineModalVisible(true); + return; + } + + const reportIDList = selectedReports?.filter((report) => !!report) ?? []; + // SearchActions.payMoneyRequestOnSearch(hash, reportIDList); + }, + }); + } + options.push({ icon: Expensicons.Download, text: translate('common.download'), diff --git a/src/languages/en.ts b/src/languages/en.ts index 03bbaf8ca8ab..a59913f1a3d1 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -4335,6 +4335,8 @@ const translations = { savedSearchesMenuItemTitle: 'Saved', groupedExpenses: 'grouped expenses', bulkActions: { + approve: 'Approve', + pay: 'Pay', delete: 'Delete', hold: 'Hold', unhold: 'Unhold', diff --git a/src/languages/es.ts b/src/languages/es.ts index 96921bd40388..f32868353517 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -4382,6 +4382,8 @@ const translations = { deleteSavedSearchConfirm: '¿Estás seguro de que quieres eliminar esta búsqueda?', groupedExpenses: 'gastos agrupados', bulkActions: { + approve: 'Aprobar', + pay: 'Pagar', delete: 'Eliminar', hold: 'Bloquear', unhold: 'Desbloquear', From 087a4d31ec5b56b91be4014500bbf11dce494219 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Fri, 1 Nov 2024 15:57:39 -0600 Subject: [PATCH 02/17] add data to selected transactions; --- src/components/Search/SearchPageHeader.tsx | 2 +- src/components/Search/index.tsx | 17 ++++++++++++++++- src/components/Search/types.ts | 6 ++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index a044339a2849..78d23bd182a7 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -184,7 +184,7 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) { setIsOfflineModalVisible(true); return; } - + console.log('over here', {selectedTransactions, selectedReports}) const reportIDList = selectedReports?.filter((report) => !!report) ?? []; SearchActions.approveMoneyRequestOnSearch(hash, reportIDList); }, diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 9238488361b0..e7caafefdd61 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -83,7 +83,18 @@ function prepareTransactionsList(item: TransactionListItemType, selectedTransact return transactions; } - return {...selectedTransactions, [item.keyForList]: {isSelected: true, canDelete: item.canDelete, canHold: item.canHold, canUnhold: item.canUnhold, action: item.action}}; + return { + ...selectedTransactions, + [item.keyForList]: { + isSelected: true, + canDelete: item.canDelete, + canHold: item.canHold, + canUnhold: item.canUnhold, + action: item.action, + reportID: item.reportID, + policyID: item.policyID, + }, + }; } function Search({queryJSON, onSearchListScroll, contentContainerStyle}: SearchProps) { @@ -228,6 +239,8 @@ function Search({queryJSON, onSearchListScroll, contentContainerStyle}: SearchPr canUnhold: transaction.canUnhold, isSelected: selectedTransactions[transaction.transactionID].isSelected, canDelete: transaction.canDelete, + reportID: transaction.reportID, + policyID: transaction.policyID, }; }); } else { @@ -245,6 +258,8 @@ function Search({queryJSON, onSearchListScroll, contentContainerStyle}: SearchPr canUnhold: transaction.canUnhold, isSelected: selectedTransactions[transaction.transactionID].isSelected, canDelete: transaction.canDelete, + reportID: transaction.reportID, + policyID: transaction.policyID, }; }); }); diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index d5be896c1c50..ebbd82768d98 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -19,6 +19,12 @@ type SelectedTransactionInfo = { /** The action that can be performed for the transaction */ action: string; + + /** The reportID of the transaction */ + reportID: string; + + /** The policyID tied to the report the transaction is reported on */ + policyID: string; }; /** Model of selected results */ From 45e3944655a7835744d9601898d0699a4665942d Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Fri, 1 Nov 2024 16:16:33 -0600 Subject: [PATCH 03/17] get reportIDs --- src/components/Search/SearchPageHeader.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index 78d23bd182a7..afb34cdea0a7 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -170,7 +170,6 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) { } const options: Array> = []; - const shouldShowApproveOption = !isOffline && selectedTransactionsKeys.every((id) => selectedTransactions[id].action === CONST.SEARCH.ACTION_TYPES.APPROVE); if (shouldShowApproveOption) { @@ -184,8 +183,10 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) { setIsOfflineModalVisible(true); return; } - console.log('over here', {selectedTransactions, selectedReports}) - const reportIDList = selectedReports?.filter((report) => !!report) ?? []; + + const reportIDList = !selectedReports.length + ? Object.values(selectedTransactions).map((transaction) => transaction.reportID) + : selectedReports?.filter((report) => !!report) ?? []; SearchActions.approveMoneyRequestOnSearch(hash, reportIDList); }, }); From 533f91e070f0cdc8513734143c373abde2fc8c05 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 4 Nov 2024 15:24:21 -0700 Subject: [PATCH 04/17] fix logic to show button --- src/components/Search/SearchContext.tsx | 10 +++++----- src/components/Search/SearchPageHeader.tsx | 13 ++++++++----- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/components/Search/SearchContext.tsx b/src/components/Search/SearchContext.tsx index f3206868d556..9a668a0ba44f 100644 --- a/src/components/Search/SearchContext.tsx +++ b/src/components/Search/SearchContext.tsx @@ -1,5 +1,6 @@ import React, {useCallback, useContext, useMemo, useState} from 'react'; import type {ReportActionListItemType, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types'; +import {isMoneyRequestReport} from '@libs/ReportUtils'; import * as SearchUIUtils from '@libs/SearchUIUtils'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; import type {SearchContext, SelectedTransactions} from './types'; @@ -22,13 +23,12 @@ const Context = React.createContext(defaultSearchContext); function getReportsFromSelectedTransactions(data: TransactionListItemType[] | ReportListItemType[] | ReportActionListItemType[], selectedTransactions: SelectedTransactions) { return (data ?? []) .filter( - (item) => - !SearchUIUtils.isTransactionListItemType(item) && - !SearchUIUtils.isReportActionListItemType(item) && - item.reportID && + (item): item is ReportListItemType => + SearchUIUtils.isReportListItemType(item) && + isMoneyRequestReport(item) && item?.transactions?.every((transaction: {keyForList: string | number}) => selectedTransactions[transaction.keyForList]?.isSelected), ) - .map((item) => item.reportID); + .map((item) => ({reportID: item.reportID, action: item.action})); } function SearchContextProvider({children}: ChildrenProps) { diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index afb34cdea0a7..5f72d47d5b12 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -170,8 +170,11 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) { } const options: Array> = []; - const shouldShowApproveOption = !isOffline && selectedTransactionsKeys.every((id) => selectedTransactions[id].action === CONST.SEARCH.ACTION_TYPES.APPROVE); - + const shouldShowApproveOption = + !isOffline && + (selectedReports.length + ? selectedReports.every((report) => report.action === CONST.SEARCH.ACTION_TYPES.APPROVE) + : selectedTransactionsKeys.every((id) => selectedTransactions[id].action === CONST.SEARCH.ACTION_TYPES.APPROVE)); if (shouldShowApproveOption) { options.push({ icon: Expensicons.ThumbsUp, @@ -186,7 +189,7 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) { const reportIDList = !selectedReports.length ? Object.values(selectedTransactions).map((transaction) => transaction.reportID) - : selectedReports?.filter((report) => !!report) ?? []; + : selectedReports?.filter((report) => !!report).map((report) => report.reportID) ?? []; SearchActions.approveMoneyRequestOnSearch(hash, reportIDList); }, }); @@ -206,7 +209,7 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) { return; } - const reportIDList = selectedReports?.filter((report) => !!report) ?? []; + const reportIDList = selectedReports?.filter((report) => !!report).map((report) => report.reportID) ?? []; // SearchActions.payMoneyRequestOnSearch(hash, reportIDList); }, }); @@ -223,7 +226,7 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) { return; } - const reportIDList = selectedReports?.filter((report) => !!report) ?? []; + const reportIDList = selectedReports?.filter((report) => !!report).map((report) => report.reportID) ?? []; SearchActions.exportSearchItemsToCSV( {query: status, jsonQuery: JSON.stringify(queryJSON), reportIDList, transactionIDList: selectedTransactionsKeys, policyIDs: [activeWorkspaceID ?? '']}, () => { From 42f53104b99484347e6445ce51c5a268bcd3427c Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 4 Nov 2024 16:33:13 -0700 Subject: [PATCH 05/17] handle bulk pay --- src/components/Search/SearchContext.tsx | 3 ++- src/components/Search/SearchPageHeader.tsx | 26 ++++++++++++++---- src/components/Search/types.ts | 27 ++++++++++++++++--- .../PayMoneyRequestOnSearchParams.ts | 5 ++-- src/libs/actions/Search.ts | 10 +++---- 5 files changed, 53 insertions(+), 18 deletions(-) diff --git a/src/components/Search/SearchContext.tsx b/src/components/Search/SearchContext.tsx index 9a668a0ba44f..7be80589ac50 100644 --- a/src/components/Search/SearchContext.tsx +++ b/src/components/Search/SearchContext.tsx @@ -2,6 +2,7 @@ import React, {useCallback, useContext, useMemo, useState} from 'react'; import type {ReportActionListItemType, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types'; import {isMoneyRequestReport} from '@libs/ReportUtils'; import * as SearchUIUtils from '@libs/SearchUIUtils'; +import CONST from '@src/CONST'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; import type {SearchContext, SelectedTransactions} from './types'; @@ -28,7 +29,7 @@ function getReportsFromSelectedTransactions(data: TransactionListItemType[] | Re isMoneyRequestReport(item) && item?.transactions?.every((transaction: {keyForList: string | number}) => selectedTransactions[transaction.keyForList]?.isSelected), ) - .map((item) => ({reportID: item.reportID, action: item.action})); + .map((item) => ({reportID: item.reportID, action: item.action ?? CONST.SEARCH.ACTION_TYPES.VIEW, total: item.total ?? 0, policyID: item.policyID ?? ''})); } function SearchContextProvider({children}: ChildrenProps) { diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index 5f72d47d5b12..9508e3285cc2 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -36,7 +36,7 @@ import type IconAsset from '@src/types/utils/IconAsset'; import {useSearchContext} from './SearchContext'; import SearchButton from './SearchRouter/SearchButton'; import SearchRouterInput from './SearchRouter/SearchRouterInput'; -import type {SearchQueryJSON} from './types'; +import type {PaymentData, SearchQueryJSON} from './types'; type HeaderWrapperProps = Pick & { text: string; @@ -132,6 +132,7 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) { const [currencyList = {}] = useOnyx(ONYXKEYS.CURRENCY_LIST); const [policyCategories] = useOnyx(ONYXKEYS.COLLECTION.POLICY_CATEGORIES); const [policyTagsLists] = useOnyx(ONYXKEYS.COLLECTION.POLICY_TAGS); + const [lastPaymentMethods = {}] = useOnyx(ONYXKEYS.NVP_LAST_PAYMENT_METHOD); const [isDeleteExpensesConfirmModalVisible, setIsDeleteExpensesConfirmModalVisible] = useState(false); const [isOfflineModalVisible, setIsOfflineModalVisible] = useState(false); const [isDownloadErrorModalVisible, setIsDownloadErrorModalVisible] = useState(false); @@ -175,6 +176,7 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) { (selectedReports.length ? selectedReports.every((report) => report.action === CONST.SEARCH.ACTION_TYPES.APPROVE) : selectedTransactionsKeys.every((id) => selectedTransactions[id].action === CONST.SEARCH.ACTION_TYPES.APPROVE)); + if (shouldShowApproveOption) { options.push({ icon: Expensicons.ThumbsUp, @@ -195,7 +197,13 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) { }); } - const shouldShowPayOption = !isOffline && selectedTransactionsKeys.every((id) => selectedTransactions[id].action === CONST.SEARCH.ACTION_TYPES.PAY); + const shouldShowPayOption = + !isOffline && + (selectedReports.length + ? selectedReports.every((report) => report.action === CONST.SEARCH.ACTION_TYPES.PAY && report.policyID && lastPaymentMethods[report.policyID]) + : selectedTransactionsKeys.every( + (id) => selectedTransactions[id].action === CONST.SEARCH.ACTION_TYPES.PAY && selectedTransactions[id].policyID && lastPaymentMethods[selectedTransactions[id].policyID], + )); if (shouldShowPayOption) { options.push({ @@ -208,9 +216,17 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) { setIsOfflineModalVisible(true); return; } - - const reportIDList = selectedReports?.filter((report) => !!report).map((report) => report.reportID) ?? []; - // SearchActions.payMoneyRequestOnSearch(hash, reportIDList); + const paymentData = ( + selectedReports.length + ? selectedReports.map((report) => ({reportID: report.reportID, amount: report.total, paymentMethod: lastPaymentMethods[report.policyID]})) + : Object.values(selectedTransactions).map((transaction) => ({ + reportID: transaction.reportID, + amount: transaction.amount, + paymentMethod: lastPaymentMethods[transaction.policyID] + })) + ) as PaymentData[]; + + SearchActions.payMoneyRequestOnSearch(hash, paymentData); }, }); } diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index ebbd82768d98..9b97ac446d5b 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -1,7 +1,7 @@ import type {ValueOf} from 'react-native-gesture-handler/lib/typescript/typeUtils'; import type {ReportActionListItemType, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types'; import type CONST from '@src/CONST'; -import type {SearchDataTypes, SearchReport} from '@src/types/onyx/SearchResults'; +import type {SearchDataTypes} from '@src/types/onyx/SearchResults'; /** Model of the selected transaction */ type SelectedTransactionInfo = { @@ -18,18 +18,36 @@ type SelectedTransactionInfo = { canUnhold: boolean; /** The action that can be performed for the transaction */ - action: string; + action: ValueOf; /** The reportID of the transaction */ reportID: string; /** The policyID tied to the report the transaction is reported on */ policyID: string; + + /** The transaction amount */ + amount: number; }; -/** Model of selected results */ +/** Model of selected transactons */ type SelectedTransactions = Record; +/** Model of selected reports */ +type SelectedReports = { + reportID: string; + policyID: string; + action: ValueOf; + total: number; +}; + +/** Model of payment data used by Search bulk actions */ +type PaymentData = { + reportID: string; + amount: number; + paymentMethod: ValueOf; +}; + type SortOrder = ValueOf; type SearchColumnType = ValueOf; type ExpenseSearchStatus = ValueOf; @@ -41,7 +59,7 @@ type SearchStatus = ExpenseSearchStatus | InvoiceSearchStatus | TripSearchStatus type SearchContext = { currentSearchHash: number; selectedTransactions: SelectedTransactions; - selectedReports: Array; + selectedReports: SelectedReports[]; setCurrentSearchHash: (hash: number) => void; setSelectedTransactions: (selectedTransactions: SelectedTransactions, data: TransactionListItemType[] | ReportListItemType[] | ReportActionListItemType[]) => void; clearSelectedTransactions: (hash?: number) => void; @@ -118,4 +136,5 @@ export type { ChatSearchStatus, SearchAutocompleteResult, AutocompleteRange, + PaymentData, }; diff --git a/src/libs/API/parameters/PayMoneyRequestOnSearchParams.ts b/src/libs/API/parameters/PayMoneyRequestOnSearchParams.ts index 10174f0baa37..ee51a1961448 100644 --- a/src/libs/API/parameters/PayMoneyRequestOnSearchParams.ts +++ b/src/libs/API/parameters/PayMoneyRequestOnSearchParams.ts @@ -1,11 +1,10 @@ type PayMoneyRequestOnSearchParams = { hash: number; - paymentType: string; /** * Stringified JSON object with type of following structure: - * Array<{reportID: string, amount: number}> + * Array<{reportID: string, amount: number, paymentMethod: string}> */ - reportsAndAmounts: string; + paymentData: string; }; export default PayMoneyRequestOnSearchParams; diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index 556fe76df4d1..dca82a66fd9c 100644 --- a/src/libs/actions/Search.ts +++ b/src/libs/actions/Search.ts @@ -2,7 +2,7 @@ import Onyx from 'react-native-onyx'; import type {OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import type {FormOnyxValues} from '@components/Form/types'; -import type {SearchQueryJSON} from '@components/Search/types'; +import type {PaymentData, SearchQueryJSON} from '@components/Search/types'; import type {ReportListItemType, TransactionListItemType} from '@components/SelectionList/types'; import * as API from '@libs/API'; import type {ExportSearchItemsToCSVParams} from '@libs/API/parameters'; @@ -35,11 +35,11 @@ Onyx.connect({ }); function handleActionButtonPress(hash: number, item: TransactionListItemType | ReportListItemType, goToItem: () => void) { - const lastPolicyPaymentMethod = item.policyID ? lastPaymentMethod?.[item.policyID] : null; + const lastPolicyPaymentMethod = item.policyID ? (lastPaymentMethod?.[item.policyID] as ValueOf) : null; const amount = isReportListItemType(item) ? item.total ?? 0 : item.formattedTotal; switch (item.action) { case CONST.SEARCH.ACTION_TYPES.PAY: - return lastPolicyPaymentMethod ? payMoneyRequestOnSearch(hash, lastPolicyPaymentMethod, {[item.reportID]: amount}) : goToItem(); + return lastPolicyPaymentMethod ? payMoneyRequestOnSearch(hash, [{reportID: item.reportID, amount, paymentMethod: lastPolicyPaymentMethod}]) : goToItem(); case CONST.SEARCH.ACTION_TYPES.APPROVE: return approveMoneyRequestOnSearch(hash, [item.reportID]); default: @@ -194,10 +194,10 @@ function approveMoneyRequestOnSearch(hash: number, reportIDList: string[]) { API.write(WRITE_COMMANDS.APPROVE_MONEY_REQUEST_ON_SEARCH, {hash, reportIDList}, {optimisticData, finallyData}); } -function payMoneyRequestOnSearch(hash: number, paymentType: string, reportsAndAmounts: Record) { +function payMoneyRequestOnSearch(hash: number, paymentData: PaymentData[]) { const {optimisticData, finallyData} = getOnyxLoadingData(hash); - API.write(WRITE_COMMANDS.PAY_MONEY_REQUEST_ON_SEARCH, {hash, paymentType, reportsAndAmounts: JSON.stringify(reportsAndAmounts)}, {optimisticData, finallyData}); + API.write(WRITE_COMMANDS.PAY_MONEY_REQUEST_ON_SEARCH, {hash, paymentData: JSON.stringify(paymentData)}, {optimisticData, finallyData}); } function unholdMoneyRequestOnSearch(hash: number, transactionIDList: string[]) { From 543d7486746ede0bc41776b6647d4b099eeddb6a Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 4 Nov 2024 16:35:49 -0700 Subject: [PATCH 06/17] fix lint --- src/components/Search/SearchPageHeader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index 9508e3285cc2..74735b3d7bbc 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -222,7 +222,7 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) { : Object.values(selectedTransactions).map((transaction) => ({ reportID: transaction.reportID, amount: transaction.amount, - paymentMethod: lastPaymentMethods[transaction.policyID] + paymentMethod: lastPaymentMethods[transaction.policyID], })) ) as PaymentData[]; From 6970f3edddbfef11ae0f2efb06dac7eb23ccef6e Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 4 Nov 2024 16:48:21 -0700 Subject: [PATCH 07/17] fix ts --- src/components/Search/index.tsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index e7caafefdd61..ce9eab45a9d7 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -49,7 +49,19 @@ const searchHeaderHeight = 54; const sortableSearchStatuses: SearchStatus[] = [CONST.SEARCH.STATUS.EXPENSE.ALL]; function mapTransactionItemToSelectedEntry(item: TransactionListItemType): [string, SelectedTransactionInfo] { - return [item.keyForList, {isSelected: true, canDelete: item.canDelete, canHold: item.canHold, canUnhold: item.canUnhold, action: item.action}]; + return [ + item.keyForList, + { + isSelected: true, + canDelete: item.canDelete, + canHold: item.canHold, + canUnhold: item.canUnhold, + action: item.action, + reportID: item.reportID, + policyID: item.policyID, + amount: item.modifiedAmount ?? item.amount, + }, + ]; } function mapToTransactionItemWithSelectionInfo(item: TransactionListItemType, selectedTransactions: SelectedTransactions, canSelectMultiple: boolean, shouldAnimateInHighlight: boolean) { @@ -93,6 +105,7 @@ function prepareTransactionsList(item: TransactionListItemType, selectedTransact action: item.action, reportID: item.reportID, policyID: item.policyID, + amount: item.modifiedAmount ?? item.amount, }, }; } @@ -241,6 +254,7 @@ function Search({queryJSON, onSearchListScroll, contentContainerStyle}: SearchPr canDelete: transaction.canDelete, reportID: transaction.reportID, policyID: transaction.policyID, + amount: transaction.modifiedAmount ?? transaction.amount, }; }); } else { @@ -260,6 +274,7 @@ function Search({queryJSON, onSearchListScroll, contentContainerStyle}: SearchPr canDelete: transaction.canDelete, reportID: transaction.reportID, policyID: transaction.policyID, + amount: transaction.modifiedAmount ?? transaction.amount, }; }); }); From 700bfc8813ec242cf8a1d9dfcaa756e56af6ab6e Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 4 Nov 2024 16:50:33 -0700 Subject: [PATCH 08/17] update dependency array --- src/components/Search/SearchPageHeader.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index 74735b3d7bbc..dc3efae4bdbc 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -341,6 +341,7 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) { activeWorkspaceID, selectedReports, styles.textWrap, + lastPaymentMethods, ]); if (shouldUseNarrowLayout) { From 166923b0a96fd200a493d916204cab488c0c86a2 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 5 Nov 2024 14:17:09 -0700 Subject: [PATCH 09/17] update param name --- src/components/Search/SearchPageHeader.tsx | 4 ++-- src/components/Search/types.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index dc3efae4bdbc..05ae72654daf 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -218,11 +218,11 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) { } const paymentData = ( selectedReports.length - ? selectedReports.map((report) => ({reportID: report.reportID, amount: report.total, paymentMethod: lastPaymentMethods[report.policyID]})) + ? selectedReports.map((report) => ({reportID: report.reportID, amount: report.total, paymentType: lastPaymentMethods[report.policyID]})) : Object.values(selectedTransactions).map((transaction) => ({ reportID: transaction.reportID, amount: transaction.amount, - paymentMethod: lastPaymentMethods[transaction.policyID], + paymentType: lastPaymentMethods[transaction.policyID], })) ) as PaymentData[]; diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index 9b97ac446d5b..d88a4e3dd3be 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -45,7 +45,7 @@ type SelectedReports = { type PaymentData = { reportID: string; amount: number; - paymentMethod: ValueOf; + paymentType: ValueOf; }; type SortOrder = ValueOf; From 8277ee5023ae528d5180a0033266139a3ed568ea Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 5 Nov 2024 15:36:12 -0700 Subject: [PATCH 10/17] fix ts --- src/libs/actions/Search.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index dca82a66fd9c..f343d0d6b849 100644 --- a/src/libs/actions/Search.ts +++ b/src/libs/actions/Search.ts @@ -39,7 +39,7 @@ function handleActionButtonPress(hash: number, item: TransactionListItemType | R const amount = isReportListItemType(item) ? item.total ?? 0 : item.formattedTotal; switch (item.action) { case CONST.SEARCH.ACTION_TYPES.PAY: - return lastPolicyPaymentMethod ? payMoneyRequestOnSearch(hash, [{reportID: item.reportID, amount, paymentMethod: lastPolicyPaymentMethod}]) : goToItem(); + return lastPolicyPaymentMethod ? payMoneyRequestOnSearch(hash, [{reportID: item.reportID, amount, paymentType: lastPolicyPaymentMethod}]) : goToItem(); case CONST.SEARCH.ACTION_TYPES.APPROVE: return approveMoneyRequestOnSearch(hash, [item.reportID]); default: From 0d3a83fcfba17c3209a62df4a6acaee3a13456b3 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 5 Nov 2024 15:50:05 -0700 Subject: [PATCH 11/17] update docs --- src/libs/API/parameters/PayMoneyRequestOnSearchParams.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/API/parameters/PayMoneyRequestOnSearchParams.ts b/src/libs/API/parameters/PayMoneyRequestOnSearchParams.ts index ee51a1961448..a12c0fdc32f2 100644 --- a/src/libs/API/parameters/PayMoneyRequestOnSearchParams.ts +++ b/src/libs/API/parameters/PayMoneyRequestOnSearchParams.ts @@ -2,7 +2,7 @@ type PayMoneyRequestOnSearchParams = { hash: number; /** * Stringified JSON object with type of following structure: - * Array<{reportID: string, amount: number, paymentMethod: string}> + * Array<{reportID: string, amount: number, paymentType: string}> */ paymentData: string; }; From 9dcc965f734d7f4c16423a352f37a346eed9a347 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Wed, 6 Nov 2024 13:27:15 -0700 Subject: [PATCH 12/17] resolve conflits --- src/components/Search/types.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index 8b30a824a5e0..bd2c439287a9 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -33,7 +33,6 @@ type SelectedTransactionInfo = { /** Model of selected transactons */ type SelectedTransactions = Record; -<<<<<<< HEAD /** Model of selected reports */ type SelectedReports = { reportID: string; @@ -42,8 +41,6 @@ type SelectedReports = { total: number; }; -======= ->>>>>>> cmartins-showButtons /** Model of payment data used by Search bulk actions */ type PaymentData = { reportID: string; From 59291607a3070f603726013b53a31aaf30f90068 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 11 Nov 2024 11:10:35 -0700 Subject: [PATCH 13/17] fix loading state --- src/components/Search/SearchPageHeader.tsx | 6 ++++-- src/libs/actions/Search.ts | 19 +++++++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index d9b089bc3d51..bf6d4292353e 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -189,10 +189,11 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) { return; } + const transactionIDList = selectedReports.length ? undefined : Object.keys(selectedTransactions); const reportIDList = !selectedReports.length ? Object.values(selectedTransactions).map((transaction) => transaction.reportID) : selectedReports?.filter((report) => !!report).map((report) => report.reportID) ?? []; - SearchActions.approveMoneyRequestOnSearch(hash, reportIDList); + SearchActions.approveMoneyRequestOnSearch(hash, reportIDList, transactionIDList); }, }); } @@ -216,6 +217,7 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) { setIsOfflineModalVisible(true); return; } + const transactionIDList = selectedReports.length ? undefined : Object.keys(selectedTransactions); const paymentData = ( selectedReports.length ? selectedReports.map((report) => ({reportID: report.reportID, amount: report.total, paymentType: lastPaymentMethods[report.policyID]})) @@ -226,7 +228,7 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) { })) ) as PaymentData[]; - SearchActions.payMoneyRequestOnSearch(hash, paymentData); + SearchActions.payMoneyRequestOnSearch(hash, paymentData, transactionIDList); }, }); } diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index ecd0fc26785a..162013773661 100644 --- a/src/libs/actions/Search.ts +++ b/src/libs/actions/Search.ts @@ -37,7 +37,7 @@ Onyx.connect({ function handleActionButtonPress(hash: number, item: TransactionListItemType | ReportListItemType, goToItem: () => void) { // The transactionID is needed to handle actions taken on `status:all` where transactions on single expense reports can be approved/paid. // We need the transactionID to display the loading indicator for that list item's action. - const transactionID = isTransactionListItemType(item) ? item.transactionID : undefined; + const transactionID = isTransactionListItemType(item) ? [item.transactionID] : undefined; switch (item.action) { case CONST.SEARCH.ACTION_TYPES.PAY: { @@ -193,33 +193,36 @@ function holdMoneyRequestOnSearch(hash: number, transactionIDList: string[], com API.write(WRITE_COMMANDS.HOLD_MONEY_REQUEST_ON_SEARCH, {hash, transactionIDList, comment}, {optimisticData, finallyData}); } -function approveMoneyRequestOnSearch(hash: number, reportIDList: string[], transactionID?: string) { +function approveMoneyRequestOnSearch(hash: number, reportIDList: string[], transactionIDList?: string[]) { const createActionLoadingData = (isLoading: boolean): OnyxUpdate[] => [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, value: { - data: transactionID - ? {[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]: {isActionLoading: isLoading}} + data: transactionIDList + ? (Object.fromEntries( + transactionIDList.map((transactionID) => [`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {isActionLoading: isLoading}]), + ) as Partial) : (Object.fromEntries(reportIDList.map((reportID) => [`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {isActionLoading: isLoading}])) as Partial), }, }, ]; - const optimisticData: OnyxUpdate[] = createActionLoadingData(true); const finallyData: OnyxUpdate[] = createActionLoadingData(false); API.write(WRITE_COMMANDS.APPROVE_MONEY_REQUEST_ON_SEARCH, {hash, reportIDList}, {optimisticData, finallyData}); } -function payMoneyRequestOnSearch(hash: number, paymentData: PaymentData[], transactionID?: string) { +function payMoneyRequestOnSearch(hash: number, paymentData: PaymentData[], transactionIDList?: string[]) { const createActionLoadingData = (isLoading: boolean): OnyxUpdate[] => [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, value: { - data: transactionID - ? {[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]: {isActionLoading: isLoading}} + data: transactionIDList + ? (Object.fromEntries( + transactionIDList.map((transactionID) => [`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {isActionLoading: isLoading}]), + ) as Partial) : (Object.fromEntries(paymentData.map((item) => [`${ONYXKEYS.COLLECTION.REPORT}${item.reportID}`, {isActionLoading: isLoading}])) as Partial), }, }, From 447cc92480d87c7660d3eaab17997ccb8dac18b4 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 18 Nov 2024 11:03:39 -0700 Subject: [PATCH 14/17] fix ts --- src/libs/actions/Search.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index 8b839c563dd6..b2156c0390d3 100644 --- a/src/libs/actions/Search.ts +++ b/src/libs/actions/Search.ts @@ -44,21 +44,21 @@ Onyx.connect({ }); function handleActionButtonPress(hash: number, item: TransactionListItemType | ReportListItemType, goToItem: () => void) { - // The transactionID is needed to handle actions taken on `status:all` where transactions on single expense reports can be approved/paid. + // The transactionIDList is needed to handle actions taken on `status:all` where transactions on single expense reports can be approved/paid. // We need the transactionID to display the loading indicator for that list item's action. - const transactionID = isTransactionListItemType(item) ? [item.transactionID] : undefined; + const transactionIDList = isTransactionListItemType(item) ? [item.transactionID] : undefined; switch (item.action) { case CONST.SEARCH.ACTION_TYPES.PAY: - return getPayActionCallback(hash, item, goToItem); + return getPayActionCallback(hash, item, goToItem, transactionIDList); case CONST.SEARCH.ACTION_TYPES.APPROVE: - return approveMoneyRequestOnSearch(hash, [item.reportID], transactionID); + return approveMoneyRequestOnSearch(hash, [item.reportID], transactionIDList); default: return goToItem(); } } -function getPayActionCallback(hash: number, item: TransactionListItemType | ReportListItemType, goToItem: () => void) { +function getPayActionCallback(hash: number, item: TransactionListItemType | ReportListItemType, goToItem: () => void, transactionIDList?: string[]) { const lastPolicyPaymentMethod = item.policyID ? (lastPaymentMethod?.[item.policyID] as ValueOf) : null; if (!lastPolicyPaymentMethod) { @@ -66,14 +66,13 @@ function getPayActionCallback(hash: number, item: TransactionListItemType | Repo } const amount = isReportListItemType(item) ? item.total ?? 0 : item.formattedTotal; - const transactionID = isTransactionListItemType(item) ? item.transactionID : undefined; if (lastPolicyPaymentMethod === CONST.IOU.PAYMENT_TYPE.ELSEWHERE) { - return payMoneyRequestOnSearch(hash, [{reportID: item.reportID, amount, paymentType: lastPolicyPaymentMethod}], transactionID); + return payMoneyRequestOnSearch(hash, [{reportID: item.reportID, amount, paymentType: lastPolicyPaymentMethod}], transactionIDList); } const hasVBBA = !!allSnapshots?.[`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`]?.data?.[`${ONYXKEYS.COLLECTION.POLICY}${item.policyID}`]?.achAccount?.bankAccountID; - return hasVBBA ? payMoneyRequestOnSearch(hash, [{reportID: item.reportID, amount, paymentType: lastPolicyPaymentMethod}], transactionID) : goToItem(); + return hasVBBA ? payMoneyRequestOnSearch(hash, [{reportID: item.reportID, amount, paymentType: lastPolicyPaymentMethod}], transactionIDList) : goToItem(); } function getOnyxLoadingData(hash: number): {optimisticData: OnyxUpdate[]; finallyData: OnyxUpdate[]} { From 518d6bf74414f813a9c999f02008f0294cf9aad6 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 18 Nov 2024 17:52:46 -0700 Subject: [PATCH 15/17] fix single expense loading spinner --- src/components/SelectionList/Search/ReportListItem.tsx | 1 + src/components/SelectionList/Search/TransactionListItem.tsx | 3 ++- src/components/SelectionList/types.ts | 5 ++++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/SelectionList/Search/ReportListItem.tsx b/src/components/SelectionList/Search/ReportListItem.tsx index b03932db2532..18352c6e0596 100644 --- a/src/components/SelectionList/Search/ReportListItem.tsx +++ b/src/components/SelectionList/Search/ReportListItem.tsx @@ -133,6 +133,7 @@ function ReportListItem({ onFocus={onFocus} onLongPressRow={onLongPressRow} shouldSyncFocus={shouldSyncFocus} + isLoading={reportItem.isActionLoading} /> ); } diff --git a/src/components/SelectionList/Search/TransactionListItem.tsx b/src/components/SelectionList/Search/TransactionListItem.tsx index a23a7048644e..42bf05179bdb 100644 --- a/src/components/SelectionList/Search/TransactionListItem.tsx +++ b/src/components/SelectionList/Search/TransactionListItem.tsx @@ -22,6 +22,7 @@ function TransactionListItem({ onFocus, onLongPressRow, shouldSyncFocus, + isLoading, }: TransactionListItemProps) { const transactionItem = item as unknown as TransactionListItemType; const styles = useThemeStyles(); @@ -85,7 +86,7 @@ function TransactionListItem({ canSelectMultiple={!!canSelectMultiple} isButtonSelected={item.isSelected} shouldShowTransactionCheckbox={false} - isLoading={transactionItem.isActionLoading} + isLoading={isLoading ?? transactionItem.isActionLoading} /> ); diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 4c7fd330ec18..a1716ab47543 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -331,7 +331,10 @@ type RadioListItemProps = ListItemProps; type TableListItemProps = ListItemProps; -type TransactionListItemProps = ListItemProps; +type TransactionListItemProps = ListItemProps & { + /** Whether the item's action is loading */ + isLoading?: boolean; +}; type ReportListItemProps = ListItemProps; From 9933888bd1576e2723560b938945b805b8a8d3d3 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Fri, 22 Nov 2024 12:37:31 -0700 Subject: [PATCH 16/17] fix hold case --- src/components/Search/SearchPageHeader.tsx | 4 ++++ src/components/Search/index.tsx | 5 +++++ src/components/Search/types.ts | 3 +++ 3 files changed, 12 insertions(+) diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index 414068c26a0e..eeae0aa63736 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -171,8 +171,11 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) { } const options: Array> = []; + const isAnyTransactionOnHold = Object.values(selectedTransactions).some((transaction) => transaction.isHeld); + const shouldShowApproveOption = !isOffline && + !isAnyTransactionOnHold && (selectedReports.length ? selectedReports.every((report) => report.action === CONST.SEARCH.ACTION_TYPES.APPROVE) : selectedTransactionsKeys.every((id) => selectedTransactions[id].action === CONST.SEARCH.ACTION_TYPES.APPROVE)); @@ -200,6 +203,7 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) { const shouldShowPayOption = !isOffline && + // !isAnyTransactionOnHold && (selectedReports.length ? selectedReports.every((report) => report.action === CONST.SEARCH.ACTION_TYPES.PAY && report.policyID && lastPaymentMethods[report.policyID]) : selectedTransactionsKeys.every( diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 22c5d5b46699..ea712ddaafd6 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -24,6 +24,7 @@ import isSearchTopmostCentralPane from '@libs/Navigation/isSearchTopmostCentralP import * as ReportUtils from '@libs/ReportUtils'; import * as SearchQueryUtils from '@libs/SearchQueryUtils'; import * as SearchUIUtils from '@libs/SearchUIUtils'; +import * as TransactionUtils from '@libs/TransactionUtils'; import Navigation from '@navigation/Navigation'; import type {AuthScreensParamList} from '@navigation/types'; import EmptySearchView from '@pages/Search/EmptySearchView'; @@ -55,6 +56,7 @@ function mapTransactionItemToSelectedEntry(item: TransactionListItemType): [stri isSelected: true, canDelete: item.canDelete, canHold: item.canHold, + isHeld: TransactionUtils.isOnHold(item), canUnhold: item.canUnhold, action: item.action, reportID: item.reportID, @@ -101,6 +103,7 @@ function prepareTransactionsList(item: TransactionListItemType, selectedTransact isSelected: true, canDelete: item.canDelete, canHold: item.canHold, + isHeld: TransactionUtils.isOnHold(item), canUnhold: item.canUnhold, action: item.action, reportID: item.reportID, @@ -249,6 +252,7 @@ function Search({queryJSON, onSearchListScroll, isSearchScreenFocused, contentCo newTransactionList[transaction.transactionID] = { action: transaction.action, canHold: transaction.canHold, + isHeld: TransactionUtils.isOnHold(transaction), canUnhold: transaction.canUnhold, isSelected: selectedTransactions[transaction.transactionID].isSelected, canDelete: transaction.canDelete, @@ -269,6 +273,7 @@ function Search({queryJSON, onSearchListScroll, isSearchScreenFocused, contentCo newTransactionList[transaction.transactionID] = { action: transaction.action, canHold: transaction.canHold, + isHeld: TransactionUtils.isOnHold(transaction), canUnhold: transaction.canUnhold, isSelected: selectedTransactions[transaction.transactionID].isSelected, canDelete: transaction.canDelete, diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index e5774d073561..af72b7f6bcc1 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -14,6 +14,9 @@ type SelectedTransactionInfo = { /** If the transaction can be put on hold */ canHold: boolean; + /** Whether the transaction is currently held */ + isHeld: boolean; + /** If the transaction can be removed from hold */ canUnhold: boolean; From eab8872f3e7f478057019434a5c7e56434143a39 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 26 Nov 2024 13:04:19 -0700 Subject: [PATCH 17/17] uncomment line --- src/components/Search/SearchPageHeader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index eeae0aa63736..0d212acd04a1 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -203,7 +203,7 @@ function SearchPageHeader({queryJSON, hash}: SearchPageHeaderProps) { const shouldShowPayOption = !isOffline && - // !isAnyTransactionOnHold && + !isAnyTransactionOnHold && (selectedReports.length ? selectedReports.every((report) => report.action === CONST.SEARCH.ACTION_TYPES.PAY && report.policyID && lastPaymentMethods[report.policyID]) : selectedTransactionsKeys.every(