Skip to content

Add ask Concierge screen - #100552

Open
rushatgabhane wants to merge 110 commits into
Expensify:mainfrom
rushatgabhane:concierge-ask-design
Open

rushatgabhane wants to merge 110 commits into
Expensify:mainfrom
rushatgabhane:concierge-ask-design

Conversation

@rushatgabhane

@rushatgabhane rushatgabhane commented Sep 7, 2026 •

Copy link
Copy Markdown
Member

Explanation of Change

Builds the Ask Concierge screen from the Figma design in the main Concierge DM: a centered welcome empty state, a chat history toggle that expands and collapses the earlier conversation, and a Your personal AI assistant subtitle plus a New chat button in question threads. It also removes the conciergeRespondInThread beta, so Concierge answers every question from the main DM in its own thread, including a message with several attachments. This needs https://github.com/Expensify/Web-Expensify/pull/57207 deployed first.

Fixed Issues

For #94503
PROPOSAL:

Tests

  1. Open the Concierge chat from the Inbox.
  2. Verify the header reads Concierge with the subtitle Your personal AI assistant, and the composer placeholder is How much did I spend last month?.
  3. Verify the chat shows the Concierge bot illustration, Ask me anything!, and the supporting copy. On a wide screen, verify the welcome and the composer are centered together. On a small screen (mobile), verify the welcome is centered above a composer docked at the bottom.
  4. Press View chat history, verify the earlier conversation appears with Ask a new question above the composer, then press Hide chat history and verify it collapses again.
  5. Ask a question, open the thread, and verify the header shows a New chat button that returns you to the Concierge chat in its welcome state.
  6. In the Concierge chat, click + > Add attachment, pick two images, type What are these? and send. Verify one message shows both images, a thread opens off it, Concierge replies once in the thread.
  • Verify that no errors appear in the JS console

Offline tests

Same as tests. The welcome state and the history toggle both read data already in Onyx, so they render offline; only expanding history beyond the cached page needs a connection.

QA Steps

  1. Open the Concierge chat.
  2. Verify the welcome state is centered with the composer on a wide screen, the composer is docked at the bottom on mobile, and the View chat history / Hide chat history toggle works.
  3. Ask a question and verify the thread header shows New chat and returns you to the Concierge chat.
  • Verify that no errors appear in the JS console

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 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 capitalized), 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 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.
  • If the PR adds or modifies the UI:
    • I asked an AI agent to review the changes for accessibility issues and addressed its findings.
    • I tested with a screen reader (VoiceOver on macOS) and verified all new/changed elements are reachable with a logical focus order.
    • I verified all new/changed elements have meaningful accessible names and roles.
    • I verified state changes are announced (e.g. checked/unchecked, expanded/collapsed, selected).
  • 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
Screen.Recording.2026-10-02.at.15.16.02.mov

Old stuff

image
Screen.Recording.2026-09-14.at.06.10.11.mov

@melvin-bot

melvin-bot Bot commented Sep 7, 2026

Copy link
Copy Markdown

Hey, I noticed you changed src/languages/en.ts in a PR from a fork. For security reasons, translations are not generated automatically for PRs from forks.

If you want to automatically generate translations for other locales, an Expensify employee will have to:

  1. Look at the code and make sure there are no malicious changes.
  2. Run the Generate static translations GitHub workflow. If you have write access and the K2 extension, you can simply click: [this button]

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 bun ./scripts/generateTranslations.ts --help

Typically, you'd want to translate only what you changed by running npx bun ./scripts/generateTranslations.ts --compare-ref main

@codecov

codecov Bot commented Sep 7, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ Changes either increased or maintained existing code coverage, great job!

Files with missing lines Coverage Δ
src/CONST/index.ts 95.90% <ø> (ø)
...components/Search/SearchRouter/useAskConcierge.tsx 96.77% <100.00%> (ø)
src/hooks/useConciergeAskState.ts 100.00% <100.00%> (ø)
src/libs/ReportUtils.ts 88.57% <100.00%> (+0.05%) ⬆️
src/libs/actions/Report/index.ts 75.37% <100.00%> (+0.77%) ⬆️
src/libs/actions/RequestConflictUtils.ts 100.00% <100.00%> (ø)
src/libs/prepareRequestPayload/index.native.ts 67.30% <100.00%> (ø)
src/pages/inbox/HeaderView.tsx 94.47% <100.00%> (+0.23%) ⬆️
src/pages/inbox/ReportScreen.tsx 95.45% <100.00%> (+0.21%) ⬆️
src/pages/inbox/report/AskConciergeEmptyState.tsx 100.00% <100.00%> (ø)
... and 14 more
... and 10 files with indirect coverage changes

@rushatgabhane

Copy link
Copy Markdown
Member Author

@mkhutornyi next PR that you can start reviewing

@rushatgabhane

