[internal QA] Add confirmation before settling Expensify Card balance - #97401
Conversation
Co-authored-by: Situ Chandra Shil <situchan@users.noreply.github.com>
Co-authored-by: Situ Chandra Shil <situchan@users.noreply.github.com>
|
I created this PR from a branch on 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. |
This comment has been minimized.
This comment has been minimized.
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
|
🤖 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 All the real CI checks pass:
So no push was needed. Let me know if you'd like me to take another look once the checklist is completed. |
|
@MelvinBot pull main |
|
@MelvinBot pull main. Update QA Steps |
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppandroid.movAndroid: mWeb ChromeiOS: HybridAppios.moviOS: mWeb SafariMacOS: Chrome / Safariweb.mov |
|
@MelvinBot mark as ready for review |
DetailsExplanation of ChangeClicking 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. New copy ( Implements the approved proposal, using the Fixed Issues$ #96998 Tests
Offline testsN/A QA StepsSame as Tests
PR Author Checklist
@MelvinBot please set the PR body to the content in the details section above |
|
The confirmation modal looks good. One gap: there's no feedback after confirming on the modal that a settlement is pending. That's also true now, but I feel like some feedback would significantly improve the UX. cc @shawnborton One idea would be to disable the button after a settlement request is made and show a tooltip indicating that a settlement has already been requested. I investigated with Claude, and seems like this should be possible with no backend changes. Here is what it said:
|
|
Should the button still be there even if you have a $0.00 balance? Feels odd that it's there. I don't love the idea of showing it but in a disabled state either. I think we recently had a similar thing come up for Travel Invoicing, and we landed to hide the button after settlement was initiated but add some small text like this (Figma): cc @Expensify/design for thoughts. |
|
Yeah I like following what we did for travel invoicing here. |
|
Also, I don't think we want the |
|
Agree with that! |
…onfirmation Co-authored-by: Situ Chandra Shil <situchan@users.noreply.github.com>
…ement is queued Co-authored-by: Situ Chandra Shil <situchan@users.noreply.github.com>
|
Pulled What changed
Notes
Checks run: Testing note: the queued state only appears once the backend marks a settlement as pending, so — as you noted — exercising the live "queued" UI needs a real settlement transaction. The behavior in the normal (no-pending) state is unchanged from what's already on this PR. |
🦜 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 diffdiff --git a/src/languages/de.ts b/src/languages/de.ts
index 9924abfb..b1a20537 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -6040,7 +6040,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 wird Ihren aktuellen Saldo am nächsten Geschäftstag ausgleichen. Sobald dies erfolgreich ist, wird der Betrag wieder zu Ihrem verbleibenden Limit hinzugefügt.',
settleBalancePaymentQueued: (amount: string) => `Die Zahlung über ${amount} ist in der Warteschlange und wird in Kürze bearbeitet.`,
cardLimit: 'Kartenlimit',
remaining: 'Verbleibend',
diff --git a/src/languages/el.ts b/src/languages/el.ts
index 3e8d265d..844e86bd 100644
--- a/src/languages/el.ts
+++ b/src/languages/el.ts
@@ -6162,8 +6162,9 @@ _Για πιο αναλυτικές οδηγίες, [επισκεφθείτε τ
balanceWillBeSettledOn: (settlementDate: string) => `Το υπόλοιπο θα διακανονιστεί στις ${settlementDate}`,
settleBalance: 'Εξόφληση υπολοίπου',
settleBalanceConfirmationTitle: 'Εξόφληση υπολοίπου;',
- settleBalanceConfirmationPrompt: 'Αυτό θα εξοφλήσει το τρέχον υπόλοιπό σας την επόμενη εργάσιμη ημέρα. Μόλις ολοκληρωθεί, το ποσό θα προστεθεί ξανά στο υπόλοιπο όριό σας.',
- settleBalancePaymentQueued: (amount: string) => `Η πληρωμή ποσού ${amount} είναι σε αναμονή και θα διεκπεραιωθεί σύντομα.`,
+ settleBalanceConfirmationPrompt:
+ 'Αυτό θα εξοφλήσει το τρέχον υπόλοιπό σας την επόμενη εργάσιμη ημέρα. Μόλις ολοκληρωθεί με επιτυχία, το ποσό θα προστεθεί ξανά στο διαθέσιμο όριό σας.',
+ settleBalancePaymentQueued: (amount: string) => `Η πληρωμή ποσού ${amount} έχει μπει σε ουρά και θα διεκπεραιωθεί σύντομα.`,
cardLimit: 'Όριο κάρτας',
remainingLimit: 'Υπόλοιπο ορίου',
requestLimitIncrease: 'Αίτημα αύξησης ορίου',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 7aa00937..abcc64f9 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -5914,8 +5914,9 @@ ${amount} para ${merchant} - ${date}`,
'El saldo actual es la suma de todas las transacciones contabilizadas con la Tarjeta Expensify que se han producido desde la última fecha de liquidación.',
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.',
+ settleBalanceConfirmationTitle: '¿Liquidar el saldo?',
+ settleBalanceConfirmationPrompt:
+ 'Esto liquidará tu saldo actual el siguiente día hábil. Una vez que se complete correctamente, el importe se añadirá de nuevo a tu límite restante.',
settleBalancePaymentQueued: (amount: string) => `El pago de ${amount} está en cola y se procesará pronto.`,
cardLimit: 'Límite de la tarjeta',
remaining: 'Restante',
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 6924574e..2d995ad7 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -6048,10 +6048,10 @@ _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 ?',
+ 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.',
- settleBalancePaymentQueued: (amount: string) => `Le paiement de ${amount} est en file d’attente et sera traité bientôt.`,
+ 'Cela réglera votre solde actuel le jour ouvrable suivant. Une fois l’opération effectuée avec succès, le montant sera réajouté à votre plafond restant.',
+ settleBalancePaymentQueued: (amount: string) => `Le paiement de ${amount} est en file d’attente et sera bientôt traité.`,
cardLimit: 'Plafond de carte',
remaining: 'Restant',
remainingLimit: 'Plafond restant',
diff --git a/src/languages/it.ts b/src/languages/it.ts
index 269280a7..d42d5243 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -6004,9 +6004,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: 'Regolare 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 andata a buon fine, l’importo verrà aggiunto di nuovo al tuo limite residuo.',
settleBalancePaymentQueued: (amount: string) => `Il pagamento di ${amount} è in coda e verrà elaborato a breve.`,
cardLimit: 'Limite carta',
remaining: 'Rimanente',
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index b52575ab..80c43b13 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -5939,9 +5939,9 @@ _詳しい手順については、[ヘルプサイトをご覧ください](${CO
currentBalanceDescription: '現在残高は、前回の精算日以降に発生し記帳されたすべての Expensify カード取引の合計です。',
balanceWillBeSettledOn: (settlementDate: string) => `残高は${settlementDate}に精算されます`,
settleBalance: '残高を清算',
- settleBalanceConfirmationTitle: '残高を清算しますか?',
- settleBalanceConfirmationPrompt: '現在の残高が翌営業日に清算されます。清算が完了すると、その金額は残りの利用限度額に戻されます。',
- settleBalancePaymentQueued: (amount: string) => `${amount} の支払いはキューに登録されており、まもなく処理されます。`,
+ settleBalanceConfirmationTitle: '残高を精算しますか?',
+ settleBalanceConfirmationPrompt: 'この操作により、現在の残高は翌営業日に精算されます。完了すると、その金額は残りの利用可能枠に戻ります。',
+ settleBalancePaymentQueued: (amount: string) => `${amount} の支払いはキューに追加され、まもなく処理されます。`,
cardLimit: 'カード上限',
remaining: '残り',
remainingLimit: '残りの上限',
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index f9918292..483296b8 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -5996,9 +5996,8 @@ _Voor meer gedetailleerde instructies, [bezoek onze help-site](${CONST.NETSUITE_
currentBalanceDescription: 'Het huidige saldo is de som van alle geboekte Expensify Kaart-transacties die hebben plaatsgevonden sinds de laatste afwikkelingsdatum.',
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.',
+ settleBalanceConfirmationTitle: 'Saldo afrekenen?',
+ settleBalanceConfirmationPrompt: 'Dit zal je huidige saldo op de volgende werkdag vereffenen. Zodra dit is gelukt, wordt het bedrag weer toegevoegd aan je resterende limiet.',
settleBalancePaymentQueued: (amount: string) => `Betaling van ${amount} staat in de wachtrij en wordt binnenkort verwerkt.`,
cardLimit: 'Kaartlimiet',
remaining: 'Resterend',
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index b62f9e3b..cbf9dcd4 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -5979,8 +5979,8 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy
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.',
- settleBalancePaymentQueued: (amount: string) => `Płatność w wysokości ${amount} jest w kolejce i wkrótce zostanie przetworzona.`,
+ 'To spłaci twoje bieżące saldo w następny dzień roboczy. Po pomyślnym zakończeniu kwota zostanie dodana z powrotem do twojego dostępnego limitu.',
+ settleBalancePaymentQueued: (amount: string) => `Płatność w wysokości ${amount} jest w kolejce i zostanie wkrótce przetworzona.`,
cardLimit: 'Limit karty',
remaining: 'Pozostało',
remainingLimit: 'Pozostały limit',
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index 1fd85fb3..f603b73f 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -5996,8 +5996,9 @@ _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.',
- settleBalancePaymentQueued: (amount: string) => `O pagamento de ${amount} está na fila e será processado em breve.`,
+ settleBalanceConfirmationPrompt:
+ 'Isso vai quitar seu saldo atual no próximo dia útil. Quando for concluído com sucesso, o valor será adicionado de volta ao seu limite disponível.',
+ settleBalancePaymentQueued: (amount: string) => `O pagamento de ${amount} está em fila e será processado em breve.`,
cardLimit: 'Limite do cartão',
remaining: 'Restante',
remainingLimit: 'Limite restante',
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index c16487f8..ed64cdf3 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -5805,8 +5805,8 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM
balanceWillBeSettledOn: (settlementDate: string) => `余额将于 ${settlementDate} 结清`,
settleBalance: '结清余额',
settleBalanceConfirmationTitle: '结清余额?',
- settleBalanceConfirmationPrompt: '这将在下一个工作日结清您的当前余额。成功后,该金额将重新计入您的剩余额度。',
- settleBalancePaymentQueued: (amount: string) => `金额为 ${amount} 的付款已排队,稍后将被处理。`,
+ settleBalanceConfirmationPrompt: '这笔款项将在下一个工作日结清您当前的余额。成功后,该金额将重新计入您的剩余额度。',
+ settleBalancePaymentQueued: (amount: string) => `金额为 ${amount} 的付款已排队,即将处理。`,
cardLimit: '卡片限额',
remaining: '剩余',
remainingLimit: '剩余额度',
Note You can apply these changes to your branch by copying the patch to your clipboard, then running |
|
@joekaufmanexpensify can you please run build and test with a live transaction?
|
|
Done — set the PR body to the content you provided. |
|
Actually, I overlooked. We already have this message:
@MelvinBot revert 34be8b6 as it's not needed |
…ce settlement is queued" This reverts commit 34be8b6.
|
Reverted Pushed the revert ( |
|
I think we're good to go. #55607 (comment) |
|
I see. I thought there was no message based on the video farther up in the PR where it isn't shown. Is that only because the balance was $0 at that time? |
yes it's just mock test. No real transaction |
|
Okay, sounds good. I will need to run a real transaction to test this, which will take ~1 day to settle. If we're confident it's working now, I can just test on staging. QA probably won't be able to test this, so it will have to be internal QA |
|
@youssef-lr all yours |
joekaufmanexpensify
left a comment
There was a problem hiding this comment.
Good for product.
|
🚧 youssef-lr has triggered a test Expensify/App build. You can view the workflow run here. |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
🚀 Deployed to staging by https://github.com/youssef-lr in version: 9.4.63-0 🚀
|
Help site reviewYes — a docs update is required. This PR gates Settle balance behind a confirmation modal, and one help article documented the old one-click flow. The affected article is How to Troubleshoot Expensify Card Issues, in the section on refreshing the workspace's Expensify Card limit. It said to click Settle Balance to trigger an early settlement in a single step — which no longer matches the app, and also used the wrong capitalization (the live label is Settle balance). I opened a draft help site PR with the fix:
What changed in the docsThe steps now read:
Copy verified against the merged strings in @situchan, please review the linked help site PR and confirm it reflects the current behavior. Then mark the linked help site PR |
|
🚀 Deployed to production by https://github.com/AndrewGable in version: 9.4.63-1 🚀
Bundle Size Analysis (Sentry): |





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.
handleSettleBalanceButtonClickinWorkspaceCardsListLabel.tsxnow opens adangerconfirm modal via theuseConfirmModalhook, andqueueExpensifyCardForBillingonly runs when the user confirms (it's a no-op on cancel/dismiss). This mirrors the existing high-consequence confirmation pattern used inWorkspaceMembersPage.tsx.New copy (
settleBalanceConfirmationTitle/settleBalanceConfirmationPrompt) was added underworkspace.expensifyCardinen.ts, and the existingsettleBalancestring is reused for the confirm button. This is New Expensify only — no Classic changes.Implements the approved proposal, using the
useConfirmModalhook per the C+ review (sinceConfirmModalis being deprecated).Fixed Issues
$ #96998
PROPOSAL: #96998 (comment)
Tests
Offline tests
N/A
QA Steps
Same as Tests
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)Avatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)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.