From 38c6aa38fed71e4f730a92b11fcfeba90e7bbd18 Mon Sep 17 00:00:00 2001 From: Neeraj Bachani Date: Wed, 13 May 2026 02:56:34 +0000 Subject: [PATCH 1/4] Add View transactions shortcut under Current balance (#88116) --- .../expensifyCard/WorkspaceCardsListLabel.tsx | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx b/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx index 40d88dcdf992..755c6d97d490 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx @@ -1,7 +1,7 @@ import {useRoute} from '@react-navigation/native'; import {hasSeenTourSelector} from '@selectors/Onboarding'; import {addDays, format} from 'date-fns'; -import React, {useEffect, useMemo, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; import type {ValueOf} from 'type-fest'; @@ -10,6 +10,7 @@ import Icon from '@components/Icon'; import Popover from '@components/Popover'; import {PressableWithFeedback} from '@components/Pressable'; import Text from '@components/Text'; +import TextLink from '@components/TextLink'; import useCurrencyForExpensifyCard from '@hooks/useCurrencyForExpensifyCard'; import {useCurrencyListActions} from '@hooks/useCurrencyList'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; @@ -21,9 +22,11 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; +import {createCardFeedKey} from '@libs/CardFeedUtils'; import {getCardSettings} from '@libs/CardUtils'; import getClickedTargetLocation from '@libs/getClickedTargetLocation'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; +import Navigation from '@navigation/Navigation'; import type {WorkspaceSplitNavigatorParamList} from '@navigation/types'; import variables from '@styles/variables'; import {queueExpensifyCardForBilling} from '@userActions/Card'; @@ -31,6 +34,7 @@ import {requestExpensifyCardLimitIncrease} from '@userActions/Policy/Policy'; import {navigateToConciergeChat} from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; type WorkspaceCardsListLabelProps = { @@ -113,6 +117,12 @@ function WorkspaceCardsListLabel({type, value, style}: WorkspaceCardsListLabelPr queueExpensifyCardForBilling(CONST.COUNTRY.US, defaultFundID); }; + const handleViewTransactionsPress = useCallback(() => { + const feedKey = createCardFeedKey(String(defaultFundID), CONST.EXPENSIFY_CARD.BANK); + const query = `type:expense feed:"${feedKey}" withdrawn:never withdrawal-status:pending`; + Navigation.navigate(ROUTES.SEARCH_ROOT.getRoute({query})); + }, [defaultFundID]); + return ( @@ -148,6 +158,14 @@ function WorkspaceCardsListLabel({type, value, style}: WorkspaceCardsListLabelPr )} + {isCurrentBalanceType && ( + + {translate('workspace.common.viewTransactions')} + + )} {isSettleDateTextDisplayed && {translate('workspace.expensifyCard.balanceWillBeSettledOn', settlementDate)}} Date: Wed, 13 May 2026 03:29:56 +0000 Subject: [PATCH 2/4] Preserve missing fund ID when building feed key and fix ts check --- src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx b/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx index 755c6d97d490..af261f4e6e49 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx @@ -118,7 +118,8 @@ function WorkspaceCardsListLabel({type, value, style}: WorkspaceCardsListLabelPr }; const handleViewTransactionsPress = useCallback(() => { - const feedKey = createCardFeedKey(String(defaultFundID), CONST.EXPENSIFY_CARD.BANK); + const fundIDForFeedKey = defaultFundID === CONST.DEFAULT_NUMBER_ID ? undefined : String(defaultFundID); + const feedKey = createCardFeedKey(fundIDForFeedKey, CONST.EXPENSIFY_CARD.BANK, undefined); const query = `type:expense feed:"${feedKey}" withdrawn:never withdrawal-status:pending`; Navigation.navigate(ROUTES.SEARCH_ROOT.getRoute({query})); }, [defaultFundID]); From 508b16c50c566b2836b767f2986f10fb28fd436b Mon Sep 17 00:00:00 2001 From: Neeraj Bachani Date: Thu, 14 May 2026 16:35:02 +0000 Subject: [PATCH 3/4] Address review: plain handler, named search query fragments --- .../expensifyCard/WorkspaceCardsListLabel.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx b/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx index af261f4e6e49..dc764b831de9 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx @@ -1,7 +1,7 @@ import {useRoute} from '@react-navigation/native'; import {hasSeenTourSelector} from '@selectors/Onboarding'; import {addDays, format} from 'date-fns'; -import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import React, {useEffect, useMemo, useRef, useState} from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; import type {ValueOf} from 'type-fest'; @@ -37,6 +37,12 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; +const EXPENSIFY_CARD_TRANSACTIONS_SEARCH_QUERY_FRAGMENTS = { + TYPE_EXPENSE: 'type:expense', + WITHDRAWN_NEVER: 'withdrawn:never', + WITHDRAWAL_STATUS_PENDING: 'withdrawal-status:pending', +} as const; + type WorkspaceCardsListLabelProps = { /** Label type */ type: ValueOf; @@ -117,12 +123,12 @@ function WorkspaceCardsListLabel({type, value, style}: WorkspaceCardsListLabelPr queueExpensifyCardForBilling(CONST.COUNTRY.US, defaultFundID); }; - const handleViewTransactionsPress = useCallback(() => { + const handleViewTransactionsPress = () => { const fundIDForFeedKey = defaultFundID === CONST.DEFAULT_NUMBER_ID ? undefined : String(defaultFundID); const feedKey = createCardFeedKey(fundIDForFeedKey, CONST.EXPENSIFY_CARD.BANK, undefined); - const query = `type:expense feed:"${feedKey}" withdrawn:never withdrawal-status:pending`; + const query = `${EXPENSIFY_CARD_TRANSACTIONS_SEARCH_QUERY_FRAGMENTS.TYPE_EXPENSE} feed:"${feedKey}" ${EXPENSIFY_CARD_TRANSACTIONS_SEARCH_QUERY_FRAGMENTS.WITHDRAWN_NEVER} ${EXPENSIFY_CARD_TRANSACTIONS_SEARCH_QUERY_FRAGMENTS.WITHDRAWAL_STATUS_PENDING}`; Navigation.navigate(ROUTES.SEARCH_ROOT.getRoute({query})); - }, [defaultFundID]); + }; return ( From b6ef76f9b3c349193b0808ac67be685a28ead610 Mon Sep 17 00:00:00 2001 From: Neeraj Bachani Date: Fri, 22 May 2026 19:19:04 +0000 Subject: [PATCH 4/4] Address review: build View transactions search query via filter form Use buildQueryStringFromFilterFormValues instead of manual query fragments. Add unit test for feed + withdrawn:never + withdrawalStatus:pending query shape. --- .../expensifyCard/WorkspaceCardsListLabel.tsx | 14 ++++---- tests/unit/Search/SearchQueryUtilsTest.ts | 33 +++++++++++++++++++ 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx b/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx index dc764b831de9..ec592a82351c 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx @@ -26,6 +26,7 @@ import {createCardFeedKey} from '@libs/CardFeedUtils'; import {getCardSettings} from '@libs/CardUtils'; import getClickedTargetLocation from '@libs/getClickedTargetLocation'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; +import {buildQueryStringFromFilterFormValues} from '@libs/SearchQueryUtils'; import Navigation from '@navigation/Navigation'; import type {WorkspaceSplitNavigatorParamList} from '@navigation/types'; import variables from '@styles/variables'; @@ -37,12 +38,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -const EXPENSIFY_CARD_TRANSACTIONS_SEARCH_QUERY_FRAGMENTS = { - TYPE_EXPENSE: 'type:expense', - WITHDRAWN_NEVER: 'withdrawn:never', - WITHDRAWAL_STATUS_PENDING: 'withdrawal-status:pending', -} as const; - type WorkspaceCardsListLabelProps = { /** Label type */ type: ValueOf; @@ -126,7 +121,12 @@ function WorkspaceCardsListLabel({type, value, style}: WorkspaceCardsListLabelPr const handleViewTransactionsPress = () => { const fundIDForFeedKey = defaultFundID === CONST.DEFAULT_NUMBER_ID ? undefined : String(defaultFundID); const feedKey = createCardFeedKey(fundIDForFeedKey, CONST.EXPENSIFY_CARD.BANK, undefined); - const query = `${EXPENSIFY_CARD_TRANSACTIONS_SEARCH_QUERY_FRAGMENTS.TYPE_EXPENSE} feed:"${feedKey}" ${EXPENSIFY_CARD_TRANSACTIONS_SEARCH_QUERY_FRAGMENTS.WITHDRAWN_NEVER} ${EXPENSIFY_CARD_TRANSACTIONS_SEARCH_QUERY_FRAGMENTS.WITHDRAWAL_STATUS_PENDING}`; + const query = buildQueryStringFromFilterFormValues({ + type: CONST.SEARCH.DATA_TYPES.EXPENSE, + feed: [feedKey], + withdrawnOn: CONST.SEARCH.DATE_PRESETS.NEVER, + withdrawalStatus: [CONST.SEARCH.SETTLEMENT_STATUS.PENDING], + }); Navigation.navigate(ROUTES.SEARCH_ROOT.getRoute({query})); }; diff --git a/tests/unit/Search/SearchQueryUtilsTest.ts b/tests/unit/Search/SearchQueryUtilsTest.ts index 71f3d71667e6..b9d501a52ff4 100644 --- a/tests/unit/Search/SearchQueryUtilsTest.ts +++ b/tests/unit/Search/SearchQueryUtilsTest.ts @@ -447,6 +447,39 @@ describe('SearchQueryUtils', () => { expect(result).toEqual('type:expense withdrawn:last-month'); }); + test('with Expensify Card current balance view transactions filters', () => { + const feedKey = '21557189_Expensify Card'; + const filterValues: Partial = { + type: CONST.SEARCH.DATA_TYPES.EXPENSE, + feed: [feedKey], + withdrawnOn: CONST.SEARCH.DATE_PRESETS.NEVER, + withdrawalStatus: [CONST.SEARCH.SETTLEMENT_STATUS.PENDING], + }; + + const result = buildQueryStringFromFilterFormValues(filterValues); + + expect(result).toEqual('type:expense feed:"21557189_Expensify Card" withdrawalStatus:pending withdrawn:never'); + + const queryJSON = buildSearchQueryJSON(result); + expect(queryJSON?.type).toBe(CONST.SEARCH.DATA_TYPES.EXPENSE); + expect(queryJSON?.flatFilters).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + key: CONST.SEARCH.SYNTAX_FILTER_KEYS.FEED, + filters: [{operator: CONST.SEARCH.SYNTAX_OPERATORS.EQUAL_TO, value: feedKey}], + }), + expect.objectContaining({ + key: CONST.SEARCH.SYNTAX_FILTER_KEYS.WITHDRAWAL_STATUS, + filters: [{operator: CONST.SEARCH.SYNTAX_OPERATORS.EQUAL_TO, value: CONST.SEARCH.SETTLEMENT_STATUS.PENDING}], + }), + expect.objectContaining({ + key: CONST.SEARCH.SYNTAX_FILTER_KEYS.WITHDRAWN, + filters: [{operator: CONST.SEARCH.SYNTAX_OPERATORS.EQUAL_TO, value: CONST.SEARCH.DATE_PRESETS.NEVER}], + }), + ]), + ); + }); + describe('limit option', () => { test('includes limit in query string when provided in form values', () => { const filterValues: Partial = {