Skip to content

Add confirmation before settling Expensify Card balance - #97401

Draft
MelvinBot wants to merge 2 commits into
mainfrom
claude-settleBalanceConfirmation
Draft

Add confirmation before settling Expensify Card balance#97401
MelvinBot wants to merge 2 commits into
mainfrom
claude-settleBalanceConfirmation

Conversation

@MelvinBot

Copy link
Copy Markdown
Contributor

Explanation of Change

Clicking Settle balance on the Expensify Card page used to fire an off-cycle balance settlement immediately, with no confirmation step — so users could (and did) trigger settlement by accident.

This gates the action behind a confirmation modal. handleSettleBalanceButtonClick in WorkspaceCardsListLabel.tsx now opens a danger confirm modal via the useConfirmModal hook, and queueExpensifyCardForBilling only runs when the user confirms (it's a no-op on cancel/dismiss). This mirrors the existing high-consequence confirmation pattern used in WorkspaceMembersPage.tsx.

New copy (settleBalanceConfirmationTitle / settleBalanceConfirmationPrompt) was added under workspace.expensifyCard in en.ts, and the existing settleBalance string is reused for the confirm button. This is New Expensify only — no Classic changes.

Implements the approved proposal, using the useConfirmModal hook per the C+ review (since ConfirmModal is being deprecated).

Note on translations: Per the auto-generation workflow, only en.ts is the source of truth; the 🦜 Polyglot Parrot workflow will generate the canonical translations for the other locales. Placeholder translations for all locales are included here so the branch type-checks — please apply Polyglot Parrot's version if it differs.

Fixed Issues

$ #96998
PROPOSAL: #96998 (comment)

Tests

// TODO: The human co-author must fill out the tests they ran before marking this PR as "ready for review". Please describe what tests you performed that validate your changes worked.

Suggested manual test:

  1. Open a workspace with an Expensify Card feed set to monthly settlement (so Settle balance is shown).
  2. Click Settle balance.
  3. Verify a confirmation modal appears with title "Settle balance?", the prompt copy, a red "Settle balance" confirm button, and a "Cancel" button.
  4. Click Cancel / dismiss — verify no settlement is initiated.
  5. Click Settle balance again, then confirm — verify the balance settlement is initiated as before.
  • Verify that no errors appear in the JS console

Offline tests

QA Steps

// TODO: These must be filled out, or the issue title must include "[No QA]."

  • 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.
  • 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

MelvinBot and others added 2 commits July 29, 2026 22:28
Co-authored-by: Situ Chandra Shil <situchan@users.noreply.github.com>
Co-authored-by: Situ Chandra Shil <situchan@users.noreply.github.com>
@MelvinBot
MelvinBot requested a review from a team July 29, 2026 22:37
@MelvinBot

Copy link
Copy Markdown
Contributor Author

I created this PR from a branch on Expensify/App since you don't have push access to a fork of it. To take ownership of this branch so you can push updates, fork the repo (if you haven't already), then run:

git remote add upstream https://github.com/Expensify/App.git
git fetch upstream claude-settleBalanceConfirmation
git checkout -b claude-settleBalanceConfirmation upstream/claude-settleBalanceConfirmation
git push -u origin claude-settleBalanceConfirmation

Then you can close this PR and open a new one from your fork, or push directly here if you'd prefer to keep this PR.

@OSBotify

Copy link
Copy Markdown
Contributor

🦜 Polyglot Parrot! 🦜

Squawk! Looks like you added some shiny new English strings. Allow me to parrot them back to you in other tongues:

View the translation diff
diff --git a/src/languages/de.ts b/src/languages/de.ts
index b74a241fb07..b19e2afdde0 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -5944,7 +5944,7 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU
             settleBalance: 'Saldo ausgleichen',
             settleBalanceConfirmationTitle: 'Saldo ausgleichen?',
             settleBalanceConfirmationPrompt:
