Skip to content

[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

Open
JmillsExpensify opened this issue May 1, 2025 · 13 comments
Assignees
Labels
External Added to denote the issue can be worked on by a contributor NewFeature Something to build that is a new item. Reviewing Has a PR in review Weekly KSv2

Comments

@JmillsExpensify
Copy link

JmillsExpensify commented May 1, 2025

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 to Filters in order to minimize customer confusion and improve overall UX:

  1. Add section headers to the three main types of fitters:
  • General
  • Expenses
  • Reports
  1. Separate each section with a line bar

  2. Rename date filters to make it more clear that they're date filters

  • Submitted to Submitted date
  • Approved to Approved date
  • Paid to Paid date
  • Exported to Exported date
  • Posted to Posted date
  1. Re-arrange the order of filters, anticipating upcoming search functionality
  • The order of the 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
  • The order of the Expenses section is:
    • Expense type
    • Merchant
    • Date
    • Total
    • Currency
    • Category
    • Tag
    • Description
    • Card
    • Posted date
    • Tax rate
    • Reimbursable
    • Billable
  • The order of the Reports section is:
    • Report ID
    • Submitted date
    • Approved date
    • Paid date
    • Exported date

All in all, the changes will look like this.

Image

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~021917916081372952479
  • Upwork Job ID: 1917916081372952479
  • Last Price Increase: 2025-05-01
  • Automatic offers:
    • nyomanjyotisa | Contributor | 107185962
Issue OwnerCurrent Issue Owner: @
@JmillsExpensify JmillsExpensify added Daily KSv2 External Added to denote the issue can be worked on by a contributor NewFeature Something to build that is a new item. labels May 1, 2025
@JmillsExpensify JmillsExpensify self-assigned this May 1, 2025
@melvin-bot melvin-bot bot changed the title Add grouping to RHP Filters (and other changes to improve UX) [$250] Add grouping to RHP Filters (and other changes to improve UX) May 1, 2025
Copy link

melvin-bot bot commented May 1, 2025

Job added to Upwork: https://www.upwork.com/jobs/~021917916081372952479

@melvin-bot melvin-bot bot added the Help Wanted Apply this label when an issue is open to proposals by contributors label May 1, 2025
Copy link

melvin-bot bot commented May 1, 2025

Current assignee @JmillsExpensify is eligible for the NewFeature assigner, not assigning anyone new.

Copy link

melvin-bot bot commented May 1, 2025

Triggered auto assignment to Contributor-plus team member for initial proposal review - @Ollyws (External)

@melvin-bot melvin-bot bot added Weekly KSv2 and removed Daily KSv2 Weekly KSv2 labels May 1, 2025
@JmillsExpensify JmillsExpensify added the Daily KSv2 label May 1, 2025
@Tony-MK
Copy link
Contributor

Tony-MK commented May 1, 2025

🚨 Edited by proposal-police: This proposal was edited at 2025-05-01 13:13:42 UTC.

Proposal

Please 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 View and Text components and pass the item in the menutItems props and the group header as the title.

Example of Reports section.

<View style={[sectionStyle, styles.pb4, styles.mh3]}>
 <Text style={sectionTitle}>{translate('Reports')}</Text>
  {section.map((item) => {
      return (
          <MenuItemWithTopDescription
           ....
  }

Unlike the InitialSettingsPage, we should mutate sectionTitle for the AdvancedSearchFilters so the title can be bold by setting fontWeight: FontUtils.fontWeight.bold.

Then, we will split the menu items into their respective groups and order them in typeFiltersKeys according to #61235 (comment).

Finally, rename date filter names in CONST.SEARCH.SYNTAX_FILTER_KEYS to include date and their translations.

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

Copy link
Contributor

github-actions bot commented May 1, 2025

⚠️ @Tony-MK Thanks for your proposal. Please update it to follow the proposal template, as proposals are only reviewed if they follow that format (note the mandatory sections).

@nyomanjyotisa
Copy link
Contributor

Proposal

Please 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:

    [CONST.SEARCH.DATA_TYPES.EXPENSE]: [
        [
            CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM,
            CONST.SEARCH.SYNTAX_FILTER_KEYS.TO,
            CONST.SEARCH.SYNTAX_FILTER_KEYS.KEYWORD,
            CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID
        ],
        [
            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.CARD_ID,
            CONST.SEARCH.SYNTAX_FILTER_KEYS.POSTED,
            CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE,
            CONST.SEARCH.SYNTAX_FILTER_KEYS.REIMBURSABLE,
            CONST.SEARCH.SYNTAX_FILTER_KEYS.BILLABLE,
        ],
        [
            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,
        ],
    ],

Create new const sections , and add the translation key for each section and its items. Following the same approach on WorkspaceMoreFeaturesPage here

    const sections = [
        {
            titleTranslationKey: 'common.general',
            items: filters[0] || [],
        },
        {
            titleTranslationKey: 'common.expenses',
            items: filters[1] || [],
        },
        {
            titleTranslationKey: 'common.reports',
            items: filters[2] || [],
        },
    ];

And change this code to the following to use the new sections const

                  {sections.map((section, index) => {
                        if (section.items.length === 0) {
                            return;
                        }

                        return (
                            // eslint-disable-next-line react/no-array-index-key
                            <View key={`${section.items.at(0)?.key}-${index}`}>
                                {index !== 0 && (
                                    <SpacerView
                                        shouldShow
                                        style={[styles.reportHorizontalRule]}
                                    />
                                )}
                                <Text style={[styles.headerText, styles.reportHorizontalRule, index === 0 ? null : styles.mt4, styles.mb2]}>
                                    {translate(section.titleTranslationKey)}
                                </Text>
                                {section.items.map((item) => {
                                    return (
                                        <MenuItemWithTopDescription
                                            key={item.description}
                                            title={item.title}
                                            titleStyle={styles.flex1}
                                            description={item.description}
                                            shouldShowRightIcon
                                            onPress={item.onPress}
                                        />
                                    );
                                })}
                            </View>
                        );
                    })}

Might need to adjust some styling

Then update the following translation to add "date", update both on en and es files

submitted: 'submitted',
approved: 'approved',
paid: 'paid',
exported: 'exported',

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

@luacmartins luacmartins self-assigned this May 1, 2025
@huult
Copy link
Contributor

huult commented May 2, 2025

Proposal

Please 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 typeFiltersKeys, we need to update the filters function to use the new structure. The updated function will look like this:

const filters = typeFiltersKeys[currentType]

 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) => {

  {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.

App/src/languages/en.ts

Lines 5247 to 5250 in d56c882

submitted: 'Submitted',
approved: 'Approved',
paid: 'Paid',
exported: 'Exported',

    submitted: 'Submitted date',
    approved: 'Approved date',
    paid: 'Paid date',
    exported: 'Exported date',

Test branch

Screen.Recording.2025-05-02.at.15.18.29.mov

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)

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.

Copy link

melvin-bot bot commented May 5, 2025

@Ollyws Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!

@melvin-bot melvin-bot bot added the Overdue label May 5, 2025
@luacmartins
Copy link
Contributor

@Ollyws did you get a chance to look at the proposals here?

@Ollyws
Copy link
Contributor

Ollyws commented May 5, 2025

@nyomanjyotisa's proposal LGTM.
🎀👀🎀 C+ reviewed

@melvin-bot melvin-bot bot removed the Overdue label May 5, 2025
Copy link

melvin-bot bot commented May 5, 2025

Current assignee @luacmartins is eligible for the choreEngineerContributorManagement assigner, not assigning anyone new.

@melvin-bot melvin-bot bot removed the Help Wanted Apply this label when an issue is open to proposals by contributors label May 5, 2025
Copy link

melvin-bot bot commented May 5, 2025

📣 @nyomanjyotisa 🎉 An offer has been automatically sent to your Upwork account for the Contributor role 🎉 Thanks for contributing to the Expensify app!

Offer link
Upwork job
Please accept the offer and leave a comment on the Github issue letting us know when we can expect a PR to be ready for review 🧑‍💻
Keep in mind: Code of Conduct | Contributing 📖

@melvin-bot melvin-bot bot added Reviewing Has a PR in review Weekly KSv2 and removed Daily KSv2 labels May 6, 2025
@nyomanjyotisa
Copy link
Contributor

The PR is ready
cc @Ollyws

@trjExpensify trjExpensify changed the title [$250] Add grouping to RHP Filters (and other changes to improve UX) [Suggested Search v1] [$250] Add grouping to RHP Filters (and other changes to improve UX) May 6, 2025
@trjExpensify trjExpensify moved this to Second Cohort - CRITICAL in [#whatsnext] #migrate May 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
External Added to denote the issue can be worked on by a contributor NewFeature Something to build that is a new item. Reviewing Has a PR in review Weekly KSv2
Projects
Status: Second Cohort - CRITICAL
Development

No branches or pull requests

6 participants