diff --git a/src/CONST.ts b/src/CONST.ts
index edceb11edd85..bb85ac16bd83 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -937,9 +937,12 @@ const CONST = {
MERCHANT: 'merchant',
FROM: 'from',
TO: 'to',
+ CATEGORY: 'category',
+ TAG: 'tag',
TOTAL: 'total',
TYPE: 'type',
ACTION: 'action',
+ TAX_AMOUNT: 'taxAmount',
},
PRIORITY_MODE: {
GSD: 'gsd',
diff --git a/src/components/Search.tsx b/src/components/Search.tsx
index fbd352a798c6..0e39039f59a0 100644
--- a/src/components/Search.tsx
+++ b/src/components/Search.tsx
@@ -65,11 +65,10 @@ function Search({query}: SearchProps) {
const ListItem = SearchUtils.getListItem(type);
const data = SearchUtils.getSections(searchResults?.data ?? {}, type);
- const shouldShowMerchant = SearchUtils.getShouldShowMerchant(searchResults?.data ?? {});
return (
}
+ customListHeader={}
ListItem={ListItem}
sections={[{data, isDisabled: false}]}
onSelectRow={(item) => {
diff --git a/src/components/SelectionList/SearchTableHeader.tsx b/src/components/SelectionList/SearchTableHeader.tsx
index 02986f22dccc..ec0267d20c04 100644
--- a/src/components/SelectionList/SearchTableHeader.tsx
+++ b/src/components/SelectionList/SearchTableHeader.tsx
@@ -1,24 +1,30 @@
import React from 'react';
import {View} from 'react-native';
-import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
+import * as SearchUtils from '@libs/SearchUtils';
import CONST from '@src/CONST';
+import type * as OnyxTypes from '@src/types/onyx';
+import SearchTableHeaderColumn from './SearchTableHeaderColumn';
type SearchTableHeaderProps = {
- /** Whether we should show the merchant or description column */
- shouldShowMerchant: boolean;
+ data: OnyxTypes.SearchResults['data'];
};
-function SearchTableHeader({shouldShowMerchant}: SearchTableHeaderProps) {
+function SearchTableHeader({data}: SearchTableHeaderProps) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const {isSmallScreenWidth, isMediumScreenWidth} = useWindowDimensions();
const {translate} = useLocalize();
const displayNarrowVersion = isMediumScreenWidth || isSmallScreenWidth;
+ const shouldShowCategoryColumn = SearchUtils.getShouldShowColumn(data, CONST.SEARCH_TABLE_COLUMNS.CATEGORY);
+ const shouldShowTagColumn = SearchUtils.getShouldShowColumn(data, CONST.SEARCH_TABLE_COLUMNS.TAG);
+ const shouldShowTaxColumn = SearchUtils.getShouldShowColumn(data, CONST.SEARCH_TABLE_COLUMNS.TAX_AMOUNT);
+ const shouldShowMerchant = SearchUtils.getShouldShowMerchant(data);
+
if (displayNarrowVersion) {
return;
}
@@ -26,27 +32,50 @@ function SearchTableHeader({shouldShowMerchant}: SearchTableHeaderProps) {
return (
-
- {translate('common.date')}
-
-
- {translate(shouldShowMerchant ? 'common.merchant' : 'common.description')}
-
-
- {translate('common.from')}
-
-
- {translate('common.to')}
-
-
- {translate('common.total')}
-
-
- {translate('common.type')}
-
-
- {translate('common.action')}
-
+
+
+
+
+
+
+
+
+
+
);
diff --git a/src/components/SelectionList/SearchTableHeaderColumn.tsx b/src/components/SelectionList/SearchTableHeaderColumn.tsx
new file mode 100644
index 000000000000..92b59f702cb1
--- /dev/null
+++ b/src/components/SelectionList/SearchTableHeaderColumn.tsx
@@ -0,0 +1,31 @@
+import React from 'react';
+import {View} from 'react-native';
+import type {StyleProp, TextStyle, ViewStyle} from 'react-native';
+import Text from '@components/Text';
+import useThemeStyles from '@hooks/useThemeStyles';
+
+type SearchTableHeaderColumnProps = {
+ shouldShow?: boolean;
+ containerStyle?: StyleProp;
+ text: string;
+ textStyle?: StyleProp;
+};
+
+export default function SearchTableHeaderColumn({containerStyle, text, textStyle, shouldShow = true}: SearchTableHeaderColumnProps) {
+ const styles = useThemeStyles();
+
+ if (!shouldShow) {
+ return null;
+ }
+
+ return (
+
+
+ {text}
+
+
+ );
+}
diff --git a/src/components/SelectionList/TextWithIconCell.tsx b/src/components/SelectionList/TextWithIconCell.tsx
new file mode 100644
index 000000000000..bd51133a362f
--- /dev/null
+++ b/src/components/SelectionList/TextWithIconCell.tsx
@@ -0,0 +1,45 @@
+import React from 'react';
+import {View} from 'react-native';
+import Icon from '@components/Icon';
+import Text from '@components/Text';
+import Tooltip from '@components/Tooltip';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
+import type IconAsset from '@src/types/utils/IconAsset';
+
+type TextWithIconCellProps = {
+ icon: IconAsset;
+ text?: string;
+ showTooltip: boolean;
+};
+
+export default function TextWithIconCell({icon, text, showTooltip}: TextWithIconCellProps) {
+ const styles = useThemeStyles();
+ const theme = useTheme();
+
+ if (!text) {
+ return null;
+ }
+
+ return (
+
+
+
+
+ {text}
+
+
+
+ );
+}
diff --git a/src/components/SelectionList/TransactionListItem.tsx b/src/components/SelectionList/TransactionListItem.tsx
index 0965ce6dabce..04929bf883d1 100644
--- a/src/components/SelectionList/TransactionListItem.tsx
+++ b/src/components/SelectionList/TransactionListItem.tsx
@@ -17,8 +17,9 @@ import * as TransactionUtils from '@libs/TransactionUtils';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import type {Transaction} from '@src/types/onyx';
-import type {SearchPersonalDetails, SearchPolicyDetails, SearchTransactionType} from '@src/types/onyx/SearchResults';
+import type {SearchAccountDetails, SearchTransactionType} from '@src/types/onyx/SearchResults';
import BaseListItem from './BaseListItem';
+import TextWithIconCell from './TextWithIconCell';
import type {ListItem, TransactionListItemProps, TransactionListItemType} from './types';
const getTypeIcon = (type?: SearchTransactionType) => {
@@ -61,6 +62,7 @@ function TransactionListItem({
const isFromExpenseReport = transactionItem.reportType === CONST.REPORT.TYPE.EXPENSE;
const date = TransactionUtils.getCreated(transactionItem as OnyxEntry, CONST.DATE.MONTH_DAY_ABBR_FORMAT);
const amount = TransactionUtils.getAmount(transactionItem as OnyxEntry, isFromExpenseReport);
+ const taxAmount = TransactionUtils.getTaxAmount(transactionItem as OnyxEntry, isFromExpenseReport);
const currency = TransactionUtils.getCurrency(transactionItem as OnyxEntry);
const description = TransactionUtils.getDescription(transactionItem as OnyxEntry);
const merchant = getMerchant();
@@ -82,7 +84,7 @@ function TransactionListItem({
/>
);
- const userCell = (participant: SearchPersonalDetails & SearchPolicyDetails) => {
+ const userCell = (participant: SearchAccountDetails) => {
const displayName = participant?.name ?? participant?.displayName ?? participant?.login;
const avatarURL = participant?.avatarURL ?? participant?.avatar;
const isWorkspace = participant?.avatarURL !== undefined;
@@ -108,6 +110,42 @@ function TransactionListItem({
);
};
+ const categoryCell = isLargeScreenWidth ? (
+
+ ) : (
+
+ );
+
+ const tagCell = isLargeScreenWidth ? (
+
+ ) : (
+
+ );
+
+ const taxCell = (
+
+ );
+
const totalCell = (
({
>
{() => (
<>
-
+
{userCell(transactionItem.from)}
({
height={variables.iconSizeXXSmall}
fill={theme.icon}
/>
- {userCell(transactionItem.to)}
+ {userCell(transactionItem.to)}
{actionCell}
- {merchantCell}
-
+
+ {merchantCell}
+
+ {categoryCell}
+ {tagCell}
+
+
+
{totalCell}
{typeCell}
@@ -216,7 +260,10 @@ function TransactionListItem({
{dateCell}
{merchantCell}
{userCell(transactionItem.from)}
- {userCell(transactionItem.to)}
+ {userCell(transactionItem.to)}
+ {transactionItem.shouldShowCategory && {categoryCell}}
+ {transactionItem.shouldShowTag && {tagCell}}
+ {transactionItem.shouldShowTax && {taxCell}}
{totalCell}
{typeCell}
{actionCell}
diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts
index 8a2a1efdd030..79e47e4aa4d7 100644
--- a/src/components/SelectionList/types.ts
+++ b/src/components/SelectionList/types.ts
@@ -1,11 +1,10 @@
import type {MutableRefObject, ReactElement, ReactNode} from 'react';
import type {GestureResponderEvent, InputModeOptions, LayoutChangeEvent, SectionListData, StyleProp, TextInput, TextStyle, ViewStyle} from 'react-native';
-import type {ValueOf} from 'type-fest';
import type {MaybePhraseKey} from '@libs/Localize';
import type {BrickRoad} from '@libs/WorkspacesSettingsUtils';
import type CONST from '@src/CONST';
import type {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon';
-import type {SearchPersonalDetails, SearchPolicyDetails} from '@src/types/onyx/SearchResults';
+import type {SearchAccountDetails, SearchTransaction} from '@src/types/onyx/SearchResults';
import type {ReceiptErrors} from '@src/types/onyx/Transaction';
import type ChildrenProps from '@src/types/utils/ChildrenProps';
import type IconAsset from '@src/types/utils/IconAsset';
@@ -128,64 +127,26 @@ type ListItem = {
brickRoadIndicator?: BrickRoad | '' | null;
};
-type TransactionListItemType = ListItem & {
- /** The ID of the transaction */
- transactionID: string;
+type TransactionListItemType = ListItem &
+ SearchTransaction & {
+ /** The personal details of the user requesting money */
+ from: SearchAccountDetails;
- /** The transaction created date */
- created: string;
+ /** The personal details of the user paying the request */
+ to: SearchAccountDetails;
- /** The edited transaction created date */
- modifiedCreated: string;
+ /** Whether we should show the merchant column */
+ shouldShowMerchant: boolean;
- /** The transaction amount */
- amount: number;
+ /** Whether we should show the category column */
+ shouldShowCategory: boolean;
- /** The edited transaction amount */
- modifiedAmount: number;
+ /** Whether we should show the tag column */
+ shouldShowTag: boolean;
- /** The transaction currency */
- currency: string;
-
- /** The edited transaction currency */
- modifiedCurrency: string;
-
- /** The transaction merchant */
- merchant: string;
-
- /** The edited transaction merchant */
- modifiedMerchant: string;
-
- /** The receipt object */
- receipt?: {source?: string};
-
- /** The personal details of the user requesting money */
- from: SearchPersonalDetails & SearchPolicyDetails;
-
- /** The personal details of the user paying the request */
- to: SearchPersonalDetails & SearchPolicyDetails;
-
- /** The transaction tag */
- tag: string;
-
- /** The transaction description */
- comment: {comment: string};
-
- /** The transaction category */
- category: string;
-
- /** The type of request */
- type: ValueOf;
-
- /** The type of report the transaction is associated with */
- reportType: string;
-
- /** The ID of the policy the transaction is associated with */
- policyID: string;
-
- /** Whether we should show the merchant column */
- shouldShowMerchant: boolean;
-};
+ /** Whether we should show the tax column */
+ shouldShowTax: boolean;
+ };
type ListItemProps = CommonListItemProps & {
/** The section list item */
@@ -448,7 +409,7 @@ export type {
SelectionListHandle,
TableListItemProps,
TransactionListItemProps,
+ TransactionListItemType,
UserListItemProps,
ValidListItem,
- TransactionListItemType,
};
diff --git a/src/languages/en.ts b/src/languages/en.ts
index d21c4883ee21..927ffbbbbf66 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -331,6 +331,7 @@ export default {
type: 'Type',
action: 'Action',
expenses: 'Expenses',
+ tax: 'Tax',
},
location: {
useCurrent: 'Use current location',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 707526056e04..4e36529c77a3 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -321,6 +321,7 @@ export default {
type: 'Tipo',
action: 'Acción',
expenses: 'Gastos',
+ tax: 'Impuesto',
},
connectionComplete: {
title: 'Conexión Completa',
diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts
index 55e81a717bac..cef9989d22d2 100644
--- a/src/libs/SearchUtils.ts
+++ b/src/libs/SearchUtils.ts
@@ -1,8 +1,10 @@
+import type {ValueOf} from 'react-native-gesture-handler/lib/typescript/typeUtils';
import TransactionListItem from '@components/SelectionList/TransactionListItem';
+import type {TransactionListItemType} from '@components/SelectionList/types';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type * as OnyxTypes from '@src/types/onyx';
-import type {SearchDataTypes, SearchTransaction, SearchTypeToItemMap} from '@src/types/onyx/SearchResults';
+import type {SearchDataTypes, SearchTypeToItemMap} from '@src/types/onyx/SearchResults';
import * as UserUtils from './UserUtils';
function getSearchType(search: OnyxTypes.SearchResults['search']): SearchDataTypes | undefined {
@@ -21,8 +23,15 @@ function getShouldShowMerchant(data: OnyxTypes.SearchResults['data']): boolean {
});
}
-function getTransactionsSections(data: OnyxTypes.SearchResults['data']): SearchTransaction[] {
+function getShouldShowColumn(data: OnyxTypes.SearchResults['data'], columnName: ValueOf) {
+ return Object.values(data).some((item) => !!item[columnName]);
+}
+
+function getTransactionsSections(data: OnyxTypes.SearchResults['data']): TransactionListItemType[] {
const shouldShowMerchant = getShouldShowMerchant(data);
+ const shouldShowCategory = getShouldShowColumn(data, CONST.SEARCH_TABLE_COLUMNS.CATEGORY);
+ const shouldShowTag = getShouldShowColumn(data, CONST.SEARCH_TABLE_COLUMNS.TAG);
+ const shouldShowTax = getShouldShowColumn(data, CONST.SEARCH_TABLE_COLUMNS.TAX_AMOUNT);
return Object.entries(data)
.filter(([key]) => key.startsWith(ONYXKEYS.COLLECTION.TRANSACTION))
.map(([, value]) => {
@@ -32,6 +41,9 @@ function getTransactionsSections(data: OnyxTypes.SearchResults['data']): SearchT
from: data.personalDetailsList?.[value.accountID],
to: isExpenseReport ? data[`${ONYXKEYS.COLLECTION.POLICY}${value.policyID}`] : data.personalDetailsList?.[value.managerID],
shouldShowMerchant,
+ shouldShowCategory,
+ shouldShowTag,
+ shouldShowTax,
keyForList: value.transactionID,
};
})
@@ -61,4 +73,4 @@ function getQueryHash(query: string): number {
return UserUtils.hashText(query, 2 ** 32);
}
-export {getListItem, getQueryHash, getSections, getShouldShowMerchant, getSearchType};
+export {getListItem, getQueryHash, getSections, getShouldShowColumn, getShouldShowMerchant, getSearchType};
diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts
index 35a39902b973..4c2f9a180518 100644
--- a/src/styles/utils/index.ts
+++ b/src/styles/utils/index.ts
@@ -1568,14 +1568,13 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({
columnWidth = getWidthStyle(variables.w44);
break;
case CONST.SEARCH_TABLE_COLUMNS.MERCHANT:
- columnWidth = styles.flex1;
- break;
case CONST.SEARCH_TABLE_COLUMNS.FROM:
- columnWidth = styles.flex1;
- break;
case CONST.SEARCH_TABLE_COLUMNS.TO:
+ case CONST.SEARCH_TABLE_COLUMNS.CATEGORY:
+ case CONST.SEARCH_TABLE_COLUMNS.TAG:
columnWidth = styles.flex1;
break;
+ case CONST.SEARCH_TABLE_COLUMNS.TAX_AMOUNT:
case CONST.SEARCH_TABLE_COLUMNS.TOTAL:
columnWidth = {...getWidthStyle(variables.w96), ...styles.alignItemsEnd};
break;
diff --git a/src/types/onyx/SearchResults.ts b/src/types/onyx/SearchResults.ts
index ead7cc591fe5..a4cf9c2870d0 100644
--- a/src/types/onyx/SearchResults.ts
+++ b/src/types/onyx/SearchResults.ts
@@ -1,7 +1,6 @@
import type {ValueOf} from 'type-fest';
import type TransactionListItem from '@components/SelectionList/TransactionListItem';
import type CONST from '@src/CONST';
-import type {Receipt} from './Transaction';
type SearchDataTypes = ValueOf;
@@ -36,35 +35,87 @@ type SearchPolicyDetails = {
};
type SearchTransaction = {
+ /** The ID of the transaction */
transactionID: string;
- parentTransactionID?: string;
- receipt?: Receipt;
- hasEReceipt?: boolean;
+
+ /** The transaction created date */
created: string;
+
+ /** The edited transaction created date */
+ modifiedCreated: string;
+
+ /** The transaction amount */
+ amount: number;
+
+ /** The edited transaction amount */
+ modifiedAmount: number;
+
+ /** The transaction currency */
+ currency: string;
+
+ /** The edited transaction currency */
+ modifiedCurrency: string;
+
+ /** The transaction merchant */
merchant: string;
- modifiedCreated?: string;
- modifiedMerchant?: string;
+
+ /** The edited transaction merchant */
+ modifiedMerchant: string;
+
+ /** The receipt object */
+ receipt?: {source?: string};
+
+ /** The transaction tag */
+ tag: string;
+
+ /** The transaction description */
+ comment: {comment: string};
+
+ /** The transaction category */
+ category: string;
+
+ /** The type of request */
+ type: ValueOf;
+
+ /** The type of report the transaction is associated with */
+ reportType: string;
+
+ /** The ID of the policy the transaction is associated with */
+ policyID: string;
+
+ /** The ID of the parent of the transaction */
+ parentTransactionID?: string;
+
+ /** If the transaction has an Ereceipt */
+ hasEReceipt?: boolean;
+
+ /** The transaction description */
description: string;
+
+ /** The transaction sender ID */
accountID: number;
+
+ /** The transaction recipient ID */
managerID: number;
- from: SearchPersonalDetails | SearchPolicyDetails;
- to: SearchPersonalDetails | SearchPolicyDetails;
- amount: number;
- modifiedAmount?: number;
- category?: string;
- currency: string;
- tag?: string;
- type: SearchTransactionType;
+
+ /** If the transaction has a Ereceipt */
hasViolation: boolean;
+
+ /** The transaction tax amount */
taxAmount?: number;
+
+ /** The ID of the report the transaction is associated with */
reportID: string;
- reportType: string;
- policyID: string;
+
+ /** The report ID of the transaction thread associated with the transaction */
transactionThreadReportID: string;
- shouldShowMerchant: boolean;
+
+ /** The action that can be performed for the transaction */
action: string;
};
+type SearchAccountDetails = Partial;
+
type SearchTransactionType = ValueOf;
type SearchQuery = ValueOf;
@@ -76,4 +127,4 @@ type SearchResults = {
export default SearchResults;
-export type {SearchQuery, SearchTransaction, SearchTransactionType, SearchPersonalDetails, SearchPolicyDetails, SearchDataTypes, SearchTypeToItemMap};
+export type {SearchQuery, SearchTransaction, SearchTransactionType, SearchPersonalDetails, SearchPolicyDetails, SearchAccountDetails, SearchDataTypes, SearchTypeToItemMap};