Skip to content

Add count and spend total selectors to the Spend footer to let customers choose the data they see #99452

Description

@JmillsExpensify

Background

The footer bar on Spend shows a count of matching expenses, a currency selector, and a total spend amount. The count counts transactions, even on a type:expense-report search where the user is working with reports. The total is a single value, so there is no way to see spend broken out by reimbursable, non-reimbursable, billable, or non-billable.

Customers have asked to see this data in several ways: expense count versus report count, different kinds of totals, and amounts in different currencies. We support only the last one today. #673654 started on the count half before we had a pattern that covered all three.

This issue applies a more holistic pattern to the whole footer. We add a selector for the count and a selector for the spend total, and we move the existing currency selector onto the same pattern so all three behave identically. #673654 remains the backend prerequisite for the count aggregates and ships on its own.

Scope

  • Add a count selector to the footer, with the options Expenses and Reports.
  • Add a spend total selector to the footer, with the options Total spend, Reimbursable, Non-reimbursable, Billable, and Non-billable.
  • Move the existing currency selector onto the same selector pattern, so all three use a popover with Apply and Reset.
  • Return both the expense count and the report count on every search by default, so the count selector applies instantly with no round trip.
  • Applying the currency or total selector retriggers the search and shows a skeleton loader on the total.
  • Each selection is sticky per search type, so a choice made on type:expense-report results does not carry over to type:expense results.
  • We'll time each new aggregate query during the backend PRs and post the timings in this issue before frontend work starts.
  • There's no change to which rows the search returns, to the results table, or to the green X selected button; this issue changes the footer only.

Product and design details

640833541-a22a1cf2-7811-4bb1-b986-7bebe702e219.1.mp4

We'll use a display popover pattern, per the video above. All three selectors reuse it. On small screens they open in the same bottom-anchored sheet the currency selector uses today, so no new component is needed.

Image Image Image

Count selector

Options are Expenses and Reports. The footer renders the selection as a label and a value, e.g. Expenses: 1,204 or Reports: 87.

We show the count selector always. The default follows the search type, because that matches the unit the user is working with in that context and the unit the green X selected button count:

  • type:expense → Expenses
  • type:expense-report → Reports

Every search returns both counts, so switching between them is client-side. There is no re-trigger and no loading state on the count.

Spend total selector

Options appear only when at least one expense in the current search results matches:

  • Total spend: always listed, and the default.
  • Reimbursable: at least one reimbursable expense in the results.
  • Non-reimbursable: at least one non-reimbursable expense in the results.
  • Billable: at least one billable expense in the results.
  • Non-billable: at least one non-billable expense in the results.

Every expense is either reimbursable or non-reimbursable, so a non-empty result set always has at least two applicable options and the selector always renders. Billable and Non-billable only appear for workspaces that track billable, since expenses elsewhere have no billable value to match on.

Each option shows the summed monetary total of the matching expenses in the selected currency, not a count.

Currency selector

No change to the currency list or to how the amount is converted. The selector moves to the popover pattern above so its interaction matches the other two, and it keeps Apply and Reset.

Apply, Reset, and re-triggering the search

Each selector is its own popover with Apply and Reset. Nothing changes in the footer until the user taps Apply.

  • Apply closes the popover and commits the new selection. For count, the footer updates immediately from data the search already returned. For currency and total, we re-trigger the search.
  • Reset returns that selector to its default for the current search type: the type-based default for count, Total spend for total, and the user's existing default for currency. The user still taps Apply to commit it.
  • While a re-triggered search is in progress, the currency and total selectors are disabled, so we never have overlapping requests for the same footer. The count selector stays interactive, since it needs no request.

Selections do not change which expenses or reports match, so the results table is untouched by a footer change. Only the footer total reloads.

Loading states

While a re-triggered search is in progress:

  • The total value shows a skeleton loader.
  • The count value shows no loader and does not change, because both counts are already returned.
  • The selector labels stay rendered, so the footer does not change height or shift as the total returns.
  • The results table shows no loaders, because the result set is unchanged.

Persistence

Each of the three selections is sticky per search type. We store the count, total, and currency selections separately for type:expense and type:expense-report, and restore them when the user returns to a search of that type. A user who always wants report counts on Reports and expense counts on Expenses sets it once.

Empty state

When a search returns no results, the footer shows Total spend with a zero amount and no total selector, since no total option applies. The count selector still follows the account-level rule above and shows a zero count.

Backend changes and query timing

#673654 delivers the backend for the count half: returning the report count on type:expense-report results and on type:expense results, so every search returns both counts and the client decides which to display. If returning both by default turns out to affect query timings, #673654 adds a param to the search command instead, and we update this OP to match.

This issue's backend work adds the four new totals as aggregates on the search command. These are requested by param rather than returned by default, since there are four of them and only one is displayed at a time.

We time each new aggregate query as part of the backend PRs and post the timings in this issue. If any one of them materially slows the search, we resolve that before frontend work starts rather than shipping a footer that is slow to update.

QA Examples

  • An account with both expenses and reports runs a type:expense-report search. The footer defaults to Reports: 87. The user opens the count selector, chooses Expenses, and taps Apply. The footer shows Expenses: 1,204 immediately, with no loader and no retriggered search.
  • A user changes the total from Total spend to Reimbursable and taps Apply. The total shows a skeleton loader while the search retriggers, then renders the reimbursable total. The count value and the results table are unaffected.
  • An account that has expenses but has never had a report runs a type:expense search. The footer shows the expense count as static text and no count selector.
  • An account with both runs a type:expense search filtered to unreported expenses, then selects Reports and applies. The footer shows Reports: 0. This is expected, since the user filtered their way into it.
  • A search returns expenses that are all reimbursable, in a workspace that does not track billable. The total selector lists Total spend and Reimbursable only.
  • A search returns a mix of reimbursable, non-reimbursable, and billable expenses. The total selector lists Total spend, Reimbursable, Non-reimbursable, Billable, and Non-billable.
  • A user selects Reimbursable on a type:expense-report search, navigates away, and returns. The footer is still on Reimbursable. They switch to a type:expense search and see that search type's own last selection, not Reimbursable.
  • A user on type:expense-report results has switched the count to Expenses. They open the count selector, tap Reset, then Apply. The footer returns to Reports immediately, with no retriggered search.
  • A search returns no results. The footer shows a zero count and Total spend at zero, with no total selector.

Activity

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

Metadata

Metadata

Labels

ImprovementItem broken or needs improvement.InternalRequires API changes or must be handled by Expensify staffReviewingHas a PR in reviewWeeklyKSv2

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions