Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Search filters: tax, expense type and tag #46736

Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ec78584
create generic multiple select component
SzymczakJ Aug 1, 2024
81298bf
clean up SearchFiltersCategoryPage
SzymczakJ Aug 2, 2024
646fce2
add SearchFiltersTaxRatePage
SzymczakJ Aug 2, 2024
ba0c5d4
Merge branch 'main' into @szymczak/selection-advanced-filters
SzymczakJ Aug 2, 2024
8c8b5f2
fix SearchFilters bug
SzymczakJ Aug 2, 2024
f01a1c1
add taxRate handling to buildQueryStringFromFilters
SzymczakJ Aug 2, 2024
5befeab
add expenseType page
SzymczakJ Aug 5, 2024
bce64fa
fix ExpenseType filter bug
SzymczakJ Aug 5, 2024
e700c7d
fix PolicyTag type
SzymczakJ Aug 5, 2024
18ccabf
fix types
SzymczakJ Aug 5, 2024
749606b
add SearchFiltersTagPage
SzymczakJ Aug 5, 2024
42a6e3e
Merge branch 'main' into @szymczak/selection-advanced-filters
SzymczakJ Aug 6, 2024
7c987ab
refactor SearchFiltersCurrencyPage
SzymczakJ Aug 6, 2024
e874d57
fix typos
SzymczakJ Aug 6, 2024
a2d0491
refactor filters using picker
SzymczakJ Aug 7, 2024
1dd85c9
fix PR comments
SzymczakJ Aug 7, 2024
cf4b28e
Merge branch 'main' into @szymczak/selection-advanced-filters
SzymczakJ Aug 7, 2024
3c0b0cf
fix PR comments
SzymczakJ Aug 8, 2024
190e44f
fix taxRate selection bugs
SzymczakJ Aug 12, 2024
da50037
Merge branch 'main' into @szymczak/selection-advanced-filters
SzymczakJ Aug 12, 2024
8e01c99
fix advanced search filters after tax filter refactor
SzymczakJ Aug 12, 2024
d626ead
fix comments
SzymczakJ Aug 12, 2024
d8f0e9d
fix Picker bug
SzymczakJ Aug 12, 2024
196e1a1
fix PR comments
SzymczakJ Aug 12, 2024
8f6b335
Merge branch 'main' into @szymczak/selection-advanced-filters
SzymczakJ Aug 12, 2024
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
2 changes: 1 addition & 1 deletion src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ type OnyxCollectionValuesMapping = {
[ONYXKEYS.COLLECTION.POLICY_DRAFTS]: OnyxTypes.Policy;
[ONYXKEYS.COLLECTION.POLICY_CATEGORIES]: OnyxTypes.PolicyCategories;
[ONYXKEYS.COLLECTION.POLICY_CATEGORIES_DRAFT]: OnyxTypes.PolicyCategories;
[ONYXKEYS.COLLECTION.POLICY_TAGS]: OnyxTypes.PolicyTagList;
[ONYXKEYS.COLLECTION.POLICY_TAGS]: OnyxTypes.PolicyTagLists;
[ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES]: OnyxTypes.RecentlyUsedCategories;
[ONYXKEYS.COLLECTION.POLICY_HAS_CONNECTIONS_DATA_BEEN_FETCHED]: boolean;
[ONYXKEYS.COLLECTION.DEPRECATED_POLICY_MEMBER_LIST]: OnyxTypes.PolicyEmployeeList;
Expand Down
14 changes: 3 additions & 11 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,32 +38,24 @@ const ROUTES = {
getRoute: ({query, isCustomQuery = false, policyIDs}: {query: SearchQueryString; isCustomQuery?: boolean; policyIDs?: string}) =>
`search?q=${query}&isCustomQuery=${isCustomQuery}${policyIDs ? `&policyIDs=${policyIDs}` : ''}` as const,
},

SEARCH_ADVANCED_FILTERS: 'search/filters',

SEARCH_ADVANCED_FILTERS_DATE: 'search/filters/date',

SEARCH_ADVANCED_FILTERS_TYPE: 'search/filters/type',

SEARCH_ADVANCED_FILTERS_STATUS: 'search/filters/status',

SEARCH_ADVANCED_FILTERS_CURRENCY: 'search/filters/currency',

SEARCH_ADVANCED_FILTERS_MERCHANT: 'search/filters/merchant',

SEARCH_ADVANCED_FILTERS_DESCRIPTION: 'search/filters/description',

SEARCH_ADVANCED_FILTERS_REPORT_ID: 'search/filters/reportID',

SEARCH_ADVANCED_FILTERS_CATEGORY: 'search/filters/category',
SEARCH_ADVANCED_FILTERS_KEYWORD: 'search/filters/keyword',
SEARCH_ADVANCED_FILTERS_CARD: 'search/filters/card',

SEARCH_ADVANCED_FILTERS_TAX_RATE: 'search/filters/taxRate',
SEARCH_ADVANCED_FILTERS_EXPENSE_TYPE: 'search/filters/expenseType',
SEARCH_ADVANCED_FILTERS_TAG: 'search/filters/tag',
SEARCH_REPORT: {
route: 'search/view/:reportID',
getRoute: (reportID: string) => `search/view/${reportID}` as const,
},

TRANSACTION_HOLD_REASON_RHP: 'search/hold',

// This is a utility route used to go to the user's concierge chat, or the sign-in page if the user's not authenticated
Expand Down
3 changes: 3 additions & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ const SCREENS = {
ADVANCED_FILTERS_CATEGORY_RHP: 'Search_Advanced_Filters_Category_RHP',
ADVANCED_FILTERS_KEYWORD_RHP: 'Search_Advanced_Filters_Keyword_RHP',
ADVANCED_FILTERS_CARD_RHP: 'Search_Advanced_Filters_Card_RHP',
ADVANCED_FILTERS_TAX_RATE_RHP: 'Search_Advanced_Filters_Tax_Rate_RHP',
ADVANCED_FILTERS_EXPENSE_TYPE_RHP: 'Search_Advanced_Filters_Expense_Type_RHP',
ADVANCED_FILTERS_TAG_RHP: 'Search_Advanced_Filters_Tag_RHP',
TRANSACTION_HOLD_REASON_RHP: 'Search_Transaction_Hold_Reason_RHP',
BOTTOM_TAB: 'Search_Bottom_Tab',
},
Expand Down
2 changes: 1 addition & 1 deletion src/components/MoneyRequestConfirmationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ type MoneyRequestConfirmationListOnyxProps = {
policyCategoriesDraft: OnyxEntry<OnyxTypes.PolicyCategories>;

/** Collection of tags attached to a policy */
policyTags: OnyxEntry<OnyxTypes.PolicyTagList>;
policyTags: OnyxEntry<OnyxTypes.PolicyTagLists>;

/** The policy of the report */
policy: OnyxEntry<OnyxTypes.Policy>;
Expand Down
4 changes: 2 additions & 2 deletions src/components/MoneyRequestConfirmationListFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,10 @@ type MoneyRequestConfirmationListFooterProps = {
policy: OnyxEntry<OnyxTypes.Policy>;

/** The policy tag lists */
policyTags: OnyxEntry<OnyxTypes.PolicyTagList>;
policyTags: OnyxEntry<OnyxTypes.PolicyTagLists>;

/** The policy tag lists */
policyTagLists: Array<ValueOf<OnyxTypes.PolicyTagList>>;
policyTagLists: Array<ValueOf<OnyxTypes.PolicyTagLists>>;

/** The rate of the transaction */
rate: number | undefined;
Expand Down
2 changes: 1 addition & 1 deletion src/components/ReportActionItem/MoneyRequestView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ type MoneyRequestViewOnyxPropsWithoutTransaction = {
policyCategories: OnyxEntry<OnyxTypes.PolicyCategories>;

/** Collection of tags attached to a policy */
policyTagList: OnyxEntry<OnyxTypes.PolicyTagList>;
policyTagList: OnyxEntry<OnyxTypes.PolicyTagLists>;

/** The expense report or iou report (only will have a value if this is a transaction thread) */
parentReport: OnyxEntry<OnyxTypes.Report>;
Expand Down
122 changes: 122 additions & 0 deletions src/components/Search/SearchMultipleSelectionPicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import React, {useCallback, useMemo, useState} from 'react';
import Button from '@components/Button';
import SelectionList from '@components/SelectionList';
import SelectableListItem from '@components/SelectionList/SelectableListItem';
import useDebouncedState from '@hooks/useDebouncedState';
import useLocalize from '@hooks/useLocalize';
import localeCompare from '@libs/LocaleCompare';
import Navigation from '@libs/Navigation/Navigation';
import type {OptionData} from '@libs/ReportUtils';
import ROUTES from '@src/ROUTES';

type SearchMultipleSelectionPickerItem = {
name: string;
value: string | string[];
};

type SearchMultipleSelectionPickerProps = {
items: SearchMultipleSelectionPickerItem[];
initiallySelectedItems: SearchMultipleSelectionPickerItem[] | undefined;
pickerTitle?: string;
onSaveSelection: (values: string[]) => void;
shouldShowTextInput?: boolean;
};

function SearchMultipleSelectionPicker({items, initiallySelectedItems, pickerTitle, onSaveSelection, shouldShowTextInput = true}: SearchMultipleSelectionPickerProps) {
const {translate} = useLocalize();

const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState('');
const [selectedItems, setSelectedItems] = useState<SearchMultipleSelectionPickerItem[]>(initiallySelectedItems ?? []);

const {sections, noResultsFound} = useMemo(() => {
const selectedItemsSection = selectedItems
.filter((item) => item?.name.toLowerCase().includes(debouncedSearchTerm?.toLowerCase()))
.sort((a, b) => localeCompare(a.name, b.name))
.map((item) => ({
text: item.name,
keyForList: item.name,
isSelected: true,
value: item.value,
}));
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))
.map((item) => ({
text: item.name,
keyForList: item.name,
isSelected: false,
value: item.value,
}));
const isEmpty = !selectedItemsSection.length && !remainingItemsSection.length;
return {
sections: isEmpty
? []
: [
{
title: undefined,
data: selectedItemsSection,
shouldShow: selectedItemsSection.length > 0,
},
{
title: pickerTitle,
data: remainingItemsSection,
shouldShow: remainingItemsSection.length > 0,
},
],
noResultsFound: isEmpty,
};
}, [selectedItems, items, pickerTitle, debouncedSearchTerm]);

const onSelectItem = useCallback(
(item: Partial<OptionData & SearchMultipleSelectionPickerItem>) => {
if (!item.text || !item.keyForList || !item.value) {
return;
}
if (item.isSelected) {
setSelectedItems(selectedItems?.filter((selectedItem) => selectedItem.name !== item.keyForList));
} else {
setSelectedItems([...(selectedItems ?? []), {name: item.text, value: item.value}]);
}
},
[selectedItems],
);

const handleConfirmSelection = useCallback(() => {
onSaveSelection(selectedItems.map((item) => item.value).flat());
Navigation.goBack(ROUTES.SEARCH_ADVANCED_FILTERS);
}, [onSaveSelection, selectedItems]);

const footerContent = useMemo(
() => (
<Button
success
text={translate('common.save')}
pressOnEnter
onPress={handleConfirmSelection}
large
/>
),
[translate, handleConfirmSelection],
);
return (
<SelectionList
sections={sections}
textInputValue={searchTerm}
onChangeText={setSearchTerm}
textInputLabel={shouldShowTextInput ? translate('common.search') : undefined}
onSelectRow={onSelectItem}
headerMessage={noResultsFound ? translate('common.noResultsFound') : undefined}
footerContent={footerContent}
shouldStopPropagation
showLoadingPlaceholder={!noResultsFound}
shouldShowTooltips
canSelectMultiple
ListItem={SelectableListItem}
/>
);
}

SearchMultipleSelectionPicker.displayName = 'SearchMultipleSelectionPicker';

export default SearchMultipleSelectionPicker;
export type {SearchMultipleSelectionPickerItem};
1 change: 0 additions & 1 deletion src/components/SelectionList/SelectableListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ function SelectableListItem<TItem extends ListItem>({
/>
</View>
</View>
{!!item.rightElement && item.rightElement}
luacmartins marked this conversation as resolved.
Show resolved Hide resolved
{canSelectMultiple && !item.isDisabled && (
<PressableWithFeedback
onPress={handleCheckboxPress}
Expand Down
4 changes: 2 additions & 2 deletions src/components/TagPicker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import * as PolicyUtils from '@libs/PolicyUtils';
import type * as ReportUtils from '@libs/ReportUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {PolicyTag, PolicyTagList, PolicyTags, RecentlyUsedTags} from '@src/types/onyx';
import type {PolicyTag, PolicyTagLists, PolicyTags, RecentlyUsedTags} from '@src/types/onyx';
import type {PendingAction} from '@src/types/onyx/OnyxCommon';

type SelectedTagOption = {
Expand All @@ -23,7 +23,7 @@ type SelectedTagOption = {

type TagPickerOnyxProps = {
/** Collection of tag list on a policy */
policyTags: OnyxEntry<PolicyTagList>;
policyTags: OnyxEntry<PolicyTagLists>;

/** List of recently used tags */
policyRecentlyUsedTags: OnyxEntry<RecentlyUsedTags>;
Expand Down
1 change: 1 addition & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3666,6 +3666,7 @@ export default {
hasKeywords: 'Has keywords',
currency: 'Currency',
},
expenseType: 'Expense type',
},
genericErrorPage: {
title: 'Uh-oh, something went wrong!',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3720,6 +3720,7 @@ export default {
hasKeywords: 'Tiene palabras clave',
currency: 'Divisa',
},
expenseType: 'Tipo de gasto',
},
genericErrorPage: {
title: '¡Oh-oh, algo salió mal!',
Expand Down
4 changes: 2 additions & 2 deletions src/libs/ModifiedExpenseMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Onyx from 'react-native-onyx';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {PolicyTagList, ReportAction} from '@src/types/onyx';
import type {PolicyTagLists, ReportAction} from '@src/types/onyx';
import * as CurrencyUtils from './CurrencyUtils';
import DateUtils from './DateUtils';
import * as Localize from './Localize';
Expand All @@ -11,7 +11,7 @@ import * as ReportActionsUtils from './ReportActionsUtils';
import * as ReportConnection from './ReportConnection';
import * as TransactionUtils from './TransactionUtils';

let allPolicyTags: OnyxCollection<PolicyTagList> = {};
let allPolicyTags: OnyxCollection<PolicyTagLists> = {};
Onyx.connect({
key: ONYXKEYS.COLLECTION.POLICY_TAGS,
waitForCollectionCallback: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,9 @@ const SearchAdvancedFiltersModalStackNavigator = createModalStackNavigator<Searc
[SCREENS.SEARCH.ADVANCED_FILTERS_CATEGORY_RHP]: () => require<ReactComponentModule>('../../../../pages/Search/SearchFiltersCategoryPage').default,
[SCREENS.SEARCH.ADVANCED_FILTERS_KEYWORD_RHP]: () => require<ReactComponentModule>('../../../../pages/Search/SearchFiltersKeywordPage').default,
[SCREENS.SEARCH.ADVANCED_FILTERS_CARD_RHP]: () => require<ReactComponentModule>('../../../../pages/Search/SearchFiltersCardPage').default,
[SCREENS.SEARCH.ADVANCED_FILTERS_TAX_RATE_RHP]: () => require<ReactComponentModule>('../../../../pages/Search/SearchFiltersTaxRatePage').default,
[SCREENS.SEARCH.ADVANCED_FILTERS_EXPENSE_TYPE_RHP]: () => require<ReactComponentModule>('../../../../pages/Search/SearchFiltersExpenseTypePage').default,
[SCREENS.SEARCH.ADVANCED_FILTERS_TAG_RHP]: () => require<ReactComponentModule>('../../../../pages/Search/SearchFiltersTagPage').default,
});

const RestrictedActionModalStackNavigator = createModalStackNavigator<SearchReportParamList>({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ const CENTRAL_PANE_TO_RHP_MAPPING: Partial<Record<CentralPaneName, string[]>> =
SCREENS.SEARCH.ADVANCED_FILTERS_REPORT_ID_RHP,
SCREENS.SEARCH.ADVANCED_FILTERS_CATEGORY_RHP,
SCREENS.SEARCH.ADVANCED_FILTERS_KEYWORD_RHP,
SCREENS.SEARCH.ADVANCED_FILTERS_TAX_RATE_RHP,
SCREENS.SEARCH.ADVANCED_FILTERS_EXPENSE_TYPE_RHP,
SCREENS.SEARCH.ADVANCED_FILTERS_TAG_RHP,
],
[SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: [
SCREENS.SETTINGS.SUBSCRIPTION.ADD_PAYMENT_CARD,
Expand Down
3 changes: 3 additions & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1025,6 +1025,9 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
[SCREENS.SEARCH.ADVANCED_FILTERS_CATEGORY_RHP]: ROUTES.SEARCH_ADVANCED_FILTERS_CATEGORY,
[SCREENS.SEARCH.ADVANCED_FILTERS_KEYWORD_RHP]: ROUTES.SEARCH_ADVANCED_FILTERS_KEYWORD,
[SCREENS.SEARCH.ADVANCED_FILTERS_CARD_RHP]: ROUTES.SEARCH_ADVANCED_FILTERS_CARD,
[SCREENS.SEARCH.ADVANCED_FILTERS_TAX_RATE_RHP]: ROUTES.SEARCH_ADVANCED_FILTERS_TAX_RATE,
[SCREENS.SEARCH.ADVANCED_FILTERS_EXPENSE_TYPE_RHP]: ROUTES.SEARCH_ADVANCED_FILTERS_EXPENSE_TYPE,
[SCREENS.SEARCH.ADVANCED_FILTERS_TAG_RHP]: ROUTES.SEARCH_ADVANCED_FILTERS_TAG,
},
},
[SCREENS.RIGHT_MODAL.RESTRICTED_ACTION]: {
Expand Down
4 changes: 2 additions & 2 deletions src/libs/OptionsListUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import type {
PolicyCategories,
PolicyCategory,
PolicyTag,
PolicyTagList,
PolicyTagLists,
PolicyTags,
Report,
ReportAction,
Expand Down Expand Up @@ -1352,7 +1352,7 @@ function getTagListSections(
/**
* Verifies that there is at least one enabled tag
*/
function hasEnabledTags(policyTagList: Array<PolicyTagList[keyof PolicyTagList]>) {
function hasEnabledTags(policyTagList: Array<PolicyTagLists[keyof PolicyTagLists]>) {
const policyTagValueList = policyTagList.map(({tags}) => Object.values(tags)).flat();

return hasEnabledOptions(policyTagValueList);
Expand Down
Loading
Loading