Skip to content

Commit c8835dc

Browse files
authored
Merge pull request Expensify#59390 from Expensify/jsenyitko-reimbursable-filter
Add filter for 'reimbursable:' and 'billable:'
2 parents 3a51947 + d997d26 commit c8835dc

File tree

22 files changed

+745
-431
lines changed

22 files changed

+745
-431
lines changed

src/CONST.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6397,6 +6397,10 @@ const CONST = {
63976397
GROUP_BY: {
63986398
REPORTS: 'reports',
63996399
},
6400+
BOOLEAN: {
6401+
YES: 'yes',
6402+
NO: 'no',
6403+
},
64006404
TABLE_COLUMN_SIZES: {
64016405
NORMAL: 'normal',
64026406
WIDE: 'wide',
@@ -6483,6 +6487,8 @@ const CONST = {
64836487
PAID: 'paid',
64846488
EXPORTED: 'exported',
64856489
POSTED: 'posted',
6490+
REIMBURSABLE: 'reimbursable',
6491+
BILLABLE: 'billable',
64866492
POLICY_ID: 'policyID',
64876493
},
64886494
EMPTY_VALUE: 'none',
@@ -6519,6 +6525,8 @@ const CONST = {
65196525
PAID: 'paid',
65206526
EXPORTED: 'exported',
65216527
POSTED: 'posted',
6528+
REIMBURSABLE: 'reimbursable',
6529+
BILLABLE: 'billable',
65226530
},
65236531
DATE_MODIFIERS: {
65246532
BEFORE: 'Before',

src/ROUTES.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ const ROUTES = {
6868
SEARCH_ADVANCED_FILTERS_PAID: 'search/filters/paid',
6969
SEARCH_ADVANCED_FILTERS_EXPORTED: 'search/filters/exported',
7070
SEARCH_ADVANCED_FILTERS_POSTED: 'search/filters/posted',
71+
SEARCH_ADVANCED_FILTERS_REIMBURSABLE: 'search/filters/reimbursable',
72+
SEARCH_ADVANCED_FILTERS_BILLABLE: 'search/filters/billable',
7173
SEARCH_ADVANCED_FILTERS_WORKSPACE: 'search/filters/workspace',
7274
SEARCH_REPORT: {
7375
route: 'search/view/:reportID/:reportActionID?',

src/SCREENS.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ const SCREENS = {
6161
ADVANCED_FILTERS_TAG_RHP: 'Search_Advanced_Filters_Tag_RHP',
6262
ADVANCED_FILTERS_FROM_RHP: 'Search_Advanced_Filters_From_RHP',
6363
ADVANCED_FILTERS_TO_RHP: 'Search_Advanced_Filters_To_RHP',
64+
ADVANCED_FILTERS_REIMBURSABLE_RHP: 'Search_Advanced_Filters_Reimbursable_RHP',
65+
ADVANCED_FILTERS_BILLABLE_RHP: 'Search_Advanced_Filters_Billable_RHP',
6466
ADVANCED_FILTERS_WORKSPACE_RHP: 'Search_Advanced_Filters_Workspace_RHP',
6567
SAVED_SEARCH_RENAME_RHP: 'Search_Saved_Search_Rename_RHP',
6668
ADVANCED_FILTERS_IN_RHP: 'Search_Advanced_Filters_In_RHP',

src/components/Search/SearchAutocompleteList.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ function SearchAutocompleteList(
165165
const groupByAutocompleteList = Object.values(CONST.SEARCH.GROUP_BY);
166166
const statusAutocompleteList = Object.values({...CONST.SEARCH.STATUS.EXPENSE, ...CONST.SEARCH.STATUS.INVOICE, ...CONST.SEARCH.STATUS.CHAT, ...CONST.SEARCH.STATUS.TRIP});
167167
const expenseTypes = Object.values(CONST.SEARCH.TRANSACTION_TYPE);
168+
const booleanTypes = Object.values(CONST.SEARCH.BOOLEAN);
168169

169170
const [userCardList] = useOnyx(ONYXKEYS.CARD_LIST);
170171
const [workspaceCardFeeds] = useOnyx(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST);
@@ -393,6 +394,15 @@ function SearchAutocompleteList(
393394
mapKey: CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID,
394395
}));
395396
}
397+
case CONST.SEARCH.SYNTAX_FILTER_KEYS.REIMBURSABLE:
398+
case CONST.SEARCH.SYNTAX_FILTER_KEYS.BILLABLE: {
399+
const filteredValues = booleanTypes.filter((value) => value.includes(autocompleteValue.toLowerCase()) && !alreadyAutocompletedKeys.includes(value)).sort();
400+
401+
return filteredValues.map((value) => ({
402+
filterKey: autocompleteKey,
403+
text: value,
404+
}));
405+
}
396406
default: {
397407
return [];
398408
}
@@ -416,6 +426,7 @@ function SearchAutocompleteList(
416426
cardAutocompleteList,
417427
allCards,
418428
groupByAutocompleteList,
429+
booleanTypes,
419430
]);
420431

421432
const sortedRecentSearches = useMemo(() => {
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import React, {useCallback, useMemo} from 'react';
2+
import {View} from 'react-native';
3+
import {useOnyx} from 'react-native-onyx';
4+
import type {ValueOf} from 'type-fest';
5+
import HeaderWithBackButton from '@components/HeaderWithBackButton';
6+
import ScreenWrapper from '@components/ScreenWrapper';
7+
import SelectionList from '@components/SelectionList';
8+
import RadioListItem from '@components/SelectionList/RadioListItem';
9+
import type {ListItem} from '@components/SelectionList/types';
10+
import useLocalize from '@hooks/useLocalize';
11+
import useThemeStyles from '@hooks/useThemeStyles';
12+
import Navigation from '@libs/Navigation/Navigation';
13+
import {updateAdvancedFilters} from '@userActions/Search';
14+
import CONST from '@src/CONST';
15+
import type {TranslationPaths} from '@src/languages/types';
16+
import ONYXKEYS from '@src/ONYXKEYS';
17+
import ROUTES from '@src/ROUTES';
18+
import type {SearchBooleanFilterKeys} from './types';
19+
20+
type BooleanFilterItem = ListItem & {
21+
value: ValueOf<typeof CONST.SEARCH.BOOLEAN>;
22+
};
23+
24+
type SearchBooleanFilterBaseProps = {
25+
/** Key used for the boolean filter */
26+
booleanKey: SearchBooleanFilterKeys;
27+
28+
/** The translation key for the page title */
29+
titleKey: TranslationPaths;
30+
};
31+
32+
function SearchBooleanFilterBase({booleanKey, titleKey}: SearchBooleanFilterBaseProps) {
33+
const styles = useThemeStyles();
34+
const {translate} = useLocalize();
35+
36+
const booleanValues = Object.values(CONST.SEARCH.BOOLEAN);
37+
const [searchAdvancedFiltersForm] = useOnyx(ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM);
38+
39+
const selectedItem = useMemo(() => {
40+
return booleanValues.find((value) => searchAdvancedFiltersForm?.[booleanKey] === value) ?? null;
41+
}, [booleanKey, searchAdvancedFiltersForm, booleanValues]);
42+
43+
const items = useMemo(() => {
44+
return booleanValues.map((value) => ({
45+
value,
46+
keyForList: value,
47+
text: translate(`common.${value}`),
48+
isSelected: selectedItem === value,
49+
}));
50+
}, [selectedItem, translate, booleanValues]);
51+
52+
const updateFilter = useCallback(
53+
(selectedFilter: BooleanFilterItem) => {
54+
const newValue = selectedFilter.isSelected ? null : selectedFilter.value;
55+
56+
updateAdvancedFilters({[booleanKey]: newValue});
57+
Navigation.goBack(ROUTES.SEARCH_ADVANCED_FILTERS);
58+
},
59+
[booleanKey],
60+
);
61+
62+
return (
63+
<ScreenWrapper
64+
testID={SearchBooleanFilterBase.displayName}
65+
shouldShowOfflineIndicatorInWideScreen
66+
offlineIndicatorStyle={styles.mtAuto}
67+
includeSafeAreaPaddingBottom={false}
68+
shouldEnableMaxHeight
69+
>
70+
<HeaderWithBackButton
71+
title={translate(titleKey)}
72+
onBackButtonPress={() => {
73+
Navigation.goBack(ROUTES.SEARCH_ADVANCED_FILTERS);
74+
}}
75+
/>
76+
<View style={[styles.flex1]}>
77+
<SelectionList
78+
shouldSingleExecuteRowSelect
79+
sections={[{data: items}]}
80+
ListItem={RadioListItem}
81+
initiallyFocusedOptionKey={selectedItem}
82+
onSelectRow={updateFilter}
83+
/>
84+
</View>
85+
</ScreenWrapper>
86+
);
87+
}
88+
89+
SearchBooleanFilterBase.displayName = 'SearchBooleanFilterBase';
90+
91+
export default SearchBooleanFilterBase;

src/components/Search/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ type QueryFilter = {
8787
value: string | number;
8888
};
8989

90+
type SearchBooleanFilterKeys = typeof CONST.SEARCH.SYNTAX_FILTER_KEYS.BILLABLE | typeof CONST.SEARCH.SYNTAX_FILTER_KEYS.REIMBURSABLE;
91+
9092
type SearchDateFilterKeys =
9193
| typeof CONST.SEARCH.SYNTAX_FILTER_KEYS.DATE
9294
| typeof CONST.SEARCH.SYNTAX_FILTER_KEYS.SUBMITTED
@@ -145,6 +147,7 @@ export type {
145147
SelectedTransactionInfo,
146148
SelectedTransactions,
147149
SearchColumnType,
150+
SearchBooleanFilterKeys,
148151
SearchDateFilterKeys,
149152
SearchStatus,
150153
SearchQueryAST,

src/languages/en.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,7 @@ const translations = {
536536
help: 'Help',
537537
expenseReports: 'Expense Reports',
538538
rateOutOfPolicy: 'Rate out of policy',
539+
reimbursable: 'Reimbursable',
539540
editYourProfile: 'Edit your profile',
540541
comments: 'Comments',
541542
unreported: 'Unreported',
@@ -5164,6 +5165,8 @@ const translations = {
51645165
paid: 'Paid',
51655166
exported: 'Exported',
51665167
posted: 'Posted',
5168+
billable: 'Billable',
5169+
reimbursable: 'Reimbursable',
51675170
},
51685171
moneyRequestReport: {
51695172
emptyStateTitle: 'This report has no expenses',

src/languages/es.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ const translations = {
527527
help: 'Ayuda',
528528
expenseReports: 'Informes de Gastos',
529529
rateOutOfPolicy: 'Tasa fuera de póliza',
530+
reimbursable: 'Reembolsable',
530531
editYourProfile: 'Edita tu perfil',
531532
comments: 'Comentarios',
532533
unreported: 'No reportado',
@@ -5217,6 +5218,8 @@ const translations = {
52175218
paid: 'Pagado',
52185219
exported: 'Exportado',
52195220
posted: 'Contabilizado',
5221+
billable: 'Facturable',
5222+
reimbursable: 'Reembolsable',
52205223
},
52215224
moneyRequestReport: {
52225225
emptyStateTitle: 'Este informe no tiene gastos',

src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,8 @@ const SearchAdvancedFiltersModalStackNavigator = createModalStackNavigator<Searc
717717
[SCREENS.SEARCH.ADVANCED_FILTERS_FROM_RHP]: () => require<ReactComponentModule>('@pages/Search/SearchAdvancedFiltersPage/SearchFiltersFromPage').default,
718718
[SCREENS.SEARCH.ADVANCED_FILTERS_TO_RHP]: () => require<ReactComponentModule>('@pages/Search/SearchAdvancedFiltersPage/SearchFiltersToPage').default,
719719
[SCREENS.SEARCH.ADVANCED_FILTERS_IN_RHP]: () => require<ReactComponentModule>('@pages/Search/SearchAdvancedFiltersPage/SearchFiltersInPage').default,
720+
[SCREENS.SEARCH.ADVANCED_FILTERS_BILLABLE_RHP]: () => require<ReactComponentModule>('@pages/Search/SearchAdvancedFiltersPage/SearchFiltersBillablePage').default,
721+
[SCREENS.SEARCH.ADVANCED_FILTERS_REIMBURSABLE_RHP]: () => require<ReactComponentModule>('@pages/Search/SearchAdvancedFiltersPage/SearchFiltersReimbursablePage').default,
720722
[SCREENS.SEARCH.ADVANCED_FILTERS_WORKSPACE_RHP]: () => require<ReactComponentModule>('@pages/Search/SearchAdvancedFiltersPage/SearchFiltersWorkspacePage').default,
721723
});
722724

src/libs/Navigation/linkingConfig/RELATIONS/SEARCH_TO_RHP.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ const SEARCH_TO_RHP: string[] = [
2626
SCREENS.SEARCH.ADVANCED_FILTERS_IN_RHP,
2727
SCREENS.SEARCH.ADVANCED_FILTERS_WORKSPACE_RHP,
2828
SCREENS.SEARCH.ADVANCED_FILTERS_CARD_RHP,
29+
SCREENS.SEARCH.ADVANCED_FILTERS_REIMBURSABLE_RHP,
30+
SCREENS.SEARCH.ADVANCED_FILTERS_BILLABLE_RHP,
2931
SCREENS.SEARCH.SAVED_SEARCH_RENAME_RHP,
3032
];
3133

0 commit comments

Comments
 (0)