diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 87f1fe8f9ff3b..f9245ba79a349 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -1283,6 +1283,20 @@ function Search({ endSpanWithAttributes(CONST.TELEMETRY.SPAN_NAVIGATE_TO_REPORTS, {[CONST.TELEMETRY.ATTRIBUTE_IS_WARM]: true}); }, []); + const measurements = useMemo(() => { + if (!filteredDataLength) { + return undefined; + } + + const firstItem = filteredData.at(0); + + if (firstItem && isTransactionListItemType(firstItem)) { + return firstItem.measurements; + } + + return undefined; + }, [filteredDataLength, filteredData]); + // On re-visits, react-freeze serves the cached layout — onLayout/onLayoutSkeleton never fire. // useFocusEffect fires on unfreeze, which is when the screen becomes visible. useFocusEffect( @@ -1429,6 +1443,7 @@ function Search({ !shouldShowTableHeader ? undefined : ( ({ columns, isLoading, violations, - customCardNames, onDEWModalOpen, isDEWBetaEnabled, lastPaymentMethod, @@ -110,14 +109,6 @@ function TransactionListItem({ backgroundColor: theme.highlightBG, }); - const amountColumnSize = transactionItem.isAmountColumnWide ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL; - const taxAmountColumnSize = transactionItem.isTaxAmountColumnWide ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL; - const dateColumnSize = transactionItem.shouldShowYear ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL; - const submittedColumnSize = transactionItem.shouldShowYearSubmitted ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL; - const approvedColumnSize = transactionItem.shouldShowYearApproved ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL; - const postedColumnSize = transactionItem.shouldShowYearPosted ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL; - const exportedColumnSize = transactionItem.shouldShowYearExported ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL; - // Prefer live Onyx policy data over snapshot to ensure fresh policy settings // like isAttendeeTrackingEnabled is not missing // Use snapshotReport/snapshotPolicy as fallbacks to fix offline issues where @@ -217,20 +208,12 @@ function TransactionListItem({ isActionLoading={isLoading ?? isActionLoading} isSelected={!!transactionItem.isSelected} isDisabled={!!isDisabled} - dateColumnSize={dateColumnSize} - submittedColumnSize={submittedColumnSize} - approvedColumnSize={approvedColumnSize} - postedColumnSize={postedColumnSize} - exportedColumnSize={exportedColumnSize} - amountColumnSize={amountColumnSize} - taxAmountColumnSize={taxAmountColumnSize} shouldShowCheckbox={!!canSelectMultiple} checkboxSentryLabel={CONST.SENTRY_LABEL.SEARCH.TRANSACTION_LIST_ITEM_CHECKBOX} style={[styles.p3, styles.pv2, shouldUseNarrowLayout ? styles.pt2 : {}]} violations={transactionViolations} onArrowRightPress={() => onSelectRow(item, transactionPreviewData)} isHover={hovered} - customCardNames={customCardNames} reportActions={exportedReportActions} /> diff --git a/src/components/SelectionListWithSections/SearchTableHeader.tsx b/src/components/SelectionListWithSections/SearchTableHeader.tsx index 7d05263b54a09..c4fe51b2d98a3 100644 --- a/src/components/SelectionListWithSections/SearchTableHeader.tsx +++ b/src/components/SelectionListWithSections/SearchTableHeader.tsx @@ -9,7 +9,7 @@ import type {TranslationPaths} from '@src/languages/types'; import type {SearchDataTypes} from '@src/types/onyx/SearchResults'; import type IconAsset from '@src/types/utils/IconAsset'; import SortableTableHeader from './SortableTableHeader'; -import type {SortableColumnName} from './types'; +import type {SortableColumnName, TransactionColumnMeasurements} from './types'; type SearchColumnConfig = { columnName: SearchColumnType; @@ -422,6 +422,7 @@ function getSearchColumns(type: ValueOf, icons: } type SearchTableHeaderProps = { + measurements?: TransactionColumnMeasurements; columns: SortableColumnName[]; type: SearchDataTypes; sortBy?: SearchColumnType; @@ -445,6 +446,7 @@ type SearchTableHeaderProps = { function SearchTableHeader({ columns, type, + measurements, sortBy, sortOrder, onSortPress, @@ -517,6 +519,7 @@ function SearchTableHeader({ return ( onSortPress(columnName, order)} /> diff --git a/src/components/SelectionListWithSections/types.ts b/src/components/SelectionListWithSections/types.ts index c240285c3ae6f..4022a4ae3aa8a 100644 --- a/src/components/SelectionListWithSections/types.ts +++ b/src/components/SelectionListWithSections/types.ts @@ -255,8 +255,84 @@ type ListItem = { isIndeterminate?: boolean; }; +// Columns that are shown in plain text +type SearchTextColumns = + | typeof CONST.SEARCH.TABLE_COLUMNS.MERCHANT + | typeof CONST.SEARCH.TABLE_COLUMNS.CATEGORY + | typeof CONST.SEARCH.TABLE_COLUMNS.TAG + | typeof CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT + | typeof CONST.SEARCH.TABLE_COLUMNS.EXCHANGE_RATE + | typeof CONST.SEARCH.TABLE_COLUMNS.DESCRIPTION + | typeof CONST.SEARCH.TABLE_COLUMNS.CARD + | typeof CONST.SEARCH.TABLE_COLUMNS.BILLABLE + | typeof CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE + | typeof CONST.SEARCH.TABLE_COLUMNS.TITLE + | typeof CONST.SEARCH.TABLE_COLUMNS.TAX_RATE + | typeof CONST.SEARCH.TABLE_COLUMNS.TAX_AMOUNT + | typeof CONST.SEARCH.TABLE_COLUMNS.REPORT_ID + | typeof CONST.SEARCH.TABLE_COLUMNS.BASE_62_REPORT_ID + | typeof CONST.SEARCH.TABLE_COLUMNS.ORIGINAL_AMOUNT + | typeof CONST.SEARCH.TABLE_COLUMNS.DATE + | typeof CONST.SEARCH.TABLE_COLUMNS.EXPORTED + | typeof CONST.SEARCH.TABLE_COLUMNS.SUBMITTED + | typeof CONST.SEARCH.TABLE_COLUMNS.APPROVED + | typeof CONST.SEARCH.TABLE_COLUMNS.POSTED + | typeof CONST.SEARCH.TABLE_COLUMNS.TOTAL + | typeof CONST.SEARCH.TABLE_COLUMNS.WITHDRAWAL_ID + | typeof CONST.SEARCH.TABLE_COLUMNS.EXPENSES + | typeof CONST.SEARCH.TABLE_COLUMNS.FEED + | typeof CONST.SEARCH.TABLE_COLUMNS.WITHDRAWN + | typeof CONST.SEARCH.TABLE_COLUMNS.BANK_ACCOUNT + | typeof CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE_TOTAL + | typeof CONST.SEARCH.TABLE_COLUMNS.NON_REIMBURSABLE_TOTAL + | typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_EXPENSES + | typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_TOTAL + | typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_CARD + | typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_FEED + | typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_BANK_ACCOUNT + | typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_WITHDRAWN + | typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_WITHDRAWAL_ID + | typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_CATEGORY + | typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_MERCHANT + | typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_TAG + | typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_MONTH + | typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_WEEK + | typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_YEAR + | typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_QUARTER + | typeof CONST.SEARCH.TABLE_COLUMNS.GROUP_WITHDRAWAL_ID; + +type SearchTransactionTextColumns = Extract>; + +type SearchExpenseReportTextColumns = Extract>; + +type SearchCardGroupingColumns = Extract; + +type SearchFromGroupingColumns = Extract; + +type SearchWithdrawalIDGroupingColumns = Extract; + +type SearchCategoryGroupingColumns = Extract; + +type SearchMerchantGroupingColumns = Extract; + +type SearchTagGroupingColumns = Extract; + +type SearchMonthGroupingColumns = Extract; + +type SearchWeekGroupingColumns = Extract; + +type SearchYearGroupingColumns = Extract; + +type SearchQuarterGroupingColumns = Extract; + type TransactionListItemType = ListItem & Transaction & { + /** The measurements for each column */ + measurements: Record; + + /** Formatted values for each column */ + formattedValues: Record; + /** Report to which the transaction belongs */ report: Report | undefined; @@ -439,6 +515,12 @@ type TransactionGroupListItemType = ListItem & { }; type TransactionReportGroupListItemType = TransactionGroupListItemType & {groupedBy: typeof CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT} & Report & { + /** The measurements for each column */ + measurements: Record; + + /** Formatted values for each column */ + formattedValues: Record; + /** The personal details of the user requesting money */ from: PersonalDetails; @@ -514,11 +596,23 @@ type TransactionReportGroupListItemType = TransactionGroupListItemType & {groupe type TransactionMemberGroupListItemType = TransactionGroupListItemType & {groupedBy: typeof CONST.SEARCH.GROUP_BY.FROM} & PersonalDetails & SearchMemberGroup & { + /** The measurements for each column */ + measurements: Record; + + /** Formatted values for each column */ + formattedValues: Record; + /** Final and formatted "from" value used for displaying and sorting */ formattedFrom?: string; }; type TransactionMonthGroupListItemType = TransactionGroupListItemType & {groupedBy: typeof CONST.SEARCH.GROUP_BY.MONTH} & SearchMonthGroup & { + /** The measurements for each column */ + measurements: Record; + + /** Formatted values for each column */ + formattedValues: Record; + /** Final and formatted "month" value used for displaying */ formattedMonth: string; @@ -528,6 +622,12 @@ type TransactionMonthGroupListItemType = TransactionGroupListItemType & {grouped type TransactionCardGroupListItemType = TransactionGroupListItemType & {groupedBy: typeof CONST.SEARCH.GROUP_BY.CARD} & PersonalDetails & SearchCardGroup & { + /** The measurements for each column */ + measurements: Record; + + /** Formatted values for each column */ + formattedValues: Record; + /** Final and formatted "cardName" value used for displaying and sorting */ formattedCardName?: string; @@ -536,31 +636,67 @@ type TransactionCardGroupListItemType = TransactionGroupListItemType & {groupedB }; type TransactionWithdrawalIDGroupListItemType = TransactionGroupListItemType & {groupedBy: typeof CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID} & SearchWithdrawalIDGroup & { + /** The measurements for each column */ + measurements: Record; + + /** Formatted values for each column */ + formattedValues: Record; + /** Final and formatted "withdrawalID" value used for displaying and sorting */ formattedWithdrawalID?: string; }; type TransactionCategoryGroupListItemType = TransactionGroupListItemType & {groupedBy: typeof CONST.SEARCH.GROUP_BY.CATEGORY} & SearchCategoryGroup & { + /** The measurements for each column */ + measurements: Record; + + /** Formatted values for each column */ + formattedValues: Record; + /** Final and formatted "category" value used for displaying and sorting */ formattedCategory?: string; }; type TransactionMerchantGroupListItemType = TransactionGroupListItemType & {groupedBy: typeof CONST.SEARCH.GROUP_BY.MERCHANT} & SearchMerchantGroup & { + /** The measurements for each column */ + measurements: Record; + + /** Formatted values for each column */ + formattedValues: Record; + /** Final and formatted "merchant" value used for displaying and sorting */ formattedMerchant?: string; }; type TransactionTagGroupListItemType = TransactionGroupListItemType & {groupedBy: typeof CONST.SEARCH.GROUP_BY.TAG} & SearchTagGroup & { + /** The measurements for each column */ + measurements: Record; + + /** Formatted values for each column */ + formattedValues: Record; + /** Final and formatted "tag" value used for displaying and sorting */ formattedTag?: string; }; type TransactionWeekGroupListItemType = TransactionGroupListItemType & {groupedBy: typeof CONST.SEARCH.GROUP_BY.WEEK} & SearchWeekGroup & { + /** The measurements for each column */ + measurements: Record; + + /** Formatted values for each column */ + formattedValues: Record; + /** Final and formatted "week" value used for displaying */ formattedWeek: string; }; type TransactionYearGroupListItemType = TransactionGroupListItemType & {groupedBy: typeof CONST.SEARCH.GROUP_BY.YEAR} & SearchYearGroup & { + /** The measurements for each column */ + measurements: Record; + + /** Formatted values for each column */ + formattedValues: Record; + /** Final and formatted "year" value used for displaying */ formattedYear: string; @@ -569,6 +705,12 @@ type TransactionYearGroupListItemType = TransactionGroupListItemType & {groupedB }; type TransactionQuarterGroupListItemType = TransactionGroupListItemType & {groupedBy: typeof CONST.SEARCH.GROUP_BY.QUARTER} & SearchQuarterGroup & { + /** The measurements for each column */ + measurements: Record; + + /** Formatted values for each column */ + formattedValues: Record; + /** Final and formatted "quarter" value used for displaying */ formattedQuarter: string; @@ -686,7 +828,6 @@ type TransactionListItemProps = ListItemProps & { isLoading?: boolean; columns?: SearchColumnType[]; violations?: Record | undefined; - customCardNames?: Record; /** Callback to fire when DEW modal should be opened */ onDEWModalOpen?: () => void; /** Whether the DEW beta flag is enabled */ @@ -1260,4 +1401,17 @@ export type { SortableColumnName, SearchListItem, UnreportedExpenseListItemType, + SearchTextColumns as TransactionTextColumns, + SearchTransactionTextColumns, + SearchExpenseReportTextColumns, + SearchCardGroupingColumns, + SearchFromGroupingColumns, + SearchWithdrawalIDGroupingColumns, + SearchCategoryGroupingColumns, + SearchMerchantGroupingColumns, + SearchTagGroupingColumns, + SearchMonthGroupingColumns, + SearchWeekGroupingColumns, + SearchYearGroupingColumns, + SearchQuarterGroupingColumns, }; diff --git a/src/components/TransactionItemRow/index.tsx b/src/components/TransactionItemRow/index.tsx index 13a5de8f1debd..4826fe4cd2a0c 100644 --- a/src/components/TransactionItemRow/index.tsx +++ b/src/components/TransactionItemRow/index.tsx @@ -6,15 +6,15 @@ import Icon from '@components/Icon'; import type {TransactionWithOptionalHighlight} from '@components/MoneyRequestReportView/MoneyRequestReportTransactionList'; import {PressableWithFeedback} from '@components/Pressable'; import RadioButton from '@components/RadioButton'; -import type {SearchColumnType, TableColumnSize} from '@components/Search/types'; +import type {SearchColumnType} from '@components/Search/types'; import ActionCell from '@components/SelectionListWithSections/Search/ActionCell'; import DateCell from '@components/SelectionListWithSections/Search/DateCell'; import ExportedIconCell from '@components/SelectionListWithSections/Search/ExportedIconCell'; import StatusCell from '@components/SelectionListWithSections/Search/StatusCell'; import TextCell from '@components/SelectionListWithSections/Search/TextCell'; -import AmountCell from '@components/SelectionListWithSections/Search/TotalCell'; import UserInfoCell from '@components/SelectionListWithSections/Search/UserInfoCell'; import WorkspaceCell from '@components/SelectionListWithSections/Search/WorkspaceCell'; +import type {TransactionListItemType} from '@components/SelectionListWithSections/types'; import Text from '@components/Text'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; @@ -23,25 +23,17 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {isCategoryMissing} from '@libs/CategoryUtils'; -import getBase62ReportID from '@libs/getBase62ReportID'; import {getIOUActionForTransactionID} from '@libs/ReportActionsUtils'; -import {getReportName} from '@libs/ReportNameUtils'; -import {isExpenseReport, isIOUReport, isSettled} from '@libs/ReportUtils'; +import {isIOUReport, isSettled} from '@libs/ReportUtils'; import StringUtils from '@libs/StringUtils'; import { getDescription, - getExchangeRate, getMerchant, - getOriginalAmountForDisplay, - getOriginalCurrencyForDisplay, - getReimbursable, - getTaxName, getCreated as getTransactionCreated, hasMissingSmartscanFields, isAmountMissing, isMerchantMissing, isScanning, - isTimeRequest, isUnreportedAndHasInvalidDistanceRateTransaction, } from '@libs/TransactionUtils'; import CONST from '@src/CONST'; @@ -53,7 +45,6 @@ import ChatBubbleCell from './DataCells/ChatBubbleCell'; import MerchantOrDescriptionCell from './DataCells/MerchantCell'; import ReceiptCell from './DataCells/ReceiptCell'; import TagCell from './DataCells/TagCell'; -import TaxCell from './DataCells/TaxCell'; import TotalCell from './DataCells/TotalCell'; import TypeCell from './DataCells/TypeCell'; import TransactionItemRowRBR from './TransactionItemRowRBR'; @@ -106,19 +97,12 @@ type TransactionWithOptionalSearchFields = TransactionWithOptionalHighlight & { }; type TransactionItemRowProps = { - transactionItem: TransactionWithOptionalSearchFields; + transactionItem: TransactionListItemType; report?: Report; policy?: Policy; shouldUseNarrowLayout: boolean; isSelected: boolean; shouldShowTooltip: boolean; - dateColumnSize: TableColumnSize; - submittedColumnSize?: TableColumnSize; - approvedColumnSize?: TableColumnSize; - postedColumnSize?: TableColumnSize; - exportedColumnSize?: TableColumnSize; - amountColumnSize: TableColumnSize; - taxAmountColumnSize: TableColumnSize; onCheckboxPress?: (transactionID: string) => void; shouldShowCheckbox?: boolean; columns?: SearchColumnType[]; @@ -137,7 +121,6 @@ type TransactionItemRowProps = { onArrowRightPress?: () => void; isHover?: boolean; shouldShowArrowRightOnNarrowLayout?: boolean; - customCardNames?: Record; reportActions?: ReportAction[]; checkboxSentryLabel?: string; }; @@ -164,13 +147,6 @@ function TransactionItemRow({ shouldUseNarrowLayout, isSelected, shouldShowTooltip, - dateColumnSize, - submittedColumnSize, - approvedColumnSize, - postedColumnSize, - exportedColumnSize, - amountColumnSize, - taxAmountColumnSize, onCheckboxPress = () => {}, shouldShowCheckbox = false, columns, @@ -189,7 +165,6 @@ function TransactionItemRow({ onArrowRightPress, isHover = false, shouldShowArrowRightOnNarrowLayout, - customCardNames, reportActions, checkboxSentryLabel, }: TransactionItemRowProps) { @@ -201,15 +176,10 @@ function TransactionItemRow({ const hasCategoryOrTag = !isCategoryMissing(transactionItem?.category) || !!transactionItem.tag; const createdAt = getTransactionCreated(transactionItem); const expensicons = useMemoizedLazyExpensifyIcons(['ArrowRight']); - const transactionThreadReportID = reportActions ? getIOUActionForTransactionID(reportActions, transactionItem.transactionID)?.childReportID : undefined; - const isDateColumnWide = dateColumnSize === CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE; - const isSubmittedColumnWide = submittedColumnSize === CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE; - const isApprovedColumnWide = approvedColumnSize === CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE; - const isPostedColumnWide = postedColumnSize === CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE; - const isExportedColumnWide = exportedColumnSize === CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE; - const isAmountColumnWide = amountColumnSize === CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE; - const isTaxAmountColumnWide = taxAmountColumnSize === CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE; + const measurements = transactionItem.measurements; + const formattedValues = transactionItem.formattedValues; + const transactionThreadReportID = reportActions ? getIOUActionForTransactionID(reportActions, transactionItem.transactionID)?.childReportID : undefined; const bgActiveStyles = useMemo(() => { if (!isSelected || !shouldHighlightItemWhenSelected) { @@ -250,22 +220,6 @@ function TransactionItemRow({ } }, [transactionItem, translate, report, policy]); - const exchangeRateMessage = getExchangeRate(transactionItem); - - const cardName = useMemo(() => { - if (transactionItem.isCardFeedDeleted) { - return translate('workspace.companyCards.deletedFeed'); - } - if (transactionItem.cardName === CONST.EXPENSE.TYPE.CASH_CARD_NAME) { - return ''; - } - const cardID = transactionItem.cardID; - if (cardID && customCardNames?.[cardID]) { - return customCardNames[cardID]; - } - return transactionItem.cardName; - }, [transactionItem.cardID, transactionItem.cardName, transactionItem.isCardFeedDeleted, customCardNames, translate]); - const renderColumn = (column: SearchColumnType): React.ReactNode => { switch (column) { case CONST.SEARCH.TABLE_COLUMNS.TYPE: @@ -297,109 +251,81 @@ function TransactionItemRow({ return ( - + ); case CONST.SEARCH.TABLE_COLUMNS.DATE: return ( - + ); case CONST.SEARCH.TABLE_COLUMNS.SUBMITTED: return ( - + ); case CONST.SEARCH.TABLE_COLUMNS.APPROVED: return ( - + ); case CONST.SEARCH.TABLE_COLUMNS.POSTED: return ( - + ); case CONST.SEARCH.TABLE_COLUMNS.EXPORTED: return ( - + ); case CONST.SEARCH.TABLE_COLUMNS.CATEGORY: return ( - + ); case CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE: return ( - {getReimbursable(transactionItem) ? translate('common.yes') : translate('common.no')} + ); case CONST.SEARCH.TABLE_COLUMNS.BILLABLE: return ( - {transactionItem.billable ? translate('common.yes') : translate('common.no')} + ); case CONST.SEARCH.TABLE_COLUMNS.ACTION: @@ -429,31 +355,18 @@ function TransactionItemRow({ return ( - {!!merchant && ( - - )} + ); case CONST.SEARCH.TABLE_COLUMNS.DESCRIPTION: return ( - {!!description && ( - - )} + ); case CONST.SEARCH.TABLE_COLUMNS.TO: @@ -464,8 +377,8 @@ function TransactionItemRow({ > {!!transactionItem.to && ( )} @@ -479,8 +392,8 @@ function TransactionItemRow({ > {!!transactionItem.from && ( )} @@ -490,9 +403,9 @@ function TransactionItemRow({ return ( - + ); case CONST.SEARCH.TABLE_COLUMNS.COMMENTS: @@ -511,75 +424,63 @@ function TransactionItemRow({ return ( - + ); case CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT: return ( - + ); case CONST.SEARCH.TABLE_COLUMNS.ORIGINAL_AMOUNT: return ( - + ); case CONST.SEARCH.TABLE_COLUMNS.REPORT_ID: return ( - + ); case CONST.SEARCH.TABLE_COLUMNS.BASE_62_REPORT_ID: return ( - + ); case CONST.SEARCH.TABLE_COLUMNS.TAX_RATE: return ( - + ); case CONST.SEARCH.TABLE_COLUMNS.TAX_AMOUNT: return ( - {isTimeRequest(transactionItem) ? null : ( - - )} + ); case CONST.SEARCH.TABLE_COLUMNS.POLICY_NAME: @@ -589,8 +490,8 @@ function TransactionItemRow({ style={[StyleUtils.getReportTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.POLICY_NAME)]} > ); @@ -598,12 +499,9 @@ function TransactionItemRow({ return ( - + ); case CONST.SEARCH.TABLE_COLUMNS.STATUS: diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index c5192dfdefb85..569ae8370c0e2 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -40,9 +40,11 @@ import type { ListItem, ReportActionListItemType, SearchListItem, + SearchTransactionTextColumns, TaskListItemType, TransactionCardGroupListItemType, TransactionCategoryGroupListItemType, + TransactionColumnMeasurements, TransactionGroupListItemType, TransactionListItemType, TransactionMemberGroupListItemType, @@ -91,9 +93,10 @@ import {setOptimisticDataForTransactionThreadPreview} from './actions/Search'; import type {CardFeedForDisplay} from './CardFeedUtils'; import {getCardFeedsForDisplay} from './CardFeedUtils'; import {doesCardFeedExist, getCardDescription, getFeedNameForDisplay} from './CardUtils'; -import {getDecodedCategoryName} from './CategoryUtils'; +import {getDecodedCategoryName, isCategoryMissing} from './CategoryUtils'; import {convertToDisplayString} from './CurrencyUtils'; import DateUtils from './DateUtils'; +import getBase62ReportID from './getBase62ReportID'; import interceptAnonymousUser from './interceptAnonymousUser'; import isSearchTopmostFullScreenRoute from './Navigation/helpers/isSearchTopmostFullScreenRoute'; import Navigation from './Navigation/Navigation'; @@ -111,6 +114,7 @@ import { isResolvedActionableWhisper, isWhisperActionTargetedToOthers, } from './ReportActionsUtils'; +import {getReportName as getReportNameUtil} from './ReportNameUtils'; import {isExportAction} from './ReportPrimaryActionUtils'; import { canDeleteMoneyRequestReport, @@ -131,6 +135,7 @@ import { isAllowedToApproveExpenseReport as isAllowedToApproveExpenseReportUtils, isArchivedReport, isClosedReport, + isExpenseReport, isInvoiceReport, isIOUReport as isIOUReportReportUtil, isMoneyRequestReport, @@ -144,11 +149,16 @@ import {buildCannedSearchQuery, buildQueryStringFromFilterFormValues, buildSearc import StringUtils from './StringUtils'; import {getIOUPayerAndReceiver} from './TransactionPreviewUtils'; import { + getBillable, getCategory, + getCurrency, getDescription, getExchangeRate, getOriginalAmountForDisplay, + getOriginalCurrencyForDisplay, + getReimbursable, getTag, + getTagForDisplay, getTaxAmount, getTaxName, getAmount as getTransactionAmount, @@ -156,6 +166,7 @@ import { getMerchant as getTransactionMerchant, isPending, isScanning, + isTimeRequest, isViolationDismissed, } from './TransactionUtils'; import {isInvalidMerchantValue} from './ValidationUtils'; @@ -204,6 +215,7 @@ type GetTransactionSectionsParams = { currentSearch: SearchKey; currentAccountID: number; currentUserEmail: string; + translate: LocalizedTranslate; formatPhoneNumber: LocaleContextProps['formatPhoneNumber']; isActionLoadingSet: ReadonlySet | undefined; bankAccountList: OnyxEntry; @@ -1111,7 +1123,7 @@ function getTransactionItemCommonFormattedProperties( const formattedTo = formatPhoneNumber(toName); const formattedTotal = getTransactionAmount(transactionItem, isExpenseReport); - const date = transactionItem?.modifiedCreated ? transactionItem.modifiedCreated : transactionItem?.created; + const date = getTransactionCreatedDate(transactionItem); const merchant = getTransactionMerchant(transactionItem); const formattedMerchant = isInvalidMerchantValue(merchant) ? '' : merchant; const submitted = report?.submitted; @@ -1661,6 +1673,18 @@ function getToFieldValueForTransaction( return emptyPersonalDetails; } +function getColumnWidthStyle(currentMaxWidth: number | undefined, columnValue: string | null | undefined): number { + const maxColumnWidthPx = 400; + // The actual average length, but lets add padding so we're more accurate, we'd + // rather go over the actual length, than under and have the text be cut off + const averageCharacterPxWithPadding = 8.45; + const columnValueCharLength = columnValue?.length ?? 0; + + // The maximum amount of space the column would need for its longest value + const columnMaxWidth = Math.round(Math.min(maxColumnWidthPx, Math.max(currentMaxWidth ?? 0, columnValueCharLength * averageCharacterPxWithPadding))); + return columnMaxWidth; +} + /** * @private * Organizes data into List Sections for display, for the TransactionListItemType of Search Results. @@ -1672,6 +1696,7 @@ function getTransactionsSections({ currentSearch, currentAccountID, currentUserEmail, + translate, formatPhoneNumber, isActionLoadingSet, bankAccountList, @@ -1682,8 +1707,8 @@ function getTransactionsSections({ }: GetTransactionSectionsParams): [TransactionListItemType[], number] { const shouldShowMerchant = getShouldShowMerchant(data); const lastExportedActionByReportID = buildLastExportedActionByReportIDMap(data); - const {shouldShowYearCreated, shouldShowYearSubmitted, shouldShowYearApproved, shouldShowYearPosted, shouldShowYearExported} = shouldShowYear(data, false, lastExportedActionByReportID); const {shouldShowAmountInWideColumn, shouldShowTaxAmountInWideColumn} = getWideAmountIndicators(data); + const {shouldShowYearCreated, shouldShowYearSubmitted, shouldShowYearApproved, shouldShowYearPosted, shouldShowYearExported} = shouldShowYear(data, false, lastExportedActionByReportID); // Pre-filter transaction keys to avoid repeated checks const transactionKeys = Object.keys(data).filter(isTransactionEntry); @@ -1698,91 +1723,215 @@ function getTransactionsSections({ // Use the provided queryJSON if available, otherwise fall back to getCurrentSearchQueryJSON() const currentQueryJSON = queryJSON ?? getCurrentSearchQueryJSON(); + const measurements = {} as Record; for (const key of transactionKeys) { - const transactionItem = data[key]; - const report = data[`${ONYXKEYS.COLLECTION.REPORT}${transactionItem.reportID}`]; + const transaction = data[key]; + const report = data[`${ONYXKEYS.COLLECTION.REPORT}${transaction.reportID}`]; + const policy = data[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]; + const actions = reportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transaction.reportID}`] ?? []; + const reportMetadata = allReportMetadata?.[`${ONYXKEYS.COLLECTION.REPORT_METADATA}${transaction.reportID}`] ?? {}; + + const reportAction = moneyRequestReportActionsByTransactionID.get(transaction.transactionID); + const isActionLoading = isActionLoadingSet?.has(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${transaction.reportID}`); + const allActions = getActions(data, allViolations, key, currentSearch, currentUserEmail, currentAccountID, bankAccountList, reportMetadata, actions); - let shouldShow = true; + let shouldShowTransaction = !!transaction.transactionID; - const isActionLoading = isActionLoadingSet?.has(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${transactionItem.reportID}`); - if (currentQueryJSON && !isActionLoading) { + if (currentQueryJSON && !isActionLoading && shouldShowTransaction) { if (currentQueryJSON.type === CONST.SEARCH.DATA_TYPES.EXPENSE) { const status = currentQueryJSON.status; if (Array.isArray(status)) { - shouldShow = status.some((expenseStatus) => { + shouldShowTransaction = status.some((expenseStatus) => { return isValidExpenseStatus(expenseStatus) ? expenseStatusActionMapping[expenseStatus](report) : false; }); } else { - shouldShow = isValidExpenseStatus(status) ? expenseStatusActionMapping[status](report) : false; + shouldShowTransaction = isValidExpenseStatus(status) ? expenseStatusActionMapping[status](report) : false; } } } - if (!transactionItem.transactionID) { - shouldShow = false; + if (!shouldShowTransaction || !transaction) { + continue; } - if (shouldShow) { - const reportAction = moneyRequestReportActionsByTransactionID.get(transactionItem.transactionID); - const policy = data[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]; - const shouldShowBlankTo = !report || isOpenExpenseReport(report); - const transactionViolations = getTransactionViolations(allViolations, transactionItem, currentUserEmail, currentAccountID ?? CONST.DEFAULT_NUMBER_ID, report, policy); - // Use Map.get() for faster lookups with default values - const fromAccountID = reportAction?.actorAccountID ?? report?.ownerAccountID; - const from = fromAccountID ? (personalDetailsMap.get(fromAccountID.toString()) ?? emptyPersonalDetails) : emptyPersonalDetails; - const to = getToFieldValueForTransaction(transactionItem, report, data.personalDetailsList, reportAction); - const isIOUReport = report?.type === CONST.REPORT.TYPE.IOU; - // Check if the card feed has been deleted. If cardFeeds is still loading (undefined), return undefined to avoid showing incorrect state. - const isCardFeedDeleted = cardFeeds === undefined ? undefined : !doesCardFeedExist(transactionItem.bank as OnyxTypes.CompanyCardFeed, cardFeeds); + const shouldShowBlankTo = !report || isOpenExpenseReport(report); + const transactionViolations = getTransactionViolations(allViolations, transaction, currentUserEmail, currentAccountID ?? CONST.DEFAULT_NUMBER_ID, report, policy); - const {formattedFrom, formattedTo, formattedTotal, formattedMerchant, date, submitted, approved, posted} = getTransactionItemCommonFormattedProperties( - transactionItem, - from, - to, - policy, - formatPhoneNumber, - report, - ); - const actions = reportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionItem.reportID}`] ?? []; - const reportMetadata = allReportMetadata?.[`${ONYXKEYS.COLLECTION.REPORT_METADATA}${transactionItem.reportID}`] ?? {}; - const allActions = getActions(data, allViolations, key, currentSearch, currentUserEmail, currentAccountID, bankAccountList, reportMetadata, actions); - const transactionSection: TransactionListItemType = { - ...transactionItem, - keyForList: transactionItem.transactionID, - action: allActions.at(0) ?? CONST.SEARCH.ACTION_TYPES.VIEW, - allActions, - report, - policy, - reportAction, - holdReportAction: holdReportActionsByTransactionID.get(transactionItem.transactionID), - from, - to, - formattedFrom, - formattedTo: shouldShowBlankTo ? '' : formattedTo, - formattedTotal, - formattedMerchant, - isCardFeedDeleted, - date, - submitted, - approved, - posted, - exported: transactionItem.reportID ? (lastExportedActionByReportID.get(transactionItem.reportID)?.created ?? '') : '', - shouldShowMerchant, - shouldShowYear: shouldShowYearCreated, - shouldShowYearSubmitted, - shouldShowYearApproved, - shouldShowYearPosted, - shouldShowYearExported, - isAmountColumnWide: shouldShowAmountInWideColumn, - isTaxAmountColumnWide: shouldShowTaxAmountInWideColumn, - violations: transactionViolations, - category: isIOUReport ? '' : transactionItem?.category, - }; + const fromAccountID = reportAction?.actorAccountID ?? report?.ownerAccountID; + const from = fromAccountID ? (personalDetailsMap.get(fromAccountID.toString()) ?? emptyPersonalDetails) : emptyPersonalDetails; + const to = getToFieldValueForTransaction(transaction, report, data.personalDetailsList, reportAction); - transactionsSections.push(transactionSection); - } + const isIOUReport = report?.type === CONST.REPORT.TYPE.IOU; + const isCardFeedDeleted = cardFeeds === undefined ? undefined : !doesCardFeedExist(transaction.bank as OnyxTypes.CompanyCardFeed, cardFeeds); + + const {formattedFrom, formattedTo, formattedTotal, formattedMerchant, date, submitted, approved, posted} = getTransactionItemCommonFormattedProperties( + transaction, + from, + to, + policy, + formatPhoneNumber, + report, + ); + + // Compute the maximum size of all of the text fields to determine how much space we need to delegate + // Handle the merchant + measurements[CONST.SEARCH.TABLE_COLUMNS.MERCHANT] = getColumnWidthStyle(measurements[CONST.SEARCH.TABLE_COLUMNS.MERCHANT], formattedMerchant); + + // Handle the category + const formattedCategory = isCategoryMissing(transaction?.category) ? '' : getDecodedCategoryName(transaction?.category ?? ''); + measurements[CONST.SEARCH.TABLE_COLUMNS.CATEGORY] = getColumnWidthStyle(measurements[CONST.SEARCH.TABLE_COLUMNS.CATEGORY], formattedCategory); + + // Handle the tag + const formattedTag = getTagForDisplay(transaction); + measurements[CONST.SEARCH.TABLE_COLUMNS.TAG] = getColumnWidthStyle(measurements[CONST.SEARCH.TABLE_COLUMNS.TAG], formattedTag); + + // Handle the amount + const transactionCurrency = getOriginalCurrencyForDisplay(transaction); + const transactionDisplayAmount = getOriginalAmountForDisplay(transaction, isExpenseReport(report)); + const formattedAmount = convertToDisplayString(transactionDisplayAmount, transactionCurrency); + measurements[CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT] = getColumnWidthStyle(measurements[CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT], formattedAmount); + + // Handle the exchange rate + const formattedExchangeRate = getExchangeRate(transaction); + measurements[CONST.SEARCH.TABLE_COLUMNS.EXCHANGE_RATE] = getColumnWidthStyle(measurements[CONST.SEARCH.TABLE_COLUMNS.EXCHANGE_RATE], formattedExchangeRate); + + // Handle the description + const formattedDescription = getDescription(transaction); + measurements[CONST.SEARCH.TABLE_COLUMNS.DESCRIPTION] = getColumnWidthStyle(measurements[CONST.SEARCH.TABLE_COLUMNS.DESCRIPTION], formattedDescription); + + // Handle the card + // JACK_TODO: This is missing customCardNames but it doesnt matter for now + const deletedFeedCardName = isCardFeedDeleted ? translate('workspace.companyCards.deletedFeed') : null; + const cashCardName = transaction.cardName === CONST.EXPENSE.TYPE.CASH_CARD_NAME ? '' : null; + const formattedCardName = deletedFeedCardName ?? cashCardName ?? transaction.cardName ?? ''; + measurements[CONST.SEARCH.TABLE_COLUMNS.CARD] = getColumnWidthStyle(measurements[CONST.SEARCH.TABLE_COLUMNS.CARD], formattedCardName); + + // Handle the billable + const formattedBillable = getBillable(transaction) ? translate('common.yes') : translate('common.no'); + measurements[CONST.SEARCH.TABLE_COLUMNS.BILLABLE] = getColumnWidthStyle(measurements[CONST.SEARCH.TABLE_COLUMNS.BILLABLE], formattedBillable); + + // Handle the reimbursable + const formattedReimbursable = getReimbursable(transaction) ? translate('common.yes') : translate('common.no'); + measurements[CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE] = getColumnWidthStyle(measurements[CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE], formattedReimbursable); + + // Handle the title + const formattedTitle = getReportNameUtil(report); + measurements[CONST.SEARCH.TABLE_COLUMNS.TITLE] = getColumnWidthStyle(measurements[CONST.SEARCH.TABLE_COLUMNS.TITLE], formattedTitle); + + // Handle the tax rate + const formattedTaxRate = !isTimeRequest(transaction) ? (getTaxName(policy, transaction) ?? transaction.taxValue ?? '') : ''; + measurements[CONST.SEARCH.TABLE_COLUMNS.TAX_RATE] = getColumnWidthStyle(measurements[CONST.SEARCH.TABLE_COLUMNS.TAX_RATE], formattedTaxRate); + + // Handle the tax + const transactionTaxAmount = getTaxAmount(transaction, true); + const transactionTaxAmountCurrency = getCurrency(transaction); + const formattedTaxAmount = !isTimeRequest(transaction) ? convertToDisplayString(transactionTaxAmount, transactionTaxAmountCurrency) : ''; + measurements[CONST.SEARCH.TABLE_COLUMNS.TAX_AMOUNT] = getColumnWidthStyle(measurements[CONST.SEARCH.TABLE_COLUMNS.TAX_AMOUNT], formattedTaxAmount); + + // Handle the report ID + const formattedReportID = transaction.reportID === CONST.REPORT.UNREPORTED_REPORT_ID ? '' : (transaction.reportID?.toString() ?? ''); + measurements[CONST.SEARCH.TABLE_COLUMNS.REPORT_ID] = getColumnWidthStyle(measurements[CONST.SEARCH.TABLE_COLUMNS.REPORT_ID], formattedReportID); + + // Handle the base62 report ID + const formattedBase62ReportID = transaction.reportID === CONST.REPORT.UNREPORTED_REPORT_ID ? '' : getBase62ReportID(Number(transaction.reportID)); + measurements[CONST.SEARCH.TABLE_COLUMNS.BASE_62_REPORT_ID] = getColumnWidthStyle(measurements[CONST.SEARCH.TABLE_COLUMNS.BASE_62_REPORT_ID], formattedBase62ReportID); + + // Handle the original amount + const originalAmountTotal = getOriginalAmountForDisplay(transaction, isExpenseReport(report)); + const originalAmountCurrency = getOriginalCurrencyForDisplay(transaction); + const formattedOriginalAmount = convertToDisplayString(originalAmountTotal, originalAmountCurrency); + measurements[CONST.SEARCH.TABLE_COLUMNS.ORIGINAL_AMOUNT] = getColumnWidthStyle(measurements[CONST.SEARCH.TABLE_COLUMNS.ORIGINAL_AMOUNT], formattedOriginalAmount); + + // Handle the date + const createdDate = date ?? ''; + const isCreatedLastYear = DateUtils.doesDateBelongToAPastYear(createdDate); + const formattedDate = DateUtils.formatWithUTCTimeZone(createdDate, isCreatedLastYear ? CONST.DATE.MONTH_DAY_YEAR_ABBR_FORMAT : CONST.DATE.MONTH_DAY_ABBR_FORMAT); + measurements[CONST.SEARCH.TABLE_COLUMNS.DATE] = getColumnWidthStyle(measurements[CONST.SEARCH.TABLE_COLUMNS.DATE], formattedDate); + + // Handle exported date + const exportDate = transaction.reportID ? (lastExportedActionByReportID.get(transaction.reportID)?.created ?? '') : ''; + const isExportedLastYear = DateUtils.doesDateBelongToAPastYear(exportDate); + const formattedExportDate = DateUtils.formatWithUTCTimeZone(exportDate, isExportedLastYear ? CONST.DATE.MONTH_DAY_YEAR_ABBR_FORMAT : CONST.DATE.MONTH_DAY_ABBR_FORMAT); + measurements[CONST.SEARCH.TABLE_COLUMNS.EXPORTED] = getColumnWidthStyle(measurements[CONST.SEARCH.TABLE_COLUMNS.EXPORTED], formattedExportDate); + + // Handle submitted date + const submittedDate = report.submitted ?? ''; + const isSubmittedLastYear = DateUtils.doesDateBelongToAPastYear(submittedDate); + const formattedSubmittedDate = DateUtils.formatWithUTCTimeZone(submittedDate, isSubmittedLastYear ? CONST.DATE.MONTH_DAY_YEAR_ABBR_FORMAT : CONST.DATE.MONTH_DAY_ABBR_FORMAT); + measurements[CONST.SEARCH.TABLE_COLUMNS.SUBMITTED] = getColumnWidthStyle(measurements[CONST.SEARCH.TABLE_COLUMNS.SUBMITTED], formattedSubmittedDate); + + // Handle approved date + const approvedDate = report.approved ?? ''; + const isApprovedLastYear = DateUtils.doesDateBelongToAPastYear(approvedDate); + const formattedApprovedDate = DateUtils.formatWithUTCTimeZone(approvedDate, isApprovedLastYear ? CONST.DATE.MONTH_DAY_YEAR_ABBR_FORMAT : CONST.DATE.MONTH_DAY_ABBR_FORMAT); + measurements[CONST.SEARCH.TABLE_COLUMNS.APPROVED] = getColumnWidthStyle(measurements[CONST.SEARCH.TABLE_COLUMNS.APPROVED], formattedApprovedDate); + + // Handle posted date + const postedDate = posted ?? ''; + const isPostedLastYear = DateUtils.doesDateBelongToAPastYear(postedDate); + const formattedPostedDate = DateUtils.formatWithUTCTimeZone(postedDate, isPostedLastYear ? CONST.DATE.MONTH_DAY_YEAR_ABBR_FORMAT : CONST.DATE.MONTH_DAY_ABBR_FORMAT); + measurements[CONST.SEARCH.TABLE_COLUMNS.POSTED] = getColumnWidthStyle(measurements[CONST.SEARCH.TABLE_COLUMNS.POSTED], formattedPostedDate); + + const transactionSection: TransactionListItemType = { + ...transaction, + measurements, + formattedValues: { + [CONST.SEARCH.TABLE_COLUMNS.DATE]: formattedDate, + [CONST.SEARCH.TABLE_COLUMNS.MERCHANT]: formattedMerchant, + [CONST.SEARCH.TABLE_COLUMNS.CATEGORY]: formattedCategory, + [CONST.SEARCH.TABLE_COLUMNS.TAG]: formattedTag, + [CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT]: formattedAmount, + [CONST.SEARCH.TABLE_COLUMNS.EXCHANGE_RATE]: formattedExchangeRate, + [CONST.SEARCH.TABLE_COLUMNS.DESCRIPTION]: formattedDescription, + [CONST.SEARCH.TABLE_COLUMNS.CARD]: formattedCardName, + [CONST.SEARCH.TABLE_COLUMNS.BILLABLE]: formattedBillable, + [CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE]: formattedReimbursable, + [CONST.SEARCH.TABLE_COLUMNS.TITLE]: formattedTitle, + [CONST.SEARCH.TABLE_COLUMNS.TAX_RATE]: formattedTaxRate, + [CONST.SEARCH.TABLE_COLUMNS.TAX_AMOUNT]: formattedTaxAmount, + [CONST.SEARCH.TABLE_COLUMNS.REPORT_ID]: formattedReportID, + [CONST.SEARCH.TABLE_COLUMNS.BASE_62_REPORT_ID]: formattedBase62ReportID, + [CONST.SEARCH.TABLE_COLUMNS.ORIGINAL_AMOUNT]: formattedOriginalAmount, + [CONST.SEARCH.TABLE_COLUMNS.EXPORTED]: formattedExportDate, + [CONST.SEARCH.TABLE_COLUMNS.SUBMITTED]: formattedSubmittedDate, + [CONST.SEARCH.TABLE_COLUMNS.APPROVED]: formattedApprovedDate, + [CONST.SEARCH.TABLE_COLUMNS.POSTED]: formattedPostedDate, + }, + keyForList: transaction.transactionID, + action: allActions.at(0) ?? CONST.SEARCH.ACTION_TYPES.VIEW, + allActions, + report, + policy, + reportAction, + holdReportAction: holdReportActionsByTransactionID.get(transaction.transactionID), + from, + to, + formattedFrom, + formattedTo: shouldShowBlankTo ? '' : formattedTo, + formattedTotal, + formattedMerchant, + isCardFeedDeleted, + date, + submitted, + approved, + posted, + exported: transaction.reportID ? (lastExportedActionByReportID.get(transaction.reportID)?.created ?? '') : '', + shouldShowMerchant, + shouldShowYear: shouldShowYearCreated, + shouldShowYearSubmitted, + shouldShowYearApproved, + shouldShowYearPosted, + shouldShowYearExported, + isAmountColumnWide: shouldShowAmountInWideColumn, + isTaxAmountColumnWide: shouldShowTaxAmountInWideColumn, + violations: transactionViolations, + category: isIOUReport ? '' : transaction?.category, + }; + + transactionsSections.push(transactionSection); } + return [transactionsSections, transactionsSections.length]; } @@ -2966,6 +3115,7 @@ function getSections({ return getTransactionsSections({ data, + translate, currentSearch, currentAccountID, currentUserEmail,