diff --git a/src/libs/SearchQueryUtils.ts b/src/libs/SearchQueryUtils.ts index 3421e88ecc5ae..989a003fed2ef 100644 --- a/src/libs/SearchQueryUtils.ts +++ b/src/libs/SearchQueryUtils.ts @@ -24,7 +24,7 @@ import type {OnyxCollectionKey, OnyxCollectionValuesMapping} from '@src/ONYXKEYS import ONYXKEYS from '@src/ONYXKEYS'; import SCREENS from '@src/SCREENS'; import type {SearchAdvancedFiltersForm} from '@src/types/form'; -import FILTER_KEYS, {ALLOWED_TYPE_FILTERS, DATE_FILTER_KEYS} from '@src/types/form/SearchAdvancedFiltersForm'; +import FILTER_KEYS, {ALLOWED_TYPE_FILTERS, AMOUNT_FILTER_KEYS, DATE_FILTER_KEYS} from '@src/types/form/SearchAdvancedFiltersForm'; import type {SearchAdvancedFiltersKey} from '@src/types/form/SearchAdvancedFiltersForm'; import type * as OnyxTypes from '@src/types/onyx'; import type {SearchDataTypes} from '@src/types/onyx/SearchResults'; @@ -240,6 +240,17 @@ function getFilters(queryJSON: SearchQueryJSON) { * - for personal filters it tries to substitute any user emails with accountIDs */ function getUpdatedFilterValue(filterName: ValueOf, filterValue: string | string[]) { + if (AMOUNT_FILTER_KEYS.includes(filterName as SearchAmountFilterKeys)) { + if (typeof filterValue === 'string') { + const backendAmount = convertToBackendAmount(Number(filterValue)); + return Number.isNaN(backendAmount) ? filterValue : backendAmount.toString(); + } + return filterValue.map((amount) => { + const backendAmount = convertToBackendAmount(Number(amount)); + return Number.isNaN(backendAmount) ? amount : backendAmount.toString(); + }); + } + if ( filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM || filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || @@ -253,17 +264,6 @@ function getUpdatedFilterValue(filterName: ValueOf getPersonalDetailByEmail(email)?.accountID.toString() ?? email); } - if (filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT || filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.TOTAL || filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.PURCHASE_AMOUNT) { - if (typeof filterValue === 'string') { - const backendAmount = convertToBackendAmount(Number(filterValue)); - return Number.isNaN(backendAmount) ? filterValue : backendAmount.toString(); - } - return filterValue.map((amount) => { - const backendAmount = convertToBackendAmount(Number(amount)); - return Number.isNaN(backendAmount) ? amount : backendAmount.toString(); - }); - } - if (filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.REPORT_ID || filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.WITHDRAWAL_ID) { const cleanIDs = (value: string) => value @@ -554,7 +554,7 @@ function buildQueryStringFromFilterFormValues(filterValues: Partial { + AMOUNT_FILTER_KEYS.forEach((filterKey) => { const amountFilter = buildAmountFilterQuery(filterKey, supportedFilterValues); filtersString.push(amountFilter); }); @@ -690,6 +690,7 @@ function buildFilterFormValuesFromQuery( }) .join(' '); } + if (DATE_FILTER_KEYS.includes(filterKey as SearchDateFilterKeys)) { const beforeKey = `${filterKey}${CONST.SEARCH.DATE_MODIFIERS.BEFORE}` as `${SearchDateFilterKeys}${typeof CONST.SEARCH.DATE_MODIFIERS.BEFORE}`; const afterKey = `${filterKey}${CONST.SEARCH.DATE_MODIFIERS.AFTER}` as `${SearchDateFilterKeys}${typeof CONST.SEARCH.DATE_MODIFIERS.AFTER}`; @@ -704,14 +705,18 @@ function buildFilterFormValuesFromQuery( filtersForm[afterKey] = afterFilter?.value.toString() ?? filtersForm[afterKey]; filtersForm[onKey] = onFilter?.value.toString() ?? filtersForm[onKey]; } - if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT || filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.TOTAL || filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.PURCHASE_AMOUNT) { + + if (AMOUNT_FILTER_KEYS.includes(filterKey as SearchAmountFilterKeys)) { + const lessThanKey = `${filterKey}${CONST.SEARCH.AMOUNT_MODIFIERS.LESS_THAN}` as `${SearchAmountFilterKeys}${typeof CONST.SEARCH.AMOUNT_MODIFIERS.LESS_THAN}`; + const greaterThanKey = `${filterKey}${CONST.SEARCH.AMOUNT_MODIFIERS.GREATER_THAN}` as `${SearchAmountFilterKeys}${typeof CONST.SEARCH.AMOUNT_MODIFIERS.GREATER_THAN}`; + // backend amount is an integer and is 2 digits longer than frontend amount - filtersForm[`${filterKey}${CONST.SEARCH.AMOUNT_MODIFIERS.LESS_THAN}`] = + filtersForm[lessThanKey] = filterList.find((filter) => filter.operator === 'lt' && validateAmount(filter.value.toString(), 0, CONST.IOU.AMOUNT_MAX_LENGTH + 2))?.value.toString() ?? - filtersForm[`${filterKey}${CONST.SEARCH.AMOUNT_MODIFIERS.LESS_THAN}`]; - filtersForm[`${filterKey}${CONST.SEARCH.AMOUNT_MODIFIERS.GREATER_THAN}`] = + filtersForm[lessThanKey]; + filtersForm[greaterThanKey] = filterList.find((filter) => filter.operator === 'gt' && validateAmount(filter.value.toString(), 0, CONST.IOU.AMOUNT_MAX_LENGTH + 2))?.value.toString() ?? - filtersForm[`${filterKey}${CONST.SEARCH.AMOUNT_MODIFIERS.GREATER_THAN}`]; + filtersForm[greaterThanKey]; } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.BILLABLE || filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.REIMBURSABLE) { const validBooleanTypes = Object.values(CONST.SEARCH.BOOLEAN); diff --git a/src/pages/Search/AdvancedSearchFilters.tsx b/src/pages/Search/AdvancedSearchFilters.tsx index b51c4194793d5..08a5c6a1b0e30 100644 --- a/src/pages/Search/AdvancedSearchFilters.tsx +++ b/src/pages/Search/AdvancedSearchFilters.tsx @@ -8,7 +8,7 @@ import type {LocaleContextProps} from '@components/LocaleContextProvider'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import {usePersonalDetails} from '@components/OnyxListItemProvider'; import ScrollView from '@components/ScrollView'; -import type {SearchDateFilterKeys, SearchFilterKey, SearchGroupBy} from '@components/Search/types'; +import type {SearchAmountFilterKeys, SearchDateFilterKeys, SearchFilterKey, SearchGroupBy} from '@components/Search/types'; import SpacerView from '@components/SpacerView'; import Text from '@components/Text'; import useAdvancedSearchFilters from '@hooks/useAdvancedSearchFilters'; @@ -34,7 +34,7 @@ import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {SearchAdvancedFiltersForm} from '@src/types/form'; -import {DATE_FILTER_KEYS} from '@src/types/form/SearchAdvancedFiltersForm'; +import {AMOUNT_FILTER_KEYS, DATE_FILTER_KEYS} from '@src/types/form/SearchAdvancedFiltersForm'; import type {CardList, PersonalDetailsList, Policy, Report, WorkspaceCardsList} from '@src/types/onyx'; import type {SearchDataTypes} from '@src/types/onyx/SearchResults'; import {getEmptyObject} from '@src/types/utils/EmptyObject'; @@ -287,6 +287,8 @@ function getFilterDisplayTitle( translate: LocaleContextProps['translate'], localeCompare: LocaleContextProps['localeCompare'], ) { + let key: SearchFilterKey = filterKey; + if (DATE_FILTER_KEYS.includes(filterKey as SearchDateFilterKeys)) { const keyOn = `${filterKey}${CONST.SEARCH.DATE_MODIFIERS.ON}` as `${SearchDateFilterKeys}${typeof CONST.SEARCH.DATE_MODIFIERS.ON}`; const keyAfter = `${filterKey}${CONST.SEARCH.DATE_MODIFIERS.AFTER}` as `${SearchDateFilterKeys}${typeof CONST.SEARCH.DATE_MODIFIERS.AFTER}`; @@ -311,15 +313,11 @@ function getFilterDisplayTitle( return dateValue.join(', '); } - const nonDateFilterKey = filterKey as Exclude; + key = filterKey as Exclude; - if ( - nonDateFilterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT || - nonDateFilterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.TOTAL || - nonDateFilterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.PURCHASE_AMOUNT - ) { - const lessThanKey = `${nonDateFilterKey}${CONST.SEARCH.AMOUNT_MODIFIERS.LESS_THAN}` as keyof SearchAdvancedFiltersForm; - const greaterThanKey = `${nonDateFilterKey}${CONST.SEARCH.AMOUNT_MODIFIERS.GREATER_THAN}` as keyof SearchAdvancedFiltersForm; + if (AMOUNT_FILTER_KEYS.includes(key as SearchAmountFilterKeys)) { + const lessThanKey = `${key}${CONST.SEARCH.AMOUNT_MODIFIERS.LESS_THAN}` as keyof SearchAdvancedFiltersForm; + const greaterThanKey = `${key}${CONST.SEARCH.AMOUNT_MODIFIERS.GREATER_THAN}` as keyof SearchAdvancedFiltersForm; const lessThan = filters[lessThanKey]; const greaterThan = filters[greaterThanKey]; @@ -340,52 +338,54 @@ function getFilterDisplayTitle( return; } - if ((nonDateFilterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.CURRENCY || nonDateFilterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.PURCHASE_CURRENCY) && filters[nonDateFilterKey]) { - const filterArray = filters[nonDateFilterKey] ?? []; + key = filterKey as Exclude; + + if ((key === CONST.SEARCH.SYNTAX_FILTER_KEYS.CURRENCY || key === CONST.SEARCH.SYNTAX_FILTER_KEYS.PURCHASE_CURRENCY) && filters[key]) { + const filterArray = filters[key] ?? []; return filterArray.sort(localeCompare).join(', '); } - if (nonDateFilterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY && filters[nonDateFilterKey]) { - const filterArray = filters[nonDateFilterKey] ?? []; + if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY && filters[key]) { + const filterArray = filters[key] ?? []; return filterArray .sort((a, b) => sortOptionsWithEmptyValue(a, b, localeCompare)) .map((value) => (value === CONST.SEARCH.CATEGORY_EMPTY_VALUE ? translate('search.noCategory') : value)) .join(', '); } - if (nonDateFilterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG && filters[nonDateFilterKey]) { - const filterArray = filters[nonDateFilterKey] ?? []; + if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG && filters[key]) { + const filterArray = filters[key] ?? []; return filterArray .sort((a, b) => sortOptionsWithEmptyValue(a, b, localeCompare)) .map((value) => (value === CONST.SEARCH.TAG_EMPTY_VALUE ? translate('search.noTag') : getCleanedTagName(value))) .join(', '); } - if (nonDateFilterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.DESCRIPTION || nonDateFilterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.TITLE) { - return filters[nonDateFilterKey]; + if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.DESCRIPTION || key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TITLE) { + return filters[key]; } - if (nonDateFilterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.REIMBURSABLE || nonDateFilterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.BILLABLE) { - const filterValue = filters[nonDateFilterKey]; + if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.REIMBURSABLE || key === CONST.SEARCH.SYNTAX_FILTER_KEYS.BILLABLE) { + const filterValue = filters[key]; return filterValue ? translate(`common.${filterValue as ValueOf}`) : undefined; } - if (nonDateFilterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.TYPE) { - const filterValue = filters[nonDateFilterKey]; + if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TYPE) { + const filterValue = filters[key]; return filterValue ? translate(`common.${filterValue as ValueOf}`) : undefined; } - if (nonDateFilterKey === CONST.SEARCH.SYNTAX_ROOT_KEYS.GROUP_BY) { - const filterValue = filters[nonDateFilterKey]; + if (key === CONST.SEARCH.SYNTAX_ROOT_KEYS.GROUP_BY) { + const filterValue = filters[key]; return filterValue ? translate(`search.filters.groupBy.${filterValue}`) : undefined; } - if (nonDateFilterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.WITHDRAWAL_TYPE) { - const filterValue = filters[nonDateFilterKey]; + if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.WITHDRAWAL_TYPE) { + const filterValue = filters[key]; return filterValue ? translate(`search.filters.withdrawalType.${filterValue}`) : undefined; } - const filterValue = filters[nonDateFilterKey]; + const filterValue = filters[key]; return Array.isArray(filterValue) ? filterValue.join(', ') : filterValue; } @@ -441,11 +441,9 @@ function getFilterExpenseDisplayTitle(filters: Partial, _: LocaleContextProps['translate'], reports?: OnyxCollection) { return filters.in - ? filters.in - .map((id) => getReportName(reports?.[`${ONYXKEYS.COLLECTION.REPORT}${id}`])) - .filter(Boolean) - .join(', ') - : undefined; + ?.map((id) => getReportName(reports?.[`${ONYXKEYS.COLLECTION.REPORT}${id}`])) + ?.filter(Boolean) + ?.join(', '); } function AdvancedSearchFilters() { @@ -511,38 +509,8 @@ function AdvancedSearchFilters() { return section.map((key) => { const onPress = singleExecution(waitForNavigate(() => Navigation.navigate(baseFilterConfig[key].route))); let filterTitle; - if ( - key === CONST.SEARCH.SYNTAX_FILTER_KEYS.DATE || - key === CONST.SEARCH.SYNTAX_FILTER_KEYS.SUBMITTED || - key === CONST.SEARCH.SYNTAX_FILTER_KEYS.APPROVED || - key === CONST.SEARCH.SYNTAX_FILTER_KEYS.PAID || - key === CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPORTED || - key === CONST.SEARCH.SYNTAX_FILTER_KEYS.WITHDRAWN || - key === CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT || - key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TOTAL || - key === CONST.SEARCH.SYNTAX_FILTER_KEYS.CURRENCY || - key === CONST.SEARCH.SYNTAX_FILTER_KEYS.DESCRIPTION || - key === CONST.SEARCH.SYNTAX_FILTER_KEYS.MERCHANT || - key === CONST.SEARCH.SYNTAX_FILTER_KEYS.REPORT_ID || - key === CONST.SEARCH.SYNTAX_FILTER_KEYS.KEYWORD || - key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TITLE || - key === CONST.SEARCH.SYNTAX_FILTER_KEYS.REIMBURSABLE || - key === CONST.SEARCH.SYNTAX_FILTER_KEYS.BILLABLE || - key === CONST.SEARCH.SYNTAX_FILTER_KEYS.WITHDRAWAL_TYPE || - key === CONST.SEARCH.SYNTAX_FILTER_KEYS.PURCHASE_AMOUNT || - key === CONST.SEARCH.SYNTAX_FILTER_KEYS.PURCHASE_CURRENCY || - key === CONST.SEARCH.SYNTAX_FILTER_KEYS.WITHDRAWAL_ID || - key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TYPE - ) { - filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, key, translate, localeCompare); - } else if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY) { - filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, key, translate, localeCompare); - } else if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG) { - filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, key, translate, localeCompare); - } else if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID) { + if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID) { filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, allCards, translate); - } else if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.POSTED) { - filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, key, translate, localeCompare); } else if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE) { filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, taxRates); } else if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPENSE_TYPE) { @@ -556,11 +524,10 @@ function AdvancedSearchFilters() { filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, workspacesData); } else if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.STATUS) { filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, currentType, groupBy, translate); - } else if (key === CONST.SEARCH.SYNTAX_ROOT_KEYS.GROUP_BY) { - filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, key, translate, localeCompare); - } else if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.GROUP_CURRENCY) { + } else { filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, key, translate, localeCompare); } + return { key, title: filterTitle, @@ -569,6 +536,7 @@ function AdvancedSearchFilters() { }; }); }); + const displaySearchButton = queryJSON && !isCannedSearchQuery(queryJSON); const sections: SectionType[] = [ diff --git a/src/types/form/SearchAdvancedFiltersForm.ts b/src/types/form/SearchAdvancedFiltersForm.ts index 392fc549e41ad..19060c5859d60 100644 --- a/src/types/form/SearchAdvancedFiltersForm.ts +++ b/src/types/form/SearchAdvancedFiltersForm.ts @@ -1,5 +1,5 @@ import type {ValueOf} from 'type-fest'; -import type {SearchDateFilterKeys, SearchGroupBy, SearchWithdrawalType} from '@components/Search/types'; +import type {SearchAmountFilterKeys, SearchDateFilterKeys, SearchGroupBy, SearchWithdrawalType} from '@components/Search/types'; import CONST from '@src/CONST'; import type {SearchDataTypes} from '@src/types/onyx/SearchResults'; import type Form from './Form'; @@ -14,6 +14,8 @@ const DATE_FILTER_KEYS: SearchDateFilterKeys[] = [ CONST.SEARCH.SYNTAX_FILTER_KEYS.WITHDRAWN, ]; +const AMOUNT_FILTER_KEYS: SearchAmountFilterKeys[] = [CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT, CONST.SEARCH.SYNTAX_FILTER_KEYS.TOTAL, CONST.SEARCH.SYNTAX_FILTER_KEYS.PURCHASE_AMOUNT]; + const FILTER_KEYS = { GROUP_BY: 'groupBy', TYPE: 'type', @@ -320,4 +322,4 @@ type SearchAdvancedFiltersForm = Form< export type {SearchAdvancedFiltersForm, SearchAdvancedFiltersKey}; export default FILTER_KEYS; -export {DATE_FILTER_KEYS, ALLOWED_TYPE_FILTERS, FILTER_KEYS}; +export {DATE_FILTER_KEYS, ALLOWED_TYPE_FILTERS, FILTER_KEYS, AMOUNT_FILTER_KEYS};