Copy link
Copy Markdown
Member Author

#100552 (comment)

fixed this. now the button will show hide chat history after sending multiple attachments

@mkhutornyi

Copy link
Copy Markdown
Contributor

Bottom border is cut off when composer expanded

Screen.Recording.2026-10-05.at.10.35.00.AM.mov

This is not fixed yet

Screen.Recording.2026-10-16.at.12.48.05.PM.mov

@mkhutornyi

Copy link
Copy Markdown
Contributor

Please check Melvin, Codex comments and below comments and address valid bugs.


1. An open Concierge task is buried behind the welcome screen

const shouldShowWelcome = hasSessionBoundary && !isHistoryExpanded && !hasSessionActivity && !hasHistoryWithoutUserMessage;

Preconditions: an open task in the Concierge chat assigned to you, its message already read, and you have messaged Concierge at least once before.

  1. Open the Concierge chat, then reload the app (or wait out the 2 hour session) and open it again.

Expected: the open task is on screen, as on main.
Actual: the "Ask me anything!" welcome screen. The task is only reachable through View chat history.

useConciergeSidePanelReportActions already guards this twice, and both guards predate this PR: showConciergeSidePanelWelcome has && !hasOpenChildTask ("the welcome state must stand down for a pinned task, or filterActions returns before it renders"), and filterActions pins the task again for the main DM ("Pin a still-open task so collapsing read history never buries something the user has to act on"). shouldShowWelcome is a second welcome gate that short-circuits in ReportActionsList before either runs, and it has no open-task term.

tests/ui/ReportActionsListTest.tsx:1195 covers this contract but stays green, because setupMainDMConciergeMocks returns [[], {status: 'loaded'}] for every key containing reportActions and ignores the selector. An empty array is truthy, so hasSessionActivity is truthy and the welcome never triggers under test.

2. "Ask Concierge" from Search posts into your previous thread

const targetReportID = !forceConcierge && isInSidePanel && sidePanelReportID ? sidePanelReportID : conciergeReportID;

  1. Open the Side Panel on the Concierge chat and send a question. The panel switches to the new thread.
  2. Open Search and use Ask Concierge to ask an unrelated question.

Expected: a new Concierge thread for the new question.
Actual: it posts into the thread from step 1, and keeps doing so for every later question until the panel is closed. The side panel has no New chat button to escape.

targetReportID falls back to sidePanelReportID, which is now the thread, so shouldRespondInThread is false. On base this was unreachable for users without the conciergeRespondInThread beta, because a Concierge thread could never land in the side panel.

3. The history toggle reads "View chat history" while messages are on screen

if (shouldRespondInThread && !willOpenThread && !isInSidePanel) {
setShowFullHistory(true);
}

Preconditions: Concierge chat with prior history, so View chat history is present.

  1. Open the Concierge chat. It shows "Ask me anything!" with View chat history above the composer.
  2. Press +, pick two images in one go, and send them. The chat expands to the full history and the button reads Hide chat history.
  3. Press Hide chat history.

Expected: the welcome screen, the way the chat looks after sending a question.
Actual: a partial list showing the attachments, under a button that reads View chat history as if the chat were collapsed.

A multi-attachment send is the only thing that posts to the DM without opening a child report, so it already counts as session activity and dismisses the welcome on its own. setShowFullHistory(true) is not what makes the attachments visible, it only hides the mismatch until the user collapses again.

@rushatgabhane

rushatgabhane commented Oct 16, 2026 •

Copy link
Copy Markdown
Member Author
  1. An open Concierge task is buried behind the welcome screen
    Preconditions: an open task in the Concierge chat assigned to you, its message already read, and you have messaged Concierge at least once before.

not a bug. after you send a messge once, we start showing the welcome screen

@rushatgabhane

Copy link
Copy Markdown
Member Author

@mkhutornyi

  1. The history toggle reads "View chat history" while messages are on screen

fixed

@rushatgabhane

Copy link
Copy Markdown
Member Author

@MelvinBot fix eslint

Co-authored-by: Rushat Gabhane <rushatgabhane@users.noreply.github.com>
@MelvinBot

Copy link
Copy Markdown
Contributor

I fixed the ESLint errors and pushed the fix in 804aff59.

  • Unsafe type assertion: the new data[key] as FileObject | undefined cast went over the seatbelt limit for RequestConflictUtils.ts. A small getQueuedFileURI helper now checks the file's shape instead of casting (RequestConflictUtils.ts:279-282).
  • file_1 / file_2 keys: the tests must use the API's key names, so I added naming-convention disable comments on those 3 lines (RequestConflictUtilsTest.ts), the same approach APITest.ts uses.

ESLint and typecheck pass locally on the changed files, and all 80 RequestConflictUtilsTest tests pass. I couldn't run the formatter here, so CI will check formatting.


view run

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants