Skip to content

Add natural language filter entry point to Spend page - #94217

Draft
marcaaron wants to merge 38 commits into
mainfrom
marcaaron-nl-filters-ui
Draft

Add natural language filter entry point to Spend page#94217
marcaaron wants to merge 38 commits into
mainfrom
marcaaron-nl-filters-ui

Conversation

@marcaaron

@marcaaron marcaaron commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

Adds a "Describe your search" entry point to the Spend page Advanced Filters. Users type a plain-English description of what they want to see ("meals over $50 last month", "card expenses Q2"), and the backend (GPT-4.1 via Web-Expensify/Web-Expensify#53678) parses it into a structured query and returns a search URL. The App navigates directly to the search results.

New screen at ROUTES.SEARCH_ADVANCED_FILTERS_DESCRIBE (/search/filters/describe). Entry point added at the top of the Advanced Filters list (and the filters dropdown on wide screens) with a sparkle icon.

Holding on:

Fixed Issues

$ https://github.com/Expensify/Expensify/issues/647828
PROPOSAL:

Tests

Prerequisites: Sign in to dev.new.expensify.com:8082. Have a workspace with at least one custom category (e.g. "Meals and Entertainment").

Entry point

  1. Navigate to the Spend page.
  2. Click Filters.
  3. Verify "Describe your search" appears at the top of the left column with a sparkle icon.
  4. Click it.
  5. Verify the screen opens at /search/filters/describe with a text input and "Create filters" button.
  6. Verify "Create filters" is disabled with an empty input.

Semantic category match

  1. Enter meals over $50 last month.
  2. Click "Create filters".
  3. Verify the search results page loads with category:"Meals and Entertainment", amount>5000, and last-month date filters in the query.

Keyword fallback (no matching category)

  1. Enter coffee over $20.
  2. Click "Create filters".
  3. Verify the query uses keyword:coffee amount>2000 — not category:coffee.

Error state

  1. Enter what are my biggest expense categories.
  2. Click "Create filters".
  3. Verify an error message appears and the URL stays on /search/filters/describe.

Back navigation

  1. From the Spend page, open Filters and tap "Describe your search".
  2. Press the back button.
  3. Verify you return to the Spend page, not past it.

Offline tests

N/A — the feature requires a live API call to OpenAI. The "Create filters" button will return an error when offline; no special offline handling is needed.

QA Steps

Prerequisites: Have an active workspace with at least one custom category (e.g. "Meals and Entertainment"). Have expenses in that workspace across different months and amounts.

Desktop: describe mode opens inline in the popup

  1. Navigate to the Spend page.
  2. Click Filters.
  3. Verify "Describe your search" appears at the top of the left column with a sparkle icon.
  4. Hover over "Describe your search".
  5. Verify the right panel switches to show a multiline text input and a "Create filters" button — no new page opens.
  6. Verify the text input fills the available height of the panel.
  7. Verify "Create filters" is disabled with the input empty.
  8. Hover over a regular filter item (e.g. "Type").
  9. Verify the right panel switches back to that filter's content.

Desktop: semantic query applies filters and closes the popup

  1. Click Filters, then hover over "Describe your search".
  2. Enter meals over $50 last month.
  3. Click Create filters.
  4. Verify the popup closes and the Spend page reloads with filter chips for category, amount, and date.

Desktop: unrecognized query shows an error inline

  1. Click Filters, then hover over "Describe your search".
  2. Enter what are my biggest expense categories.
  3. Click Create filters.
  4. Verify an error message appears in the panel and the popup stays open.

Mobile: describe mode opens a full-screen page

  1. Navigate to the Spend page and open Advanced Filters.
  2. Verify "Describe your search" appears at the top of the filter list with a sparkle icon.
  3. Tap it.
  4. Verify a full-screen page opens with a multiline text input and "Create filters" button.
  5. Verify the input is auto-focused.
  6. Press Back.
  7. Verify you return to Advanced Filters, not past it.

Mobile: semantic query navigates to results

  1. Open Advanced Filters and tap "Describe your search".
  2. Enter card expenses last 30 days.
  3. Tap Create filters.
  4. Verify the Spend page loads with expenseType:card and a 30-day date range as filter chips.

Network error resets the loading state (desktop and mobile)

  1. Open "Describe your search" and enter any query (e.g. meals last month).
  2. Disconnect from the network.
  3. Click or tap Create filters.
  4. Verify the button shows a loading spinner briefly, then returns to its normal enabled state.
  5. Verify an error message appears — the button must not stay frozen in a loading state.
  6. Reconnect and verify the button is usable again.

Desktop: popup closes before navigating on success

  1. Click Filters, hover over "Describe your search", and enter a valid query.
  2. Click Create filters.
  3. Verify the filters popup closes completely before the Spend page updates with the new filter chips — the popup must not linger over the results.

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalization), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • If any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))
  • If new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

Screenshots/Videos

Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari

