Skip to content

Commit

Permalink
Merge pull request #51033 from Expensify/revert-51015-revert-50122-cm…
Browse files Browse the repository at this point in the history
…artins-addNoOperator

Support empty value for categories and tags v2
  • Loading branch information
luacmartins authored Oct 21, 2024
2 parents 36805e2 + 00127bc commit f28662e
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 15 deletions.
1 change: 1 addition & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5729,6 +5729,7 @@ const CONST = {
KEYWORD: 'keyword',
IN: 'in',
},
EMPTY_VALUE: 'none',
},

REFERRER: {
Expand Down
16 changes: 14 additions & 2 deletions src/components/Search/SearchMultipleSelectionPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import useLocalize from '@hooks/useLocalize';
import localeCompare from '@libs/LocaleCompare';
import Navigation from '@libs/Navigation/Navigation';
import type {OptionData} from '@libs/ReportUtils';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';

type SearchMultipleSelectionPickerItem = {
Expand All @@ -28,14 +29,25 @@ function SearchMultipleSelectionPicker({items, initiallySelectedItems, pickerTit
const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState('');
const [selectedItems, setSelectedItems] = useState<SearchMultipleSelectionPickerItem[]>(initiallySelectedItems ?? []);

const sortOptionsWithEmptyValue = (a: SearchMultipleSelectionPickerItem, b: SearchMultipleSelectionPickerItem) => {
// Always show `No category` and `No tag` as the first option
if (a.value === CONST.SEARCH.EMPTY_VALUE) {
return -1;
}
if (b.value === CONST.SEARCH.EMPTY_VALUE) {
return 1;
}
return localeCompare(a.name, b.name);
};

useEffect(() => {
setSelectedItems(initiallySelectedItems ?? []);
}, [initiallySelectedItems]);

const {sections, noResultsFound} = useMemo(() => {
const selectedItemsSection = selectedItems
.filter((item) => item?.name.toLowerCase().includes(debouncedSearchTerm?.toLowerCase()))
.sort((a, b) => localeCompare(a.name, b.name))
.sort((a, b) => sortOptionsWithEmptyValue(a, b))
.map((item) => ({
text: item.name,
keyForList: item.name,
Expand All @@ -44,7 +56,7 @@ function SearchMultipleSelectionPicker({items, initiallySelectedItems, pickerTit
}));
const remainingItemsSection = items
.filter((item) => selectedItems.some((selectedItem) => selectedItem.value === item.value) === false && item?.name.toLowerCase().includes(debouncedSearchTerm?.toLowerCase()))
.sort((a, b) => localeCompare(a.name, b.name))
.sort((a, b) => sortOptionsWithEmptyValue(a, b))
.map((item) => ({
text: item.name,
keyForList: item.name,
Expand Down
2 changes: 2 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4349,6 +4349,8 @@ const translations = {
current: 'Current',
past: 'Past',
},
noCategory: 'No category',
noTag: 'No tag',
expenseType: 'Expense type',
recentSearches: 'Recent searches',
recentChats: 'Recent chats',
Expand Down
2 changes: 2 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4397,6 +4397,8 @@ const translations = {
current: 'Actual',
past: 'Anterior',
},
noCategory: 'Sin categoría',
noTag: 'Sin etiqueta',
expenseType: 'Tipo de gasto',
recentSearches: 'Búsquedas recientes',
recentChats: 'Chats recientes',
Expand Down
4 changes: 3 additions & 1 deletion src/libs/SearchUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -728,14 +728,16 @@ function buildFilterFormValuesFromQuery(
.filter((item) => !!item)
.map((tagList) => getTagNamesFromTagsLists(tagList ?? {}))
.flat();
tags.push(CONST.SEARCH.EMPTY_VALUE);
filtersForm[filterKey] = filters[filterKey]?.map((tag) => tag.value.toString()).filter((name) => tags.includes(name));
}
if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY) {
const categories = policyID
? Object.values(policyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`] ?? {}).map((category) => category.name)
: Object.values(policyCategories ?? {})
.map((xd) => Object.values(xd ?? {}).map((category) => category.name))
.map((item) => Object.values(item ?? {}).map((category) => category.name))
.flat();
categories.push(CONST.SEARCH.EMPTY_VALUE);
filtersForm[filterKey] = filters[filterKey]?.map((category) => category.value.toString()).filter((name) => categories.includes(name));
}
if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.KEYWORD) {
Expand Down
32 changes: 28 additions & 4 deletions src/pages/Search/AdvancedSearchFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,17 @@ function getFilterParticipantDisplayTitle(accountIDs: string[], personalDetails:
.join(', ');
}

const sortOptionsWithEmptyValue = (a: string, b: string) => {
// Always show `No category` and `No tag` as the first option
if (a === CONST.SEARCH.EMPTY_VALUE) {
return -1;
}
if (b === CONST.SEARCH.EMPTY_VALUE) {
return 1;
}
return localeCompare(a, b);
};

function getFilterDisplayTitle(filters: Partial<SearchAdvancedFiltersForm>, fieldName: AdvancedFiltersKeys, translate: LocaleContextProps['translate']) {
if (fieldName === CONST.SEARCH.SYNTAX_FILTER_KEYS.DATE) {
// the value of date filter is a combination of dateBefore + dateAfter values
Expand Down Expand Up @@ -175,14 +186,27 @@ function getFilterDisplayTitle(filters: Partial<SearchAdvancedFiltersForm>, fiel
return;
}

if (
(fieldName === CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY || fieldName === CONST.SEARCH.SYNTAX_FILTER_KEYS.CURRENCY || fieldName === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG) &&
filters[fieldName]
) {
if (fieldName === CONST.SEARCH.SYNTAX_FILTER_KEYS.CURRENCY && filters[fieldName]) {
const filterArray = filters[fieldName] ?? [];
return filterArray.sort(localeCompare).join(', ');
}

if (fieldName === CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY && filters[fieldName]) {
const filterArray = filters[fieldName] ?? [];
return filterArray
.sort(sortOptionsWithEmptyValue)
.map((value) => (value === CONST.SEARCH.EMPTY_VALUE ? translate('search.noCategory') : value))
.join(', ');
}

if (fieldName === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG && filters[fieldName]) {
const filterArray = filters[fieldName] ?? [];
return filterArray
.sort(sortOptionsWithEmptyValue)
.map((value) => (value === CONST.SEARCH.EMPTY_VALUE ? translate('search.noTag') : value))
.join(', ');
}

if (fieldName === CONST.SEARCH.SYNTAX_FILTER_KEYS.DESCRIPTION) {
return filters[fieldName];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import useSafePaddingBottomStyle from '@hooks/useSafePaddingBottomStyle';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import * as SearchActions from '@userActions/Search';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';

Expand All @@ -17,19 +18,27 @@ function SearchFiltersCategoryPage() {
const {translate} = useLocalize();

const [searchAdvancedFiltersForm] = useOnyx(ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM);
const selectedCategoriesItems = searchAdvancedFiltersForm?.category?.map((category) => ({name: category, value: category}));
const selectedCategoriesItems = searchAdvancedFiltersForm?.category?.map((category) => {
if (category === CONST.SEARCH.EMPTY_VALUE) {
return {name: translate('search.noCategory'), value: category};
}
return {name: category, value: category};
});
const policyID = searchAdvancedFiltersForm?.policyID ?? '-1';
const [allPolicyIDCategories] = useOnyx(ONYXKEYS.COLLECTION.POLICY_CATEGORIES);
const singlePolicyCategories = allPolicyIDCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`];

const categoryItems = useMemo(() => {
const items = [{name: translate('search.noCategory'), value: CONST.SEARCH.EMPTY_VALUE as string}];
if (!singlePolicyCategories) {
const uniqueCategoryNames = new Set<string>();
Object.values(allPolicyIDCategories ?? {}).map((policyCategories) => Object.values(policyCategories ?? {}).forEach((category) => uniqueCategoryNames.add(category.name)));
return Array.from(uniqueCategoryNames).map((categoryName) => ({name: categoryName, value: categoryName}));
items.push(...Array.from(uniqueCategoryNames).map((categoryName) => ({name: categoryName, value: categoryName})));
} else {
items.push(...Object.values(singlePolicyCategories ?? {}).map((category) => ({name: category.name, value: category.name})));
}
return Object.values(singlePolicyCategories ?? {}).map((category) => ({name: category.name, value: category.name}));
}, [allPolicyIDCategories, singlePolicyCategories]);
return items;
}, [allPolicyIDCategories, singlePolicyCategories, translate]);

const onSaveSelection = useCallback((values: string[]) => SearchActions.updateAdvancedFilters({category: values}), []);
const safePaddingBottomStyle = useSafePaddingBottomStyle();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import {getTagNamesFromTagsLists} from '@libs/PolicyUtils';
import * as SearchActions from '@userActions/Search';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {PolicyTagLists} from '@src/types/onyx';
Expand All @@ -18,12 +19,18 @@ function SearchFiltersTagPage() {
const {translate} = useLocalize();

const [searchAdvancedFiltersForm] = useOnyx(ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM);
const selectedTagsItems = searchAdvancedFiltersForm?.tag?.map((tag) => ({name: tag, value: tag}));
const selectedTagsItems = searchAdvancedFiltersForm?.tag?.map((tag) => {
if (tag === CONST.SEARCH.EMPTY_VALUE) {
return {name: translate('search.noTag'), value: tag};
}
return {name: tag, value: tag};
});
const policyID = searchAdvancedFiltersForm?.policyID ?? '-1';
const [allPoliciesTagsLists] = useOnyx(ONYXKEYS.COLLECTION.POLICY_TAGS);
const singlePolicyTagsList: PolicyTagLists | undefined = allPoliciesTagsLists?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`];

const tagItems = useMemo(() => {
const items = [{name: translate('search.noTag'), value: CONST.SEARCH.EMPTY_VALUE as string}];
if (!singlePolicyTagsList) {
const uniqueTagNames = new Set<string>();
const tagListsUnpacked = Object.values(allPoliciesTagsLists ?? {}).filter((item) => !!item) as PolicyTagLists[];
Expand All @@ -33,10 +40,12 @@ function SearchFiltersTagPage() {
})
.flat()
.forEach((tag) => uniqueTagNames.add(tag));
return Array.from(uniqueTagNames).map((tagName) => ({name: tagName, value: tagName}));
items.push(...Array.from(uniqueTagNames).map((tagName) => ({name: tagName, value: tagName})));
} else {
items.push(...getTagNamesFromTagsLists(singlePolicyTagsList).map((name) => ({name, value: name})));
}
return getTagNamesFromTagsLists(singlePolicyTagsList).map((name) => ({name, value: name}));
}, [allPoliciesTagsLists, singlePolicyTagsList]);
return items;
}, [allPoliciesTagsLists, singlePolicyTagsList, translate]);

const updateTagFilter = useCallback((values: string[]) => SearchActions.updateAdvancedFilters({tag: values}), []);

Expand Down

0 comments on commit f28662e

Please sign in to comment.