From e4bbc3bb35b173709a2e17d84c804bb0f2539c74 Mon Sep 17 00:00:00 2001 From: Tsaqif Date: Thu, 15 Jan 2026 16:49:51 +0700 Subject: [PATCH 01/10] Fix report not displayed in search results after submitting or approving while offline Signed-off-by: Tsaqif --- .../MoneyRequestReportNavigation.tsx | 1 - .../Search/ExpenseReportListItemRow.tsx | 1 + .../Search/StatusCell.tsx | 7 +++++-- .../SelectionListWithSections/types.ts | 3 +++ src/libs/SearchUIUtils.ts | 18 +++++++----------- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx index c2a0d096c1f66..9a9d656b1c38c 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx @@ -56,7 +56,6 @@ function MoneyRequestReportNavigation({reportID, shouldDisplayNarrowVersion}: Mo archivedReportsIDList: archivedReportsIdSet, isActionLoadingSet, cardFeeds, - shouldSkipActionFiltering: true, }); results = getSortedSections(type, status ?? '', searchData, localeCompare, translate, sortBy, sortOrder, groupBy).map((value) => value.reportID); } diff --git a/src/components/SelectionListWithSections/Search/ExpenseReportListItemRow.tsx b/src/components/SelectionListWithSections/Search/ExpenseReportListItemRow.tsx index 5435fe69448ae..2cf992150bdca 100644 --- a/src/components/SelectionListWithSections/Search/ExpenseReportListItemRow.tsx +++ b/src/components/SelectionListWithSections/Search/ExpenseReportListItemRow.tsx @@ -113,6 +113,7 @@ function ExpenseReportListItemRow({ ), diff --git a/src/components/SelectionListWithSections/Search/StatusCell.tsx b/src/components/SelectionListWithSections/Search/StatusCell.tsx index 08b419ae9c022..e9eb9b7c0e004 100644 --- a/src/components/SelectionListWithSections/Search/StatusCell.tsx +++ b/src/components/SelectionListWithSections/Search/StatusCell.tsx @@ -12,9 +12,12 @@ type StatusCellProps = { /** The statusNum of the report */ statusNum?: number; + + /** Whether the report's state/status is pending*/ + isPending?: boolean; }; -function StatusCell({stateNum, statusNum}: StatusCellProps) { +function StatusCell({stateNum, statusNum, isPending}: StatusCellProps) { const styles = useThemeStyles(); const theme = useTheme(); const {translate} = useLocalize(); @@ -41,7 +44,7 @@ function StatusCell({stateNum, statusNum}: StatusCellProps) { } return ( - + {statusText} diff --git a/src/components/SelectionListWithSections/types.ts b/src/components/SelectionListWithSections/types.ts index e41dd3bb3cb7e..2c1b0c3ba2353 100644 --- a/src/components/SelectionListWithSections/types.ts +++ b/src/components/SelectionListWithSections/types.ts @@ -436,6 +436,9 @@ type TransactionReportGroupListItemType = TransactionGroupListItemType & {groupe /** The date the report was exported */ exported?: string; + /** Whether the report's state/status is pending */ + hasPendingReportState?: boolean; + /** * Whether we should show the report year. * This is true if at least one report in the dataset was created in past years diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index 01415c68c99af..196d2aac94cb4 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -156,7 +156,6 @@ type GetReportSectionsParams = { allTransactionViolations: OnyxCollection; bankAccountList: OnyxEntry; reportActions?: Record; - shouldSkipActionFiltering?: boolean; }; const transactionColumnNamesToSortingProperty: TransactionSorting = { @@ -377,7 +376,6 @@ type GetSectionsParams = { isActionLoadingSet?: ReadonlySet; cardFeeds?: OnyxCollection; allTransactionViolations?: OnyxCollection; - shouldSkipActionFiltering?: boolean; }; /** @@ -1727,7 +1725,6 @@ function getReportSections({ allTransactionViolations, bankAccountList, reportActions = {}, - shouldSkipActionFiltering = false, }: GetReportSectionsParams): [TransactionGroupListItemType[], number] { const shouldShowMerchant = getShouldShowMerchant(data); @@ -1745,6 +1742,7 @@ function getReportSections({ const allViolations = getViolations(data); const queryJSON = getCurrentSearchQueryJSON(); + const actionFromQuery = queryJSON?.flatFilters?.find((filter) => filter.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.ACTION)?.filters?.at(0)?.value; const reportIDToTransactions: Record = {}; const {reportKeys, transactionKeys} = Object.keys(data).reduce( @@ -1792,11 +1790,6 @@ function getReportSections({ shouldShow = isValidExpenseStatus(status) ? expenseStatusActionMapping[status](reportItem) : false; } } - const actionFromQuery = queryJSON?.flatFilters?.find((filter) => filter.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.ACTION)?.filters?.at(0)?.value; - - if (!shouldSkipActionFiltering && actionFromQuery && isValidActionFilter(actionFromQuery)) { - shouldShow = shouldShow && actionFilterMapping[actionFromQuery](reportItem); - } } if (shouldShow) { @@ -1812,12 +1805,16 @@ function getReportSections({ const formattedFrom = formatPhoneNumber(getDisplayNameOrDefault(fromDetails)); const formattedTo = !shouldShowBlankTo ? formatPhoneNumber(getDisplayNameOrDefault(toDetails)) : ''; - const formattedStatus = getReportStatusTranslation({stateNum: reportItem.stateNum, statusNum: reportItem.statusNum, translate}); const allReportTransactions = getTransactionsForReport(data, reportItem.reportID); const policy = getPolicyFromKey(data, reportItem); + let hasPendingReportState; + if (actionFromQuery && isValidActionFilter(actionFromQuery)) { + hasPendingReportState = !actionFilterMapping[actionFromQuery](reportItem); + } + const hasAnyViolationsForReport = hasAnyViolations( reportItem.reportID, allTransactionViolations ?? allViolations, @@ -1850,6 +1847,7 @@ function getReportSections({ formattedFrom, formattedTo, formattedStatus, + hasPendingReportState, transactions, ...(reportPendingAction ? {pendingAction: reportPendingAction} : {}), shouldShowYear: shouldShowYearCreatedReport, @@ -2109,7 +2107,6 @@ function getSections({ isActionLoadingSet, cardFeeds, allTransactionViolations, - shouldSkipActionFiltering, }: GetSectionsParams) { if (type === CONST.SEARCH.DATA_TYPES.CHAT) { return getReportActionsSections(data); @@ -2130,7 +2127,6 @@ function getSections({ allTransactionViolations, bankAccountList, reportActions, - shouldSkipActionFiltering, }); } From 6e6691d8b7c768932276b1f34e8892a9994575d9 Mon Sep 17 00:00:00 2001 From: Tsaqif Date: Thu, 15 Jan 2026 21:52:58 +0700 Subject: [PATCH 02/10] Narrow screen related changes Signed-off-by: Tsaqif --- src/components/ReportSearchHeader/index.tsx | 6 +- src/components/ReportSearchHeader/types.ts | 3 +- .../Search/ExpenseReportListItemRow.tsx | 2 +- .../Search/StatusCell.tsx | 2 +- .../SelectionListWithSections/types.ts | 2 +- src/libs/SearchUIUtils.ts | 6 +- tests/unit/Search/SearchUIUtilsTest.ts | 67 ------------------- 7 files changed, 13 insertions(+), 75 deletions(-) diff --git a/src/components/ReportSearchHeader/index.tsx b/src/components/ReportSearchHeader/index.tsx index 1c2969b56ee2f..fcc4f74ae76cd 100755 --- a/src/components/ReportSearchHeader/index.tsx +++ b/src/components/ReportSearchHeader/index.tsx @@ -9,6 +9,10 @@ function ReportSearchHeader({report, style, transactions, avatarBorderColor}: Re const styles = useThemeStyles(); const {isLargeScreenWidth} = useResponsiveLayout(); + const statusContainerStyle = useMemo(() => { + return [isLargeScreenWidth ? styles.mt1 : styles.mt0Half, report?.isReportStatePending && styles.offlineFeedbackPending]; + }, [isLargeScreenWidth, styles.mt1, styles.mt0Half, report?.isReportStatePending, styles.offlineFeedbackPending]); + const middleContent = useMemo(() => { return ( ); }, [report, transactions, avatarBorderColor, styles.fontWeightNormal, styles.textLineHeightNormal, styles.minHeight4, styles.mt1, isLargeScreenWidth, styles.textMicro, styles.mt0Half]); diff --git a/src/components/ReportSearchHeader/types.ts b/src/components/ReportSearchHeader/types.ts index 8f82b3529a9e6..9a41b6ed6d57f 100644 --- a/src/components/ReportSearchHeader/types.ts +++ b/src/components/ReportSearchHeader/types.ts @@ -1,11 +1,12 @@ import type {ColorValue, StyleProp, ViewStyle} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import type {TransactionListItemType} from '@components/SelectionListWithSections/types'; +import type {ExpenseReportListItemType} from '@components/SelectionListWithSections/types'; import type {Report} from '@src/types/onyx'; type ReportSearchHeaderProps = { /** Report, if we're showing the details for one and using AvatarWithDisplay */ - report?: OnyxEntry; + report?: ExpenseReportListItemType, /** Additional styles to add to the component */ style?: StyleProp; diff --git a/src/components/SelectionListWithSections/Search/ExpenseReportListItemRow.tsx b/src/components/SelectionListWithSections/Search/ExpenseReportListItemRow.tsx index 2cf992150bdca..5699c94fb54b5 100644 --- a/src/components/SelectionListWithSections/Search/ExpenseReportListItemRow.tsx +++ b/src/components/SelectionListWithSections/Search/ExpenseReportListItemRow.tsx @@ -113,7 +113,7 @@ function ExpenseReportListItemRow({ ), diff --git a/src/components/SelectionListWithSections/Search/StatusCell.tsx b/src/components/SelectionListWithSections/Search/StatusCell.tsx index e9eb9b7c0e004..76e4e5227194b 100644 --- a/src/components/SelectionListWithSections/Search/StatusCell.tsx +++ b/src/components/SelectionListWithSections/Search/StatusCell.tsx @@ -13,7 +13,7 @@ type StatusCellProps = { /** The statusNum of the report */ statusNum?: number; - /** Whether the report's state/status is pending*/ + /** Whether the report's state/status is pending */ isPending?: boolean; }; diff --git a/src/components/SelectionListWithSections/types.ts b/src/components/SelectionListWithSections/types.ts index 2c1b0c3ba2353..5f439fcca4c8e 100644 --- a/src/components/SelectionListWithSections/types.ts +++ b/src/components/SelectionListWithSections/types.ts @@ -437,7 +437,7 @@ type TransactionReportGroupListItemType = TransactionGroupListItemType & {groupe exported?: string; /** Whether the report's state/status is pending */ - hasPendingReportState?: boolean; + isReportStatePending?: boolean; /** * Whether we should show the report year. diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index 196d2aac94cb4..e7f9cdb40d1d7 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -1810,9 +1810,9 @@ function getReportSections({ const allReportTransactions = getTransactionsForReport(data, reportItem.reportID); const policy = getPolicyFromKey(data, reportItem); - let hasPendingReportState; + let isReportStatePending; if (actionFromQuery && isValidActionFilter(actionFromQuery)) { - hasPendingReportState = !actionFilterMapping[actionFromQuery](reportItem); + isReportStatePending = !actionFilterMapping[actionFromQuery](reportItem); } const hasAnyViolationsForReport = hasAnyViolations( @@ -1847,7 +1847,7 @@ function getReportSections({ formattedFrom, formattedTo, formattedStatus, - hasPendingReportState, + isReportStatePending, transactions, ...(reportPendingAction ? {pendingAction: reportPendingAction} : {}), shouldShowYear: shouldShowYearCreatedReport, diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index 7d9d0a74caa63..b61c49ef8e913 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -2240,73 +2240,6 @@ describe('SearchUIUtils', () => { }); }); - describe('Test getSections with shouldSkipActionFiltering option', () => { - beforeEach(() => { - // Mock getCurrentSearchQueryJSON to return a query with action filter - (SearchQueryUtils.getCurrentSearchQueryJSON as jest.Mock).mockReturnValue({ - type: 'expense-report', - filters: { - operator: 'and', - left: {operator: 'eq', left: 'action', right: 'approve'}, - }, - flatFilters: [ - { - key: 'action', - filters: [{operator: 'eq', value: 'approve'}], - }, - ], - }); - }); - - afterEach(() => { - (SearchQueryUtils.getCurrentSearchQueryJSON as jest.Mock).mockClear(); - }); - - it('should return all expense reports when shouldSkipActionFiltering is true', () => { - const result = SearchUIUtils.getSections({ - type: CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT, - data: searchResults.data, - currentAccountID: 2074551, - currentUserEmail: '', - translate: translateLocal, - formatPhoneNumber, - bankAccountList: {}, - shouldSkipActionFiltering: true, - })[0] as TransactionGroupListItemType[]; - - expect(result.length).toBe(4); // All expense reports returned - }); - - it('should return only filtered expense reports when shouldSkipActionFiltering is false', () => { - const result = SearchUIUtils.getSections({ - type: CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT, - data: searchResults.data, - currentAccountID: 2074551, - currentUserEmail: '', - translate: translateLocal, - formatPhoneNumber, - bankAccountList: {}, - shouldSkipActionFiltering: false, - })[0] as TransactionGroupListItemType[]; - - expect(result.length).toBe(2); // Only filtered expense reports returned - }); - - it('should apply default filtering behavior when shouldSkipActionFiltering is undefined', () => { - const result = SearchUIUtils.getSections({ - type: CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT, - data: searchResults.data, - currentAccountID: 2074551, - currentUserEmail: '', - translate: translateLocal, - formatPhoneNumber, - bankAccountList: {}, - })[0] as TransactionGroupListItemType[]; - - expect(result.length).toBe(2); // Default behavior applies filtering - }); - }); - describe('Test getSortedSections', () => { it('should return getSortedReportActionData result when type is CHAT', () => { const sortedActions = SearchUIUtils.getSortedSections( From 098dab8bc436ea5e163e6a08658abe5913f5e387 Mon Sep 17 00:00:00 2001 From: Tsaqif Date: Thu, 15 Jan 2026 22:28:17 +0700 Subject: [PATCH 03/10] Fix tests Signed-off-by: Tsaqif --- src/components/ReportSearchHeader/index.tsx | 13 ++++++++++++- src/components/ReportSearchHeader/types.ts | 7 ++----- tests/unit/Search/SearchUIUtilsTest.ts | 5 ----- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/components/ReportSearchHeader/index.tsx b/src/components/ReportSearchHeader/index.tsx index fcc4f74ae76cd..a0162ff13355c 100755 --- a/src/components/ReportSearchHeader/index.tsx +++ b/src/components/ReportSearchHeader/index.tsx @@ -28,7 +28,18 @@ function ReportSearchHeader({report, style, transactions, avatarBorderColor}: Re parentNavigationStatusContainerStyles={statusContainerStyle} /> ); - }, [report, transactions, avatarBorderColor, styles.fontWeightNormal, styles.textLineHeightNormal, styles.minHeight4, styles.mt1, isLargeScreenWidth, styles.textMicro, styles.mt0Half]); + }, [ + report, + transactions, + avatarBorderColor, + styles.fontWeightNormal, + styles.textLineHeightNormal, + styles.minHeight4, + styles.mt1, + isLargeScreenWidth, + styles.textMicro, + statusContainerStyle, + ]); return ( ; diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index b61c49ef8e913..dee7b31e7c281 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -25,7 +25,6 @@ import {setOptimisticDataForTransactionThreadPreview} from '@userActions/Search' import CONST from '@src/CONST'; import IntlStore from '@src/languages/IntlStore'; import type {CardFeedForDisplay} from '@src/libs/CardFeedUtils'; -import * as SearchQueryUtils from '@src/libs/SearchQueryUtils'; import * as SearchUIUtils from '@src/libs/SearchUIUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -48,10 +47,6 @@ jest.mock('@userActions/Search', () => ({ ...jest.requireActual('@userActions/Search'), setOptimisticDataForTransactionThreadPreview: jest.fn(), })); -jest.mock('@src/libs/SearchQueryUtils', () => ({ - ...jest.requireActual('@src/libs/SearchQueryUtils'), - getCurrentSearchQueryJSON: jest.fn(), -})); const adminAccountID = 18439984; const adminEmail = 'admin@policy.com'; From f0a984ef862b0a41a0607ee43b857c7a433a1eba Mon Sep 17 00:00:00 2001 From: Tsaqif Date: Fri, 16 Jan 2026 08:09:39 +0700 Subject: [PATCH 04/10] Using report pendingFields Signed-off-by: Tsaqif --- src/components/Search/index.tsx | 1 + src/libs/ReportUtils.ts | 11 ++++++++++- src/libs/SearchUIUtils.ts | 18 +++--------------- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 4c6c8414bb778..12aace0e6dcda 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -436,6 +436,7 @@ function Search({ return [filteredData1, filteredData1.length, allLength]; }, [ searchKey, + isFocused, exportReportActions, validGroupBy, isDataLoaded, diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index f15613ff14ef4..b7b62c0c1970f 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -64,7 +64,7 @@ import type {ReportTransactionsAndViolations} from '@src/types/onyx/DerivedValue import type {Attendee, Participant} from '@src/types/onyx/IOU'; import type {OriginalMessageExportedToIntegration} from '@src/types/onyx/OldDotAction'; import type Onboarding from '@src/types/onyx/Onboarding'; -import type {ErrorFields, Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; +import type {ErrorFields, Errors, Icon, PendingAction, PendingFields} from '@src/types/onyx/OnyxCommon'; import type { OriginalMessageChangeLog, OriginalMessageChangePolicy, @@ -6119,6 +6119,14 @@ function getPendingChatMembers(accountIDs: number[], previousPendingChatMembers: return [...previousPendingChatMembers, ...pendingChatMembers]; } +/** + * Get pendingFields of a report + */ +function getReportPendingFields(reportID: string): PendingFields | undefined { + const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + return report?.pendingFields; +} + /** * Gets the parent navigation subtitle for the report */ @@ -12999,6 +13007,7 @@ export { getParticipantsList, getParticipants, getPendingChatMembers, + getReportPendingFields, getPersonalDetailsForAccountID, getPolicyDescriptionText, getPolicyExpenseChat, diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index e7f9cdb40d1d7..e0e65a68712f2 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -96,6 +96,7 @@ import { getPolicyName, getReportName, getReportOrDraftReport, + getReportPendingFields, getReportStatusTranslation, getSearchReportName, hasAnyViolations, @@ -242,21 +243,10 @@ const expenseStatusActionMapping = { [CONST.SEARCH.STATUS.EXPENSE.ALL]: () => true, }; -const actionFilterMapping = { - [CONST.SEARCH.ACTION_FILTERS.SUBMIT]: expenseStatusActionMapping[CONST.SEARCH.STATUS.EXPENSE.DRAFTS], - [CONST.SEARCH.ACTION_FILTERS.APPROVE]: expenseStatusActionMapping[CONST.SEARCH.STATUS.EXPENSE.OUTSTANDING], - [CONST.SEARCH.ACTION_FILTERS.PAY]: expenseStatusActionMapping[CONST.SEARCH.STATUS.EXPENSE.APPROVED], - [CONST.SEARCH.ACTION_FILTERS.EXPORT]: () => true, -}; - function isValidExpenseStatus(status: unknown): status is ValueOf { return typeof status === 'string' && status in expenseStatusActionMapping; } -function isValidActionFilter(action: unknown): action is ValueOf { - return typeof action === 'string' && action in actionFilterMapping; -} - function getExpenseStatusOptions(translate: LocalizedTranslate): Array> { return [ {text: translate('common.unreported'), value: CONST.SEARCH.STATUS.EXPENSE.UNREPORTED}, @@ -1810,10 +1800,8 @@ function getReportSections({ const allReportTransactions = getTransactionsForReport(data, reportItem.reportID); const policy = getPolicyFromKey(data, reportItem); - let isReportStatePending; - if (actionFromQuery && isValidActionFilter(actionFromQuery)) { - isReportStatePending = !actionFilterMapping[actionFromQuery](reportItem); - } + const pendingFields = getReportPendingFields(reportItem.reportID); + const isReportStatePending = !!pendingFields?.nextStep; const hasAnyViolationsForReport = hasAnyViolations( reportItem.reportID, From a38c7231272d5386f7875fa28c4527a894ef53f5 Mon Sep 17 00:00:00 2001 From: Tsaqif Date: Fri, 16 Jan 2026 09:05:50 +0700 Subject: [PATCH 05/10] Remove unused const Signed-off-by: Tsaqif --- src/libs/SearchUIUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index e0e65a68712f2..09fbd2ba1307e 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -1732,7 +1732,6 @@ function getReportSections({ const allViolations = getViolations(data); const queryJSON = getCurrentSearchQueryJSON(); - const actionFromQuery = queryJSON?.flatFilters?.find((filter) => filter.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.ACTION)?.filters?.at(0)?.value; const reportIDToTransactions: Record = {}; const {reportKeys, transactionKeys} = Object.keys(data).reduce( From c88293087e829b4b8dc4033b123233d9330eefd3 Mon Sep 17 00:00:00 2001 From: Tsaqif Date: Fri, 16 Jan 2026 09:23:55 +0700 Subject: [PATCH 06/10] Compare with nextStep update Signed-off-by: Tsaqif --- src/libs/SearchUIUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index 09fbd2ba1307e..3504ade38cce2 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -1800,7 +1800,7 @@ function getReportSections({ const policy = getPolicyFromKey(data, reportItem); const pendingFields = getReportPendingFields(reportItem.reportID); - const isReportStatePending = !!pendingFields?.nextStep; + const isReportStatePending = pendingFields?.nextStep === CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE; const hasAnyViolationsForReport = hasAnyViolations( reportItem.reportID, From d3b4799b271caac65bf6b5397c24ff5d18caa7a0 Mon Sep 17 00:00:00 2001 From: Tsaqif Date: Fri, 16 Jan 2026 10:43:06 +0700 Subject: [PATCH 07/10] Fix test Signed-off-by: Tsaqif --- tests/unit/Search/SearchUIUtilsTest.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index dee7b31e7c281..28d90ebb1c4b4 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -1031,6 +1031,7 @@ const transactionReportGroupListItems = [ }, hasVisibleViolations: false, isOneTransactionReport: true, + isReportStatePending: false, isWaitingOnBankAccount: false, keyForList: '123456789', managerID: 18439984, @@ -1132,6 +1133,7 @@ const transactionReportGroupListItems = [ }, hasVisibleViolations: true, isOneTransactionReport: true, + isReportStatePending: false, isWaitingOnBankAccount: false, keyForList: '11111', managerID: 18439984, @@ -1240,6 +1242,7 @@ const transactionReportGroupListItems = [ formattedTo: 'Approver', hasVisibleViolations: false, isOneTransactionReport: false, + isReportStatePending: false, isOwnPolicyExpenseChat: false, isWaitingOnBankAccount: false, managerID: 1111111, @@ -1420,6 +1423,7 @@ const transactionReportGroupListItems = [ }, hasVisibleViolations: false, isOneTransactionReport: true, + isReportStatePending: false, isWaitingOnBankAccount: false, keyForList: reportID5, managerID: 18439984, From ffaa8bea292e8bb42eb5982089498be64534fc0d Mon Sep 17 00:00:00 2001 From: Tsaqif Date: Wed, 28 Jan 2026 09:40:58 +0700 Subject: [PATCH 08/10] Using pending fields live data Signed-off-by: Tsaqif --- src/components/Search/index.tsx | 1 - src/libs/ReportUtils.ts | 11 +---------- src/libs/SearchUIUtils.ts | 5 ++--- 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 8cb07ac1d058b..6034026dc50f8 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -425,7 +425,6 @@ function Search({ return [filteredData1, filteredData1.length, allLength]; }, [ searchKey, - isFocused, exportReportActions, validGroupBy, isDataLoaded, diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index a89157e2043db..4aab135bb547f 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -64,7 +64,7 @@ import type {ReportTransactionsAndViolations} from '@src/types/onyx/DerivedValue import type {Attendee, Participant} from '@src/types/onyx/IOU'; import type {OriginalMessageExportedToIntegration} from '@src/types/onyx/OldDotAction'; import type Onboarding from '@src/types/onyx/Onboarding'; -import type {ErrorFields, Errors, Icon, PendingAction, PendingFields} from '@src/types/onyx/OnyxCommon'; +import type {ErrorFields, Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; import type { OriginalMessageChangeLog, OriginalMessageChangePolicy, @@ -6213,14 +6213,6 @@ function getPendingChatMembers(accountIDs: number[], previousPendingChatMembers: return [...previousPendingChatMembers, ...pendingChatMembers]; } -/** - * Get pendingFields of a report - */ -function getReportPendingFields(reportID: string): PendingFields | undefined { - const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; - return report?.pendingFields; -} - /** * Gets the parent navigation subtitle for the report */ @@ -13103,7 +13095,6 @@ export { getParticipantsList, getParticipants, getPendingChatMembers, - getReportPendingFields, getPersonalDetailsForAccountID, getPolicyDescriptionText, getPolicyExpenseChat, diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index de314f13bb10c..fe7e102e90d99 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -104,7 +104,6 @@ import { getPolicyName, getReportName, getReportOrDraftReport, - getReportPendingFields, getReportStatusTranslation, getSearchReportName, hasAnyViolations, @@ -1905,14 +1904,14 @@ function getReportSections({ const formattedFrom = formatPhoneNumber(getDisplayNameOrDefault(fromDetails)); const formattedTo = !shouldShowBlankTo ? formatPhoneNumber(getDisplayNameOrDefault(toDetails)) : ''; + const formattedStatus = getReportStatusTranslation({stateNum: reportItem.stateNum, statusNum: reportItem.statusNum, translate}); const allReportTransactions = getTransactionsForReport(data, reportItem.reportID); const policyFromKey = getPolicyFromKey(data, reportItem); const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${reportItem?.policyID ?? String(CONST.DEFAULT_NUMBER_ID)}`] ?? policyFromKey; - const pendingFields = getReportPendingFields(reportItem.reportID); - const isReportStatePending = pendingFields?.nextStep === CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE; + const isReportStatePending = reportItem?.pendingFields?.nextStep === CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE; const hasAnyViolationsForReport = hasAnyViolations( reportItem.reportID, From 4cbebe166fa29b54b53042b68948e38d7d6df893 Mon Sep 17 00:00:00 2001 From: Tsaqif Date: Thu, 29 Jan 2026 08:02:40 +0700 Subject: [PATCH 09/10] add offline check Signed-off-by: Tsaqif --- src/components/ReportSearchHeader/index.tsx | 4 ++-- src/components/Search/index.tsx | 2 ++ .../Search/ExpenseReportListItemRow.tsx | 2 +- src/components/SelectionListWithSections/types.ts | 4 ++-- src/libs/SearchUIUtils.ts | 9 +++++++-- tests/unit/Search/SearchUIUtilsTest.ts | 8 ++++---- 6 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/components/ReportSearchHeader/index.tsx b/src/components/ReportSearchHeader/index.tsx index a0162ff13355c..b6f3d61744066 100755 --- a/src/components/ReportSearchHeader/index.tsx +++ b/src/components/ReportSearchHeader/index.tsx @@ -10,8 +10,8 @@ function ReportSearchHeader({report, style, transactions, avatarBorderColor}: Re const {isLargeScreenWidth} = useResponsiveLayout(); const statusContainerStyle = useMemo(() => { - return [isLargeScreenWidth ? styles.mt1 : styles.mt0Half, report?.isReportStatePending && styles.offlineFeedbackPending]; - }, [isLargeScreenWidth, styles.mt1, styles.mt0Half, report?.isReportStatePending, styles.offlineFeedbackPending]); + return [isLargeScreenWidth ? styles.mt1 : styles.mt0Half, report?.shouldShowStatusAsPending && styles.offlineFeedbackPending]; + }, [isLargeScreenWidth, styles.mt1, styles.mt0Half, report?.shouldShowStatusAsPending, styles.offlineFeedbackPending]); const middleContent = useMemo(() => { return ( diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 6034026dc50f8..b97180a550b20 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -420,11 +420,13 @@ function Search({ queryJSON, isActionLoadingSet, cardFeeds, + isOffline, allTransactionViolations: violations, }); return [filteredData1, filteredData1.length, allLength]; }, [ searchKey, + isOffline, exportReportActions, validGroupBy, isDataLoaded, diff --git a/src/components/SelectionListWithSections/Search/ExpenseReportListItemRow.tsx b/src/components/SelectionListWithSections/Search/ExpenseReportListItemRow.tsx index aedebb9f82377..7b26ae1f927c1 100644 --- a/src/components/SelectionListWithSections/Search/ExpenseReportListItemRow.tsx +++ b/src/components/SelectionListWithSections/Search/ExpenseReportListItemRow.tsx @@ -113,7 +113,7 @@ function ExpenseReportListItemRow({ ), diff --git a/src/components/SelectionListWithSections/types.ts b/src/components/SelectionListWithSections/types.ts index 7e4baa104cb03..c7047a42c664e 100644 --- a/src/components/SelectionListWithSections/types.ts +++ b/src/components/SelectionListWithSections/types.ts @@ -448,8 +448,8 @@ type TransactionReportGroupListItemType = TransactionGroupListItemType & {groupe /** The date the report was exported */ exported?: string; - /** Whether the report's state/status is pending */ - isReportStatePending?: boolean; + /** Whether the status field should be shown in a pending state */ + shouldShowStatusAsPending?: boolean; /** * Whether we should show the report year. diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index fe7e102e90d99..f32ec56ea21e6 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -165,6 +165,7 @@ type GetReportSectionsParams = { translate: LocalizedTranslate; formatPhoneNumber: LocaleContextProps['formatPhoneNumber']; isActionLoadingSet: ReadonlySet | undefined; + isOffline: boolean | undefined; allTransactionViolations: OnyxCollection; bankAccountList: OnyxEntry; reportActions?: Record; @@ -405,6 +406,7 @@ type GetSectionsParams = { archivedReportsIDList?: ArchivedReportsIDSet; queryJSON?: SearchQueryJSON; isActionLoadingSet?: ReadonlySet; + isOffline?: boolean; cardFeeds?: OnyxCollection; allTransactionViolations?: OnyxCollection; }; @@ -1820,6 +1822,7 @@ function getReportSections({ currentAccountID, currentUserEmail, translate, + isOffline, formatPhoneNumber, isActionLoadingSet, allTransactionViolations, @@ -1911,7 +1914,7 @@ function getReportSections({ const policyFromKey = getPolicyFromKey(data, reportItem); const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${reportItem?.policyID ?? String(CONST.DEFAULT_NUMBER_ID)}`] ?? policyFromKey; - const isReportStatePending = reportItem?.pendingFields?.nextStep === CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE; + const shouldShowStatusAsPending = isOffline && reportItem?.pendingFields?.nextStep === CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE; const hasAnyViolationsForReport = hasAnyViolations( reportItem.reportID, @@ -1945,8 +1948,8 @@ function getReportSections({ formattedFrom, formattedTo, formattedStatus, - isReportStatePending, transactions, + shouldShowStatusAsPending, ...(reportPendingAction ? {pendingAction: reportPendingAction} : {}), shouldShowYear: shouldShowYearCreatedReport, shouldShowYearSubmitted: shouldShowYearSubmittedReport, @@ -2355,6 +2358,7 @@ function getSections({ archivedReportsIDList, queryJSON, isActionLoadingSet, + isOffline, cardFeeds, allTransactionViolations, }: GetSectionsParams) { @@ -2373,6 +2377,7 @@ function getSections({ currentAccountID, currentUserEmail, translate, + isOffline, formatPhoneNumber, isActionLoadingSet, allTransactionViolations, diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index e61b58fb218da..91b1c9ad23044 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -1069,7 +1069,7 @@ const transactionReportGroupListItems = [ }, hasVisibleViolations: false, isOneTransactionReport: true, - isReportStatePending: false, + shouldShowStatusAsPending: false, isWaitingOnBankAccount: false, keyForList: '123456789', managerID: 18439984, @@ -1171,7 +1171,7 @@ const transactionReportGroupListItems = [ }, hasVisibleViolations: true, isOneTransactionReport: true, - isReportStatePending: false, + shouldShowStatusAsPending: false, isWaitingOnBankAccount: false, keyForList: '11111', managerID: 18439984, @@ -1280,7 +1280,7 @@ const transactionReportGroupListItems = [ formattedTo: 'Approver', hasVisibleViolations: false, isOneTransactionReport: false, - isReportStatePending: false, + shouldShowStatusAsPending: false, isOwnPolicyExpenseChat: false, isWaitingOnBankAccount: false, managerID: 1111111, @@ -1461,7 +1461,7 @@ const transactionReportGroupListItems = [ }, hasVisibleViolations: false, isOneTransactionReport: true, - isReportStatePending: false, + shouldShowStatusAsPending: false, isWaitingOnBankAccount: false, keyForList: reportID5, managerID: 18439984, From 8bd70a3527f4df2e961a34ee9ace9396e1aa600e Mon Sep 17 00:00:00 2001 From: Tsaqif Date: Thu, 29 Jan 2026 09:46:18 +0700 Subject: [PATCH 10/10] Fix test Signed-off-by: Tsaqif --- src/libs/SearchUIUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index b3975ed63e212..7a5afc9897309 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -1957,7 +1957,7 @@ function getReportSections({ const policyFromKey = getPolicyFromKey(data, reportItem); const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${reportItem?.policyID ?? String(CONST.DEFAULT_NUMBER_ID)}`] ?? policyFromKey; - const shouldShowStatusAsPending = isOffline && reportItem?.pendingFields?.nextStep === CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE; + const shouldShowStatusAsPending = !!isOffline && reportItem?.pendingFields?.nextStep === CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE; const hasAnyViolationsForReport = hasAnyViolations( reportItem.reportID,