Adds PARSE_EXPENSE_FILTERS to READ_COMMANDS, ParseExpenseFiltersParams type,
and the ReadCommandParameters mapping for the NL spend filter feature.
Wires the SearchNLFilterPage into the navigation layer:
- SCREENS.SEARCH.ADVANCED_FILTERS_DESCRIBE_RHP screen constant
- ROUTES.SEARCH_ADVANCED_FILTERS_DESCRIBE static route ('search/filters/describe')
- SearchAdvancedFiltersParamList type entry
- Linking config entry
- SearchAdvancedFiltersModalStackNavigator registration
- Stub SearchNLFilterPage component (full implementation in next phase)
Replaces stub with full implementation: TextInput for query input,
loading state on submit button, success navigation to search results,
and inline error display from ParseExpenseFilters API response.
Adds translation keys for en/es.
Add "Describe your search" as the first item in the filter panel with a
Sparkles icon, separated from the rest of the sections by a horizontal
rule. Tapping it navigates to the SearchNLFilterPage.
ParseExpenseFilters returns dynamic data (success/failure with payload)
rather than Onyx-persisted state, so it belongs in SIDE_EFFECT_REQUEST_COMMANDS
and uses makeRequestWithSideEffects. Adds the action function and
ParseExpenseFiltersResult type; extends Response.ts with the API response fields.
Main replaced AdvancedSearchFilters.tsx with SearchAdvancedFiltersPage + a
unified ADVANCED_FILTERS_CONTENT_RHP route (filterKey param) for all filters.
Resolved conflicts by:
- Keeping both ADVANCED_FILTERS_DESCRIBE_RHP and ADVANCED_FILTERS_CONTENT_RHP
  in types.ts and config.ts
- Migrating the "Describe your search" MenuItem from the deleted
  AdvancedSearchFilters.tsx to SearchAdvancedFiltersBase.tsx
- Including all new Search.ts exports from main alongside parseExpenseFilters
@OSBotify

This comment has been minimized.

@codecov

codecov Bot commented Jun 22, 2026

Copy link
Copy Markdown

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.

Files with missing lines Coverage Δ
src/ROUTES.ts 23.65% <ø> (+0.25%) ⬆️
src/SCREENS.ts 100.00% <ø> (ø)
src/libs/API/types.ts 100.00% <ø> (ø)
src/libs/Navigation/linkingConfig/config.ts 66.66% <ø> (ø)
src/styles/index.ts 63.00% <ø> (ø)
...h/SearchPageHeader/SearchAdvancedFiltersButton.tsx 68.00% <0.00%> (ø)
...gation/AppNavigator/ModalStackNavigators/index.tsx 6.37% <0.00%> (-0.02%) ⬇️
...hAdvancedFiltersPage/SearchAdvancedFiltersBase.tsx 0.00% <0.00%> (ø)
src/libs/actions/Search.ts 47.50% <0.00%> (-0.46%) ⬇️
src/pages/Search/SearchNLFilterPage.tsx 0.00% <0.00%> (ø)
... and 2 more
... and 457 files with indirect coverage changes

@marcaaron marcaaron changed the title [WIP] Add natural language filter entry point to Spend page [HOLD] Add natural language filter entry point to Spend page Jun 23, 2026
@OSBotify

This comment has been minimized.

# Conflicts:
#	android/app/build.gradle
#	android/app/src/main/java/com/expensify/chat/MainApplication.kt
#	config/eslint/eslint.seatbelt.tsv
#	ios/AppDelegate.swift
#	ios/NewExpensify/Info.plist
#	ios/NotificationServiceExtension/Info.plist
#	ios/ShareViewController/Info.plist
#	package-lock.json
#	package.json
#	src/CONST/index.ts
#	src/ROUTES.ts
#	src/SCREENS.ts
#	src/components/MoneyReportHeaderActions/MoneyReportHeaderSecondaryActions.tsx
#	src/components/MoneyReportHeaderKYCDropdown.tsx
#	src/components/MoneyRequestConfirmationList/sections/DateField.tsx
#	src/components/MoneyRequestHeaderSecondaryActions.tsx
#	src/components/ReportActionItem/MoneyRequestView.tsx
#	src/components/Search/FilterDropdowns/SearchAdvancedFiltersPopup/index.tsx
#	src/components/Search/hooks/useSearchListViewState.ts
#	src/components/Search/index.tsx
#	src/components/Tables/WorkspaceExpensifyCardsTable/WorkspaceExpensifyCardsTableRow.tsx
#	src/hooks/useDeleteTransactions.ts
#	src/hooks/useExpenseActions.ts
#	src/hooks/useLifecycleActions.tsx
#	src/hooks/useReportSubmitToPopover.tsx
#	src/hooks/useSearchBulkActions.ts
#	src/hooks/useSelectedTransactionsActions.ts
#	src/hooks/useUnreadMarker.ts
#	src/languages/de.ts
#	src/languages/en.ts
#	src/languages/es.ts
#	src/languages/fr.ts
#	src/languages/it.ts
#	src/languages/ja.ts
#	src/languages/nl.ts
#	src/languages/pl.ts
#	src/languages/pt-BR.ts
#	src/languages/zh-hans.ts
#	src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
#	src/libs/Navigation/linkingConfig/OldRoutes.ts
#	src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts
#	src/libs/Navigation/linkingConfig/config.ts
#	src/libs/PolicyUtils.ts
#	src/libs/ReportUtils.ts
#	src/libs/SearchUIUtils.ts
#	src/libs/actions/IOU/MoneyRequest.ts
#	src/libs/actions/IOU/ReportWorkflow.ts
#	src/libs/actions/SplitExpenses.ts
#	src/pages/home/ForYouSection/useReviewFlaggedExpenses.ts
#	src/pages/home/RecentlyAddedSection/useRecentlyAddedData.ts
#	src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx
#	src/pages/iou/request/step/IOURequestStepAmount.tsx
#	src/pages/iou/request/step/IOURequestStepConfirmation.tsx
#	src/pages/iou/request/step/IOURequestStepDate.tsx
#	src/pages/workspace/WorkspaceInitialPage.tsx
#	src/pages/workspace/WorkspaceMembersPage.tsx
#	src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx
#	src/pages/workspace/rules/AgentRulesSection.tsx
#	src/pages/workspace/tags/DynamicWorkspaceEditTagsPage.tsx
#	src/pages/workspace/tags/WorkspaceTagsPage.tsx
#	tests/actions/IOUTest/SplitTest.ts
#	tests/navigation/getMatchingNewRouteTest.ts
#	tests/ui/ForYouSectionTest.tsx
#	tests/ui/MoneyRequestReportPreview.test.tsx
#	tests/ui/ReportActionItemTest.tsx
#	tests/unit/PolicyUtilsTest.ts
#	tests/unit/hooks/useReviewFlaggedExpenses.test.ts
@OSBotify

