Skip to content

Commit e2e2e3b

Browse files
authored
Merge pull request Expensify#67965 from Expensify/cmartins-addWithdrawnFilter
Add withdrawn filter
2 parents 2e67fd3 + b6e00d0 commit e2e2e3b

32 files changed

Lines changed: 575 additions & 383 deletions

src/CONST/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6399,6 +6399,7 @@ const CONST = {
63996399
PAID: 'paid',
64006400
EXPORTED: 'exported',
64016401
POSTED: 'posted',
6402+
WITHDRAWN: 'withdrawn',
64026403
TITLE: 'title',
64036404
ASSIGNEE: 'assignee',
64046405
REIMBURSABLE: 'reimbursable',
@@ -6443,6 +6444,7 @@ const CONST = {
64436444
PAID: 'paid',
64446445
EXPORTED: 'exported',
64456446
POSTED: 'posted',
6447+
WITHDRAWN: 'withdrawn',
64466448
TITLE: 'title',
64476449
ASSIGNEE: 'assignee',
64486450
REIMBURSABLE: 'reimbursable',

src/ROUTES.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ const ROUTES = {
8282
SEARCH_ADVANCED_FILTERS_PAID: 'search/filters/paid',
8383
SEARCH_ADVANCED_FILTERS_EXPORTED: 'search/filters/exported',
8484
SEARCH_ADVANCED_FILTERS_POSTED: 'search/filters/posted',
85+
SEARCH_ADVANCED_FILTERS_WITHDRAWN: 'search/filters/withdrawn',
8586
SEARCH_ADVANCED_FILTERS_TITLE: 'search/filters/title',
8687
SEARCH_ADVANCED_FILTERS_ASSIGNEE: 'search/filters/assignee',
8788
SEARCH_ADVANCED_FILTERS_REIMBURSABLE: 'search/filters/reimbursable',

src/SCREENS.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const SCREENS = {
5252
ADVANCED_FILTERS_PAID_RHP: 'Search_Advanced_Filters_Paid_RHP',
5353
ADVANCED_FILTERS_EXPORTED_RHP: 'Search_Advanced_Filters_Exported_RHP',
5454
ADVANCED_FILTERS_POSTED_RHP: 'Search_Advanced_Filters_Posted_RHP',
55+
ADVANCED_FILTERS_WITHDRAWN_RHP: 'Search_Advanced_Filters_Withdrawn_RHP',
5556
ADVANCED_FILTERS_CURRENCY_RHP: 'Search_Advanced_Filters_Currency_RHP',
5657
ADVANCED_FILTERS_DESCRIPTION_RHP: 'Search_Advanced_Filters_Description_RHP',
5758
ADVANCED_FILTERS_MERCHANT_RHP: 'Search_Advanced_Filters_Merchant_RHP',

src/components/Search/SearchAutocompleteList.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@ function SearchAutocompleteList(
432432
text: status,
433433
}));
434434
}
435+
case CONST.SEARCH.SYNTAX_FILTER_KEYS.WITHDRAWN:
435436
case CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPORTED:
436437
case CONST.SEARCH.SYNTAX_FILTER_KEYS.POSTED: {
437438
const filteredDatePresets = (getDatePresets(autocompleteKey, true) ?? [])

src/components/Search/SearchPageHeader/SearchFiltersBar.tsx

Lines changed: 89 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import SingleSelectPopup from '@components/Search/FilterDropdowns/SingleSelectPo
1717
import UserSelectPopup from '@components/Search/FilterDropdowns/UserSelectPopup';
1818
import {useSearchContext} from '@components/Search/SearchContext';
1919
import type {SearchDateValues} from '@components/Search/SearchDatePresetFilterBase';
20-
import type {SearchQueryJSON, SingularSearchStatus} from '@components/Search/types';
20+
import type {SearchDateFilterKeys, SearchQueryJSON, SingularSearchStatus} from '@components/Search/types';
2121
import SearchFiltersSkeleton from '@components/Skeletons/SearchFiltersSkeleton';
2222
import useEnvironment from '@hooks/useEnvironment';
2323
import useLocalize from '@hooks/useLocalize';
@@ -42,6 +42,7 @@ import {
4242
} from '@libs/SearchQueryUtils';
4343
import {getDatePresets, getFeedOptions, getGroupByOptions, getStatusOptions, getTypeOptions} from '@libs/SearchUIUtils';
4444
import CONST from '@src/CONST';
45+
import type {TranslationPaths} from '@src/languages/types';
4546
import ONYXKEYS from '@src/ONYXKEYS';
4647
import ROUTES from '@src/ROUTES';
4748
import type {SearchAdvancedFiltersForm} from '@src/types/form';
@@ -142,26 +143,51 @@ function SearchFiltersBar({queryJSON, headerButtonsOptions, isMobileSelectionMod
142143
return [value, displayText];
143144
}, [filterFormValues.dateOn, filterFormValues.dateAfter, filterFormValues.dateBefore, translate]);
144145

145-
const [posted, displayPosted] = useMemo(() => {
146-
const value: SearchDateValues = {
147-
[CONST.SEARCH.DATE_MODIFIERS.ON]: filterFormValues.postedOn,
148-
[CONST.SEARCH.DATE_MODIFIERS.AFTER]: filterFormValues.postedAfter,
149-
[CONST.SEARCH.DATE_MODIFIERS.BEFORE]: filterFormValues.postedBefore,
150-
};
146+
const createDateDisplayValue = useCallback(
147+
(filterValues: {on?: string; after?: string; before?: string}): [SearchDateValues, string[]] => {
148+
const value: SearchDateValues = {
149+
[CONST.SEARCH.DATE_MODIFIERS.ON]: filterValues.on,
150+
[CONST.SEARCH.DATE_MODIFIERS.AFTER]: filterValues.after,
151+
[CONST.SEARCH.DATE_MODIFIERS.BEFORE]: filterValues.before,
152+
};
151153

152-
const displayText: string[] = [];
153-
if (value.On) {
154-
displayText.push(isSearchDatePreset(value.On) ? translate(`search.filters.date.presets.${value.On}`) : `${translate('common.on')} ${DateUtils.formatToReadableString(value.On)}`);
155-
}
156-
if (value.After) {
157-
displayText.push(`${translate('common.after')} ${DateUtils.formatToReadableString(value.After)}`);
158-
}
159-
if (value.Before) {
160-
displayText.push(`${translate('common.before')} ${DateUtils.formatToReadableString(value.Before)}`);
161-
}
154+
const displayText: string[] = [];
155+
if (value.On) {
156+
displayText.push(
157+
isSearchDatePreset(value.On) ? translate(`search.filters.date.presets.${value.On}`) : `${translate('common.on')} ${DateUtils.formatToReadableString(value.On)}`,
158+
);
159+
}
160+
if (value.After) {
161+
displayText.push(`${translate('common.after')} ${DateUtils.formatToReadableString(value.After)}`);
162+
}
163+
if (value.Before) {
164+
displayText.push(`${translate('common.before')} ${DateUtils.formatToReadableString(value.Before)}`);
165+
}
162166

163-
return [value, displayText];
164-
}, [filterFormValues.postedOn, filterFormValues.postedAfter, filterFormValues.postedBefore, translate]);
167+
return [value, displayText];
168+
},
169+
[translate],
170+
);
171+
172+
const [posted, displayPosted] = useMemo(
173+
() =>
174+
createDateDisplayValue({
175+
on: filterFormValues.postedOn,
176+
after: filterFormValues.postedAfter,
177+
before: filterFormValues.postedBefore,
178+
}),
179+
[filterFormValues.postedOn, filterFormValues.postedAfter, filterFormValues.postedBefore, createDateDisplayValue],
180+
);
181+
182+
const [withdrawn, displayWithdrawn] = useMemo(
183+
() =>
184+
createDateDisplayValue({
185+
on: filterFormValues.withdrawnOn,
186+
after: filterFormValues.withdrawnAfter,
187+
before: filterFormValues.withdrawnBefore,
188+
}),
189+
[filterFormValues.withdrawnOn, filterFormValues.withdrawnAfter, filterFormValues.withdrawnBefore, createDateDisplayValue],
190+
);
165191

166192
const updateFilterForm = useCallback(
167193
(values: Partial<SearchAdvancedFiltersForm>) => {
@@ -236,29 +262,38 @@ function SearchFiltersBar({queryJSON, headerButtonsOptions, isMobileSelectionMod
236262
[translate, feedOptions, feed, updateFilterForm],
237263
);
238264

239-
const postedPickerComponent = useCallback(
240-
({closeOverlay}: PopoverComponentProps) => {
241-
const onChange = (selectedDates: SearchDateValues) => {
242-
const dateFormValues = {
243-
postedOn: selectedDates[CONST.SEARCH.DATE_MODIFIERS.ON],
244-
postedAfter: selectedDates[CONST.SEARCH.DATE_MODIFIERS.AFTER],
245-
postedBefore: selectedDates[CONST.SEARCH.DATE_MODIFIERS.BEFORE],
265+
const createDatePickerComponent = useCallback(
266+
(filterKey: SearchDateFilterKeys, value: SearchDateValues, translationKey: TranslationPaths) => {
267+
return ({closeOverlay}: PopoverComponentProps) => {
268+
const onChange = (selectedDates: SearchDateValues) => {
269+
const dateFormValues = {
270+
[`${filterKey}On`]: selectedDates[CONST.SEARCH.DATE_MODIFIERS.ON],
271+
[`${filterKey}After`]: selectedDates[CONST.SEARCH.DATE_MODIFIERS.AFTER],
272+
[`${filterKey}Before`]: selectedDates[CONST.SEARCH.DATE_MODIFIERS.BEFORE],
273+
};
274+
275+
updateFilterForm(dateFormValues);
246276
};
247277

248-
updateFilterForm(dateFormValues);
278+
return (
279+
<DateSelectPopup
280+
label={translate(translationKey)}
281+
value={value}
282+
onChange={onChange}
283+
closeOverlay={closeOverlay}
284+
presets={getDatePresets(filterKey, true)}
285+
/>
286+
);
249287
};
250-
251-
return (
252-
<DateSelectPopup
253-
label={translate('search.filters.posted')}
254-
value={posted}
255-
onChange={onChange}
256-
closeOverlay={closeOverlay}
257-
presets={getDatePresets(CONST.SEARCH.SYNTAX_FILTER_KEYS.POSTED, true)}
258-
/>
259-
);
260288
},
261-
[posted, translate, updateFilterForm],
289+
[translate, updateFilterForm],
290+
);
291+
292+
const postedPickerComponent = useMemo(() => createDatePickerComponent(CONST.SEARCH.SYNTAX_FILTER_KEYS.POSTED, posted, 'search.filters.posted'), [createDatePickerComponent, posted]);
293+
294+
const withdrawnPickerComponent = useMemo(
295+
() => createDatePickerComponent(CONST.SEARCH.SYNTAX_FILTER_KEYS.WITHDRAWN, withdrawn, 'search.filters.withdrawn'),
296+
[createDatePickerComponent, withdrawn],
262297
);
263298

264299
const statusComponent = useCallback(
@@ -331,6 +366,7 @@ function SearchFiltersBar({queryJSON, headerButtonsOptions, isMobileSelectionMod
331366
const shouldDisplayGroupByFilter = isDevelopment;
332367
const shouldDisplayFeedFilter = feedOptions.length > 1 && !!filterFormValues.feed;
333368
const shouldDisplayPostedFilter = !!filterFormValues.feed && (!!filterFormValues.postedOn || !!filterFormValues.postedAfter || !!filterFormValues.postedBefore);
369+
const shouldDisplayWithdrawnFilter = !!filterFormValues.withdrawnOn || !!filterFormValues.withdrawnAfter || !!filterFormValues.withdrawnBefore;
334370

335371
const filterList = [
336372
{
@@ -369,6 +405,16 @@ function SearchFiltersBar({queryJSON, headerButtonsOptions, isMobileSelectionMod
369405
},
370406
]
371407
: []),
408+
...(shouldDisplayWithdrawnFilter
409+
? [
410+
{
411+
label: translate('search.filters.withdrawn'),
412+
PopoverComponent: withdrawnPickerComponent,
413+
value: displayWithdrawn,
414+
filterKey: FILTER_KEYS.WITHDRAWN_ON,
415+
},
416+
]
417+
: []),
372418
{
373419
label: translate('common.status'),
374420
PopoverComponent: statusComponent,
@@ -395,18 +441,23 @@ function SearchFiltersBar({queryJSON, headerButtonsOptions, isMobileSelectionMod
395441
groupBy,
396442
displayDate,
397443
displayPosted,
444+
displayWithdrawn,
398445
filterFormValues.from,
399446
filterFormValues.feed,
400447
filterFormValues.postedOn,
401448
filterFormValues.postedAfter,
402449
filterFormValues.postedBefore,
450+
filterFormValues.withdrawnOn,
451+
filterFormValues.withdrawnAfter,
452+
filterFormValues.withdrawnBefore,
403453
translate,
404454
typeComponent,
405455
groupByComponent,
406456
statusComponent,
407457
datePickerComponent,
408458
userPickerComponent,
409459
postedPickerComponent,
460+
withdrawnPickerComponent,
410461
status,
411462
personalDetails,
412463
isDevelopment,

src/components/Search/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ type SearchDateFilterKeys =
120120
| typeof CONST.SEARCH.SYNTAX_FILTER_KEYS.APPROVED
121121
| typeof CONST.SEARCH.SYNTAX_FILTER_KEYS.PAID
122122
| typeof CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPORTED
123-
| typeof CONST.SEARCH.SYNTAX_FILTER_KEYS.POSTED;
123+
| typeof CONST.SEARCH.SYNTAX_FILTER_KEYS.POSTED
124+
| typeof CONST.SEARCH.SYNTAX_FILTER_KEYS.WITHDRAWN;
124125

125126
type SearchFilterKey =
126127
| ValueOf<typeof CONST.SEARCH.SYNTAX_FILTER_KEYS>

src/languages/de.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6003,6 +6003,7 @@ const translations = {
60036003
paid: 'Zahlungsdatum',
60046004
exported: 'Exportiertes Datum',
60056005
posted: 'Buchungsdatum',
6006+
withdrawn: 'Storniert',
60066007
billable: 'Abrechenbar',
60076008
reimbursable: 'Erstattungsfähig',
60086009
groupBy: {

src/languages/en.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5975,6 +5975,7 @@ const translations = {
59755975
paid: 'Paid date',
59765976
exported: 'Exported date',
59775977
posted: 'Posted date',
5978+
withdrawn: 'Withdrawn date',
59785979
billable: 'Billable',
59795980
reimbursable: 'Reimbursable',
59805981
groupBy: {

src/languages/es.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5997,6 +5997,7 @@ const translations = {
59975997
paid: 'Fecha de pago',
59985998
exported: 'Fecha de exportación',
59995999
posted: 'Fecha de contabilización',
6000+
withdrawn: 'Fecha de retirada',
60006001
billable: 'Facturable',
60016002
reimbursable: 'Reembolsable',
60026003
groupBy: {

src/languages/fr.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6016,6 +6016,7 @@ const translations = {
60166016
paid: 'Date de paiement',
60176017
exported: 'Date exportée',
60186018
posted: 'Date de publication',
6019+
withdrawn: 'Date de retrait',
60196020
billable: 'Facturable',
60206021
reimbursable: 'Remboursable',
60216022
groupBy: {

0 commit comments

Comments
 (0)