diff --git a/src/components/Search/SearchRouter/SearchRouterList.tsx b/src/components/Search/SearchRouter/SearchRouterList.tsx index b38901a78ce..237d78efec4 100644 --- a/src/components/Search/SearchRouter/SearchRouterList.tsx +++ b/src/components/Search/SearchRouter/SearchRouterList.tsx @@ -22,7 +22,7 @@ import {getCardDescription, isCard, isCardIssued, mergeCardListWithWorkspaceFeed import {combineOrderingOfReportsAndPersonalDetails, getSearchOptions, getValidOptions} from '@libs/OptionsListUtils'; import type {Options, SearchOption} from '@libs/OptionsListUtils'; import Performance from '@libs/Performance'; -import {getAllTaxRates} from '@libs/PolicyUtils'; +import {getAllTaxRates, getCleanedTagName} from '@libs/PolicyUtils'; import type {OptionData} from '@libs/ReportUtils'; import { getAutocompleteCategories, @@ -229,13 +229,17 @@ function SearchRouterList( case CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG: { const autocompleteList = autocompleteValue ? tagAutocompleteList : recentTagsAutocompleteList ?? []; const filteredTags = autocompleteList - .filter((tag) => tag.toLowerCase().includes(autocompleteValue.toLowerCase()) && !alreadyAutocompletedKeys.includes(tag)) + .filter( + (tag) => getCleanedTagName(tag).toLowerCase().includes(autocompleteValue.toLowerCase()) && !alreadyAutocompletedKeys.includes(getCleanedTagName(tag).toLowerCase()), + ) .sort() .slice(0, 10); return filteredTags.map((tagName) => ({ filterKey: CONST.SEARCH.SEARCH_USER_FRIENDLY_KEYS.TAG, - text: tagName, + text: getCleanedTagName(tagName), + autocompleteID: tagName, + mapKey: CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG, })); } case CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY: { diff --git a/src/components/Search/SearchRouter/buildSubstitutionsMap.ts b/src/components/Search/SearchRouter/buildSubstitutionsMap.ts index b81faca6f5f..892aa050ef9 100644 --- a/src/components/Search/SearchRouter/buildSubstitutionsMap.ts +++ b/src/components/Search/SearchRouter/buildSubstitutionsMap.ts @@ -60,7 +60,8 @@ function buildSubstitutionsMap( filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM || filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.IN || - filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID + filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID || + filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG ) { const displayValue = getFilterDisplayValue(filterKey, filterValue, personalDetails, reports, cardList); diff --git a/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts b/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts index 117745fee48..84895efb33a 100644 --- a/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts +++ b/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts @@ -1,5 +1,6 @@ import type {SearchAutocompleteQueryRange, SearchFilterKey} from '@components/Search/types'; -import * as parser from '@libs/SearchParser/autocompleteParser'; +import {parse} from '@libs/SearchParser/autocompleteParser'; +import {sanitizeSearchValue} from '@libs/SearchQueryUtils'; type SubstitutionMap = Record; @@ -18,7 +19,7 @@ const getSubstitutionMapKey = (filterKey: SearchFilterKey, value: string) => `${ * return: `A from:9876 A` */ function getQueryWithSubstitutions(changedQuery: string, substitutions: SubstitutionMap) { - const parsed = parser.parse(changedQuery) as {ranges: SearchAutocompleteQueryRange[]}; + const parsed = parse(changedQuery) as {ranges: SearchAutocompleteQueryRange[]}; const searchAutocompleteQueryRanges = parsed.ranges; @@ -31,11 +32,12 @@ function getQueryWithSubstitutions(changedQuery: string, substitutions: Substitu for (const range of searchAutocompleteQueryRanges) { const itemKey = getSubstitutionMapKey(range.key, range.value); - const substitutionEntry = substitutions[itemKey]; + let substitutionEntry = substitutions[itemKey]; if (substitutionEntry) { const substitutionStart = range.start + lengthDiff; const substitutionEnd = range.start + range.length; + substitutionEntry = sanitizeSearchValue(substitutionEntry); // generate new query but substituting "user-typed" value with the entity id/email from substitutions resultQuery = resultQuery.slice(0, substitutionStart) + substitutionEntry + changedQuery.slice(substitutionEnd); diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 4b35152c767..9e5e03acd1b 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -363,7 +363,7 @@ function getTagNamesFromTagsLists(policyTagLists: PolicyTagLists): string[] { for (const policyTagList of Object.values(policyTagLists ?? {})) { for (const tag of Object.values(policyTagList.tags ?? {})) { - uniqueTagNames.add(getCleanedTagName(tag.name)); + uniqueTagNames.add(tag.name); } } return Array.from(uniqueTagNames); diff --git a/src/libs/SearchQueryUtils.ts b/src/libs/SearchQueryUtils.ts index a71fb32c75e..e754107fd0b 100644 --- a/src/libs/SearchQueryUtils.ts +++ b/src/libs/SearchQueryUtils.ts @@ -14,7 +14,7 @@ import localeCompare from './LocaleCompare'; import Log from './Log'; import {validateAmount} from './MoneyRequestUtils'; import {getPersonalDetailByEmail} from './PersonalDetailsUtils'; -import {getTagNamesFromTagsLists} from './PolicyUtils'; +import {getCleanedTagName, getTagNamesFromTagsLists} from './PolicyUtils'; import {getReportName} from './ReportUtils'; import {parse as parseSearchQuery} from './SearchParser/searchParser'; import {hashText} from './UserUtils'; @@ -559,6 +559,9 @@ function getFilterDisplayValue( const frontendAmount = convertToFrontendAmountAsInteger(Number(filterValue)); return Number.isNaN(frontendAmount) ? filterValue : frontendAmount.toString(); } + if (filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG) { + return getCleanedTagName(filterValue); + } return filterValue; } diff --git a/src/pages/Search/AdvancedSearchFilters.tsx b/src/pages/Search/AdvancedSearchFilters.tsx index d1c4b3649c7..005c5c96837 100644 --- a/src/pages/Search/AdvancedSearchFilters.tsx +++ b/src/pages/Search/AdvancedSearchFilters.tsx @@ -21,7 +21,7 @@ import {convertToDisplayStringWithoutCurrency} from '@libs/CurrencyUtils'; import localeCompare from '@libs/LocaleCompare'; import Navigation from '@libs/Navigation/Navigation'; import {createDisplayName} from '@libs/PersonalDetailsUtils'; -import {getAllTaxRates, getTagNamesFromTagsLists, isPolicyFeatureEnabled} from '@libs/PolicyUtils'; +import {getAllTaxRates, getCleanedTagName, getTagNamesFromTagsLists, isPolicyFeatureEnabled} from '@libs/PolicyUtils'; import {getReportName} from '@libs/ReportUtils'; import {buildCannedSearchQuery, buildQueryStringFromFilterFormValues, buildSearchQueryJSON, isCannedSearchQuery} from '@libs/SearchQueryUtils'; import {getExpenseTypeTranslationKey} from '@libs/SearchUIUtils'; @@ -306,7 +306,7 @@ function getFilterDisplayTitle(filters: Partial, filt const filterArray = filters[nonDateFilterKey] ?? []; return filterArray .sort(sortOptionsWithEmptyValue) - .map((value) => (value === CONST.SEARCH.EMPTY_VALUE ? translate('search.noTag') : value)) + .map((value) => (value === CONST.SEARCH.EMPTY_VALUE ? translate('search.noTag') : getCleanedTagName(value))) .join(', '); } diff --git a/src/pages/Search/SearchAdvancedFiltersPage/SearchFiltersTagPage.tsx b/src/pages/Search/SearchAdvancedFiltersPage/SearchFiltersTagPage.tsx index 6c58c046920..b65a82ad7f8 100644 --- a/src/pages/Search/SearchAdvancedFiltersPage/SearchFiltersTagPage.tsx +++ b/src/pages/Search/SearchAdvancedFiltersPage/SearchFiltersTagPage.tsx @@ -6,9 +6,9 @@ import ScreenWrapper from '@components/ScreenWrapper'; import SearchMultipleSelectionPicker from '@components/Search/SearchMultipleSelectionPicker'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import {updateAdvancedFilters} from '@libs/actions/Search'; import Navigation from '@libs/Navigation/Navigation'; -import {getTagNamesFromTagsLists} from '@libs/PolicyUtils'; -import * as SearchActions from '@userActions/Search'; +import {getCleanedTagName, getTagNamesFromTagsLists} from '@libs/PolicyUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -23,7 +23,7 @@ function SearchFiltersTagPage() { if (tag === CONST.SEARCH.EMPTY_VALUE) { return {name: translate('search.noTag'), value: tag}; } - return {name: tag, value: tag}; + return {name: getCleanedTagName(tag), value: tag}; }); const policyID = searchAdvancedFiltersForm?.policyID; const [allPolicyTagLists = {}] = useOnyx(ONYXKEYS.COLLECTION.POLICY_TAGS); @@ -38,14 +38,14 @@ function SearchFiltersTagPage() { .map(getTagNamesFromTagsLists) .flat() .forEach((tag) => uniqueTagNames.add(tag)); - items.push(...Array.from(uniqueTagNames).map((tagName) => ({name: tagName, value: tagName}))); + items.push(...Array.from(uniqueTagNames).map((tagName) => ({name: getCleanedTagName(tagName), value: tagName}))); } else { - items.push(...getTagNamesFromTagsLists(singlePolicyTagLists).map((name) => ({name, value: name}))); + items.push(...getTagNamesFromTagsLists(singlePolicyTagLists).map((name) => ({name: getCleanedTagName(name), value: name}))); } return items; }, [allPolicyTagLists, singlePolicyTagLists, translate]); - const updateTagFilter = useCallback((values: string[]) => SearchActions.updateAdvancedFilters({tag: values}), []); + const updateTagFilter = useCallback((values: string[]) => updateAdvancedFilters({tag: values}), []); return (