-                'Dadurch wird dein aktueller Saldo am nächsten Werktag ausgeglichen. Nach erfolgreicher Abwicklung wird der Betrag deinem verbleibenden Limit wieder gutgeschrieben.',
+                'Dies begleicht Ihren aktuellen Saldo am nächsten Werktag. Nach erfolgreicher Ausführung wird der Betrag wieder zu Ihrem verbleibenden Limit hinzugefügt.',
             cardLimit: 'Kartenlimit',
             remainingLimit: 'Verbleibendes Limit',
             requestLimitIncrease: 'Anhebung des Anfragelimits',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 5e0b971003f..caed2eac571 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -5780,7 +5780,7 @@ ${amount} para ${merchant} - ${date}`,
             balanceWillBeSettledOn: (settlementDate) => `El saldo se liquidará el ${settlementDate}.`,
             settleBalance: 'Liquidar saldo',
             settleBalanceConfirmationTitle: '¿Liquidar saldo?',
-            settleBalanceConfirmationPrompt: 'Esto liquidará tu saldo actual el próximo día hábil. Una vez completado, el importe se sumará de nuevo a tu límite restante.',
+            settleBalanceConfirmationPrompt: 'Esto liquidará tu saldo actual el siguiente día hábil. Una vez completado con éxito, el importe se añadirá de nuevo a tu límite restante.',
             cardLimit: 'Límite de la tarjeta',
             remainingLimit: 'Límite restante',
             requestLimitIncrease: 'Solicitar aumento de límite',
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 336b330632e..b37d37d33fe 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -5954,9 +5954,8 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST.
             currentBalanceDescription: 'Le solde actuel est la somme de toutes les transactions Carte Expensify comptabilisées depuis la dernière date de règlement.',
             balanceWillBeSettledOn: (settlementDate: string) => `Le solde sera réglé le ${settlementDate}`,
             settleBalance: 'Régler le solde',
-            settleBalanceConfirmationTitle: 'Régler le solde ?',
-            settleBalanceConfirmationPrompt:
-                'Cela réglera votre solde actuel le prochain jour ouvrable. Une fois l’opération réussie, le montant sera de nouveau ajouté à votre limite restante.',
+            settleBalanceConfirmationTitle: 'Régler le solde ?',
+            settleBalanceConfirmationPrompt: 'Cela réglera votre solde actuel le jour ouvrable suivant. Une fois l’opération réussie, le montant sera réintégré à votre plafond restant.',
             cardLimit: 'Plafond de carte',
             remainingLimit: 'Plafond restant',
             requestLimitIncrease: 'Demander une augmentation de la limite',
diff --git a/src/languages/it.ts b/src/languages/it.ts
index 862b3a19f55..b86033d8236 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -5911,9 +5911,9 @@ _Per istruzioni più dettagliate, [visita il nostro sito di assistenza](${CONST.
             currentBalanceDescription: 'Il saldo attuale è la somma di tutte le transazioni contabilizzate della Carta Expensify che si sono verificate dalla data dell’ultima liquidazione.',
             balanceWillBeSettledOn: (settlementDate: string) => `Il saldo sarà regolato il ${settlementDate}`,
             settleBalance: 'Saldo da saldare',
-            settleBalanceConfirmationTitle: 'Saldare il saldo?',
+            settleBalanceConfirmationTitle: 'Salda il saldo?',
             settleBalanceConfirmationPrompt:
-                'Questa operazione salderà il tuo saldo attuale il giorno lavorativo successivo. Una volta completata, l’importo verrà aggiunto nuovamente al tuo limite rimanente.',
+                'Questo salderà il tuo saldo attuale nel giorno lavorativo successivo. Una volta completata l’operazione, l’importo verrà aggiunto di nuovo al tuo limite rimanente.',
             cardLimit: 'Limite carta',
             remainingLimit: 'Limite rimanente',
             requestLimitIncrease: 'Richiedi aumento limite',
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index 4c1cfd067a3..951d48a5b28 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -5851,7 +5851,7 @@ _詳しい手順については、[ヘルプサイトをご覧ください](${CO
             balanceWillBeSettledOn: (settlementDate: string) => `残高は${settlementDate}に精算されます`,
             settleBalance: '残高を清算',
             settleBalanceConfirmationTitle: '残高を清算しますか?',
-            settleBalanceConfirmationPrompt: '現在の残高が翌営業日に清算されます。清算が完了すると、その金額は残りの利用限度額に戻されます。',
+            settleBalanceConfirmationPrompt: 'この操作により、現在の残高は翌営業日に精算されます。正常に完了すると、その金額は残りの利用可能枠に戻されます。',
             cardLimit: 'カード上限',
             remainingLimit: '残りの上限',
             requestLimitIncrease: 'リクエスト上限の引き上げ',
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 1cb78630207..ab7eeb17e1e 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -5906,8 +5906,7 @@ _Voor meer gedetailleerde instructies, [bezoek onze help-site](${CONST.NETSUITE_
             balanceWillBeSettledOn: (settlementDate: string) => `Saldo wordt vereffend op ${settlementDate}`,
             settleBalance: 'Saldo vereffenen',
             settleBalanceConfirmationTitle: 'Saldo vereffenen?',
-            settleBalanceConfirmationPrompt:
-                'Hiermee wordt je huidige saldo op de volgende werkdag vereffend. Zodra dit is gelukt, wordt het bedrag weer aan je resterende limiet toegevoegd.',
+            settleBalanceConfirmationPrompt: 'Dit zal je huidige saldo op de volgende werkdag vereffenen. Zodra dit is gelukt, wordt het bedrag weer toegevoegd aan je resterende limiet.',
             cardLimit: 'Kaartlimiet',
             remainingLimit: 'Resterende limiet',
             requestLimitIncrease: 'Verhoging van limiet aanvragen',
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 03bc01d575e..16799739634 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -5886,8 +5886,7 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy
             balanceWillBeSettledOn: (settlementDate: string) => `Saldo zostanie rozliczone w dniu ${settlementDate}`,
             settleBalance: 'Ureguluj saldo',
             settleBalanceConfirmationTitle: 'Uregulować saldo?',
-            settleBalanceConfirmationPrompt:
-                'Spowoduje to uregulowanie bieżącego salda w następnym dniu roboczym. Po pomyślnym zakończeniu kwota zostanie ponownie dodana do pozostałego limitu.',
+            settleBalanceConfirmationPrompt: 'To spłaci twoje bieżące saldo w następnym dniu roboczym. Po pomyślnym zakończeniu kwota zostanie ponownie dodana do twojego dostępnego limitu.',
             cardLimit: 'Limit karty',
             remainingLimit: 'Pozostały limit',
             requestLimitIncrease: 'Zwiększ limit żądań',
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index a0656426d50..f22fd51772f 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -5900,7 +5900,8 @@ _Para instruções mais detalhadas, [visite nossa central de ajuda](${CONST.NETS
             balanceWillBeSettledOn: (settlementDate: string) => `O saldo será liquidado em ${settlementDate}`,
             settleBalance: 'Quitar saldo',
             settleBalanceConfirmationTitle: 'Quitar saldo?',
-            settleBalanceConfirmationPrompt: 'Isso quitará seu saldo atual no próximo dia útil. Após a conclusão, o valor será adicionado novamente ao seu limite restante.',
+            settleBalanceConfirmationPrompt:
+                'Isso vai quitar seu saldo atual no próximo dia útil. Assim que for concluído com sucesso, o valor será adicionado de volta ao seu limite disponível.',
             cardLimit: 'Limite do cartão',
             remainingLimit: 'Limite restante',
             requestLimitIncrease: 'Solicitar aumento de limite',
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index bcbaea1e7f0..b49f78a7333 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -5728,7 +5728,7 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM
             balanceWillBeSettledOn: (settlementDate: string) => `余额将于 ${settlementDate} 结清`,
             settleBalance: '结清余额',
             settleBalanceConfirmationTitle: '结清余额?',
-            settleBalanceConfirmationPrompt: '这将在下一个工作日结清您的当前余额。成功后,该金额将重新计入您的剩余额度。',
+            settleBalanceConfirmationPrompt: '这将在下一个工作日结清您的当前余额。成功后,该金额将被重新添加到您的剩余额度中。',
             cardLimit: '卡片限额',
             remainingLimit: '剩余额度',
             requestLimitIncrease: '请求提高限额',

Note

You can apply these changes to your branch by copying the patch to your clipboard, then running pbpaste | git apply 😉

View workflow run

@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

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

Files with missing lines Coverage Δ
...orkspace/expensifyCard/WorkspaceCardsListLabel.tsx 0.00% <0.00%> (ø)
... and 7 files with indirect coverage changes

@MelvinBot

Copy link
Copy Markdown
Contributor Author

🤖 I looked into the failing checks and there's nothing to fix in the code — every substantive check is green.

The only failing check is PR Author Checklist / checklist, and it fails simply because the checklist boxes aren't ticked ("0 out of 38 checklist items"). That's a human-only check — I won't tick the author checklist boxes or fill in the Tests / QA Steps sections on your behalf (the PR body flags those as TODOs for the human co-author). To turn it green, please complete the checklist items and the Tests/QA sections, then it'll re-run and pass.

All the real CI checks pass:

  • typecheck ✅, all 8 test (job N) ✅, Bun tests
  • ESLint check ✅, Oxfmt check ✅, React Compiler Compliance ✅, Storybook tests
  • All build (...) jobs ✅, generateTranslations ✅, spellcheck ✅, Check for unused styles ✅, Compare knip issues against main
  • baseline-perf-tests ✅ (branch-perf-tests was still running when I checked — it's the perf comparison, which reports as a separate comment and doesn't gate the merge)

So no push was needed. Let me know if you'd like me to take another look once the checklist is completed.

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.

3 participants