Skip to content
Closed
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
2 changes: 2 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3459,6 +3459,7 @@ const CONST = {
},
TAB_SEARCH: {
ALL: 'all',
DRAFTS: 'drafts',
},
STATUS_TEXT_MAX_LENGTH: 100,

Expand Down Expand Up @@ -4749,6 +4750,7 @@ const CONST = {

SEARCH_DATA_TYPES: {
TRANSACTION: 'transaction',
REPORT: 'report',
},
} as const;

Expand Down
129 changes: 119 additions & 10 deletions src/components/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import ROUTES from '@src/ROUTES';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
import SelectionList from './SelectionList';
import SearchTableHeader from './SelectionList/SearchTableHeader';
import SearchTableHeader from './SelectionList/Search/SearchTableHeader';
import TableListItemSkeleton from './Skeletons/TableListItemSkeleton';

type SearchProps = {
Expand All @@ -27,7 +27,116 @@ function Search({query}: SearchProps) {
useCustomBackHandler();

const hash = SearchUtils.getQueryHash(query);
const [searchResults, searchResultsMeta] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`);
// const [searchResults, searchResultsMeta] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`);

const searchResults = {
"data": {
"personalDetailsList": {
"645": {
"accountID": 645,
"avatar": "https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/default-avatar_22.png",
"displayName": "ccc@cc.com",
"firstName": "",
"lastName": "",
"login": "ccc@cc.com",
"payPalMeAddress": "",
"phoneNumber": "",
"pronouns": "",
"timezone": {
"automatic": true,
"selected": "America/Los_Angeles"
},
"validated": ""
},
"844": {
"accountID": 844,
"avatar": "https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/default-avatar_5.png",
"displayName": "long ass name to test text overflowing container dfdf",
"firstName": "long ass name to test text overflowing container",
"lastName": "dfdf",
"login": "cc2@cc.com",
"payPalMeAddress": "",
"phoneNumber": "",
"pronouns": "__predefined_thonThons",
"timezone": {
"automatic": true,
"selected": "America/Los_Angeles"
},
"validated": ""
},
},
"report_5544039625114797": {
"reportID": 5544039625114797,
"reportName": "Dummy report",
"total": 2234,
"currency": "USD",
"action": "view",
},
"transactions_1068903897709851923": {
"accountID": 844,
"action": "view",
"amount": -30885,
"canDelete": false,
"category": "Uncategorized",
"comment": {
"comment": ""
},
"created": "2023-10-28",
"currency": "USD",
"hasEReceipt": false,
"managerID": 645,
"merchant": "NEWHAIR",
"modifiedAmount": 0,
"modifiedCreated": "",
"modifiedCurrency": "",
"modifiedMerchant": "",
"parentTransactionID": "",
"policyID": "296A4D9E7F12B910",
"reportID": "5544039625114797",
"reportType": "expense",
"tag": "",
"transactionID": "1068903897709851923",
"transactionThreadReportID": "0",
"type": "card"
},
"transactions_1145103166671192292": {
"accountID": 844,
"action": "view",
"amount": 0,
"canDelete": true,
"category": "Ttt",
"comment": {
"comment": ""
},
"created": "2024-05-09 20:43:28",
"currency": "CAD",
"hasEReceipt": false,
"managerID": 645,
"merchant": "(none)",
"modifiedAmount": -3300,
"modifiedCreated": "",
"modifiedCurrency": "CAD",
"modifiedMerchant": "test",
"parentTransactionID": "",
"policyID": "3CC70F43B8D5D63C",
"receipt": {
"source": "https://www.expensify.com.dev/receipts/w_db31f7b4f64a0285ba742b3eef356001ba39e076.png"
},
"reportID": "5544039625114797",
"reportType": "expense",
"tag": "",
"taxAmount": 0,
"transactionID": "1145103166671192292",
"transactionThreadReportID": "3314198419415278",
"type": "cash"
},
},
"search": {
"hasMoreResults": true,
"offset": 0,
"type": "report"
}
}

useEffect(() => {
if (isOffline) {
Expand All @@ -37,16 +146,16 @@ function Search({query}: SearchProps) {
SearchActions.search(query);
}, [query, isOffline]);

const isLoading = (!isOffline && isLoadingOnyxValue(searchResultsMeta)) || searchResults?.data === undefined;
const shouldShowEmptyState = !isLoading && isEmptyObject(searchResults?.data);
// const isLoading = (!isOffline && isLoadingOnyxValue(searchResultsMeta)) || searchResults?.data === undefined;
// const shouldShowEmptyState = !isLoading && isEmptyObject(searchResults?.data);

if (isLoading) {
return <TableListItemSkeleton shouldAnimate />;
}
// if (isLoading) {
// return <TableListItemSkeleton shouldAnimate />;
// }

if (shouldShowEmptyState) {
return <EmptySearchView />;
}
// if (shouldShowEmptyState) {
// return <EmptySearchView />;
// }

const openReport = (reportID?: string) => {
if (!reportID) {
Expand Down
125 changes: 125 additions & 0 deletions src/components/SelectionList/Search/ReportListItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import React from 'react';
import {View} from 'react-native';
import useWindowDimensions from '@hooks/useWindowDimensions';
import useThemeStyles from '@hooks/useThemeStyles';
import type {ListItem, ReportListItemProps, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types';
import BaseListItem from '@components/SelectionList/BaseListItem';
import TextWithTooltip from '@components/TextWithTooltip';
import useStyleUtils from '@hooks/useStyleUtils';
import CONST from '@src/CONST';
import type {ThemeStyles} from '@styles/index';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import Button from '@components/Button';
import useLocalize from '@hooks/useLocalize';
import type {TranslationPaths} from '@src/languages/types';
import type {Phrase, PhraseParameters} from '@libs/Localize';
import TransactionListItem from './TransactionListItem';

const reportNameCell = (showTooltip: boolean, item: ReportListItemType, styles: ThemeStyles) => (
<TextWithTooltip
shouldShowTooltip={showTooltip}
text={item.reportName}
style={[styles.optionDisplayName, styles.label, styles.pre, styles.justifyContentCenter]}
/>
)

const groupedExpensesCell = (showTooltip: boolean, item: ReportListItemType, styles: ThemeStyles, translate: <TKey extends TranslationPaths>(phraseKey: TKey, ...phraseParameters: PhraseParameters<Phrase<TKey>>) => string) => (
<TextWithTooltip
shouldShowTooltip={showTooltip}
text={translate('search.groupedExpenses', item.transactionsCount)}
style={[styles.optionDisplayName, styles.label, styles.pre, styles.justifyContentCenter]}
/>
)

const totalCell = (showTooltip: boolean, item: ReportListItemType, styles: ThemeStyles) => (
<TextWithTooltip
shouldShowTooltip={showTooltip}
text={CurrencyUtils.convertToDisplayString(item.total, item.currency)}
style={[styles.optionDisplayName, styles.label, styles.pre, styles.justifyContentCenter]}
/>
)

const actionCell = (item: ReportListItemType, styles: ThemeStyles, translate: <TKey extends TranslationPaths>(phraseKey: TKey, ...phraseParameters: PhraseParameters<Phrase<TKey>>) => string, onSelectRow: (item: ReportListItemType) => void) => (
<Button
text={translate('common.view')}
onPress={() => {
onSelectRow(item);
}}
small
pressOnEnter
style={[styles.p0]}
/>
)

function ReportListItem<TItem extends ListItem>({
item,
isFocused,
showTooltip,
isDisabled,
canSelectMultiple,
onSelectRow,
onDismissError,
shouldPreventDefaultFocusOnSelectRow,
onFocus,
shouldSyncFocus,
}: ReportListItemProps<TItem>) {
const reportItem = item as unknown as ReportListItemType;
const styles = useThemeStyles();
const {isLargeScreenWidth} = useWindowDimensions();
const StyleUtils = useStyleUtils();
const {translate} = useLocalize();

const listItemPressableStyle = [styles.selectionListPressableItemWrapper, styles.pv3, item.isSelected && styles.activeComponentBG, isFocused && styles.sidebarLinkActive];

if (!isLargeScreenWidth) {
return (
<></>
);
}

return (
<BaseListItem
item={item}
pressableStyle={listItemPressableStyle}
wrapperStyle={[styles.flexRow, styles.flex1, styles.justifyContentBetween, styles.userSelectNone, styles.alignItemsCenter]}
containerStyle={[styles.mb3]}
isFocused={isFocused}
isDisabled={isDisabled}
showTooltip={showTooltip}
canSelectMultiple={canSelectMultiple}
onSelectRow={onSelectRow}
onDismissError={onDismissError}
shouldPreventDefaultFocusOnSelectRow={shouldPreventDefaultFocusOnSelectRow}
errors={item.errors}
pendingAction={item.pendingAction}
keyForList={item.keyForList}
onFocus={onFocus}
shouldSyncFocus={shouldSyncFocus}
hoverStyle={item.isSelected && styles.activeComponentBG}
>
{() => (
<View>
<View style={[styles.flex1, styles.flexRow, styles.gap3, styles.alignItemsCenter]}>
<View style={[styles.flex1]}>
{reportNameCell(showTooltip, reportItem, styles)}
{groupedExpensesCell(showTooltip, reportItem, styles, translate)}
</View>
<View style={[StyleUtils.getSearchTableColumnStyles(CONST.SEARCH_TABLE_COLUMNS.TOTAL)]}>{totalCell(showTooltip, reportItem, styles)}</View>
<View style={[StyleUtils.getSearchTableColumnStyles(CONST.SEARCH_TABLE_COLUMNS.ACTION)]}>{actionCell(reportItem, styles, translate, onSelectRow)}</View>
</View>
<View>
{item.transactions.map((transaction: TransactionListItemType) => (
<TransactionListItem
item={transaction}
/>
))}
</View>
</View>
)}
</BaseListItem>
);
}

ReportListItem.displayName = 'ReportListItem';

export default ReportListItem;
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ 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 BaseListItem from './BaseListItem';
import type {ListItem, TransactionListItemProps, TransactionListItemType} from './types';
import BaseListItem from '@components/SelectionList/BaseListItem';
import type {ListItem, TransactionListItemProps, TransactionListItemType} from '@components/SelectionList/types';

const getTypeIcon = (type?: SearchTransactionType) => {
switch (type) {
Expand Down
24 changes: 24 additions & 0 deletions src/components/SelectionList/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,26 @@ type TransactionListItemType = ListItem & {
shouldShowMerchant: boolean;
};

type ReportListItemType = ListItem & {
/** The ID of the report */
reportID: string;

/** Name of the report */
reportName: string;

/** Report total */
total: number;

/** Report currency */
currency: string;

/** The next step the user can take */
action: string;

/** The number of transactions in this report */
transactionsCount: number;
};

type ListItemProps<TItem extends ListItem> = CommonListItemProps<TItem> & {
/** The section list item */
item: TItem;
Expand Down Expand Up @@ -245,6 +265,8 @@ type TableListItemProps<TItem extends ListItem> = ListItemProps<TItem>;

type TransactionListItemProps<TItem extends ListItem> = ListItemProps<TItem>;

type ReportListItemProps<TItem extends ListItem> = ListItemProps<TItem>;

type ValidListItem = typeof RadioListItem | typeof UserListItem | typeof TableListItem | typeof InviteMemberListItem | typeof TransactionListItem;

type Section<TItem extends ListItem> = {
Expand Down Expand Up @@ -451,4 +473,6 @@ export type {
UserListItemProps,
ValidListItem,
TransactionListItemType,
ReportListItemProps,
ReportListItemType,
};
1 change: 1 addition & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2631,6 +2631,7 @@ export default {
subtitle: 'Try creating something using the green + button.',
},
},
groupedExpenses: (transactionsCount) => `${transactionsCount} grouped expenses`,
},
genericErrorPage: {
title: 'Uh-oh, something went wrong!',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2670,6 +2670,7 @@ export default {
subtitle: 'Por favor intenta crear algo usando el botón verde.',
},
},
groupedExpenses: (transactionsCount) => `${transactionsCount} grouped expenses`,
},
genericErrorPage: {
title: '¡Oh-oh, algo salió mal!',
Expand Down
13 changes: 12 additions & 1 deletion src/libs/SearchUtils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import TransactionListItem from '@components/SelectionList/TransactionListItem';
import TransactionListItem from '@components/SelectionList/Search/TransactionListItem';
import ReportListItem from '@components/SelectionList/Search/ReportListItem';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type * as OnyxTypes from '@src/types/onyx';
Expand All @@ -9,6 +10,8 @@ function getSearchType(search: OnyxTypes.SearchResults['search']): SearchDataTyp
switch (search.type) {
case CONST.SEARCH_DATA_TYPES.TRANSACTION:
return CONST.SEARCH_DATA_TYPES.TRANSACTION;
case CONST.SEARCH_DATA_TYPES.REPORT:
return CONST.SEARCH_DATA_TYPES.REPORT;
default:
return undefined;
}
Expand Down Expand Up @@ -42,11 +45,19 @@ function getTransactionsSections(data: OnyxTypes.SearchResults['data']): SearchT
});
}

function getReportSections(data: OnyxTypes.SearchResults['data']): SearchTransaction[] {
return data;
}

const searchTypeToItemMap: SearchTypeToItemMap = {
[CONST.SEARCH_DATA_TYPES.TRANSACTION]: {
listItem: TransactionListItem,
getSections: getTransactionsSections,
},
[CONST.SEARCH_DATA_TYPES.REPORT]: {
listItem: ReportListItem,
getSections: getTransactionsSections,
},
};

function getListItem<K extends keyof SearchTypeToItemMap>(type: K): SearchTypeToItemMap[K]['listItem'] {
Expand Down
Loading