diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 553c2928cb95c..0a16220696a33 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -260,6 +260,7 @@ function Search({ const [visibleColumns] = useOnyx(ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM, {selector: columnsSelector}); const [customCardNames] = useOnyx(ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES); const [cardList] = useOnyx(ONYXKEYS.CARD_LIST); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const isExpenseReportType = type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT; const {markReportIDAsMultiTransactionExpense, unmarkReportIDAsMultiTransactionExpense} = useWideRHPActions(); @@ -483,6 +484,7 @@ function Search({ customCardNames, allReportMetadata, cardList, + conciergeReportID, onyxPersonalDetailsList, }); return [filteredData1, filteredData1.length, allLength]; @@ -509,6 +511,7 @@ function Search({ customCardNames, allReportMetadata, cardList, + conciergeReportID, onyxPersonalDetailsList, ]); @@ -546,6 +549,7 @@ function Search({ cardFeeds, allReportMetadata, cardList, + conciergeReportID, }); return { ...item, @@ -569,6 +573,7 @@ function Search({ bankAccountList, allReportMetadata, cardList, + conciergeReportID, ]); const hasLoadedAllTransactions = useMemo(() => { diff --git a/src/components/SelectionListWithSections/Search/TransactionGroupListItem.tsx b/src/components/SelectionListWithSections/Search/TransactionGroupListItem.tsx index d048fb30b92d6..69ead1923bc19 100644 --- a/src/components/SelectionListWithSections/Search/TransactionGroupListItem.tsx +++ b/src/components/SelectionListWithSections/Search/TransactionGroupListItem.tsx @@ -116,6 +116,7 @@ function TransactionGroupListItem({ const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); const [cardFeeds] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER); const [cardList] = useOnyx(ONYXKEYS.CARD_LIST); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); let transactions: TransactionListItemType[]; if (isExpenseReportType) { @@ -135,6 +136,7 @@ function TransactionGroupListItem({ allReportMetadata, cardFeeds, cardList, + conciergeReportID, }) as [TransactionListItemType[], number]; transactions = sectionData.map((transactionItem) => ({ ...transactionItem, diff --git a/src/hooks/useSearchSections.ts b/src/hooks/useSearchSections.ts index 91a65110fd04a..446f2c3101d65 100644 --- a/src/hooks/useSearchSections.ts +++ b/src/hooks/useSearchSections.ts @@ -33,6 +33,7 @@ function useSearchSections(): UseSearchSectionsResult { const [cardList] = useOnyx(ONYXKEYS.CARD_LIST); const archivedReportsIdSet = useArchivedReportsIdSet(); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const {type, status, sortBy, sortOrder, groupBy} = lastSearchQuery?.queryJSON ?? {}; const searchResultsData = currentSearchResults?.data; @@ -59,6 +60,7 @@ function useSearchSections(): UseSearchSectionsResult { cardFeeds, allReportMetadata, cardList, + conciergeReportID, }); allReports = getSortedSections(type, status ?? '', searchData, localeCompare, translate, sortBy, sortOrder, groupBy).map((value) => value.reportID); } diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index a3c5b1643924c..82a20a5235eb0 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -496,6 +496,7 @@ type GetSectionsParams = { allTransactionViolations?: OnyxCollection; visibleReportActionsData?: OnyxTypes.VisibleReportActionsDerivedValue; allReportMetadata: OnyxCollection; + conciergeReportID: string | undefined; onyxPersonalDetailsList?: OnyxTypes.PersonalDetailsList; }; @@ -2025,6 +2026,7 @@ function getActions( function getTaskSections( data: OnyxTypes.SearchResults['data'], formatPhoneNumber: LocaleContextProps['formatPhoneNumber'], + conciergeReportID: string | undefined, archivedReportsIDList?: ArchivedReportsIDSet, ): [TaskListItemType[], number] { const {shouldShowYearCreated} = shouldShowYear(data); @@ -2064,7 +2066,7 @@ function getTaskSections( const policy = data[`${ONYXKEYS.COLLECTION.POLICY}${parentReport.policyID}`]; const isParentReportArchived = archivedReportsIDList?.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${parentReport?.reportID}`); // eslint-disable-next-line @typescript-eslint/no-deprecated - const parentReportName = getReportName({report: parentReport, policy, isReportArchived: isParentReportArchived}); + const parentReportName = getReportName({report: parentReport, policy, isReportArchived: isParentReportArchived, conciergeReportID}); const icons = getIcons(parentReport, formatPhoneNumber, personalDetails, null, '', -1, policy, undefined, isParentReportArchived); const parentReportIcon = icons?.at(0); @@ -2919,13 +2921,14 @@ function getSections({ visibleReportActionsData, allReportMetadata, cardList, + conciergeReportID, onyxPersonalDetailsList, }: GetSectionsParams) { if (type === CONST.SEARCH.DATA_TYPES.CHAT) { return getReportActionsSections(data, visibleReportActionsData); } if (type === CONST.SEARCH.DATA_TYPES.TASK) { - return getTaskSections(data, formatPhoneNumber, archivedReportsIDList); + return getTaskSections(data, formatPhoneNumber, conciergeReportID, archivedReportsIDList); } if (type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT) { diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index 6c4bc59fc5e9b..944d1c60a9fea 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -10,6 +10,7 @@ import TransactionGroupListItem from '@components/SelectionListWithSections/Sear import TransactionListItem from '@components/SelectionListWithSections/Search/TransactionListItem'; import type { ReportActionListItemType, + TaskListItemType, TransactionCardGroupListItemType, TransactionCategoryGroupListItemType, TransactionGroupListItemType, @@ -2469,6 +2470,7 @@ describe('SearchUIUtils', () => { formatPhoneNumber, bankAccountList: {}, allReportMetadata: {}, + conciergeReportID: undefined, }); expect(filteredReportActions).toStrictEqual(reportActionListItems); expect(allReportActionsLength).toBe(6); @@ -2485,6 +2487,7 @@ describe('SearchUIUtils', () => { formatPhoneNumber, bankAccountList: {}, allReportMetadata: {}, + conciergeReportID: undefined, })[0], ).toEqual(transactionsListItems); }); @@ -2512,6 +2515,7 @@ describe('SearchUIUtils', () => { formatPhoneNumber, bankAccountList: {}, allReportMetadata: {}, + conciergeReportID: undefined, })[0] as TransactionListItemType[]; const distanceTransaction = result.find((item) => item.transactionID === distanceTransactionID); @@ -2546,6 +2550,7 @@ describe('SearchUIUtils', () => { formatPhoneNumber, bankAccountList: {}, allReportMetadata: {}, + conciergeReportID: undefined, })[0] as TransactionGroupListItemType[]; const reportGroup = result.find((group) => group.transactions?.some((transaction) => transaction.transactionID === distanceTransactionID)); @@ -2570,6 +2575,7 @@ describe('SearchUIUtils', () => { formatPhoneNumber, bankAccountList: {}, allReportMetadata: {}, + conciergeReportID: undefined, })[0], ).toStrictEqual(transactionReportGroupListItems); }); @@ -2605,6 +2611,7 @@ describe('SearchUIUtils', () => { formatPhoneNumber, bankAccountList: {}, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionReportGroupListItemType[], number]; expect(resultWithoutOnyx.at(0)?.primaryAvatar?.source).not.toBe(customAvatarUrl); @@ -2619,6 +2626,7 @@ describe('SearchUIUtils', () => { formatPhoneNumber, bankAccountList: {}, allReportMetadata: {}, + conciergeReportID: undefined, onyxPersonalDetailsList, }) as [TransactionReportGroupListItemType[], number]; @@ -2658,6 +2666,7 @@ describe('SearchUIUtils', () => { formatPhoneNumber, bankAccountList: {}, allReportMetadata: {}, + conciergeReportID: undefined, onyxPersonalDetailsList, }) as [TransactionReportGroupListItemType[], number]; @@ -2700,6 +2709,7 @@ describe('SearchUIUtils', () => { formatPhoneNumber, bankAccountList: {}, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionReportGroupListItemType[], number]; const reportWithoutOnyx = resultWithoutOnyx.find((item) => item.reportID === reportID2); @@ -2714,6 +2724,7 @@ describe('SearchUIUtils', () => { formatPhoneNumber, bankAccountList: {}, allReportMetadata: {}, + conciergeReportID: undefined, onyxPersonalDetailsList, }) as [TransactionReportGroupListItemType[], number]; @@ -2747,6 +2758,7 @@ describe('SearchUIUtils', () => { formatPhoneNumber, bankAccountList: {}, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionReportGroupListItemType[], number]; const reportGroupWithoutOnyx = resultWithoutOnyx.find((item) => item.reportID === reportID); @@ -2762,6 +2774,7 @@ describe('SearchUIUtils', () => { formatPhoneNumber, bankAccountList: {}, allReportMetadata: {}, + conciergeReportID: undefined, onyxPersonalDetailsList, }) as [TransactionReportGroupListItemType[], number]; @@ -2801,6 +2814,7 @@ describe('SearchUIUtils', () => { translate: translateLocal, formatPhoneNumber, bankAccountList: {}, + conciergeReportID: undefined, allReportMetadata: {}, onyxPersonalDetailsList, }) as [TransactionReportGroupListItemType[], number]; @@ -2848,6 +2862,7 @@ describe('SearchUIUtils', () => { formatPhoneNumber, bankAccountList: {}, allReportMetadata: {}, + conciergeReportID: undefined, })[0]; const resultReportFirst = SearchUIUtils.getSections({ type: CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT, @@ -2858,6 +2873,7 @@ describe('SearchUIUtils', () => { formatPhoneNumber, bankAccountList: {}, allReportMetadata: {}, + conciergeReportID: undefined, })[0]; expect(resultTransactionFirst).toBeDefined(); @@ -2882,6 +2898,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.FROM, allReportMetadata: {}, + conciergeReportID: undefined, })[0], ).toStrictEqual(transactionMemberGroupListItems); }); @@ -2910,6 +2927,7 @@ describe('SearchUIUtils', () => { cardFeeds: mockCardFeeds, cardList: cardListMock, allReportMetadata: {}, + conciergeReportID: undefined, })[0], ).toStrictEqual(transactionCardGroupListItems); }); @@ -2926,6 +2944,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID, allReportMetadata: {}, + conciergeReportID: undefined, })[0], ).toStrictEqual(transactionWithdrawalIDGroupListItems); }); @@ -2951,6 +2970,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionWithdrawalIDGroupListItemType[], number]; expect(result).toHaveLength(0); @@ -2968,6 +2988,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.CATEGORY, allReportMetadata: {}, + conciergeReportID: undefined, })[0], ).toStrictEqual(transactionCategoryGroupListItems); }); @@ -2999,6 +3020,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.CATEGORY, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionCategoryGroupListItemType[], number]; expect(result).toHaveLength(2); @@ -3060,6 +3082,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.MONTH, allReportMetadata: {}, + conciergeReportID: undefined, })[0], ).toStrictEqual(transactionMonthGroupListItems); }); @@ -3093,6 +3116,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.MONTH, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionMonthGroupListItemType[], number]; expect(result).toHaveLength(2); @@ -3111,6 +3135,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.MONTH, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionMonthGroupListItemType[], number]; expect(result).toHaveLength(2); @@ -3187,6 +3212,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.YEAR, allReportMetadata: {}, + conciergeReportID: undefined, })[0], ).toStrictEqual(transactionYearGroupListItems); }); @@ -3218,6 +3244,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.YEAR, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionYearGroupListItemType[], number]; expect(result).toHaveLength(2); @@ -3236,6 +3263,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.YEAR, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionYearGroupListItemType[], number]; expect(result).toHaveLength(2); @@ -3605,6 +3633,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.QUARTER, allReportMetadata: {}, + conciergeReportID: undefined, })[0], ).toStrictEqual(transactionQuarterGroupListItems); }); @@ -3638,6 +3667,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.QUARTER, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionQuarterGroupListItemType[], number]; expect(result).toHaveLength(2); @@ -3656,6 +3686,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.QUARTER, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionQuarterGroupListItemType[], number]; expect(result).toHaveLength(2); @@ -3714,6 +3745,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.WEEK, allReportMetadata: {}, + conciergeReportID: undefined, })[0], ).toStrictEqual(transactionWeekGroupListItems); }); @@ -3745,6 +3777,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.WEEK, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionWeekGroupListItemType[], number]; expect(result).toHaveLength(2); @@ -3799,6 +3832,7 @@ describe('SearchUIUtils', () => { right: CONST.SEARCH.DATA_TYPES.EXPENSE, }, }, + conciergeReportID: undefined, }) as [TransactionCategoryGroupListItemType[], number]; // Each category section should have a transactionsQueryJSON with a hash @@ -3842,6 +3876,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.CATEGORY, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionCategoryGroupListItemType[], number]; expect(result).toHaveLength(3); @@ -3883,6 +3918,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.CATEGORY, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionCategoryGroupListItemType[], number]; expect(result).toHaveLength(3); @@ -3913,6 +3949,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.CATEGORY, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionCategoryGroupListItemType[], number]; expect(result).toHaveLength(1); @@ -3948,6 +3985,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.CATEGORY, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionCategoryGroupListItemType[], number]; expect(result).toHaveLength(2); @@ -3972,6 +4010,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.MERCHANT, allReportMetadata: {}, + conciergeReportID: undefined, })[0], ).toStrictEqual(transactionMerchantGroupListItems); }); @@ -4004,6 +4043,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.MERCHANT, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionMerchantGroupListItemType[], number]; expect(result).toHaveLength(2); @@ -4050,6 +4090,7 @@ describe('SearchUIUtils', () => { right: CONST.SEARCH.DATA_TYPES.EXPENSE, }, }, + conciergeReportID: undefined, }) as [TransactionMerchantGroupListItemType[], number]; expect(result).toHaveLength(1); @@ -4098,6 +4139,7 @@ describe('SearchUIUtils', () => { right: CONST.SEARCH.DATA_TYPES.EXPENSE, }, }, + conciergeReportID: undefined, }) as [TransactionMerchantGroupListItemType[], number]; expect(result).toHaveLength(1); @@ -4146,6 +4188,7 @@ describe('SearchUIUtils', () => { right: CONST.SEARCH.DATA_TYPES.EXPENSE, }, }, + conciergeReportID: undefined, }) as [TransactionMerchantGroupListItemType[], number]; expect(result).toHaveLength(1); @@ -4194,6 +4237,7 @@ describe('SearchUIUtils', () => { right: CONST.SEARCH.DATA_TYPES.EXPENSE, }, }, + conciergeReportID: undefined, }) as [TransactionMerchantGroupListItemType[], number]; expect(result).toHaveLength(1); @@ -4261,6 +4305,7 @@ describe('SearchUIUtils', () => { right: CONST.SEARCH.DATA_TYPES.EXPENSE, }, }, + conciergeReportID: undefined, }) as [TransactionMerchantGroupListItemType[], number]; // Each merchant section should have a transactionsQueryJSON with a hash @@ -4305,6 +4350,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.MERCHANT, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionMerchantGroupListItemType[], number]; expect(result).toHaveLength(3); @@ -4347,6 +4393,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.MERCHANT, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionMerchantGroupListItemType[], number]; expect(result).toHaveLength(3); @@ -4368,6 +4415,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.TAG, allReportMetadata: {}, + conciergeReportID: undefined, })[0], ).toStrictEqual(transactionTagGroupListItems); }); @@ -4395,6 +4443,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.TAG, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionTagGroupListItemType[], number]; // formattedTag should have unescaped colons for display @@ -4430,6 +4479,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.TAG, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionTagGroupListItemType[], number]; expect(result).toHaveLength(2); @@ -4458,6 +4508,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.TAG, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionTagGroupListItemType[], number]; expect(result).toHaveLength(1); @@ -4522,6 +4573,7 @@ describe('SearchUIUtils', () => { right: CONST.SEARCH.DATA_TYPES.EXPENSE, }, }, + conciergeReportID: undefined, }) as [TransactionTagGroupListItemType[], number]; // Each tag section should have a transactionsQueryJSON with a hash @@ -4532,6 +4584,315 @@ describe('SearchUIUtils', () => { } }); + it('should return getTaskSections result when type is TASK', () => { + const taskReportID = 'task_report_100'; + const parentReportID = 'parent_report_200'; + const taskCreatorAccountID = 11111; + const taskAssigneeAccountID = 22222; + + const taskReport = { + type: CONST.REPORT.TYPE.TASK, + reportID: taskReportID, + reportName: 'Fix the login bug', + description: 'The login page has a bug that needs fixing', + accountID: taskCreatorAccountID, + managerID: taskAssigneeAccountID, + parentReportID, + stateNum: CONST.REPORT.STATE_NUM.OPEN, + statusNum: CONST.REPORT.STATUS_NUM.OPEN, + created: '2025-01-15 10:00:00', + } as const; + + const taskData: OnyxTypes.SearchResults['data'] = { + personalDetailsList: { + [taskCreatorAccountID]: { + accountID: taskCreatorAccountID, + avatar: '', + displayName: 'Task Creator', + login: 'creator@test.com', + }, + [taskAssigneeAccountID]: { + accountID: taskAssigneeAccountID, + avatar: '', + displayName: 'Task Assignee', + login: 'assignee@test.com', + }, + }, + [`report_${taskReportID}`]: taskReport, + }; + + const [result, count] = SearchUIUtils.getSections({ + type: CONST.SEARCH.DATA_TYPES.TASK, + data: taskData, + currentAccountID: taskCreatorAccountID, + currentUserEmail: 'creator@test.com', + translate: translateLocal, + formatPhoneNumber, + bankAccountList: {}, + allReportMetadata: {}, + conciergeReportID: '999', + }) as [TaskListItemType[], number]; + + expect(result).toHaveLength(1); + expect(count).toBe(1); + expect(result.at(0)?.reportName).toBe('Fix the login bug'); + // formatPhoneNumber replaces regular spaces with non-breaking spaces (\u00A0) + expect(result.at(0)?.formattedAssignee).toBe('Task\u00A0Assignee'); + expect(result.at(0)?.formattedCreatedBy).toBe('Task\u00A0Creator'); + expect(result.at(0)?.keyForList).toBe(taskReportID); + }); + + it('should return empty task sections when no task reports exist in data', () => { + const nonTaskData: OnyxTypes.SearchResults['data'] = { + personalDetailsList: {}, + }; + + const [result, count] = SearchUIUtils.getSections({ + type: CONST.SEARCH.DATA_TYPES.TASK, + data: nonTaskData, + currentAccountID: 1, + currentUserEmail: 'test@test.com', + translate: translateLocal, + formatPhoneNumber, + bankAccountList: {}, + allReportMetadata: {}, + conciergeReportID: '999', + }) as [TaskListItemType[], number]; + + expect(result).toHaveLength(0); + expect(count).toBe(0); + }); + + it('should handle multiple tasks in getSections with TASK type', () => { + const taskReportID1 = 'task_report_301'; + const taskReportID2 = 'task_report_302'; + const creatorID = 33333; + const assigneeID = 44444; + + const taskReportA = { + type: CONST.REPORT.TYPE.TASK, + reportID: taskReportID1, + reportName: 'First task', + description: 'First task description', + accountID: creatorID, + managerID: assigneeID, + parentReportID: 'parent_1', + stateNum: CONST.REPORT.STATE_NUM.OPEN, + statusNum: CONST.REPORT.STATUS_NUM.OPEN, + created: '2025-01-10 10:00:00', + } as const; + + const taskReportB = { + type: CONST.REPORT.TYPE.TASK, + reportID: taskReportID2, + reportName: 'Second task', + description: 'Second task description', + accountID: assigneeID, + managerID: creatorID, + parentReportID: 'parent_2', + stateNum: CONST.REPORT.STATE_NUM.OPEN, + statusNum: CONST.REPORT.STATUS_NUM.OPEN, + created: '2025-01-11 10:00:00', + } as const; + + const multiTaskData: OnyxTypes.SearchResults['data'] = { + personalDetailsList: { + [creatorID]: { + accountID: creatorID, + avatar: '', + displayName: 'Creator User', + login: 'creator@test.com', + }, + [assigneeID]: { + accountID: assigneeID, + avatar: '', + displayName: 'Assignee User', + login: 'assignee@test.com', + }, + }, + [`report_${taskReportID1}`]: taskReportA, + [`report_${taskReportID2}`]: taskReportB, + }; + + const [result, count] = SearchUIUtils.getSections({ + type: CONST.SEARCH.DATA_TYPES.TASK, + data: multiTaskData, + currentAccountID: creatorID, + currentUserEmail: 'creator@test.com', + translate: translateLocal, + formatPhoneNumber, + bankAccountList: {}, + allReportMetadata: {}, + conciergeReportID: '999', + }) as [TaskListItemType[], number]; + + expect(result).toHaveLength(2); + expect(count).toBe(2); + expect(result.some((item) => item.reportName === 'First task')).toBe(true); + expect(result.some((item) => item.reportName === 'Second task')).toBe(true); + }); + + it('should pass conciergeReportID through to getTaskSections and strip HTML from task names', () => { + const taskReportID = 'task_report_400'; + const creatorID = 55555; + const assigneeID = 66666; + + const htmlTaskReport = { + type: CONST.REPORT.TYPE.TASK, + reportID: taskReportID, + reportName: 'Bold task name', + description: 'Italic description', + accountID: creatorID, + managerID: assigneeID, + parentReportID: 'parent_html', + stateNum: CONST.REPORT.STATE_NUM.OPEN, + statusNum: CONST.REPORT.STATUS_NUM.OPEN, + created: '2025-01-20 10:00:00', + } as const; + + const taskDataWithHTML: OnyxTypes.SearchResults['data'] = { + personalDetailsList: { + [creatorID]: { + accountID: creatorID, + avatar: '', + displayName: 'HTML Creator', + login: 'creator@test.com', + }, + [assigneeID]: { + accountID: assigneeID, + avatar: '', + displayName: 'HTML Assignee', + login: 'assignee@test.com', + }, + }, + [`report_${taskReportID}`]: htmlTaskReport, + }; + + const [result] = SearchUIUtils.getSections({ + type: CONST.SEARCH.DATA_TYPES.TASK, + data: taskDataWithHTML, + currentAccountID: creatorID, + currentUserEmail: 'creator@test.com', + translate: translateLocal, + formatPhoneNumber, + bankAccountList: {}, + allReportMetadata: {}, + conciergeReportID: '999', + }) as [TaskListItemType[], number]; + + expect(result).toHaveLength(1); + // HTML should be stripped from reportName and description + expect(result.at(0)?.reportName).toBe('Bold task name'); + expect(result.at(0)?.description).toBe('Italic description'); + }); + + it('should filter out non-task reports when type is TASK', () => { + const taskReportID = 'task_report_500'; + const nonTaskReportID = 'non_task_report_501'; + const creatorID = 77777; + + const realTaskReport = { + type: CONST.REPORT.TYPE.TASK, + reportID: taskReportID, + reportName: 'A real task', + description: 'This is a task', + accountID: creatorID, + managerID: creatorID, + parentReportID: 'parent_mixed', + stateNum: CONST.REPORT.STATE_NUM.OPEN, + statusNum: CONST.REPORT.STATUS_NUM.OPEN, + created: '2025-01-25 10:00:00', + } as const; + + const mixedData: OnyxTypes.SearchResults['data'] = { + personalDetailsList: { + [creatorID]: { + accountID: creatorID, + avatar: '', + displayName: 'Mixed Creator', + login: 'creator@test.com', + }, + }, + [`report_${taskReportID}`]: realTaskReport, + [`report_${nonTaskReportID}`]: { + type: CONST.REPORT.TYPE.CHAT, + reportID: nonTaskReportID, + reportName: 'Not a task', + }, + }; + + const [result, count] = SearchUIUtils.getSections({ + type: CONST.SEARCH.DATA_TYPES.TASK, + data: mixedData, + currentAccountID: creatorID, + currentUserEmail: 'creator@test.com', + translate: translateLocal, + formatPhoneNumber, + bankAccountList: {}, + allReportMetadata: {}, + conciergeReportID: '999', + }) as [TaskListItemType[], number]; + + // Only the task report should be included + expect(result).toHaveLength(1); + expect(count).toBe(1); + expect(result.at(0)?.reportName).toBe('A real task'); + }); + + it('should handle task with conciergeReportID as undefined', () => { + const taskReportID = 'task_report_600'; + const creatorID = 88888; + const assigneeID = 99999; + + const noConciergeTaskReport = { + type: CONST.REPORT.TYPE.TASK, + reportID: taskReportID, + reportName: 'Task without concierge', + description: 'No concierge here', + accountID: creatorID, + managerID: assigneeID, + parentReportID: 'parent_no_concierge', + stateNum: CONST.REPORT.STATE_NUM.OPEN, + statusNum: CONST.REPORT.STATUS_NUM.OPEN, + created: '2025-02-01 10:00:00', + } as const; + + const taskData: OnyxTypes.SearchResults['data'] = { + personalDetailsList: { + [creatorID]: { + accountID: creatorID, + avatar: '', + displayName: 'Creator', + login: 'creator@test.com', + }, + [assigneeID]: { + accountID: assigneeID, + avatar: '', + displayName: 'Assignee', + login: 'assignee@test.com', + }, + }, + [`report_${taskReportID}`]: noConciergeTaskReport, + }; + + // conciergeReportID is undefined, simulating the case where it hasn't been loaded from Onyx yet + const [result, count] = SearchUIUtils.getSections({ + type: CONST.SEARCH.DATA_TYPES.TASK, + data: taskData, + currentAccountID: creatorID, + currentUserEmail: 'creator@test.com', + translate: translateLocal, + formatPhoneNumber, + bankAccountList: {}, + allReportMetadata: {}, + conciergeReportID: undefined, + }) as [TaskListItemType[], number]; + + expect(result).toHaveLength(1); + expect(count).toBe(1); + expect(result.at(0)?.reportName).toBe('Task without concierge'); + }); + describe('getReportSections computed fields (totalDisplaySpend, nonReimbursableSpend, reimbursableSpend, isAllScanning)', () => { const testReportID = 'spend-test-report'; const testTxID1 = 'spend-tx-1'; @@ -4573,6 +4934,7 @@ describe('SearchUIUtils', () => { formatPhoneNumber, bankAccountList: {}, allReportMetadata: {}, + conciergeReportID: undefined, })[0] as TransactionReportGroupListItemType[]; const item = sections.find((s) => s.keyForList === testReportID); @@ -4686,6 +5048,7 @@ describe('SearchUIUtils', () => { formatPhoneNumber, bankAccountList: {}, allReportMetadata: {}, + conciergeReportID: undefined, })[0] as TransactionReportGroupListItemType[]; const item = sections.find((s) => s.keyForList === avatarTestReportID); @@ -5193,6 +5556,7 @@ describe('SearchUIUtils', () => { bankAccountList: {}, groupBy: CONST.SEARCH.GROUP_BY.TAG, allReportMetadata: {}, + conciergeReportID: undefined, }) as [TransactionTagGroupListItemType[], number]; // Then sort the sections