feat/80393: FE - Add "Top merchants" suggested search with group by filter#80425
feat/80393: FE - Add "Top merchants" suggested search with group by filter#80425btkcodedev wants to merge 19 commits intoExpensify:mainfrom
Conversation
|
Hey, I noticed you changed If you want to automatically generate translations for other locales, an Expensify employee will have to:
Alternatively, if you are an external contributor, you can run the translation script locally with your own OpenAI API key. To learn more, try running: npx ts-node ./scripts/generateTranslations.ts --helpTypically, you'd want to translate only what you changed by running |
|
@shubham1206agra Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 182be118c2
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| searchQuery: buildQueryStringFromFilterFormValues( | ||
| { | ||
| type: CONST.SEARCH.DATA_TYPES.EXPENSE, | ||
| groupBy: CONST.SEARCH.GROUP_BY.MERCHANT, | ||
| dateOn: CONST.SEARCH.DATE_PRESETS.LAST_MONTH, |
There was a problem hiding this comment.
Add merchant grouping handling in section builders
The new Top Merchants suggestion sets groupBy: MERCHANT here, but getSections() / getSortedSections() only handle FROM/CARD/WITHDRAWAL_ID. For groupBy=merchant the switch falls through to getTransactionsSections, while getListItem() still picks TransactionGroupListItem because groupBy is truthy, so each item lacks transactions and groupItem.transactions becomes undefined at render time. That means opening the Top merchants suggestion or using group-by:merchant will either show ungrouped results with grouped columns or crash the grouped list item. Add a merchant grouping path (and sorting) similar to the existing group-by cases.
Useful? React with 👍 / 👎.
Codecov Report❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.
|
|
Feature tested and verified after resolving automated comments: Screen.Recording.2026-01-24.at.11.14.10.PM.mov |
tests/unit/SearchUIUtilsTest.ts
Outdated
There was a problem hiding this comment.
@btkcodedev Please update tests inside tests/unit/Search/SearchUIUtilsTest
There was a problem hiding this comment.
@btkcodedev It's still not fixed. Please look at other files for correct tests.
There was a problem hiding this comment.
GH actions passed. Reviewer checklist pending
There was a problem hiding this comment.
@btkcodedev Again, this is not correct. Please see similar files for correct tests. You cannot suppress all ESLint errors without comments on why. And the reason must be valid too.
There was a problem hiding this comment.
Corrected that, but still has ESLint suppressions for unavoidable any types in Jest mocks
src/languages/es.ts
Outdated
| unapprovedCard: 'Tarjeta no aprobada', | ||
| reconciliation: 'Conciliación', | ||
| topSpenders: 'Mayores gastadores', | ||
| topMerchants: 'Principales comerciantes', |
There was a problem hiding this comment.
Yes, i've used claude for it, all translations looks good and professional
There was a problem hiding this comment.
Read the contributor doc and checklist for how to confirm translation.
There was a problem hiding this comment.
I verified any copy/text shown in the product is localized by adding it to src/languages/* files
There was a problem hiding this comment.
Read
If any non-english text was added/modified, I used JaimeGPT to get English > Spanish translation. I then posted it in #expensify-open-source and it was approved by an internal Expensify engineer.
There was a problem hiding this comment.
@shubham1206agra shouldn't the generateTranslations script be run on this PR. Only Spanish needs internal approval?
There was a problem hiding this comment.
@trjExpensify, it should have triggered. Not sure how to do this manually.
src/components/Search/index.tsx
Outdated
| // Trigger searches for all groups to load their transactions (only for merchant grouping) | ||
| if (validGroupBy === CONST.SEARCH.GROUP_BY.MERCHANT) { | ||
| for (const item of filteredData as TransactionGroupListItemType[]) { | ||
| if (!item.transactionsQueryJSON || isExpenseReportType) { | ||
| continue; | ||
| } | ||
| handleSearch({ | ||
| queryJSON: item.transactionsQueryJSON, | ||
| searchKey: undefined, | ||
| offset: 0, | ||
| shouldCalculateTotals: false, | ||
| isLoading: false, | ||
| }); | ||
| } | ||
| } |
There was a problem hiding this comment.
"Select All" must first load all expenses. Otherwise, it selects only the currently expanded merchant expenses.
There was a problem hiding this comment.
@btkcodedev This bug is present for Top Spenders too. So, can you please report it so it can be handled separately? Also, remove this workaround.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ce85b06f25
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
src/libs/SearchUIUtils.ts
Outdated
| const isAuditor = policy.role === CONST.POLICY.ROLE.AUDITOR; | ||
| const isEligibleForTopSpendersSuggestion = isPaidPolicy && (isAdmin || isAuditor || isApprover); | ||
| const isEligibleForTopMerchantsSuggestion = isPaidPolicy && (isAdmin || isAuditor || isApprover); |
There was a problem hiding this comment.
Broaden Top merchants suggestion visibility
The new visibility gate for Top merchants is isPaidPolicy && (isAdmin || isAuditor || isApprover), which means regular workspace members (e.g., submitters) will never see the suggested search even though they belong to a workspace and can use group-by merchant. This hides the feature for a large portion of workspace users and contradicts the “all accounts with a workspace” intent. Consider removing the role check or aligning it with the actual availability expectations so non-admin members still get the suggestion.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
That is expected I believe
There was a problem hiding this comment.
The issue suggested following the same pattern as Top Spenders, so Top Merchants was also allowed within the same scope.
Frontend Release 2: Add "Top Merchants" suggested search
Follow the same pattern as "Top Spenders" and "Top Categories"
There was a problem hiding this comment.
The doc is expecting all workspace members for Top Merchants, same as Top Categories:
Add a Top Merchants suggested search to all accounts with a workspace
There was a problem hiding this comment.
Done. Scope changed to all workspace members
neil-marcellini
left a comment
There was a problem hiding this comment.
Partial review; we need the limit filter first.
| key: CONST.SEARCH.SEARCH_KEYS.TOP_MERCHANTS, | ||
| translationPath: 'search.topMerchants', | ||
| type: CONST.SEARCH.DATA_TYPES.EXPENSE, | ||
| icon: Expensicons.Receipt, |
There was a problem hiding this comment.
There seems to be no basket icon in @assets.
~/src/components/Icon
import Bank from '@assets/images/bank.svg';
import Bell from '@assets/images/bell.svg';
import Bill from '@assets/images/bill.svg';
import boltSlash from '@assets/images/bolt-slash.svg';
import Bolt from '@assets/images/bolt.svg';
import Bookmark from '@assets/images/bookmark.svg';
import Bug from '@assets/images/bug.svg';
import Building from '@assets/images/building.svg';
import Buildings from '@assets/images/buildings.svg';
Closest fit is a building icon, or we can keep the current receipt icon.
| }, | ||
| { | ||
| sortBy: CONST.SEARCH.TABLE_COLUMNS.GROUP_TOTAL, | ||
| sortOrder: CONST.SEARCH.SORT_ORDER.DESC, |
There was a problem hiding this comment.
We need to add a limit:10 here somewhere.
|
HOLDing until this is merged |
|
Hi @btkcodedev we are going to have @TaduJR take this over because he recently worked on a related PR and has agreed to work in our timezone. We will work with you to come to an agreement on compensation for your work so far. |
|
As you say @neil-marcellini |
|
@neil-marcellini The PR for this feature is merged - #80672 |
|
@btkcodedev Please close this PR. And please request this in main issue. |


Explanation of Change
We need to add the ability to Group By Merchant from Reports > Expenses (group-by:merchant) and add a "Top Merchants" suggested search to all accounts with a workspace. This feature should follow the same pattern as "Top Spenders" and other group-by options (from, card, withdrawal-id) to maintain consistency across the application.
Fixed Issues
$ #80393
PROPOSAL: #80393 (comment)
Tests
Offline tests
Same as above
QA Steps
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectioncanBeMissingparam foruseOnyxtoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
1000000334.mp4
Android: mWeb Chrome
screen-20260124-205403.mp4
iOS: Native
Screen.Recording.2026-01-24.at.7.50.11.PM.mov
iOS: mWeb Safari
Screen.Recording.2026-01-24.at.9.01.20.PM.mov
MacOS: Chrome / Safari
Screen.Recording.2026-01-24.at.7.06.12.PM.mov