Keep merchant rules import button active offline for the all-skipped path - #96976
Keep merchant rules import button active offline for the all-skipped path#96976MelvinBot wants to merge 4 commits into
Conversation
Co-authored-by: Joe Ph <gijoe0295@users.noreply.github.com>
|
I created this PR from an upstream branch since I don't have push access to your fork. To take ownership of this branch and be able to push updates, run: Then you can close this PR and open a new one from your fork. |
|
The only failing check is PR Author Checklist / checklist, and it's not something I can push a code fix for. It fails because Every other check is passing or still running — none of them have genuinely failed. To get this green: edit the PR description to complete the author checklist and write out the Tests / Offline tests / QA steps (or add |
|
@MelvinBot update test in ImportedMerchantRulesPageTest.ts to satisfy shouldDisableButtonWhenOffline change from ImportedMerchantRulesPage.tsx |
Co-authored-by: Joe Ph <gijoe0295@users.noreply.github.com>
|
🤖 Added tests covering the Since the offline behavior lives in the render path (not in the two pure helpers the file already tested), I renamed
The existing Verification
The render test mirrors the sibling |
…lineButton Co-authored-by: Joe Ph <gijoe0295@users.noreply.github.com>
|
🤖 Fixed by merging the latest Root cause: The A typecheck regression had landed on Fix: Merged the now-fixed The only other red check is |
|
🤖 Confirming the fix landed — As noted earlier, the failure was a typecheck regression on |
|
🤖 The failing checks are now resolved. After I merged the fixed The only remaining red check is |
DetailsExplanation of ChangeThe "Import" button in the merchant-rules import flow was disabled whenever the device was offline. This blocked the client-side-only path where every parsed row is a duplicate of an existing rule and/or references a category that doesn't exist — in that case The offline guard lived hard-coded in the shared
Fixed Issues$ #96842 Tests
Offline testsQA Steps
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppScreen.Recording.2026-08-01.at.10.44.23.movAndroid: mWeb ChromeScreen.Recording.2026-07-30.at.00.09.31.moviOS: HybridAppScreen.Recording.2026-07-30.at.01.08.53.moviOS: mWeb SafariScreen.Recording.2026-07-29.at.23.59.05.movMacOS: Chrome / SafariScreen.Recording.2026-07-29.at.23.51.48.mov |
|
✅ Done — I've set the PR body to the provided content. You can see it on PR #96976. |
|
Fixed — the Root cause: The check wasn't failing on an unchecked box — it was crashing. The Fix (PR body only, no code changes):
The re-run triggered by the body edit (run 30683777921) is green, and no other checks are failing. |
|
@MelvinBot add test file to PR description |
|
✅ Done — I added the merchant-rules-test.csv test file to the PR description. It now appears in a dedicated Test file section and is linked from the "Upload the ... file" step in both the Tests and QA Steps sections. |
|
|
Re-applied the fix — the What happened: I fixed this earlier (run passed at 04:21), but the checklist heading got reverted to Root cause (unchanged): The Fix (PR body only):
|
|
✅ Both changes are already in place on the PR body — no edit was needed:
|
|
@MelvinBot Mark the Pull Request ready for review |
|
@gijoe0295 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] |
|
✅ Done — I've marked PR #96976 as ready for review. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6efa9828ca
ℹ️ 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".
| columnRoles={columnRoles} | ||
| isButtonLoading={isImportingRules} | ||
| customHeaderText={translate('workspace.rules.merchantRules.importRulesSupportingText')} | ||
| shouldDisableButtonWhenOffline={false} |
There was a problem hiding this comment.
Keep the offline guard for imports that need the API
With this set to false, the merchant-rules Import button is enabled for every offline spreadsheet, not just the all-skipped client-only case. If the offline spreadsheet contains any new valid rule, importRules() falls through to importMerchantRulesSpreadsheet(), which uses API.makeRequestWithSideEffects and is not queued for retry, so the user can press Import offline and get an immediate import-failed modal even though the previous guard correctly blocked the server-required path. Please only bypass the offline disablement when the parsed import would actually short-circuit locally.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
This blanket guard exists because most spreadsheet imports need a live server round-trip to build their confirmation modal. But the merchant-rules flow has a client-side-only path: when every parsed row is a duplicate of an existing rule and/or references a category that doesn't exist, ImportedMerchantRulesPage.importRules short-circuits and builds the "No merchant rules have been added…" modal locally, without calling the API.
So in the reported scenario (re-importing the same CSV where all rows already exist), no network is required — yet the offline guard on the shared button still blocks it, so the modal never appears offline.
Co-authored-by: {"message":"Not Found","documentation_url":"https://docs.github.com/rest/issues/comments#get-an-issue-comment","status":"404"} <{"message":"Not Found","documentation_url":"https://docs.github.com/rest/issues/comments#get-an-issue-comment","status":"404"}@users.noreply.github.com>
|
@justinpersaud 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] |
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppScreen.Recording.2026-08-01.at.10.45.19.movAndroid: mWeb ChromeScreen.Recording.2026-08-01.at.12.17.33.moviOS: HybridAppScreen.Recording.2026-08-01.at.13.18.20.moviOS: mWeb SafariScreen.Recording.2026-08-01.at.12.20.32.movMacOS: Chrome / SafariScreen.Recording.2026-08-01.at.11.56.08.mov |
|
LGTM ! |
|
@justinpersaud All yours ! |
Explanation of Change
The "Import" button in the merchant-rules import flow was disabled whenever the device was offline. This blocked the client-side-only path where every parsed row is a duplicate of an existing rule and/or references a category that doesn't exist — in that case
ImportedMerchantRulesPage.importRulesshort-circuits and builds the "No merchant rules have been added…" confirmation modal locally, with no API call. Since no network is required for that path, the button should stay active offline.The offline guard lived hard-coded in the shared
ImportSpreadsheetColumnscomponent (isDisabled={isOffline}), which is correct for its other consumers (categories, tags, members, per diem, company cards, wallet transactions) that need a live server round-trip. Rather than change behavior for everyone, this PR:shouldDisableButtonWhenOfflineprop (defaulttrue, preserving existing behavior for every other importer) toImportSpreadsheetColumns, applied asisDisabled={shouldDisableButtonWhenOffline && isOffline}.shouldDisableButtonWhenOffline={false}from the merchant-rules page so its button stays active offline.Test file
merchant-rules-test.csv
Fixed Issues
$ #96842
PROPOSAL: #96842 (comment)
Tests
go offline
Offline tests
QA Steps
go offline
PR Author Checklist
### Fixed Issuessection aboveTestssectionQA stepssectionAvatar, I verified the components usingAvatarhave been tested & I retested again)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps./** comment above it */thisproperly so there are no scoping issues (i.e. foronClick={this.submit}the methodthis.submitshould be bound tothisin the constructor)thisare necessary to be bound (i.e. avoidthis.submit = this.submit.bind(this);ifthis.submitis never passed to a component event handler likeonClick)Screenshots/Videos
Android: HybridApp
Screen.Recording.2026-08-01.at.10.44.23.mov
Android: mWeb Chrome
Screen.Recording.2026-07-30.at.00.09.31.mov
iOS: HybridApp
Screen.Recording.2026-07-30.at.01.08.53.mov
iOS: mWeb Safari
Screen.Recording.2026-07-29.at.23.59.05.mov
MacOS: Chrome / Safari
Screen.Recording.2026-07-29.at.23.51.48.mov