Skip to content

Commit

Permalink
Implement action buttons for transactions and reports in Search
Browse files Browse the repository at this point in the history
  • Loading branch information
Kicu committed Jun 21, 2024
1 parent 619cd3c commit 1584ced
Show file tree
Hide file tree
Showing 13 changed files with 150 additions and 82 deletions.
3 changes: 2 additions & 1 deletion src/components/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ function Search({query, policyIDs, sortBy, sortOrder}: SearchProps) {

const ListItem = SearchUtils.getListItem(type);

const data = SearchUtils.getSections(searchResults?.data ?? {}, type);
const searchContext = {searchHash: hash};
const data = SearchUtils.getSections(searchResults?.data ?? {}, type, searchContext);
const sortedData = SearchUtils.getSortedSections(type, data, sortBy, sortOrder);

const onSortPress = (column: SearchColumnType, order: SortOrder) => {
Expand Down
23 changes: 11 additions & 12 deletions src/components/SelectionList/Search/ActionCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React from 'react';
import Badge from '@components/Badge';
import Button from '@components/Button';
import * as Expensicons from '@components/Icon/Expensicons';
import type {TransactionListItemType} from '@components/SelectionList/types';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as SearchUtils from '@libs/SearchUtils';
Expand All @@ -11,27 +10,24 @@ import type {SearchTransactionAction} from '@src/types/onyx/SearchResults';

const actionTranslationsMap: Record<SearchTransactionAction, TranslationPaths> = {
view: 'common.view',
// Todo add translation for Review
review: 'common.view',
review: 'common.view', // Todo fix translation
done: 'common.done',
paid: 'iou.settledExpensify',
approve: 'iou.approve',
pay: 'iou.pay',
submit: 'common.submit',
hold: 'iou.hold',
unhold: 'iou.unholdExpense',
};

type ActionCellProps = {
transactionItem: TransactionListItemType;
action: SearchTransactionAction;
transactionIDs: string[];
searchHash: number;
goToItem: () => void;
};

function ActionCell({transactionItem, goToItem}: ActionCellProps) {
function ActionCell({action, transactionIDs, searchHash, goToItem}: ActionCellProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();

const {action, amount} = transactionItem;

const text = translate(actionTranslationsMap[action]);

if (['done', 'paid'].includes(action)) {
Expand Down Expand Up @@ -59,13 +55,16 @@ function ActionCell({transactionItem, goToItem}: ActionCellProps) {
}

const command = SearchUtils.getTransactionActionCommand(action);
const reportsAndAmounts = {[transactionItem.reportID ?? -1]: amount};

return (
<Button
text={text}
onPress={() => {
command('', reportsAndAmounts);
if (!command) {
return;
}

command(searchHash, transactionIDs, '');
}}
small
pressOnEnter
Expand Down
22 changes: 17 additions & 5 deletions src/components/SelectionList/Search/ExpenseItemHeaderNarrow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,35 @@ import React, {memo} from 'react';
import {View} from 'react-native';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import type {TransactionListItemType} from '@components/SelectionList/types';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import type {SearchAccountDetails} from '@src/types/onyx/SearchResults';
import type {SearchAccountDetails, SearchTransactionAction} from '@src/types/onyx/SearchResults';
import ActionCell from './ActionCell';
import UserInfoCell from './UserInfoCell';

type ExpenseItemHeaderNarrowProps = {
transactionItem: TransactionListItemType;
participantFrom: SearchAccountDetails;
participantTo: SearchAccountDetails;
participantFromDisplayName: string;
participantToDisplayName: string;
action: SearchTransactionAction;
transactionIDs: string[];
searchHash: number;
onButtonPress: () => void;
};

function ExpenseItemHeaderNarrow({transactionItem, participantFrom, participantFromDisplayName, participantTo, participantToDisplayName, onButtonPress}: ExpenseItemHeaderNarrowProps) {
function ExpenseItemHeaderNarrow({
participantFrom,
participantFromDisplayName,
participantTo,
participantToDisplayName,
action,
transactionIDs,
searchHash,
onButtonPress,
}: ExpenseItemHeaderNarrowProps) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const theme = useTheme();
Expand Down Expand Up @@ -49,7 +59,9 @@ function ExpenseItemHeaderNarrow({transactionItem, participantFrom, participantF
</View>
<View style={[StyleUtils.getWidthStyle(variables.w80)]}>
<ActionCell
transactionItem={transactionItem}
action={action}
transactionIDs={transactionIDs}
searchHash={searchHash}
goToItem={onButtonPress}
/>
</View>
Expand Down
35 changes: 11 additions & 24 deletions src/components/SelectionList/Search/ReportListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react';
import {View} from 'react-native';
import Button from '@components/Button';
import BaseListItem from '@components/SelectionList/BaseListItem';
import type {ListItem, ReportListItemProps, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types';
import Text from '@components/Text';
Expand All @@ -14,6 +13,7 @@ import Navigation from '@libs/Navigation/Navigation';
import {getSearchParams} from '@libs/SearchUtils';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import ActionCell from './ActionCell';
import ExpenseItemHeaderNarrow from './ExpenseItemHeaderNarrow';
import TransactionListItem from './TransactionListItem';
import TransactionListItemRow from './TransactionListItemRow';
Expand All @@ -29,10 +29,6 @@ type ReportCellProps = {
reportItem: ReportListItemType;
} & CellProps;

type ActionCellProps = {
onButtonPress: () => void;
} & CellProps;

function TotalCell({showTooltip, isLargeScreenWidth, reportItem}: ReportCellProps) {
const styles = useThemeStyles();

Expand All @@ -45,21 +41,6 @@ function TotalCell({showTooltip, isLargeScreenWidth, reportItem}: ReportCellProp
);
}

function ActionCell({onButtonPress}: ActionCellProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();

return (
<Button
text={translate('common.view')}
onPress={onButtonPress}
small
pressOnEnter
style={[styles.w100]}
/>
);
}

function ReportListItem<TItem extends ListItem>({
item,
isFocused,
Expand Down Expand Up @@ -124,6 +105,9 @@ function ReportListItem<TItem extends ListItem>({
);
}

// every child item comes from the same search hash, so it doesn't matter which one we use, we just need the hash
const {searchHash} = reportItem.transactions[0];

return (
<BaseListItem
item={item}
Expand All @@ -146,11 +130,13 @@ function ReportListItem<TItem extends ListItem>({
<View style={styles.flex1}>
{!isLargeScreenWidth && (
<ExpenseItemHeaderNarrow
transactionItem={{action: 'view'}}
participantFrom={participantFrom}
participantFromDisplayName={participantFromDisplayName}
participantTo={participantTo}
participantToDisplayName={participantToDisplayName}
action={reportItem.action ?? 'view'}
transactionIDs={[]}
searchHash={searchHash}
onButtonPress={handleOnButtonPress}
/>
)}
Expand All @@ -176,9 +162,10 @@ function ReportListItem<TItem extends ListItem>({
<View style={StyleUtils.getSearchTableColumnStyles(CONST.SEARCH_TABLE_COLUMNS.TYPE)} />
<View style={StyleUtils.getSearchTableColumnStyles(CONST.SEARCH_TABLE_COLUMNS.ACTION)}>
<ActionCell
showTooltip={showTooltip}
isLargeScreenWidth={isLargeScreenWidth}
onButtonPress={handleOnButtonPress}
action={reportItem.action ?? 'view'}
transactionIDs={[]}
searchHash={searchHash}
goToItem={handleOnButtonPress}
/>
</View>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,13 @@ function TransactionListItemRow({item, showTooltip, onButtonPress, showItemHeade
<View style={containerStyle}>
{showItemHeaderOnNarrowLayout && (
<ExpenseItemHeaderNarrow
transactionItem={item}
participantFrom={item.from}
participantFromDisplayName={item.formattedFrom}
participantTo={item.to}
participantToDisplayName={item.formattedTo}
action={item.action}
transactionIDs={[item.transactionID]}
searchHash={item.searchHash}
onButtonPress={onButtonPress}
/>
)}
Expand Down Expand Up @@ -343,7 +345,9 @@ function TransactionListItemRow({item, showTooltip, onButtonPress, showItemHeade
</View>
<View style={[StyleUtils.getSearchTableColumnStyles(CONST.SEARCH_TABLE_COLUMNS.ACTION)]}>
<ActionCell
transactionItem={item}
action={item.action}
transactionIDs={[item.transactionID]}
searchHash={item.searchHash}
goToItem={onButtonPress}
/>
</View>
Expand Down
3 changes: 3 additions & 0 deletions src/components/SelectionList/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ type TransactionListItemType = ListItem &
* This is true if at least one transaction in the dataset was created in past years
*/
shouldShowYear: boolean;

/** Hash of search that was used to retrieve this item, needed for performing Actions on it */
searchHash: number;
};

type ReportListItemType = ListItem &
Expand Down
7 changes: 7 additions & 0 deletions src/libs/API/parameters/HoldRequestOnSearchParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
type HoldRequestOnSearchParams = {
searchHash: number;
transactionIDList: string[];
comment?: string;
};

export default HoldRequestOnSearchParams;
6 changes: 6 additions & 0 deletions src/libs/API/parameters/UnholdRequestOnSearchParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type UnholdRequestOnSearchParams = {
searchHash: number;
transactionIDList: string[];
};

export default UnholdRequestOnSearchParams;
2 changes: 2 additions & 0 deletions src/libs/API/parameters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,5 @@ export type {default as UpdateSubscriptionAutoRenewParams} from './UpdateSubscri
export type {default as UpdateSubscriptionAddNewUsersAutomaticallyParams} from './UpdateSubscriptionAddNewUsersAutomaticallyParams';
export type {default as GenerateSpotnanaTokenParams} from './GenerateSpotnanaTokenParams';
export type {default as UpdateSubscriptionSizeParams} from './UpdateSubscriptionSizeParams';
export type {default as HoldRequestOnSearchParams} from './HoldRequestOnSearchParams';
export type {default as UnholdRequestOnSearchParams} from './UnholdRequestOnSearchParams';
5 changes: 5 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ const WRITE_COMMANDS = {
UPDATE_SUBSCRIPTION_AUTO_RENEW: 'UpdateSubscriptionAutoRenew',
UPDATE_SUBSCRIPTION_ADD_NEW_USERS_AUTOMATICALLY: 'UpdateSubscriptionAddNewUsersAutomatically',
UPDATE_SUBSCRIPTION_SIZE: 'UpdateSubscriptionSize',
HOLD_MONEY_REQUEST_ON_SEARCH: 'HoldRequestOnSearch',
UNHOLD_MONEY_REQUEST_ON_SEARCH: 'UnHoldRequestOnSearch',
} as const;

type WriteCommand = ValueOf<typeof WRITE_COMMANDS>;
Expand Down Expand Up @@ -455,6 +457,9 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.UPDATE_SUBSCRIPTION_AUTO_RENEW]: Parameters.UpdateSubscriptionAutoRenewParams;
[WRITE_COMMANDS.UPDATE_SUBSCRIPTION_ADD_NEW_USERS_AUTOMATICALLY]: Parameters.UpdateSubscriptionAddNewUsersAutomaticallyParams;
[WRITE_COMMANDS.UPDATE_SUBSCRIPTION_SIZE]: Parameters.UpdateSubscriptionSizeParams;

[WRITE_COMMANDS.HOLD_MONEY_REQUEST_ON_SEARCH]: Parameters.HoldRequestOnSearchParams;
[WRITE_COMMANDS.UNHOLD_MONEY_REQUEST_ON_SEARCH]: Parameters.UnholdRequestOnSearchParams;
};

const READ_COMMANDS = {
Expand Down
34 changes: 17 additions & 17 deletions src/libs/SearchUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ import * as UserUtils from './UserUtils';

type SortOrder = ValueOf<typeof CONST.SORT_ORDER>;
type SearchColumnType = ValueOf<typeof CONST.SEARCH_TABLE_COLUMNS>;
type SearchDataContext = {
searchHash: number;
};

const columnNamesToSortingProperty = {
[CONST.SEARCH_TABLE_COLUMNS.TO]: 'formattedTo' as const,
Expand Down Expand Up @@ -121,7 +124,7 @@ function shouldShowYear(data: TransactionListItemType[] | ReportListItemType[] |
return false;
}

function getTransactionsSections(data: OnyxTypes.SearchResults['data']): TransactionListItemType[] {
function getTransactionsSections(data: OnyxTypes.SearchResults['data'], context: SearchDataContext): TransactionListItemType[] {
const shouldShowMerchant = getShouldShowMerchant(data);

const doesDataContainAPastYearTransaction = shouldShowYear(data);
Expand All @@ -147,6 +150,7 @@ function getTransactionsSections(data: OnyxTypes.SearchResults['data']): Transac
formattedMerchant,
date,
shouldShowMerchant,
searchHash: context.searchHash,
shouldShowCategory: true,
shouldShowTag: true,
shouldShowTax: true,
Expand All @@ -156,7 +160,7 @@ function getTransactionsSections(data: OnyxTypes.SearchResults['data']): Transac
});
}

function getReportSections(data: OnyxTypes.SearchResults['data']): ReportListItemType[] {
function getReportSections(data: OnyxTypes.SearchResults['data'], context: SearchDataContext): ReportListItemType[] {
const shouldShowMerchant = getShouldShowMerchant(data);

const doesDataContainAPastYearTransaction = shouldShowYear(data);
Expand Down Expand Up @@ -194,6 +198,7 @@ function getReportSections(data: OnyxTypes.SearchResults['data']): ReportListIte
formattedMerchant,
date,
shouldShowMerchant,
searchHash: context.searchHash,
shouldShowCategory: true,
shouldShowTag: true,
shouldShowTax: true,
Expand Down Expand Up @@ -229,8 +234,8 @@ function getListItem<K extends keyof SearchTypeToItemMap>(type: K): SearchTypeTo
return searchTypeToItemMap[type].listItem;
}

function getSections<K extends keyof SearchTypeToItemMap>(data: OnyxTypes.SearchResults['data'], type: K): ReturnType<SearchTypeToItemMap[K]['getSections']> {
return searchTypeToItemMap[type].getSections(data) as ReturnType<SearchTypeToItemMap[K]['getSections']>;
function getSections<K extends keyof SearchTypeToItemMap>(data: OnyxTypes.SearchResults['data'], type: K, context: SearchDataContext): ReturnType<SearchTypeToItemMap[K]['getSections']> {
return searchTypeToItemMap[type].getSections(data, context) as ReturnType<SearchTypeToItemMap[K]['getSections']>;
}

function getSortedSections<K extends keyof SearchTypeToItemMap>(
Expand Down Expand Up @@ -278,20 +283,15 @@ function getSortedTransactionData(data: TransactionListItemType[], sortBy?: Sear
});
}

function getTransactionActionCommand(action: Omit<SearchTransactionAction, 'view' | 'done' | 'paid'>): (searchHash: string, reportsAndAmounts: Record<string, number>) => void {
if (action === 'pay') {
return SearchActions.payMoneyRequest;
}

if (action === 'approve') {
return SearchActions.approveMoneyRequest;
}

function getTransactionActionCommand(
action: Omit<SearchTransactionAction, 'view' | 'done' | 'paid'>,
): ((searchHash: number, transactionIDList: string[], comment?: string) => void) | undefined {
if (action === 'hold') {
return SearchActions.holdMoneyRequest;
return SearchActions.holdMoneyRequestOnSearch;
}
if (action === 'unhold') {
return SearchActions.unholdMoneyRequestOnSearch;
}

return SearchActions.submitMoneyRequest;
}

function getSearchParams() {
Expand All @@ -300,4 +300,4 @@ function getSearchParams() {
}

export {getListItem, getQueryHash, getSections, getSortedSections, getShouldShowMerchant, getSearchType, getTransactionActionCommand, getSearchParams, shouldShowYear};
export type {SearchColumnType, SortOrder};
export type {SearchColumnType, SortOrder, SearchDataContext};
Loading

0 comments on commit 1584ced

Please sign in to comment.