Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8254,6 +8254,11 @@ const CONST = {
search: true,
reportView: true,
},
DECLINED: {
column: this.TABLE_COLUMNS.DECLINED,
search: true,
reportView: false,
},
MCC: {column: this.TABLE_COLUMNS.MCC, search: true, reportView: true},
TAX_CODE: {
column: this.TABLE_COLUMNS.TAX_CODE,
Expand Down Expand Up @@ -8336,6 +8341,7 @@ const CONST = {
EXPORTED: this.TABLE_COLUMNS.EXPORTED,
STATUS: this.TABLE_COLUMNS.STATUS,
PAID_STATUS: this.TABLE_COLUMNS.PAID_STATUS,
DECLINED: this.TABLE_COLUMNS.DECLINED,
TITLE: this.TABLE_COLUMNS.TITLE,
FROM: this.TABLE_COLUMNS.FROM,
TO: this.TABLE_COLUMNS.TO,
Expand Down Expand Up @@ -8550,6 +8556,7 @@ const CONST = {
REIMBURSABLE: 'reimbursable',
BILLABLE: 'billable',
HELD: 'held',
DECLINED: 'declined',
TAX_RATE: 'taxrate',
TOTAL_AMOUNT: 'amount',
ATTENDEES: 'attendees',
Expand Down Expand Up @@ -8835,6 +8842,7 @@ const CONST = {
[this.TABLE_COLUMNS.REIMBURSABLE]: 'reimbursable',
[this.TABLE_COLUMNS.BILLABLE]: 'billable',
[this.TABLE_COLUMNS.HELD]: 'held',
[this.TABLE_COLUMNS.DECLINED]: 'declined',
[this.TABLE_COLUMNS.TAX_RATE]: 'tax-rate',
[this.TABLE_COLUMNS.TOTAL_AMOUNT]: 'amount',
[this.TABLE_COLUMNS.TOTAL]: 'total',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import TextCell from '@components/Search/SearchList/ListItem/TextCell';
import TotalCell from '@components/Search/SearchList/ListItem/TotalCell';
import UserInfoCell from '@components/Search/SearchList/ListItem/UserInfoCell';
import WorkspaceCell from '@components/Search/SearchList/ListItem/WorkspaceCell';
import YesNoCell from '@components/Search/SearchList/ListItem/YesNoCell';
import {useRowSelection} from '@components/Search/SearchSelectionProvider';
import {useListItemContext, useListItemHovered} from '@components/SelectionList/ListItemContext';

Expand Down Expand Up @@ -158,6 +159,11 @@ function ExpenseReportListItemRowWide({
/>
</View>
),
[CONST.SEARCH.TABLE_COLUMNS.DECLINED]: (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ CONSISTENCY-3 (docs)

This new DECLINED cell renders a Yes/No value with <View style={[getSearchColumnStyles(column)]}><Text selectable dataSet={COPYABLE_TEXT_DATA_SET}>{value ? translate('common.yes') : translate('common.no')}</Text></View>, which is the exact structure already implemented by the renderYesNoCell helper in TransactionItemRowWide.tsx (lines 183-195). Duplicating this across the two row components means a future change to the Yes/No cell (styling, copy, clipboard behavior) has to be made in two places and can drift.

Extract the Yes/No cell into a shared component (e.g. SearchList/ListItem/YesNoCell.tsx) and use it from both rows:

[CONST.SEARCH.TABLE_COLUMNS.DECLINED]: (
    <YesNoCell
        columnName={CONST.SEARCH.TABLE_COLUMNS.DECLINED}
        value={!!item.declined}
    />
),

and have TransactionItemRowWide.tsx's renderYesNoCell delegate to the same component.


Reviewed at: 73c2826 | Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the Yes/No cell into a shared YesNoCell component that both rows use, in 6131f51.

<View style={[getSearchColumnStyles(CONST.SEARCH.TABLE_COLUMNS.DECLINED)]}>
<YesNoCell value={!!item.declined} />
Comment thread
c3024 marked this conversation as resolved.
</View>
),
[CONST.SEARCH.TABLE_COLUMNS.TITLE]: (
<View style={[getSearchColumnStyles(CONST.SEARCH.TABLE_COLUMNS.TITLE)]}>
<TextCell
Expand Down
27 changes: 27 additions & 0 deletions src/components/Search/SearchList/ListItem/YesNoCell.tsx

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we use this for other components too? We use yes/no for reimbursable, billable, etc, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It already is used. TransactionItemRowWide renders Reimbursable, Billable, Held and Rejected through YesNoCell (via renderYesNoCell), and the report row uses it for Rejected. ExpenseReportListItemRowWide.tsx has only declined so there are no other changes here.

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Text from '@components/Text';

import useLocalize from '@hooks/useLocalize';

import {COPYABLE_TEXT_DATA_SET} from '@libs/SelectionScraper';

import React from 'react';

type YesNoCellProps = {
/** Whether the cell shows Yes or No */
value: boolean;
};

function YesNoCell({value}: YesNoCellProps) {
const {translate} = useLocalize();

return (
<Text
selectable
dataSet={COPYABLE_TEXT_DATA_SET}
>
{value ? translate('common.yes') : translate('common.no')}
</Text>
);
}

export default YesNoCell;
6 changes: 4 additions & 2 deletions src/components/Search/SearchList/ListItem/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ import type {
SearchMerchantGroup,
SearchMonthGroup,
SearchQuarterGroup,
SearchReport,
SearchTagGroup,
SearchTask,
SearchTransaction,
SearchTransactionAction,
SearchWeekGroup,
SearchWithdrawalIDGroup,
Expand Down Expand Up @@ -71,7 +73,7 @@ type ExpenseReportListItemProps<TItem extends ListItem> = ListItemProps<TItem> &
};

type TransactionListItemType = ListItem &
Transaction & {
SearchTransaction & {
/** Report to which the transaction belongs */
report: Report | undefined;

Expand Down Expand Up @@ -227,7 +229,7 @@ type TransactionGroupListItemType = ListItem & {

type ExpenseReportListItemType = TransactionReportGroupListItemType;

type TransactionReportGroupListItemType = TransactionGroupListItemType & {groupedBy: typeof CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT} & Report & {
type TransactionReportGroupListItemType = TransactionGroupListItemType & {groupedBy: typeof CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT} & SearchReport & {
/** The personal details of the user requesting money */
from: PersonalDetails;

Expand Down
8 changes: 8 additions & 0 deletions src/components/Search/SearchTableHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ const getExpenseHeaders = (groupBy?: SearchGroupBy): SearchColumnConfig[] => [
columnName: CONST.SEARCH.TABLE_COLUMNS.HELD,
translationKey: 'search.filters.held',
},
{
columnName: CONST.SEARCH.TABLE_COLUMNS.DECLINED,
translationKey: 'search.filters.rejected',
},
{
columnName: CONST.SEARCH.TABLE_COLUMNS.MCC,
translationKey: 'common.mcc',
Expand Down Expand Up @@ -296,6 +300,10 @@ const getExpenseReportHeaders = (profileIcon?: IconAsset): SearchColumnConfig[]
columnName: CONST.SEARCH.TABLE_COLUMNS.PAID_STATUS,
translationKey: 'common.paidStatus',
},
{
columnName: CONST.SEARCH.TABLE_COLUMNS.DECLINED,
translationKey: 'search.filters.rejected',
},
{
columnName: CONST.SEARCH.TABLE_COLUMNS.TITLE,
translationKey: 'common.title',
Expand Down
5 changes: 4 additions & 1 deletion src/components/Search/SortableTableHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ function SortableTableHeader({
const sortByColumnName = sortColumnName ?? columnName;
const isActive = sortBy === sortByColumnName;
const isYesNoColumn =
columnName === CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE || columnName === CONST.SEARCH.TABLE_COLUMNS.BILLABLE || columnName === CONST.SEARCH.TABLE_COLUMNS.HELD;
columnName === CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE ||
columnName === CONST.SEARCH.TABLE_COLUMNS.BILLABLE ||
columnName === CONST.SEARCH.TABLE_COLUMNS.HELD ||
columnName === CONST.SEARCH.TABLE_COLUMNS.DECLINED;
const isConversionAmountColumn =
columnName === CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED ||
columnName === CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED ||
Expand Down
11 changes: 4 additions & 7 deletions src/components/TransactionItemRow/TransactionItemRowWide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import TextCell from '@components/Search/SearchList/ListItem/TextCell';
import AmountCell from '@components/Search/SearchList/ListItem/TotalCell';
import UserInfoCell from '@components/Search/SearchList/ListItem/UserInfoCell';
import WorkspaceCell from '@components/Search/SearchList/ListItem/WorkspaceCell';
import YesNoCell from '@components/Search/SearchList/ListItem/YesNoCell';
import type {SearchColumnType} from '@components/Search/types';
import Text from '@components/Text';

import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
Expand Down Expand Up @@ -185,12 +185,7 @@ function TransactionItemRowWide({
key={columnName}
style={[getSearchColumnStyles(columnName)]}
>
<Text
selectable
dataSet={COPYABLE_TEXT_DATA_SET}
>
{value ? translate('common.yes') : translate('common.no')}
</Text>
<YesNoCell value={value} />
</View>
);

Expand Down Expand Up @@ -374,6 +369,8 @@ function TransactionItemRowWide({
return renderYesNoCell(column, !!transactionItem.billable);
case CONST.SEARCH.TABLE_COLUMNS.HELD:
return renderYesNoCell(column, isOnHold(transactionItem));
case CONST.SEARCH.TABLE_COLUMNS.DECLINED:
return renderYesNoCell(column, !!transactionItem.declined);
case CONST.SEARCH.TABLE_COLUMNS.ACTION:
return (
<View
Expand Down
5 changes: 4 additions & 1 deletion src/components/TransactionItemRow/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {ModifiedMouseEvent} from '@libs/Navigation/helpers/openInternalRout

import type {CardList, PersonalDetails, Policy, PolicyCategories, PolicyTagLists, Report, ReportAction, TransactionViolation} from '@src/types/onyx';
import type {Attendee} from '@src/types/onyx/IOU';
import type {SearchTransactionAction} from '@src/types/onyx/SearchResults';
import type {SearchTransaction, SearchTransactionAction} from '@src/types/onyx/SearchResults';

import type {StyleProp, ViewStyle} from 'react-native';

Expand Down Expand Up @@ -60,6 +60,9 @@ type TransactionWithOptionalSearchFields = TransactionWithOptionalHighlight & {

/** ID of the policy the transaction belongs to, provided on search transactions */
policyID?: string;

/** Whether the expense was ever rejected, provided on search transactions when the Rejected column is shown or sorted by */
Comment thread
c3024 marked this conversation as resolved.
declined?: SearchTransaction['declined'];
};

type TransactionItemRowProps = {
Expand Down
21 changes: 17 additions & 4 deletions src/libs/SearchUIUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ const transactionColumnNamesToSortingProperty: TransactionSorting = {
[CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE]: 'reimbursable' as const,
[CONST.SEARCH.TABLE_COLUMNS.BILLABLE]: 'billable' as const,
[CONST.SEARCH.TABLE_COLUMNS.HELD]: null,
[CONST.SEARCH.TABLE_COLUMNS.DECLINED]: 'declined' as const,
[CONST.SEARCH.TABLE_COLUMNS.MCC]: 'mcc' as const,
[CONST.SEARCH.TABLE_COLUMNS.TAX_CODE]: 'taxCode' as const,
[CONST.SEARCH.TABLE_COLUMNS.TYPE]: null,
Expand Down Expand Up @@ -3967,16 +3968,15 @@ function compareValues(a: unknown, b: unknown, sortOrder: SortOrder, sortBy: str
/**
* @private
* Gets the value to use for sorting a transaction by a given property.
* Handles special cases like comments and boolean fields (reimbursable, billable, held).
* Handles special cases like comments and boolean fields (reimbursable, billable, held, declined).
*/
function getTransactionSortValue(transaction: TransactionListItemType, sortingProperty: string): unknown {
if (sortingProperty === 'comment') {
return transaction.comment?.comment;
}

if (sortingProperty === 'reimbursable' || sortingProperty === 'billable') {
const boolValue = transaction[sortingProperty as keyof TransactionListItemType];
return boolValue ? CONST.SEARCH.BOOLEAN.YES : CONST.SEARCH.BOOLEAN.NO;
if (sortingProperty === 'reimbursable' || sortingProperty === 'billable' || sortingProperty === 'declined') {
Comment thread
c3024 marked this conversation as resolved.
return transaction[sortingProperty] ? CONST.SEARCH.BOOLEAN.YES : CONST.SEARCH.BOOLEAN.NO;
}

if (sortingProperty === CONST.SEARCH.TABLE_COLUMNS.HELD) {
Expand Down Expand Up @@ -4293,6 +4293,15 @@ function getSortedReportData(
});
}

if (sortBy === CONST.SEARCH.TABLE_COLUMNS.DECLINED) {
return data.sort((a, b) => {
// Ranked as 'yes'/'no' to match the backend, with a report that never got the field ranked as not rejected.
const aValue = a.declined ? CONST.SEARCH.BOOLEAN.YES : CONST.SEARCH.BOOLEAN.NO;
const bValue = b.declined ? CONST.SEARCH.BOOLEAN.YES : CONST.SEARCH.BOOLEAN.NO;
return compareValues(aValue, bValue, sortOrder, sortBy, localeCompare);
});
}

const sortingProperty =
sortBy === CONST.SEARCH.SORT_BY_COLUMNS.CATEGORY_GL_CODE || sortBy === CONST.SEARCH.SORT_BY_COLUMNS.TAG_GL_CODE ? undefined : expenseReportColumnNamesToSortingProperty[sortBy];

Expand Down Expand Up @@ -4551,6 +4560,8 @@ function getSearchColumnTranslationKey(column: SearchSortBy, type?: SearchDataTy
return 'common.billable';
case CONST.SEARCH.TABLE_COLUMNS.HELD:
return 'search.filters.held';
case CONST.SEARCH.TABLE_COLUMNS.DECLINED:
return 'search.filters.rejected';
case CONST.SEARCH.TABLE_COLUMNS.MCC:
return 'common.mcc';
case CONST.SEARCH.TABLE_COLUMNS.TAX_CODE:
Expand Down Expand Up @@ -6776,6 +6787,7 @@ function getColumnsToShow({
[CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE]: false,
[CONST.SEARCH.TABLE_COLUMNS.BILLABLE]: false,
[CONST.SEARCH.TABLE_COLUMNS.HELD]: false,
[CONST.SEARCH.TABLE_COLUMNS.DECLINED]: false,
[CONST.SEARCH.TABLE_COLUMNS.MCC]: false,
[CONST.SEARCH.TABLE_COLUMNS.TAX_CODE]: false,
[CONST.SEARCH.TABLE_COLUMNS.TAX_RATE]: false,
Expand Down Expand Up @@ -7183,6 +7195,7 @@ function getTableMinWidth(
column === CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE ||
column === CONST.SEARCH.TABLE_COLUMNS.BILLABLE ||
column === CONST.SEARCH.TABLE_COLUMNS.HELD ||
column === CONST.SEARCH.TABLE_COLUMNS.DECLINED ||
column === CONST.SEARCH.TABLE_COLUMNS.MCC ||
column === CONST.SEARCH.TABLE_COLUMNS.TAX_CODE
) {
Expand Down
1 change: 1 addition & 0 deletions src/libs/getTransactionFromTransactionListItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const SEARCH_TRANSACTION_ROW_ONLY_FIELDS: Record<Exclude<keyof TransactionListIt
approved: true,
exported: true,
exportedTo: true,
declined: true,
policy: true,
reportAction: true,
holdReportAction: true,
Expand Down
1 change: 1 addition & 0 deletions src/styles/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2122,6 +2122,7 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({
case CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE:
case CONST.SEARCH.TABLE_COLUMNS.BILLABLE:
case CONST.SEARCH.TABLE_COLUMNS.HELD:
case CONST.SEARCH.TABLE_COLUMNS.DECLINED:
columnWidth = {...getWidthStyle(variables.w80)};
break;
case CONST.SEARCH.TABLE_COLUMNS.MCC:
Expand Down
18 changes: 16 additions & 2 deletions src/types/onyx/SearchResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,12 +285,24 @@ type SearchQuarterGroup = SearchGroupBase & {
quarter: number;
};

/** Model of an expense in the search results */
type SearchTransaction = Transaction & {
/** Whether the expense was ever rejected, only returned when the Rejected column is shown or sorted by */
declined?: boolean;
};

/** Model of a report in the search results */
type SearchReport = Report & {
/** Whether the expense report was ever rejected, only returned when the Rejected column is shown or sorted by */
declined?: boolean;
};

/** SearchResultDataType */
type SearchResultDataType = PrefixedRecord<typeof ONYXKEYS.COLLECTION.TRANSACTION, Transaction> &
type SearchResultDataType = PrefixedRecord<typeof ONYXKEYS.COLLECTION.TRANSACTION, SearchTransaction> &
Partial<Record<typeof ONYXKEYS.PERSONAL_DETAILS_LIST, Record<string, PersonalDetails> | undefined>> &
PrefixedRecord<typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS, Record<string, ReportAction>> &
Partial<PrefixedRecord<typeof ONYXKEYS.COLLECTION.REPORT_METADATA, ReportMetadata>> &
PrefixedRecord<typeof ONYXKEYS.COLLECTION.REPORT, Report> &
PrefixedRecord<typeof ONYXKEYS.COLLECTION.REPORT, SearchReport> &
PrefixedRecord<typeof ONYXKEYS.COLLECTION.POLICY, Policy> &
PrefixedRecord<typeof ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, TransactionViolation[]> &
PrefixedRecord<typeof ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, ReportNameValuePairs> &
Expand Down Expand Up @@ -326,6 +338,8 @@ export default SearchResults;
export type {
ListItemDataType,
SearchTask,
SearchTransaction,
SearchReport,
SearchTransactionAction,
SearchDataTypes,
SearchResultsInfo,
Expand Down
Loading
Loading