Skip to content

[HOLD for https://github.com/Expensify/Expensify/issues/664297] Show Scanning in the date column and pin scanning expenses to the top of transaction lists - #93469

Open
wildan-m wants to merge 30 commits into
Expensify:mainfrom
wildan-m:wildan/89966-scanning-date-sort-top

Conversation

@wildan-m

@wildan-m wildan-m commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

A freshly scanned receipt is created with a placeholder date of today at midnight, so the expense list shows a real-looking date for a receipt whose date isn't actually known yet — and under the default newest-first sort, the in-progress scan sinks below every other expense created that day, making it hard to track the scan's progress.

While the receipt is scanning, the date cell now shows "Scanning…" — the same treatment and copy the merchant cell already uses — and the row is pinned to the top of both the Expenses tab and the report's transaction table, regardless of the selected sort column or direction. Both changes key off the existing scanning state, so once the scan completes the row automatically reverts to its real date and chronological position with no extra plumbing. The pin takes precedence over the existing prioritization of rows that need attention, and only applies on the views that show the scanning state.

Fixed Issues

$ #89966
PROPOSAL: #89966 (comment)

Tests

  1. Sign in with an account that has a workspace and create two manual expenses dated today (any amounts).
  2. Press the global create button > Create expense > Scan, upload a receipt image, and create the expense.
  3. While the receipt is still scanning, go to Reports > Expenses. Verify the scanning expense shows "Scanning…" in the Date column (alongside the merchant's existing "Scanning…") and sits at the top of the list under the default Date descending sort.
  4. Click the Date header to switch to ascending. Verify the scanning row stays at the top.
  5. Click the Amount header and check both directions. Verify the scanning row stays at the top even though its amount is 0, which would otherwise rank it last under descending.
  6. Open the expense report that contains the scanning expense. In the default (ungrouped) layout, verify the transaction table also shows "Scanning…" in the Date column and keeps the scanning row at the top in both Date sort directions.
  7. Switch the report to a grouped layout (group by Category, then Tag). Verify the scanning expense is pinned to the top of its own section — an uncategorised scan sits atop the "Uncategorized" section (same for "Untagged" under the tag layout).
  8. Wait for the scan to complete. Verify the row reverts to the receipt's real date and sorts back into its normal position in each layout.
  • Verify that no errors appear in the JS console

Offline tests

  1. Go offline, then create a scan expense (global create > Create expense > Scan).
  2. Verify the expense stays in the scanning state: the Date column shows "Scanning…" and the row is pinned to the top of the Expenses tab and the report's transaction table under any sort.
  3. Go back online. Verify the scan completes and the row reverts to its real date and normal sort position.

QA Steps

Same as tests.

  • 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 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 shown in the product is localized by adding it to src/languages/* files and using the translation method
    • I verified all numbers, amounts, dates and phone numbers shown in the product are using the localization methods
    • 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)
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named "index.js". All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • 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)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.ts or at the top of the file that uses the constant) are defined as such
  • I verified that if a function's arguments changed that all usages have also been updated correctly
  • 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.
  • If a new page is added, I verified it's using the ScrollView component to make it scrollable when more elements are added to the page.
  • 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
Kapture.2026-06-13.at.22.09.41.mp4
Android: mWeb Chrome
Kapture.2026-06-13.at.22.13.41.mp4
iOS: Native
Kapture.2026-06-13.at.21.48.29.mp4
iOS: mWeb Safari
Kapture.2026-06-13.at.22.00.44.mp4
MacOS: Chrome / Safari
Kapture.2026-06-13.at.21.09.07.mp4

@codecov

codecov Bot commented Jun 12, 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 Δ
...components/Search/SearchList/ListItem/DateCell.tsx 100.00% <100.00%> (ø)
src/components/Search/hooks/useSearchSnapshot.ts 87.50% <ø> (ø)
...ts/TransactionItemRow/TransactionItemRowNarrow.tsx 97.14% <100.00%> (+97.14%) ⬆️
...ents/TransactionItemRow/TransactionItemRowWide.tsx 99.14% <100.00%> (+<0.01%) ⬆️
src/libs/SearchUIUtils.ts 63.08% <100.00%> (+0.21%) ⬆️
src/libs/TransactionUtils/index.ts 89.76% <100.00%> (+0.05%) ⬆️
...earch/SearchList/ListItem/GroupChildrenContent.tsx 0.00% <0.00%> (ø)
...h/SearchList/ListItem/TransactionGroupListItem.tsx 74.50% <0.00%> (ø)
...stReportView/MoneyRequestReportTransactionList.tsx 0.56% <0.00%> (-0.01%) ⬇️
... and 12 files with indirect coverage changes

@wildan-m
wildan-m force-pushed the wildan/89966-scanning-date-sort-top branch from 5d97546 to 9a2463b Compare June 12, 2026 21:28
@wildan-m
wildan-m marked this pull request as ready for review June 13, 2026 15:16
@wildan-m
wildan-m requested review from a team as code owners June 13, 2026 15:16
@melvin-bot
melvin-bot Bot requested review from DylanDylann and trjExpensify and removed request for a team June 13, 2026 15:16
@melvin-bot

melvin-bot Bot commented Jun 13, 2026

Copy link
Copy Markdown

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

@melvin-bot
melvin-bot Bot removed the request for review from a team June 13, 2026 15:16

@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: 9a2463bddf

ℹ️ 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".

Comment thread src/components/Search/SearchList/ListItem/DateCell.tsx Outdated
On the narrow layout a transaction's category renders only as the date
cell's suffix. The scanning override replaced the whole display string,
so a category set on a still-scanning receipt vanished until the scan
finished. Apply the override to the date portion only and keep the
suffix. Adds a render test for scanning + category on narrow layout.
@DylanDylann

Copy link
Copy Markdown
Contributor

@codex review

@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: 99802d467f

ℹ️ 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".

Comment on lines +372 to +375
// Scanning transactions stay pinned to the top regardless of the selected sort column or direction
const scanningComparison = compareScanningPriority(a, b);
if (scanningComparison !== 0) {
return scanningComparison;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve scan pinning when report rows are grouped

When an expense report is using the default grouped layout (the report layout selection defaults to category unless matrix is stored), this comparator only pins the order of sortedTransactions; the rendered rows are then regrouped via groupTransactionsByCategory/Tag(resolvedTransactions, ...), whose group sorting can move the scan’s group below other groups and puts the empty category/tag group last. A freshly scanned receipt usually has no category/tag, so in the default report transaction table it still appears near the bottom instead of staying pinned to the top. The grouping path needs to keep scanning transactions/groups ahead of the normal group order.

Useful? React with 👍 / 👎.

missingFieldError={missingFieldError}
categoryForDisplay={categoryForDisplay}
createdAt={createdAt}
createdDateTextOverride={createdDateTextOverride}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I suggest passing isScanning to keep things simple

Comment thread src/libs/SearchUIUtils.ts
sortOrder?: SortOrder,
options?: SortSectionsOptions,
) {
if (!sortBy || !sortOrder) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@wildan-m Should we combine the scanning sort into the current function instead of creating a new function just for scanning sort?

@DylanDylann

Copy link
Copy Markdown
Contributor

@wildan-m The scanning transaction is not at the top when sorting by merchant

Screen.Recording.2026-06-15.at.13.43.00.mov

@trjExpensify trjExpensify left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Open the expense report that contains the scanning expense. Verify the transaction table also shows "Scanning…" in the Date column and keeps the row at the top in both Date sort directions.

Can you expand on this? In the multi-expense report view, we have the reportLayout group by: Category, Tag, None setting.

If the user is using the Category layout, and the expense scanning has been categorised already, the expense should go in that section. If it hasn't been categorised, it goes in a "Uncategorised" section like it does today. Same logic exists for Tag.

Image

If the user is using the None layout, then I agree.. "Scanning..." expenses will just sit at the top of the table until done, and then sorted however they are sorting the table (the default being by date).

wildan-m added 6 commits June 15, 2026 22:28
…ch sort

- Grouped report layout: float the group containing an in-progress scan to the
  top. A fresh scan has no category/tag, so its group otherwise sorted to the
  bottom of the grouped report (and stayed there under any column sort).
- Pass an isScanning boolean to DateCell, which renders the scanning label,
  instead of threading a precomputed override string through the row variants.
- Combine the scanning pin into getSortedTransactionData as the primary
  comparator, removing the separate column-sort helper.
- Add a unit test asserting the scanning group is pinned to the top.
@DylanDylann

Copy link
Copy Markdown
Contributor

@wildan-m Is this ready?

@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: d6b8f92eff

ℹ️ 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".

Comment thread src/libs/SearchUIUtils.ts Outdated
});
};

return data.sort((a, b) => compareScanningPriority(a, b) || compareColumn(a, b));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Apply scanning priority to custom report-column sorts

When the user sorts the Expenses search by submitterUserID, submitterPayrollID, or orderDealNumbers, getSortedTransactionData returns from the custom-column branch above before reaching this comparator, so scanning transactions are sorted only by that report field instead of being pinned to the top. That leaves scans buried for those supported sortable columns, contrary to the new “regardless of selected column” behavior.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@wildan-m Please check this comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in cce3777 🙏 Those columns (submitterUserID/submitterPayrollID/orderDealNumbers) bypassed the shared compareScanningPriority(a, b) || compareColumn(a, b) comparator via an early return data.sort(...). Moved them into compareColumn so scanning is pinned for every column, and extended the "pin regardless of column" test to cover all three.

@DylanDylann

Copy link
Copy Markdown
Contributor

Yep, on Classic, if using the category layout and scanning an uncategorised expense it goes in the "Uncategorized" section. Same for tag layout.

@wildan-m I think in this case, we should always pin the scanning expense to the top of the Uncategorized section?

cc @trjExpensify Could you confirm again this?

The submitter/order-number custom columns returned from an early branch in
getSortedTransactionData that skipped compareScanningPriority, so scans were
not pinned when sorting by those columns (flagged by Codex). Move that branch
into compareColumn so the shared comparator pins scanning for every column.
@DylanDylann

Copy link
Copy Markdown
Contributor

@trjExpensify Kindly bump ☝️

@trjExpensify

Copy link
Copy Markdown
Contributor

Correct, if they didn't categorize it, we put the scanning expense atop the uncategorized section when using the category layout. 👍

…ng-date-sort-top

# Conflicts:
#	src/components/Search/SearchList/ListItem/DateCell.tsx
#	src/components/TransactionItemRow/TransactionItemRowNarrow.tsx
#	tests/unit/Search/SearchUIUtilsTest.ts
@wildan-m
wildan-m force-pushed the wildan/89966-scanning-date-sort-top branch from ee045bc to 7a8a55a Compare July 8, 2026 04:42
Per trj's confirmation, grouped layouts (Category/Tag) now pin the scanning
expense to the top of its section (e.g. atop the Uncategorized section),
matching Expensify Classic. The grouping consumes the scanning-pinned
resolvedTransactions list again instead of the column-only sort.
@DylanDylann

Copy link
Copy Markdown
Contributor

@codex review

@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: 9e62d4fb2c

ℹ️ 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".

Comment thread src/libs/SearchUIUtils.ts Outdated
});
};

return data.sort((a, b) => compareScanningPriority(a, b) || compareColumn(a, b));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Pin scanning rows inside grouped search results

When the Expenses search is grouped, this new comparator is never applied to the expanded child rows: getSortedSections takes the groupBy branch and sorts only the group headers, while GroupChildrenContent rebuilds the children with getSections and does not call getSortedTransactionData. In a grouped Expenses view (e.g. grouped by category/tag), an in-progress scan with today's placeholder date or amount 0 still follows the backend column sort inside its group, so it can remain below other rows even though ungrouped lists are pinned here.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 0ca256d — added sortTransactionsScanningFirst at the three sites that build grouped Search children (they never reached getSortedTransactionData), plus a unit test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is the paginated / server-sorted case discussed here: #93469 (comment)

Short version: the client-side pin can only reorder rows already loaded, so a scan on an unfetched page has nothing to pin. Making it work across pages is a bigger Search-data change that may be outside this PR's scope.

wildan-m added 4 commits July 8, 2026 19:55
Grouped Search children are rebuilt from a per-group snapshot via getSections and
never pass through getSortedTransactionData, so an in-progress scan followed the
backend column sort inside its group instead of being pinned (flagged by Codex).

Add sortTransactionsScanningFirst and apply it wherever those children are built,
so a scanning expense sits atop its section — matching trj's confirmed behavior and
the ungrouped list and report layouts.
@wildan-m

Copy link
Copy Markdown
Contributor Author

Thanks for confirming, @trjExpensify 🙏 — @DylanDylann was right. Grouped layouts now pin the scanning expense atop its section: report view in 9e62d4f, Search group-by view in 0ca256d.

@DylanDylann

Copy link
Copy Markdown
Contributor

@codex review

@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: 9253974026

ℹ️ 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".

Comment thread src/libs/SearchUIUtils.ts Outdated
});
};

return data.sort((a, b) => compareScanningPriority(a, b) || compareColumn(a, b));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep acknowledged scans in the sorted result set

When a receipt is still in SCANNING but the optimistic ADD has already been acknowledged, changing to a server-sorted column such as Amount descending on a result set with more than one page can make the backend return a page that does not contain the zero-amount scan. This comparator only pins items already present in the paginated snapshot, so the scanning expense is not pinned to the top and may disappear until more results are fetched. Please merge active scanning transactions into the search data (as the optimistic path does) or otherwise ensure the page being sorted contains them before applying this priority.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@wildan-m It's worth taking a look

Comment thread src/libs/SearchUIUtils.ts Outdated
Comment on lines +464 to +483
{
text: translate('common.unreported'),
value: CONST.SEARCH.STATUS.EXPENSE.UNREPORTED,
},
{
text: translate('common.draft'),
value: CONST.SEARCH.STATUS.EXPENSE.DRAFTS,
},
{
text: translate('common.outstanding'),
value: CONST.SEARCH.STATUS.EXPENSE.OUTSTANDING,
},
{
text: translate('iou.approved'),
value: CONST.SEARCH.STATUS.EXPENSE.APPROVED,
},
{
text: translate('iou.settledExpensify'),
value: CONST.SEARCH.STATUS.EXPENSE.PAID,
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

How did this change end up in this PR? I would suggest reverting this change to keep PR clean and avoid unrelated updates

@melvin-bot
melvin-bot Bot requested a review from roryabraham July 13, 2026 07:35
@DylanDylann

Copy link
Copy Markdown
Contributor

@roryabraham I haven't finished reviewing this PR yet. I approved it by mistake. Sorry for the wrong bump

@DylanDylann

Copy link
Copy Markdown
Contributor

@wildan-m When I try sorting multiple times, the scanning expense sometimes isn't pinned to the top

Screen.Recording.2026-07-13.at.14.53.42.mov

wildan-m added 2 commits July 13, 2026 15:53
The branch had drifted from main's refactored getSortedTransactionData and
accumulated unrelated object-literal reformatting, bloating the SearchUIUtils
diff to ~600 lines. Reset the file to main and re-apply scanning minimally:
getSortedTransactionData now column-sorts (getColumnSortedTransactionData) then
pins scans via sortTransactionsScanningFirst. This pins every column uniformly
(subsuming the earlier submitter-column fix) and shrinks the diff to ~30 lines.
@wildan-m

Copy link
Copy Markdown
Contributor Author

Thanks for catching this, @DylanDylann. It's the same cause Codex flagged at SearchUIUtils.ts:4210.

The pin runs on the client, so it can only reorder the rows already loaded on screen. When you sort by a column the server sorts (like Amount) and there's more than one page of results, the server returns a page ordered by that column — and a scan (amount 0, no date yet) can land on a page that hasn't loaded. Since it's not on screen, there's nothing to pin, so it isn't pinned and looks like it disappears until more rows load. That's why it's hit-or-miss — it depends on which page the scan falls on. When the results fit on one page (the usual case), it pins correctly every time.

Making it work across pages would be a bigger change to how Search loads data — pulling active scans in from the local data and merging them into the results before sorting, while keeping totals, counts, and "load more" correct. That feels outside the original scope here (show the Scanning date and pin scans in the list), but let me know if you think it should be handled as part of this PR.

@DylanDylann

Copy link
Copy Markdown
Contributor

I think it's worth fixing, because we need to keep scanning expenses at the top of the list, even when users sort by any field. So the scanning transaction should be prioritized to appear on the first page, regardless of the sorting. @roryabraham I believe we need to make some adjustments on the backend side to achieve this expected behavior

@DylanDylann

Copy link
Copy Markdown
Contributor

@roryabraham Kindly bump ☝️

@roryabraham

Copy link
Copy Markdown
Contributor

@MelvinBot create a new GitHub issue to ensure that Scanning expenses always appear at the front of search results on the expense search page. Create it in the Expensify/Expensify repo, assign me, and label it Daily. Put this PR on HOLD for the issue you created.

@MelvinBot MelvinBot changed the title Show Scanning in the date column and pin scanning expenses to the top of transaction lists [HOLD for https://github.com/Expensify/Expensify/issues/664297] Show Scanning in the date column and pin scanning expenses to the top of transaction lists Jul 25, 2026
@MelvinBot

Copy link
Copy Markdown
Contributor

🤖 Done, roryabraham:

  • Created issue: Expensify/Expensify#664297 — "Scanning expenses should always appear at the front of the expense Search results page" — assigned to you and labeled Daily.
  • Put this PR on HOLD for that issue (added the [HOLD for https://github.com/Expensify/Expensify/issues/664297] prefix to the PR title).

This PR pins scanning expenses to the top of the Expenses tab and report transaction tables; the new issue tracks extending that same treatment to the expense Search results page. I didn't find an existing duplicate for the Search-page ordering.

@roryabraham

Copy link
Copy Markdown
Contributor

Quick update: I have a draft PR for the backend change. But I still need to test end-to-end

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.

5 participants