This comment has been minimized.

@marcaaron marcaaron changed the title [HOLD] Add natural language filter entry point to Spend page Add natural language filter entry point to Spend page Jul 2, 2026
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

🚧 marcaaron has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

This comment has been minimized.

@marcaaron
marcaaron requested a review from JmillsExpensify July 2, 2026 22:28
@marcaaron

Copy link
Copy Markdown
Contributor Author

@JmillsExpensify pretty rough version of this, I think, but working. One thing that is not 100% working is resolving the workspace. For example, I ask for all anthropic receipts that I can see. And the AI locked it to my default workspace, so I need to figure out why. But I think it works pretty well for most other cases.

@JmillsExpensify

Copy link
Copy Markdown
Contributor

Nice, I just took it for a spin. Some things I noticed:

  • We seem to be injecting in my default workspace for every query. Let's also filter on a specific workspace if that's specified in the query
CleanShot 2026-07-07 at 13 29 14@2x
  • I enter this query: "All my expenses paid last month". For this one, we missed filtering on "from:me" but it otherwise worked!
CleanShot 2026-07-07 at 13 49 25@2x
  • For this query: "Show me my draft reports", this one failed with the form error to try and rephrase. Seems like we need to find a way to extract words that don't directly relate to the syntax? I did update the query to "All my draft reports" and that worked (but it also failed to filter on from:me).

That's it from the first run!

@marcaaron
marcaaron requested review from a team as code owners August 11, 2026 20:52
@melvin-bot
melvin-bot Bot requested review from joekaufmanexpensify and truph01 and removed request for a team August 11, 2026 20:52
@melvin-bot

melvin-bot Bot commented Aug 11, 2026

Copy link
Copy Markdown

@truph01 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]

@marcaaron
marcaaron marked this pull request as draft August 11, 2026 20:52
Comment thread src/pages/Search/SearchNLFilterPage.tsx
Comment thread src/components/Search/FilterComponents/AdvancedFilters/SearchNLFilterContent.tsx Outdated
Comment thread src/libs/actions/Search.ts Outdated
Comment thread src/components/Search/FilterDropdowns/SearchAdvancedFiltersPopup/index.tsx Outdated
Comment thread src/pages/Search/SearchNLFilterPage.tsx

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 414135e2b3

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/components/Search/FilterComponents/AdvancedFilters/SearchNLFilterContent.tsx Outdated
@quinthar quinthar removed the #migrate label Aug 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🚧 marcaaron has triggered a test Expensify/App build. You can view the workflow run here.

- Pin left column to its designed width so the right content panel isn't squeezed
- Describe button now activates on hover (matching all other filter items) instead of requiring a click
- Replace single-line autoGrowHeight input with a multiline textarea that fills the available panel height
@Expensify Expensify deleted a comment from github-actions Bot Aug 15, 2026
@marcaaron

marcaaron commented Aug 15, 2026

Copy link
Copy Markdown
Contributor Author

@shawnborton @dubielzyk-expensify @dannymcclain are you guys thinking something like this:

Screenshot 2026-08-14 at 3 46 58 PM

Or more of a start small and auto-grow to a max situation?

This is the only other mock that I have, so wasn't 100% sure.

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants