Skip to content
Open
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
12 changes: 10 additions & 2 deletions src/components/Search/SearchPageHeader/SearchFiltersBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {emailSelector} from '@selectors/Session';
import React, {useCallback, useContext, useMemo, useRef} from 'react';
import type {ReactNode} from 'react';
import {FlatList, View} from 'react-native';
import type {ValueOf} from 'type-fest';
import Button from '@components/Button';
import ButtonWithDropdownMenu from '@components/ButtonWithDropdownMenu';
import type {DropdownOption} from '@components/ButtonWithDropdownMenu/types';
Expand Down Expand Up @@ -298,8 +299,15 @@ function SearchFiltersBar({

// If the type has changed, reset the status so we dont have an invalid status selected
if (updatedFilterFormValues.type !== searchAdvancedFiltersForm.type) {
updatedFilterFormValues.status = CONST.SEARCH.STATUS.EXPENSE.ALL;
updatedFilterFormValues.columns = [];
updatedFilterFormValues.status = CONST.SEARCH.STATUS.EXPENSE.ALL;
// Filter out invalid "has" values for the new type
if (updatedFilterFormValues.has && updatedFilterFormValues.type) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ CONSISTENCY-3 (docs)

The logic for filtering invalid "has" values is duplicated in both SearchFiltersBar.tsx and SearchFiltersTypePage.tsx. This increases maintenance overhead and raises bug risk.

Suggested fix: Extract this logic into a reusable utility function:

// In SearchUIUtils.ts or a new utility file
function filterValidHasValues(
  hasValues: string[] | undefined,
  type: SearchDataTypes,
  translate: LocalizedTranslate
): string[] | undefined {
  if (\!hasValues || \!type) return undefined;
  
  const validHasOptions = getHasOptions(translate, type);
  const validHasValues = new Set(validHasOptions.map((option) => option.value));
  const filteredHasValues = hasValues.filter((hasValue) => 
    validHasValues.has(hasValue as ValueOf<typeof CONST.SEARCH.HAS_VALUES>)
  );
  
  return filteredHasValues.length > 0 ? filteredHasValues : undefined;
}

Then use it in both locations:

updatedFilterFormValues.has = filterValidHasValues(
  updatedFilterFormValues.has,
  updatedFilterFormValues.type,
  translate
);

const validHasOptions = getHasOptions(translate, updatedFilterFormValues.type);
const validHasValues = new Set(validHasOptions.map((option) => option.value));
const filteredHasValues = updatedFilterFormValues.has.filter((hasValue) => validHasValues.has(hasValue as ValueOf<typeof CONST.SEARCH.HAS_VALUES>));
updatedFilterFormValues.has = filteredHasValues.length > 0 ? filteredHasValues : undefined;
}
}

if (updatedFilterFormValues.groupBy !== searchAdvancedFiltersForm.groupBy) {
Expand All @@ -317,7 +325,7 @@ function SearchFiltersBar({
Navigation.setParams({q: queryString, rawQuery: undefined});
});
},
[searchAdvancedFiltersForm, queryJSON.sortBy, queryJSON.sortOrder],
[searchAdvancedFiltersForm, queryJSON.sortBy, queryJSON.sortOrder, translate],
);

const openAdvancedFilters = useCallback(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, {useCallback, useMemo, useState} from 'react';
import {View} from 'react-native';
import type {ValueOf} from 'type-fest';
import FixedFooter from '@components/FixedFooter';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
Expand All @@ -12,7 +13,7 @@ import useOnyx from '@hooks/useOnyx';
import useThemeStyles from '@hooks/useThemeStyles';
import {updateAdvancedFilters} from '@libs/actions/Search';
import Navigation from '@libs/Navigation/Navigation';
import {getTypeOptions} from '@libs/SearchUIUtils';
import {getHasOptions, getTypeOptions} from '@libs/SearchUIUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
Expand Down Expand Up @@ -44,16 +45,20 @@ function SearchFiltersTypePage() {

const applyChanges = useCallback(() => {
const hasTypeChanged = selectedItem !== searchAdvancedFiltersForm?.type;
const validHasOptions = getHasOptions(translate, selectedItem);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ CONSISTENCY-3 (docs)

The logic for filtering invalid "has" values is duplicated from SearchFiltersBar.tsx. This increases maintenance overhead and raises bug risk.

Suggested fix: Extract this logic into a reusable utility function (see comment in SearchFiltersBar.tsx) and use it here:

const filteredHasValues = filterValidHasValues(
  searchAdvancedFiltersForm?.has,
  selectedItem,
  translate
);

const updatedFilters = {
  type: selectedItem,
  ...(hasTypeChanged && {
    groupBy: null,
    status: CONST.SEARCH.STATUS.EXPENSE.ALL,
    has: filteredHasValues,
  }),
};

const validHasValues = new Set(validHasOptions.map((option) => option.value));
const filteredHasValues = searchAdvancedFiltersForm?.has?.filter((hasValue) => validHasValues.has(hasValue as ValueOf<typeof CONST.SEARCH.HAS_VALUES>));
const updatedFilters = {
type: selectedItem,
...(hasTypeChanged && {
groupBy: null,
status: CONST.SEARCH.STATUS.EXPENSE.ALL,
has: filteredHasValues,
}),
};
updateAdvancedFilters(updatedFilters);
Navigation.goBack(ROUTES.SEARCH_ADVANCED_FILTERS.getRoute());
}, [searchAdvancedFiltersForm?.type, selectedItem]);
}, [searchAdvancedFiltersForm?.has, searchAdvancedFiltersForm?.type, selectedItem, translate]);

return (
<ScreenWrapper
Expand Down
Loading