diff --git a/assets/images/simple-illustrations/simple-illustration__envelopereceipt.svg b/assets/images/simple-illustrations/simple-illustration__envelopereceipt.svg
deleted file mode 100644
index eb2bad31620d..000000000000
--- a/assets/images/simple-illustrations/simple-illustration__envelopereceipt.svg
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
\ No newline at end of file
diff --git a/src/CONST.ts b/src/CONST.ts
index 0becab6d3483..db29c1195e48 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -5251,6 +5251,8 @@ const CONST = {
SEARCH: {
RESULTS_PAGE_SIZE: 50,
DATA_TYPES: {
+ TRANSACTION: 'transaction',
+ REPORT: 'report',
EXPENSE: 'expense',
INVOICE: 'invoice',
TRIP: 'trip',
@@ -5279,10 +5281,9 @@ const CONST = {
STATUS: {
EXPENSE: {
ALL: 'all',
+ SHARED: 'shared',
DRAFTS: 'drafts',
- OUTSTANDING: 'outstanding',
- APPROVED: 'approved',
- PAID: 'paid',
+ FINISHED: 'finished',
},
INVOICE: {
ALL: 'all',
@@ -5297,6 +5298,14 @@ const CONST = {
PAID: 'paid',
},
},
+ TAB: {
+ EXPENSE: {
+ ALL: 'type:expense status:all',
+ SHARED: 'type:expense status:shared',
+ DRAFTS: 'type:expense status:drafts',
+ FINISHED: 'type:expense status:finished',
+ },
+ },
TABLE_COLUMNS: {
RECEIPT: 'receipt',
DATE: 'date',
diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx
index 1441266c76de..126c81961cee 100644
--- a/src/components/Button/index.tsx
+++ b/src/components/Button/index.tsx
@@ -29,9 +29,6 @@ type ButtonProps = Partial & {
/** The fill color to pass into the icon. */
iconFill?: string;
- /** The fill color to pass into the icon when the button is hovered. */
- iconHoverFill?: string;
-
/** Any additional styles to pass to the left icon container. */
iconStyles?: StyleProp;
@@ -83,15 +80,9 @@ type ButtonProps = Partial & {
/** Additional text styles */
textStyles?: StyleProp;
- /** Additional text styles when the button is hovered */
- textHoverStyles?: StyleProp;
-
/** Whether we should use the default hover style */
shouldUseDefaultHover?: boolean;
- /** Additional hover styles */
- hoverStyles?: StyleProp;
-
/** Whether we should use the success theme color */
success?: boolean;
@@ -179,7 +170,6 @@ function Button(
iconRight = Expensicons.ArrowRight,
iconFill,
- iconHoverFill,
icon = null,
iconStyles = [],
iconRightStyles = [],
@@ -204,10 +194,8 @@ function Button(
style = [],
innerStyles = [],
textStyles = [],
- textHoverStyles = [],
shouldUseDefaultHover = true,
- hoverStyles = undefined,
success = false,
danger = false,
@@ -250,7 +238,6 @@ function Button(
danger && styles.buttonDangerText,
!!icon && styles.textAlignLeft,
textStyles,
- isHovered && textHoverStyles,
link && styles.link,
link && isHovered && StyleUtils.getColorStyle(theme.linkHover),
link && styles.fontWeightNormal,
@@ -272,7 +259,7 @@ function Button(
) : (
& {
subtitleStyles?: StyleProp;
@@ -100,23 +100,13 @@ type SearchPageHeaderProps = {
type SearchHeaderOptionValue = DeepValueOf | undefined;
-type HeaderContent = {
- icon: IconAsset;
- titleText: TranslationPaths;
+const headerContent: {[key in SearchStatus]: {icon: IconAsset; titleText: TranslationPaths}} = {
+ all: {icon: Illustrations.MoneyReceipts, titleText: 'common.expenses'},
+ shared: {icon: Illustrations.SendMoney, titleText: 'common.shared'},
+ drafts: {icon: Illustrations.Pencil, titleText: 'common.drafts'},
+ finished: {icon: Illustrations.CheckmarkCircle, titleText: 'common.finished'},
};
-function getHeaderContent(type: SearchDataTypes): HeaderContent {
- switch (type) {
- case CONST.SEARCH.DATA_TYPES.INVOICE:
- return {icon: Illustrations.EnvelopeReceipt, titleText: 'workspace.common.invoices'};
- case CONST.SEARCH.DATA_TYPES.TRIP:
- return {icon: Illustrations.Luggage, titleText: 'travel.trips'};
- case CONST.SEARCH.DATA_TYPES.EXPENSE:
- default:
- return {icon: Illustrations.MoneyReceipts, titleText: 'common.expenses'};
- }
-}
-
function SearchPageHeader({queryJSON, hash, onSelectDeleteOption, setOfflineModalOpen, setDownloadErrorModalOpen, isCustomQuery, data}: SearchPageHeaderProps) {
const {translate} = useLocalize();
const theme = useTheme();
@@ -141,10 +131,10 @@ function SearchPageHeader({queryJSON, hash, onSelectDeleteOption, setOfflineModa
.map((item) => item.reportID),
[data, selectedTransactions],
);
- const {status, type} = queryJSON;
- const headerSubtitle = isCustomQuery ? SearchUtils.getSearchHeaderTitle(queryJSON) : translate(getHeaderContent(type).titleText);
+ const {status} = queryJSON;
+ const headerSubtitle = isCustomQuery ? SearchUtils.getSearchHeaderTitle(queryJSON) : translate(headerContent[status]?.titleText);
const headerTitle = isCustomQuery ? translate('search.filtersHeader') : '';
- const headerIcon = isCustomQuery ? Illustrations.Filters : getHeaderContent(type).icon;
+ const headerIcon = isCustomQuery ? Illustrations.Filters : headerContent[status]?.icon;
const subtitleStyles = isCustomQuery ? {} : styles.textHeadlineH2;
diff --git a/src/components/Search/SearchStatusBar.tsx b/src/components/Search/SearchStatusBar.tsx
deleted file mode 100644
index 7c1ffeff1818..000000000000
--- a/src/components/Search/SearchStatusBar.tsx
+++ /dev/null
@@ -1,166 +0,0 @@
-import React from 'react';
-import Button from '@components/Button';
-import * as Expensicons from '@components/Icon/Expensicons';
-import ScrollView from '@components/ScrollView';
-import useLocalize from '@hooks/useLocalize';
-import useSingleExecution from '@hooks/useSingleExecution';
-import useStyleUtils from '@hooks/useStyleUtils';
-import useTheme from '@hooks/useTheme';
-import useThemeStyles from '@hooks/useThemeStyles';
-import Navigation from '@libs/Navigation/Navigation';
-import * as SearchUtils from '@libs/SearchUtils';
-import CONST from '@src/CONST';
-import type {TranslationPaths} from '@src/languages/types';
-import type {SearchDataTypes} from '@src/types/onyx/SearchResults';
-import type IconAsset from '@src/types/utils/IconAsset';
-import type {ExpenseSearchStatus, InvoiceSearchStatus, SearchQueryString, SearchStatus, TripSearchStatus} from './types';
-
-type SearchStatusBarProps = {
- type: SearchDataTypes;
- status: SearchStatus;
-};
-
-const expenseOptions: Array<{key: ExpenseSearchStatus; icon: IconAsset; text: TranslationPaths; query: SearchQueryString}> = [
- {
- key: CONST.SEARCH.STATUS.EXPENSE.ALL,
- icon: Expensicons.All,
- text: 'common.all',
- query: SearchUtils.buildCannedSearchQuery(CONST.SEARCH.DATA_TYPES.EXPENSE, CONST.SEARCH.STATUS.EXPENSE.ALL),
- },
- {
- key: CONST.SEARCH.STATUS.EXPENSE.DRAFTS,
- icon: Expensicons.Pencil,
- text: 'common.drafts',
- query: SearchUtils.buildCannedSearchQuery(CONST.SEARCH.DATA_TYPES.EXPENSE, CONST.SEARCH.STATUS.EXPENSE.DRAFTS),
- },
- {
- key: CONST.SEARCH.STATUS.EXPENSE.OUTSTANDING,
- icon: Expensicons.Hourglass,
- text: 'common.outstanding',
- query: SearchUtils.buildCannedSearchQuery(CONST.SEARCH.DATA_TYPES.EXPENSE, CONST.SEARCH.STATUS.EXPENSE.OUTSTANDING),
- },
- {
- key: CONST.SEARCH.STATUS.EXPENSE.APPROVED,
- icon: Expensicons.ThumbsUp,
- text: 'iou.approved',
- query: SearchUtils.buildCannedSearchQuery(CONST.SEARCH.DATA_TYPES.EXPENSE, CONST.SEARCH.STATUS.EXPENSE.APPROVED),
- },
- {
- key: CONST.SEARCH.STATUS.EXPENSE.PAID,
- icon: Expensicons.MoneyBag,
- text: 'iou.settledExpensify',
- query: SearchUtils.buildCannedSearchQuery(CONST.SEARCH.DATA_TYPES.EXPENSE, CONST.SEARCH.STATUS.EXPENSE.PAID),
- },
-];
-
-const invoiceOptions: Array<{key: InvoiceSearchStatus; icon: IconAsset; text: TranslationPaths; query: SearchQueryString}> = [
- {
- key: CONST.SEARCH.STATUS.INVOICE.ALL,
- icon: Expensicons.All,
- text: 'common.all',
- query: SearchUtils.buildCannedSearchQuery(CONST.SEARCH.DATA_TYPES.INVOICE, CONST.SEARCH.STATUS.INVOICE.ALL),
- },
- {
- key: CONST.SEARCH.STATUS.INVOICE.OUTSTANDING,
- icon: Expensicons.Hourglass,
- text: 'common.outstanding',
- query: SearchUtils.buildCannedSearchQuery(CONST.SEARCH.DATA_TYPES.INVOICE, CONST.SEARCH.STATUS.INVOICE.OUTSTANDING),
- },
- {
- key: CONST.SEARCH.STATUS.INVOICE.PAID,
- icon: Expensicons.MoneyBag,
- text: 'iou.settledExpensify',
- query: SearchUtils.buildCannedSearchQuery(CONST.SEARCH.DATA_TYPES.INVOICE, CONST.SEARCH.STATUS.INVOICE.PAID),
- },
-];
-
-const tripOptions: Array<{key: TripSearchStatus; icon: IconAsset; text: TranslationPaths; query: SearchQueryString}> = [
- {
- key: CONST.SEARCH.STATUS.TRIP.ALL,
- icon: Expensicons.All,
- text: 'common.all',
- query: SearchUtils.buildCannedSearchQuery(CONST.SEARCH.DATA_TYPES.TRIP, CONST.SEARCH.STATUS.TRIP.ALL),
- },
- {
- key: CONST.SEARCH.STATUS.TRIP.DRAFTS,
- icon: Expensicons.Pencil,
- text: 'common.drafts',
- query: SearchUtils.buildCannedSearchQuery(CONST.SEARCH.DATA_TYPES.TRIP, CONST.SEARCH.STATUS.TRIP.DRAFTS),
- },
- {
- key: CONST.SEARCH.STATUS.TRIP.OUTSTANDING,
- icon: Expensicons.Hourglass,
- text: 'common.outstanding',
- query: SearchUtils.buildCannedSearchQuery(CONST.SEARCH.DATA_TYPES.TRIP, CONST.SEARCH.STATUS.TRIP.OUTSTANDING),
- },
- {
- key: CONST.SEARCH.STATUS.TRIP.APPROVED,
- icon: Expensicons.ThumbsUp,
- text: 'iou.approved',
- query: SearchUtils.buildCannedSearchQuery(CONST.SEARCH.DATA_TYPES.TRIP, CONST.SEARCH.STATUS.TRIP.APPROVED),
- },
- {
- key: CONST.SEARCH.STATUS.TRIP.PAID,
- icon: Expensicons.MoneyBag,
- text: 'iou.settledExpensify',
- query: SearchUtils.buildCannedSearchQuery(CONST.SEARCH.DATA_TYPES.TRIP, CONST.SEARCH.STATUS.TRIP.PAID),
- },
-];
-
-function getOptions(type: SearchDataTypes) {
- switch (type) {
- case CONST.SEARCH.DATA_TYPES.INVOICE:
- return invoiceOptions;
- case CONST.SEARCH.DATA_TYPES.TRIP:
- return tripOptions;
- case CONST.SEARCH.DATA_TYPES.EXPENSE:
- default:
- return expenseOptions;
- }
-}
-
-function SearchStatusBar({type, status}: SearchStatusBarProps) {
- const {singleExecution} = useSingleExecution();
- const styles = useThemeStyles();
- const StyleUtils = useStyleUtils();
- const theme = useTheme();
- const {translate} = useLocalize();
- const options = getOptions(type);
-
- return (
-
- {options.map((item, index) => {
- const onPress = singleExecution(() => Navigation.setParams({q: item.query}));
- const isActive = status === item.key;
- const isFirstItem = index === 0;
- const isLastItem = index === options.length - 1;
-
- return (
-
- );
- })}
-
- );
-}
-
-SearchStatusBar.displayName = 'SearchStatusBar';
-
-export default SearchStatusBar;
diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx
index db729a9aa77d..df5398bffffa 100644
--- a/src/components/Search/index.tsx
+++ b/src/components/Search/index.tsx
@@ -31,7 +31,6 @@ import ROUTES from '@src/ROUTES';
import type SearchResults from '@src/types/onyx/SearchResults';
import {useSearchContext} from './SearchContext';
import SearchPageHeader from './SearchPageHeader';
-import SearchStatusBar from './SearchStatusBar';
import type {SearchColumnType, SearchQueryJSON, SearchStatus, SelectedTransactionInfo, SelectedTransactions, SortOrder} from './types';
type SearchProps = {
@@ -89,7 +88,7 @@ function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) {
const [selectedTransactionsToDelete, setSelectedTransactionsToDelete] = useState([]);
const [deleteExpensesConfirmModalVisible, setDeleteExpensesConfirmModalVisible] = useState(false);
const [downloadErrorModalVisible, setDownloadErrorModalVisible] = useState(false);
- const {type, status, sortBy, sortOrder, hash} = queryJSON;
+ const {status, sortBy, sortOrder, hash} = queryJSON;
const [currentSearchResults] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`);
@@ -177,12 +176,11 @@ function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) {
const searchResults = currentSearchResults?.data ? currentSearchResults : lastSearchResultsRef.current;
- // There's a race condition in Onyx which makes it return data from the previous Search, so in addition to checking that the data is loaded
- // we also need to check that the searchResults matches the type and status of the current search
- const isDataLoaded = searchResults?.data !== undefined && searchResults?.search?.type === type && searchResults?.search?.status === status;
+ const isDataLoaded = searchResults?.data !== undefined;
const shouldShowLoadingState = !isOffline && !isDataLoaded;
const shouldShowLoadingMoreItems = !shouldShowLoadingState && searchResults?.search?.isLoading && searchResults?.search?.offset > 0;
- const isSearchResultsEmpty = !searchResults?.data || SearchUtils.isSearchResultsEmpty(searchResults);
+
+ const isSearchResultsEmpty = !searchResults || SearchUtils.isSearchResultsEmpty(searchResults);
const prevIsSearchResultEmpty = usePrevious(isSearchResultsEmpty);
useEffect(() => {
@@ -205,14 +203,16 @@ function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) {
);
}
- if (searchResults === undefined) {
+ const type = SearchUtils.getSearchType(searchResults?.search);
+
+ if (searchResults === undefined || type === undefined) {
Log.alert('[Search] Undefined search type');
return null;
}
- const ListItem = SearchUtils.getListItem(status);
- const data = SearchUtils.getSections(status, searchResults.data, searchResults.search);
- const sortedData = SearchUtils.getSortedSections(status, data, sortBy, sortOrder);
+ const ListItem = SearchUtils.getListItem(type, status);
+ const data = SearchUtils.getSections(type, status, searchResults.data, searchResults.search);
+ const sortedData = SearchUtils.getSortedSections(type, status, data, sortBy, sortOrder);
const sortedSelectedData = sortedData.map((item) => mapToItemWithSelectionInfo(item, selectedTransactions, canSelectMultiple));
const shouldShowEmptyState = !isDataLoaded || data.length === 0;
@@ -225,10 +225,6 @@ function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) {
queryJSON={queryJSON}
hash={hash}
/>
-
>
);
@@ -285,7 +281,7 @@ function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) {
};
const toggleAllTransactions = () => {
- const areItemsOfReportType = status !== CONST.SEARCH.STATUS.EXPENSE.ALL;
+ const areItemsOfReportType = searchResults?.search.type === CONST.SEARCH.DATA_TYPES.REPORT;
const flattenedItems = areItemsOfReportType ? (data as ReportListItemType[]).flatMap((item) => item.transactions) : data;
const isAllSelected = flattenedItems.length === Object.keys(selectedTransactions).length;
@@ -322,10 +318,7 @@ function Search({queryJSON, policyIDs, isCustomQuery}: SearchProps) {
setOfflineModalOpen={() => setOfflineModalVisible(true)}
setDownloadErrorModalOpen={() => setDownloadErrorModalVisible(true)}
/>
-
+
sections={[{data: sortedSelectedData, isDisabled: false}]}
turnOnSelectionModeOnLongPress
diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts
index c933deb8ee03..54b26ea6bac4 100644
--- a/src/components/Search/types.ts
+++ b/src/components/Search/types.ts
@@ -1,6 +1,5 @@
import type {ValueOf} from 'react-native-gesture-handler/lib/typescript/typeUtils';
import type CONST from '@src/CONST';
-import type {SearchDataTypes} from '@src/types/onyx/SearchResults';
/** Model of the selected transaction */
type SelectedTransactionInfo = {
@@ -26,9 +25,7 @@ type SelectedTransactions = Record;
type SortOrder = ValueOf;
type SearchColumnType = ValueOf;
type ExpenseSearchStatus = ValueOf;
-type InvoiceSearchStatus = ValueOf;
-type TripSearchStatus = ValueOf;
-type SearchStatus = ExpenseSearchStatus | InvoiceSearchStatus | TripSearchStatus;
+type SearchStatus = ExpenseSearchStatus;
type SearchContext = {
currentSearchHash: number;
@@ -58,7 +55,7 @@ type QueryFilters = {
type SearchQueryString = string;
type SearchQueryAST = {
- type: SearchDataTypes;
+ type: string;
status: SearchStatus;
sortBy: SearchColumnType;
sortOrder: SortOrder;
@@ -84,7 +81,4 @@ export type {
QueryFilter,
QueryFilters,
AdvancedFiltersKeys,
- ExpenseSearchStatus,
- InvoiceSearchStatus,
- TripSearchStatus,
};
diff --git a/src/components/SelectionList/SearchTableHeader.tsx b/src/components/SelectionList/SearchTableHeader.tsx
index a5547eb9e710..f68b3c6d17ff 100644
--- a/src/components/SelectionList/SearchTableHeader.tsx
+++ b/src/components/SelectionList/SearchTableHeader.tsx
@@ -9,6 +9,7 @@ import * as SearchUtils from '@libs/SearchUtils';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import type * as OnyxTypes from '@src/types/onyx';
+import type {SearchDataTypes} from '@src/types/onyx/SearchResults';
import SortableHeaderText from './SortableHeaderText';
type SearchColumnConfig = {
@@ -85,6 +86,19 @@ const expenseHeaders: SearchColumnConfig[] = [
},
];
+function getSearchColumns(type: SearchDataTypes): SearchColumnConfig[] {
+ switch (type) {
+ case CONST.SEARCH.DATA_TYPES.TRANSACTION:
+ case CONST.SEARCH.DATA_TYPES.REPORT:
+ case CONST.SEARCH.DATA_TYPES.EXPENSE:
+ case CONST.SEARCH.DATA_TYPES.INVOICE:
+ case CONST.SEARCH.DATA_TYPES.TRIP:
+ return expenseHeaders;
+ default:
+ return expenseHeaders;
+ }
+}
+
type SearchTableHeaderProps = {
data: OnyxTypes.SearchResults['data'];
metadata: OnyxTypes.SearchResults['search'];
@@ -101,15 +115,16 @@ function SearchTableHeader({data, metadata, sortBy, sortOrder, onSortPress, shou
const {isSmallScreenWidth, isMediumScreenWidth} = useWindowDimensions();
const {translate} = useLocalize();
const displayNarrowVersion = isMediumScreenWidth || isSmallScreenWidth;
+ const type = SearchUtils.getSearchType(metadata);
- if (displayNarrowVersion) {
+ if (displayNarrowVersion || !type) {
return;
}
return (
- {expenseHeaders.map(({columnName, translationKey, shouldShow, isColumnSortable}) => {
+ {getSearchColumns(type).map(({columnName, translationKey, shouldShow, isColumnSortable}) => {
if (!shouldShow(data, metadata)) {
return null;
}
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 796105bf8ed2..ea5c8f14ae86 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -375,7 +375,6 @@ export default {
filterLogs: 'Filter Logs',
network: 'Network',
reportID: 'Report ID',
- outstanding: 'Outstanding',
},
location: {
useCurrent: 'Use current location',
@@ -2096,7 +2095,6 @@ export default {
viewTrip: 'View trip',
viewTripDetails: 'View trip details',
trip: 'Trip',
- trips: 'Trips',
tripSummary: 'Trip summary',
departs: 'Departs',
errorMessage: 'Something went wrong. Please try again later.',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index cad3b16fe7cc..54679d9f5cfe 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -365,7 +365,6 @@ export default {
filterLogs: 'Registros de filtrado',
network: 'La red',
reportID: 'ID del informe',
- outstanding: 'Pendiente',
},
connectionComplete: {
title: 'Conexión completa',
@@ -2128,7 +2127,6 @@ export default {
viewTrip: 'Ver viaje',
viewTripDetails: 'Ver detalles del viaje',
trip: 'Viaje',
- trips: 'Viajes',
tripSummary: 'Resumen del viaje',
departs: 'Sale',
errorMessage: 'Ha ocurrido un error. Por favor, inténtalo mas tarde.',
diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx
index a9df43ad5b64..f023e94bec9b 100644
--- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx
+++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx
@@ -18,7 +18,7 @@ import Navigation, {navigationRef} from '@libs/Navigation/Navigation';
import type {RootStackParamList, State} from '@libs/Navigation/types';
import {isCentralPaneName} from '@libs/NavigationUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
-import * as SearchUtils from '@libs/SearchUtils';
+import {getCurrentSearchParams} from '@libs/SearchUtils';
import type {BrickRoad} from '@libs/WorkspacesSettingsUtils';
import {getChatTabBrickRoad} from '@libs/WorkspacesSettingsUtils';
import BottomTabAvatar from '@pages/home/sidebar/BottomTabAvatar';
@@ -88,14 +88,14 @@ function BottomTabBar({selectedTab}: BottomTabBarProps) {
return;
}
interceptAnonymousUser(() => {
- const currentSearchParams = SearchUtils.getCurrentSearchParams();
+ const currentSearchParams = getCurrentSearchParams();
if (currentSearchParams) {
const {q, ...rest} = currentSearchParams;
const policy = PolicyUtils.getPolicy(currentSearchParams?.policyIDs);
Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: q, ...rest, policyIDs: policy ? currentSearchParams?.policyIDs : undefined}));
return;
}
- Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: SearchUtils.buildCannedSearchQuery()}));
+ Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: CONST.SEARCH.TAB.EXPENSE.ALL}));
});
}, [selectedTab]);
diff --git a/src/libs/Navigation/switchPolicyID.ts b/src/libs/Navigation/switchPolicyID.ts
index a37ccb0c2506..28de413b0904 100644
--- a/src/libs/Navigation/switchPolicyID.ts
+++ b/src/libs/Navigation/switchPolicyID.ts
@@ -4,7 +4,6 @@ import {getPathFromState} from '@react-navigation/native';
import type {Writable} from 'type-fest';
import getIsNarrowLayout from '@libs/getIsNarrowLayout';
import {isCentralPaneName} from '@libs/NavigationUtils';
-import * as SearchUtils from '@libs/SearchUtils';
import CONST from '@src/CONST';
import type {Route} from '@src/ROUTES';
import ROUTES from '@src/ROUTES';
@@ -85,7 +84,7 @@ export default function switchPolicyID(navigation: NavigationContainerRef>;
const action: StackNavigationAction = getActionFromState(stateFromPath, linkingConfig.config);
diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts
index 48af780eab5b..bb49697a8345 100644
--- a/src/libs/SearchUtils.ts
+++ b/src/libs/SearchUtils.ts
@@ -76,6 +76,23 @@ function getTransactionItemCommonFormattedProperties(
};
}
+function isSearchDataType(type: string): type is SearchDataTypes {
+ const searchDataTypes: string[] = Object.values(CONST.SEARCH.DATA_TYPES);
+ return searchDataTypes.includes(type);
+}
+
+function getSearchType(search: OnyxTypes.SearchResults['search'] | undefined): SearchDataTypes | undefined {
+ if (!search) {
+ return undefined;
+ }
+
+ if (!isSearchDataType(search.type)) {
+ return undefined;
+ }
+
+ return search.type;
+}
+
function getShouldShowMerchant(data: OnyxTypes.SearchResults['data']): boolean {
return Object.values(data).some((item) => {
const merchant = item.modifiedMerchant ? item.modifiedMerchant : item.merchant ?? '';
@@ -233,16 +250,45 @@ function getReportSections(data: OnyxTypes.SearchResults['data'], metadata: Onyx
return Object.values(reportIDToTransactions);
}
-function getListItem(status: SearchStatus): ListItemType {
- return status === CONST.SEARCH.STATUS.EXPENSE.ALL ? TransactionListItem : ReportListItem;
+function getListItem(type: SearchDataTypes, status: SearchStatus): ListItemType {
+ switch (type) {
+ case CONST.SEARCH.DATA_TYPES.TRANSACTION:
+ case CONST.SEARCH.DATA_TYPES.EXPENSE:
+ case CONST.SEARCH.DATA_TYPES.REPORT:
+ case CONST.SEARCH.DATA_TYPES.INVOICE:
+ case CONST.SEARCH.DATA_TYPES.TRIP:
+ return status === CONST.SEARCH.STATUS.EXPENSE.ALL ? TransactionListItem : ReportListItem;
+ default:
+ return TransactionListItem;
+ }
}
-function getSections(status: SearchStatus, data: OnyxTypes.SearchResults['data'], metadata: OnyxTypes.SearchResults['search']) {
- return status === CONST.SEARCH.STATUS.EXPENSE.ALL ? getTransactionsSections(data, metadata) : getReportSections(data, metadata);
+function getSections(type: SearchDataTypes, status: SearchStatus, data: OnyxTypes.SearchResults['data'], metadata: OnyxTypes.SearchResults['search']) {
+ switch (type) {
+ case CONST.SEARCH.DATA_TYPES.TRANSACTION:
+ case CONST.SEARCH.DATA_TYPES.EXPENSE:
+ case CONST.SEARCH.DATA_TYPES.REPORT:
+ case CONST.SEARCH.DATA_TYPES.INVOICE:
+ case CONST.SEARCH.DATA_TYPES.TRIP:
+ return status === CONST.SEARCH.STATUS.EXPENSE.ALL ? getTransactionsSections(data, metadata) : getReportSections(data, metadata);
+ default:
+ return getTransactionsSections(data, metadata);
+ }
}
-function getSortedSections(status: SearchStatus, data: ListItemDataType, sortBy?: SearchColumnType, sortOrder?: SortOrder) {
- return status === CONST.SEARCH.STATUS.EXPENSE.ALL ? getSortedTransactionData(data as TransactionListItemType[], sortBy, sortOrder) : getSortedReportData(data as ReportListItemType[]);
+function getSortedSections(type: SearchDataTypes, status: SearchStatus, data: ListItemDataType, sortBy?: SearchColumnType, sortOrder?: SortOrder) {
+ switch (type) {
+ case CONST.SEARCH.DATA_TYPES.TRANSACTION:
+ case CONST.SEARCH.DATA_TYPES.EXPENSE:
+ case CONST.SEARCH.DATA_TYPES.REPORT:
+ case CONST.SEARCH.DATA_TYPES.INVOICE:
+ case CONST.SEARCH.DATA_TYPES.TRIP:
+ return status === CONST.SEARCH.STATUS.EXPENSE.ALL
+ ? getSortedTransactionData(data as TransactionListItemType[], sortBy, sortOrder)
+ : getSortedReportData(data as ReportListItemType[]);
+ default:
+ return getSortedTransactionData(data as TransactionListItemType[], sortBy, sortOrder);
+ }
}
function getQueryHash(query: string, policyID?: string, sortBy?: string, sortOrder?: string): number {
@@ -525,10 +571,6 @@ function getSearchHeaderTitle(queryJSON: SearchQueryJSON) {
return title;
}
-function buildCannedSearchQuery(type: SearchDataTypes = CONST.SEARCH.DATA_TYPES.EXPENSE, status: SearchStatus = CONST.SEARCH.STATUS.EXPENSE.ALL): SearchQueryString {
- return normalizeQuery(`type:${type} status:${status}`);
-}
-
export {
buildQueryStringFromFilters,
buildSearchQueryJSON,
@@ -538,6 +580,7 @@ export {
getListItem,
getQueryHash,
getSearchHeaderTitle,
+ getSearchType,
getSections,
getShouldShowMerchant,
getSortedSections,
@@ -546,6 +589,5 @@ export {
isTransactionListItemType,
normalizeQuery,
shouldShowYear,
- buildCannedSearchQuery,
getExpenseTypeTranslationKey,
};
diff --git a/src/pages/Search/EmptySearchView.tsx b/src/pages/Search/EmptySearchView.tsx
index 88b2a38b3603..eaa6c13bcad6 100644
--- a/src/pages/Search/EmptySearchView.tsx
+++ b/src/pages/Search/EmptySearchView.tsx
@@ -32,6 +32,8 @@ function EmptySearchView({type}: EmptySearchViewProps) {
buttonText: translate('search.searchResults.emptyTripResults.buttonText'),
buttonAction: () => Navigation.navigate(ROUTES.TRAVEL_MY_TRIPS),
};
+ case CONST.SEARCH.DATA_TYPES.TRANSACTION:
+ case CONST.SEARCH.DATA_TYPES.REPORT:
case CONST.SEARCH.DATA_TYPES.EXPENSE:
case CONST.SEARCH.DATA_TYPES.INVOICE:
default:
diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx
index d5725c2d40a2..e2495c02d44f 100644
--- a/src/pages/Search/SearchPage.tsx
+++ b/src/pages/Search/SearchPage.tsx
@@ -7,7 +7,8 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import type {AuthScreensParamList} from '@libs/Navigation/types';
-import * as SearchUtils from '@libs/SearchUtils';
+import {buildSearchQueryJSON} from '@libs/SearchUtils';
+import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
@@ -18,8 +19,9 @@ function SearchPage({route}: SearchPageProps) {
const styles = useThemeStyles();
const {policyIDs, q, isCustomQuery} = route.params;
- const queryJSON = useMemo(() => SearchUtils.buildSearchQueryJSON(q, policyIDs), [q, policyIDs]);
- const handleOnBackButtonPress = () => Navigation.goBack(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: SearchUtils.buildCannedSearchQuery()}));
+ const queryJSON = useMemo(() => buildSearchQueryJSON(q, policyIDs), [q, policyIDs]);
+
+ const handleOnBackButtonPress = () => Navigation.goBack(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: CONST.SEARCH.TAB.EXPENSE.ALL}));
// On small screens this page is not displayed, the configuration is in the file: src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx
// To avoid calling hooks in the Search component when this page isn't visible, we return null here.
diff --git a/src/pages/Search/SearchPageBottomTab.tsx b/src/pages/Search/SearchPageBottomTab.tsx
index 029407026dc8..1b9e81c8a035 100644
--- a/src/pages/Search/SearchPageBottomTab.tsx
+++ b/src/pages/Search/SearchPageBottomTab.tsx
@@ -12,12 +12,13 @@ import useThemeStyles from '@hooks/useThemeStyles';
import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode';
import Navigation from '@libs/Navigation/Navigation';
import type {AuthScreensParamList} from '@libs/Navigation/types';
-import * as SearchUtils from '@libs/SearchUtils';
+import {buildSearchQueryJSON} from '@libs/SearchUtils';
import TopBar from '@navigation/AppNavigator/createCustomBottomTabNavigator/TopBar';
+import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import SCREENS from '@src/SCREENS';
-import SearchTypeMenu from './SearchTypeMenu';
+import SearchStatusMenu from './SearchStatusMenu';
function SearchPageBottomTab() {
const {translate} = useLocalize();
@@ -36,13 +37,13 @@ function SearchPageBottomTab() {
const searchParams = activeCentralPaneRoute.params as AuthScreensParamList[typeof SCREENS.SEARCH.CENTRAL_PANE];
return {
- queryJSON: SearchUtils.buildSearchQueryJSON(searchParams.q, searchParams.policyIDs),
+ queryJSON: buildSearchQueryJSON(searchParams.q, searchParams.policyIDs),
policyIDs: searchParams.policyIDs,
isCustomQuery: searchParams.isCustomQuery,
};
}, [activeCentralPaneRoute]);
- const handleOnBackButtonPress = () => Navigation.goBack(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: SearchUtils.buildCannedSearchQuery()}));
+ const handleOnBackButtonPress = () => Navigation.goBack(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: CONST.SEARCH.TAB.EXPENSE.ALL}));
return (
-
diff --git a/src/pages/Search/SearchTypeMenu.tsx b/src/pages/Search/SearchStatusMenu.tsx
similarity index 57%
rename from src/pages/Search/SearchTypeMenu.tsx
rename to src/pages/Search/SearchStatusMenu.tsx
index a2d84209114b..9fd812ead9a1 100644
--- a/src/pages/Search/SearchTypeMenu.tsx
+++ b/src/pages/Search/SearchStatusMenu.tsx
@@ -1,69 +1,75 @@
import React from 'react';
import {View} from 'react-native';
import MenuItem from '@components/MenuItem';
-import type {SearchQueryJSON} from '@components/Search/types';
+import type {SearchQueryJSON, SearchStatus} from '@components/Search/types';
import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useSingleExecution from '@hooks/useSingleExecution';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
+import {normalizeQuery} from '@libs/SearchUtils';
import * as SearchUtils from '@libs/SearchUtils';
import variables from '@styles/variables';
import * as Expensicons from '@src/components/Icon/Expensicons';
import CONST from '@src/CONST';
import type {Route} from '@src/ROUTES';
import ROUTES from '@src/ROUTES';
-import type {SearchDataTypes} from '@src/types/onyx/SearchResults';
import type IconAsset from '@src/types/utils/IconAsset';
-import SearchTypeMenuNarrow from './SearchTypeMenuNarrow';
+import SearchStatusMenuNarrow from './SearchStatusMenuNarrow';
-type SearchTypeMenuProps = {
+type SearchStatusMenuProps = {
queryJSON: SearchQueryJSON;
isCustomQuery: boolean;
};
-type SearchTypeMenuItem = {
+type SearchStatusMenuItem = {
title: string;
- type: SearchDataTypes;
+ status: SearchStatus;
icon: IconAsset;
route?: Route;
};
-function SearchTypeMenu({queryJSON, isCustomQuery}: SearchTypeMenuProps) {
- const {type} = queryJSON;
+function SearchStatusMenu({queryJSON, isCustomQuery}: SearchStatusMenuProps) {
+ const {status} = queryJSON;
const styles = useThemeStyles();
const {shouldUseNarrowLayout} = useResponsiveLayout();
const {singleExecution} = useSingleExecution();
const {translate} = useLocalize();
- const typeMenuItems: SearchTypeMenuItem[] = [
+ const statusMenuItems: SearchStatusMenuItem[] = [
{
title: translate('common.expenses'),
- type: CONST.SEARCH.DATA_TYPES.EXPENSE,
+ status: CONST.SEARCH.STATUS.EXPENSE.ALL,
icon: Expensicons.Receipt,
- route: ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: SearchUtils.buildCannedSearchQuery()}),
+ route: ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: normalizeQuery(CONST.SEARCH.TAB.EXPENSE.ALL)}),
},
{
- title: translate('workspace.common.invoices'),
- type: CONST.SEARCH.DATA_TYPES.INVOICE,
- icon: Expensicons.InvoiceGeneric,
- route: ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: SearchUtils.buildCannedSearchQuery(CONST.SEARCH.DATA_TYPES.INVOICE, CONST.SEARCH.STATUS.INVOICE.ALL)}),
+ title: translate('common.shared'),
+ status: CONST.SEARCH.STATUS.EXPENSE.SHARED,
+ icon: Expensicons.Send,
+ route: ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: normalizeQuery(CONST.SEARCH.TAB.EXPENSE.SHARED)}),
},
{
- title: translate('travel.trips'),
- type: CONST.SEARCH.DATA_TYPES.TRIP,
- icon: Expensicons.Suitcase,
- route: ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: SearchUtils.buildCannedSearchQuery(CONST.SEARCH.DATA_TYPES.TRIP, CONST.SEARCH.STATUS.TRIP.ALL)}),
+ title: translate('common.drafts'),
+ status: CONST.SEARCH.STATUS.EXPENSE.DRAFTS,
+ icon: Expensicons.Pencil,
+ route: ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: normalizeQuery(CONST.SEARCH.TAB.EXPENSE.DRAFTS)}),
+ },
+ {
+ title: translate('common.finished'),
+ status: CONST.SEARCH.STATUS.EXPENSE.FINISHED,
+ icon: Expensicons.CheckCircle,
+ route: ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: normalizeQuery(CONST.SEARCH.TAB.EXPENSE.FINISHED)}),
},
];
- const activeItemIndex = typeMenuItems.findIndex((item) => item.type === type);
+ const activeItemIndex = statusMenuItems.findIndex((item) => item.status === status);
if (shouldUseNarrowLayout) {
const title = isCustomQuery ? SearchUtils.getSearchHeaderTitle(queryJSON) : undefined;
return (
-
@@ -72,7 +78,7 @@ function SearchTypeMenu({queryJSON, isCustomQuery}: SearchTypeMenuProps) {
return (
- {typeMenuItems.map((item, index) => {
+ {statusMenuItems.map((item, index) => {
const onPress = singleExecution(() => Navigation.navigate(item.route));
return (
@@ -96,7 +102,7 @@ function SearchTypeMenu({queryJSON, isCustomQuery}: SearchTypeMenuProps) {
);
}
-SearchTypeMenu.displayName = 'SearchTypeMenu';
+SearchStatusMenu.displayName = 'SearchStatusMenu';
-export default SearchTypeMenu;
-export type {SearchTypeMenuItem};
+export default SearchStatusMenu;
+export type {SearchStatusMenuItem};
diff --git a/src/pages/Search/SearchTypeMenuNarrow.tsx b/src/pages/Search/SearchStatusMenuNarrow.tsx
similarity index 91%
rename from src/pages/Search/SearchTypeMenuNarrow.tsx
rename to src/pages/Search/SearchStatusMenuNarrow.tsx
index 08c7852cb037..8850b0fb2b0f 100644
--- a/src/pages/Search/SearchTypeMenuNarrow.tsx
+++ b/src/pages/Search/SearchStatusMenuNarrow.tsx
@@ -10,15 +10,15 @@ import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import Navigation from '@libs/Navigation/Navigation';
import * as Expensicons from '@src/components/Icon/Expensicons';
-import type {SearchTypeMenuItem} from './SearchTypeMenu';
+import type {SearchStatusMenuItem} from './SearchStatusMenu';
-type SearchTypeMenuNarrowProps = {
- typeMenuItems: SearchTypeMenuItem[];
+type SearchStatusMenuNarrowProps = {
+ statusMenuItems: SearchStatusMenuItem[];
activeItemIndex: number;
title?: string;
};
-function SearchTypeMenuNarrow({typeMenuItems, activeItemIndex, title}: SearchTypeMenuNarrowProps) {
+function SearchStatusMenuNarrow({statusMenuItems, activeItemIndex, title}: SearchStatusMenuNarrowProps) {
const theme = useTheme();
const styles = useThemeStyles();
const {singleExecution} = useSingleExecution();
@@ -30,7 +30,7 @@ function SearchTypeMenuNarrow({typeMenuItems, activeItemIndex, title}: SearchTyp
const openMenu = () => setIsPopoverVisible(true);
const closeMenu = () => setIsPopoverVisible(false);
- const popoverMenuItems = typeMenuItems.map((item, index) => {
+ const popoverMenuItems = statusMenuItems.map((item, index) => {
const isSelected = title ? false : index === activeItemIndex;
return {
@@ -105,6 +105,6 @@ function SearchTypeMenuNarrow({typeMenuItems, activeItemIndex, title}: SearchTyp
);
}
-SearchTypeMenuNarrow.displayName = 'SearchTypeMenuNarrow';
+SearchStatusMenuNarrow.displayName = 'SearchStatusMenuNarrow';
-export default SearchTypeMenuNarrow;
+export default SearchStatusMenuNarrow;
diff --git a/src/pages/settings/Subscription/CardSection/CardSection.tsx b/src/pages/settings/Subscription/CardSection/CardSection.tsx
index 797b28de4107..48caeed2bb96 100644
--- a/src/pages/settings/Subscription/CardSection/CardSection.tsx
+++ b/src/pages/settings/Subscription/CardSection/CardSection.tsx
@@ -17,7 +17,6 @@ import * as User from '@libs/actions/User';
import DateUtils from '@libs/DateUtils';
import Navigation from '@libs/Navigation/Navigation';
import {getPaymentMethodDescription} from '@libs/PaymentUtils';
-import * as SearchUtils from '@libs/SearchUtils';
import * as SubscriptionUtils from '@libs/SubscriptionUtils';
import * as PaymentMethods from '@userActions/PaymentMethods';
import * as Subscription from '@userActions/Subscription';
@@ -174,7 +173,7 @@ function CardSection() {
wrapperStyle={styles.sectionMenuItemTopDescription}
title={translate('subscription.cardSection.viewPaymentHistory')}
titleStyle={styles.textStrong}
- onPress={() => Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: SearchUtils.buildCannedSearchQuery()}))}
+ onPress={() => Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: CONST.SEARCH.TAB.EXPENSE.ALL}))}
hoverAndPressStyle={styles.hoveredComponentBG}
/>
)}
diff --git a/src/styles/index.ts b/src/styles/index.ts
index 918c7927852b..fe65e48bc4a1 100644
--- a/src/styles/index.ts
+++ b/src/styles/index.ts
@@ -4253,7 +4253,7 @@ const styles = (theme: ThemeColors) =>
tabText: (isSelected: boolean) =>
({
marginLeft: 8,
- ...FontUtils.fontFamily.platform.EXP_NEUE_BOLD,
+ ...(isSelected ? FontUtils.fontFamily.platform.EXP_NEUE_BOLD : FontUtils.fontFamily.platform.EXP_NEUE),
color: isSelected ? theme.text : theme.textSupporting,
lineHeight: variables.lineHeightNormal,
fontSize: variables.fontSizeNormal,
diff --git a/src/types/onyx/SearchResults.ts b/src/types/onyx/SearchResults.ts
index e67b5b30f1d6..228a49e263f7 100644
--- a/src/types/onyx/SearchResults.ts
+++ b/src/types/onyx/SearchResults.ts
@@ -14,6 +14,13 @@ type ListItemType = T extends typeof CONST.SEARCH.STATUS
/** Model of search list item data type */
type ListItemDataType = T extends typeof CONST.SEARCH.STATUS.EXPENSE.ALL ? TransactionListItemType[] : ReportListItemType[];
+/** Model of search result section */
+type SectionsType = T extends typeof CONST.SEARCH.DATA_TYPES.TRANSACTION
+ ? TransactionListItemType[]
+ : T extends typeof CONST.SEARCH.DATA_TYPES.REPORT
+ ? ReportListItemType[]
+ : never;
+
/** Model of columns to show for search results */
type ColumnsToShow = {
/** Whether the category column should be shown */
@@ -32,10 +39,7 @@ type SearchResultsInfo = {
offset: number;
/** Type of search */
- type: SearchDataTypes;
-
- /** The status filter for the current search */
- status: SearchStatus;
+ type: string;
/** Whether the user can fetch more search results */
hasMoreResults: boolean;
@@ -226,4 +230,4 @@ type SearchResults = {
export default SearchResults;
-export type {ListItemType, ListItemDataType, SearchTransaction, SearchTransactionType, SearchTransactionAction, SearchPersonalDetails, SearchDataTypes, SearchReport};
+export type {ListItemType, ListItemDataType, SearchTransaction, SearchTransactionType, SearchTransactionAction, SearchPersonalDetails, SearchDataTypes, SearchReport, SectionsType};