-
Notifications
You must be signed in to change notification settings - Fork 3.2k
[Suggested Search v1] [$250] Add grouping to RHP Filters
(and other changes to improve UX)
#61235
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
Comments
Filters
(and other changes to improve UX)Filters
(and other changes to improve UX)
Job added to Upwork: https://www.upwork.com/jobs/~021917916081372952479 |
Current assignee @JmillsExpensify is eligible for the NewFeature assigner, not assigning anyone new. |
Triggered auto assignment to Contributor-plus team member for initial proposal review - @Ollyws ( |
🚨 Edited by proposal-police: This proposal was edited at 2025-05-01 13:13:42 UTC. ProposalPlease re-state the problem that we are trying to solve in this issue.Add grouping to RHP Filters (and other changes to improve UX) What is the root cause of that problem?New Feature What changes do you think we should make in order to solve the problem?In AdvancedSearchFilters.tsx, we should group the menu items by using the Example of Reports section. <View style={[sectionStyle, styles.pb4, styles.mh3]}>
<Text style={sectionTitle}>{translate('Reports')}</Text>
{section.map((item) => {
return (
<MenuItemWithTopDescription
....
} Unlike the Then, we will split the menu items into their respective groups and order them in Finally, rename date filter names in What specific scenarios should we cover in automated tests to prevent reintroducing this issue in the future?What alternative solutions did you explore? (Optional)N/A |
|
ProposalPlease re-state the problem that we are trying to solve in this issue.Add grouping to RHP Filters (and other changes to improve UX) What is the root cause of that problem?Changes request What changes do you think we should make in order to solve the problem?Update this code to match the requirement on OP Example for the expense:
Create new const
And change this code to the following to use the new
Might need to adjust some styling Then update the following translation to add "date", update both on en and es files App/src/libs/SearchQueryUtils.ts Lines 68 to 71 in 61274d4
What specific scenarios should we cover in automated tests to prevent reintroducing this issue in the future?None What alternative solutions did you explore? (Optional)N/A |
ProposalPlease re-state the problem that we are trying to solve in this issue.Add grouping to RHP Filters (and other changes to improve UX) What is the root cause of that problem?New feature What changes do you think we should make in order to solve the problem?To implement this requirement, we should update typeFiltersKeys to use the following structure: Record<
string,
Array<{
title: ValueOf<typeof CONST.SEARCH.FILTER_SECTION_TITLES>;
filters: Array<ValueOf<typeof CONST.SEARCH.SYNTAX_FILTER_KEYS>>;
}>
> Example: [CONST.SEARCH.DATA_TYPES.EXPENSE]: [
{
title: CONST.SEARCH.FILTER_SECTION_TITLES.GENERAL,
filters: [
CONST.SEARCH.SYNTAX_FILTER_KEYS.KEYWORD,
CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM,
CONST.SEARCH.SYNTAX_FILTER_KEYS.TO,
CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID,
],
},
{
title: CONST.SEARCH.FILTER_SECTION_TITLES.EXPENSES,
filters: [
CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPENSE_TYPE,
CONST.SEARCH.SYNTAX_FILTER_KEYS.MERCHANT,
CONST.SEARCH.SYNTAX_FILTER_KEYS.DATE,
CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT,
CONST.SEARCH.SYNTAX_FILTER_KEYS.CURRENCY,
CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY,
CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG,
CONST.SEARCH.SYNTAX_FILTER_KEYS.DESCRIPTION,
CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE,
CONST.SEARCH.SYNTAX_FILTER_KEYS.REIMBURSABLE,
CONST.SEARCH.SYNTAX_FILTER_KEYS.BILLABLE,
],
},
{
title: CONST.SEARCH.FILTER_SECTION_TITLES.CARD,
filters: [
CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID,
CONST.SEARCH.SYNTAX_FILTER_KEYS.POSTED,
],
},
{
title: CONST.SEARCH.FILTER_SECTION_TITLES.REPORTS,
filters: [
CONST.SEARCH.SYNTAX_FILTER_KEYS.REPORT_ID,
CONST.SEARCH.SYNTAX_FILTER_KEYS.SUBMITTED,
CONST.SEARCH.SYNTAX_FILTER_KEYS.APPROVED,
CONST.SEARCH.SYNTAX_FILTER_KEYS.PAID,
CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPORTED,
],
},
], By applying this structure, each section will have a clearly defined title, helping to avoid cases where sections are missing titles. So we update typeFiltersKeys to use the following structure: const typeFiltersKeys: Record<
string,
Array<{
title: ValueOf<typeof CONST.SEARCH.FILTER_SECTION_TITLES>;
filters: Array<ValueOf<typeof CONST.SEARCH.SYNTAX_FILTER_KEYS>>;
}>
> = {
[CONST.SEARCH.DATA_TYPES.EXPENSE]: [
{
title: CONST.SEARCH.FILTER_SECTION_TITLES.GENERAL,
filters: [CONST.SEARCH.SYNTAX_FILTER_KEYS.KEYWORD, CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM, CONST.SEARCH.SYNTAX_FILTER_KEYS.TO, CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID],
},
{
title: CONST.SEARCH.FILTER_SECTION_TITLES.EXPENSES,
filters: [
CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPENSE_TYPE,
CONST.SEARCH.SYNTAX_FILTER_KEYS.MERCHANT,
CONST.SEARCH.SYNTAX_FILTER_KEYS.DATE,
CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT,
CONST.SEARCH.SYNTAX_FILTER_KEYS.CURRENCY,
CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY,
CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG,
CONST.SEARCH.SYNTAX_FILTER_KEYS.DESCRIPTION,
CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE,
CONST.SEARCH.SYNTAX_FILTER_KEYS.REIMBURSABLE,
CONST.SEARCH.SYNTAX_FILTER_KEYS.BILLABLE,
],
},
{
title: CONST.SEARCH.FILTER_SECTION_TITLES.CARD,
filters: [CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID, CONST.SEARCH.SYNTAX_FILTER_KEYS.POSTED],
},
{
title: CONST.SEARCH.FILTER_SECTION_TITLES.REPORTS,
filters: [
CONST.SEARCH.SYNTAX_FILTER_KEYS.REPORT_ID,
CONST.SEARCH.SYNTAX_FILTER_KEYS.SUBMITTED,
CONST.SEARCH.SYNTAX_FILTER_KEYS.APPROVED,
CONST.SEARCH.SYNTAX_FILTER_KEYS.PAID,
CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPORTED,
],
},
],
[CONST.SEARCH.DATA_TYPES.INVOICE]: [
{
title: CONST.SEARCH.FILTER_SECTION_TITLES.GENERAL,
filters: [CONST.SEARCH.SYNTAX_FILTER_KEYS.KEYWORD, CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM, CONST.SEARCH.SYNTAX_FILTER_KEYS.TO, CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID],
},
{
title: CONST.SEARCH.FILTER_SECTION_TITLES.EXPENSES,
filters: [
CONST.SEARCH.SYNTAX_FILTER_KEYS.MERCHANT,
CONST.SEARCH.SYNTAX_FILTER_KEYS.DATE,
CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT,
CONST.SEARCH.SYNTAX_FILTER_KEYS.CURRENCY,
CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY,
CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG,
CONST.SEARCH.SYNTAX_FILTER_KEYS.DESCRIPTION,
CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE,
],
},
{
title: CONST.SEARCH.FILTER_SECTION_TITLES.CARD,
filters: [CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID, CONST.SEARCH.SYNTAX_FILTER_KEYS.POSTED],
},
{
title: CONST.SEARCH.FILTER_SECTION_TITLES.REPORTS,
filters: [
CONST.SEARCH.SYNTAX_FILTER_KEYS.REPORT_ID,
CONST.SEARCH.SYNTAX_FILTER_KEYS.SUBMITTED,
CONST.SEARCH.SYNTAX_FILTER_KEYS.APPROVED,
CONST.SEARCH.SYNTAX_FILTER_KEYS.PAID,
CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPORTED,
],
},
],
[CONST.SEARCH.DATA_TYPES.TRIP]: [
{
title: CONST.SEARCH.FILTER_SECTION_TITLES.GENERAL,
filters: [CONST.SEARCH.SYNTAX_FILTER_KEYS.KEYWORD, CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM, CONST.SEARCH.SYNTAX_FILTER_KEYS.TO, CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID],
},
{
title: CONST.SEARCH.FILTER_SECTION_TITLES.EXPENSES,
filters: [
CONST.SEARCH.SYNTAX_FILTER_KEYS.MERCHANT,
CONST.SEARCH.SYNTAX_FILTER_KEYS.DATE,
CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT,
CONST.SEARCH.SYNTAX_FILTER_KEYS.CURRENCY,
CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY,
CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG,
CONST.SEARCH.SYNTAX_FILTER_KEYS.DESCRIPTION,
CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE,
],
},
{
title: CONST.SEARCH.FILTER_SECTION_TITLES.CARD,
filters: [CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID, CONST.SEARCH.SYNTAX_FILTER_KEYS.POSTED],
},
{
title: CONST.SEARCH.FILTER_SECTION_TITLES.REPORTS,
filters: [
CONST.SEARCH.SYNTAX_FILTER_KEYS.REPORT_ID,
CONST.SEARCH.SYNTAX_FILTER_KEYS.SUBMITTED,
CONST.SEARCH.SYNTAX_FILTER_KEYS.APPROVED,
CONST.SEARCH.SYNTAX_FILTER_KEYS.PAID,
CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPORTED,
],
},
],
[CONST.SEARCH.DATA_TYPES.CHAT]: [
{
title: CONST.SEARCH.FILTER_SECTION_TITLES.GENERAL,
filters: [
CONST.SEARCH.SYNTAX_FILTER_KEYS.KEYWORD,
CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM,
CONST.SEARCH.SYNTAX_FILTER_KEYS.TO,
CONST.SEARCH.SYNTAX_FILTER_KEYS.IN,
CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID,
],
},
{
title: CONST.SEARCH.FILTER_SECTION_TITLES.TIME,
filters: [CONST.SEARCH.SYNTAX_FILTER_KEYS.DATE],
},
],
}; After we update the structure of
const filters = typeFiltersKeys[currentType]
.map((section) => {
return section.filters // update this line
.map((key) => {
// 'feed' filter row does not appear in advanced filters, it is created using selected cards
if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.FEED) {
return;
}
const onPress = singleExecution(waitForNavigate(() => Navigation.navigate(baseFilterConfig[key].route)));
let filterTitle;
if (
key === CONST.SEARCH.SYNTAX_FILTER_KEYS.DATE ||
key === CONST.SEARCH.SYNTAX_FILTER_KEYS.SUBMITTED ||
key === CONST.SEARCH.SYNTAX_FILTER_KEYS.APPROVED ||
key === CONST.SEARCH.SYNTAX_FILTER_KEYS.PAID ||
key === CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPORTED ||
key === CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT ||
key === CONST.SEARCH.SYNTAX_FILTER_KEYS.CURRENCY ||
key === CONST.SEARCH.SYNTAX_FILTER_KEYS.DESCRIPTION ||
key === CONST.SEARCH.SYNTAX_FILTER_KEYS.MERCHANT ||
key === CONST.SEARCH.SYNTAX_FILTER_KEYS.REPORT_ID ||
key === CONST.SEARCH.SYNTAX_FILTER_KEYS.KEYWORD
) {
filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, key, translate);
} else if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY) {
if (!shouldDisplayCategoryFilter) {
return;
}
filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, key, translate);
} else if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG) {
if (!shouldDisplayTagFilter) {
return;
}
filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, key, translate);
} else if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID) {
if (!shouldDisplayCardFilter) {
return;
}
filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, allCards, translate);
} else if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.POSTED) {
if (!shouldDisplayCardFilter) {
return;
}
filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, key, translate);
} else if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE) {
if (!shouldDisplayTaxFilter) {
return;
}
filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, taxRates);
} else if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPENSE_TYPE) {
filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, translate);
} else if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM || key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO) {
filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters[key] ?? [], personalDetails);
} else if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.IN) {
filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, translate, reports);
} else if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.REIMBURSABLE || key === CONST.SEARCH.SYNTAX_FILTER_KEYS.BILLABLE) {
filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, key, translate);
} else if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID) {
if (!shouldDisplayWorkspaceFilter) {
return;
}
const workspacesData = workspaces.flatMap((value) => value.data);
filterTitle = baseFilterConfig[key].getTitle(searchAdvancedFilters, workspacesData);
}
return {
key,
title: filterTitle,
description: translate(baseFilterConfig[key].description),
onPress,
sectionTitle: section.title, // add this
};
})
.filter((filter): filter is NonNullable<typeof filter> => !!filter);
})
.filter((section) => !!section.length); and render the text title:
{filters.map((section, index) => {
return (
// eslint-disable-next-line react/no-array-index-key
<View key={`${section.at(0)?.key}-${index}`}>
{index !== 0 && (
<SpacerView
shouldShow
style={[styles.reportHorizontalRule]}
/>
)}
<Text style={[styles.headerText, styles.ph5, styles.mb1, index !== 0 ? styles.mt5 : undefined]}>{section.at(0)?.sectionTitle}</Text> // add this
{section.map((item) => (
<MenuItemWithTopDescription
key={item.description}
title={item.title}
titleStyle={styles.flex1}
description={item.description}
shouldShowRightIcon
onPress={item.onPress}
/>
))}
</View>
);
})} With this change, we finish update requirement item 1, item 2, and item 4. For item 3, we simple change the text in transaltion file. Lines 5247 to 5250 in d56c882
submitted: 'Submitted date',
approved: 'Approved date',
paid: 'Paid date',
exported: 'Exported date', Test branchScreen.Recording.2025-05-02.at.15.18.29.movWhat specific scenarios should we cover in automated tests to prevent reintroducing this issue in the future?None What alternative solutions did you explore? (Optional)Reminder: Please use plain English, be brief and avoid jargon. Feel free to use images, charts or pseudo-code if necessary. Do not post large multi-line diffs or write walls of text. Do not create PRs unless you have been hired for this job. |
@Ollyws Uh oh! This issue is overdue by 2 days. Don't forget to update your issues! |
@Ollyws did you get a chance to look at the proposals here? |
@nyomanjyotisa's proposal LGTM. |
Current assignee @luacmartins is eligible for the choreEngineerContributorManagement assigner, not assigning anyone new. |
📣 @nyomanjyotisa 🎉 An offer has been automatically sent to your Upwork account for the Contributor role 🎉 Thanks for contributing to the Expensify app! Offer link |
Filters
(and other changes to improve UX)Filters
(and other changes to improve UX)
We're fielding a decent amount of questions across sales/support around how to effectively use
Filters
, and just as importantly, understand what filter applies to which data type. Accordingly, we're going to apply the following updates toFilters
in order to minimize customer confusion and improve overall UX:General
Expenses
Reports
Separate each section with a line bar
Rename date filters to make it more clear that they're date filters
Submitted
toSubmitted date
Approved
toApproved date
Paid
toPaid date
Exported
toExported date
Posted
toPosted date
General
section is:Type
(future consideration; not being changed in this issue)Group by
(future consideration; not being changed in this issue)Status
(future consideration; not being changed in this issue)From
To
Has keywords
Workspace
Expenses
section is:Expense type
Merchant
Date
Total
Currency
Category
Tag
Description
Card
Posted date
Tax rate
Reimbursable
Billable
Reports
section is:Report ID
Submitted date
Approved date
Paid date
Exported date
All in all, the changes will look like this.
Upwork Automation - Do Not Edit
Issue Owner
Current Issue Owner: @The text was updated successfully, but these errors were encountered: