Skip to content

[Due for payment 2026-05-12] Add Locked VBA (Verified Bank Account) widget to Time Sensitive #83939

Description

@mountiny

Summary

When a user's Verified Bank Account (VBA) is locked — either a workspace VBA (policy.achAccount.state === 'LOCKED') or a personal bank account (accountData.state === 'LOCKED') — there is currently no indication on the HomePage and no guided flow to resolve it in NewDot.

In OldDot, users see a "Fix" button on their locked bank account in settings, which opens the UnlockWithdrawalBankAccount modal. That modal calls the BankAccount_UnlockSend / InitiateBankAccountUnlock API to create a Concierge conversation escalated to Operations for manual resolution.

We should add a widget to the Time Sensitive section on the HomePage that alerts users when they have a locked bank account and provides a CTA to initiate the unlock flow (matching OldDot behavior).

Current Behavior

  • The SETTLEMENT_ACCOUNT_LOCKED report action posts a chat message with a link to workspace settings, but the workspace settings page treats a locked VBA as "no VBA" (hasVBA only returns true for OPEN state), leading to a dead end.
  • BankAccount.isLocked() helper exists in src/libs/models/BankAccount.ts but is unused.
  • CONST.BANK_ACCOUNT.STATE.LOCKED constant is defined but not checked anywhere in the Time Sensitive section.
  • There is no equivalent of OldDot's BankAccount_UnlockSend API integration in NewDot.

Expected Behavior

A new widget appears in the Time Sensitive section when any of the user's bank accounts are in the LOCKED state:

  • Workspace VBAs: Show for workspace admins when policy.achAccount.state === 'LOCKED'
  • Personal bank accounts: Show when bankAccountList[].accountData.state === 'LOCKED'

The widget should display:

  • A lock icon with a danger-colored background
  • Title: "Your business bank account has been locked" (workspace) / "Your bank account has been locked" (personal)
  • Subtitle: workspace name (for workspace VBAs) or "Wallet" (for personal)
  • CTA button: "Fix" (reuse existing ctaFix translation, danger: true style)
  • On press: Call InitiateBankAccountUnlock API with the bankAccountID, then navigate to Concierge chat (matching OldDot behavior)

Data Availability

No new backend data is needed to detect locked VBAs:

  • policy.achAccount.state already comes down via ONYXKEYS.COLLECTION.POLICY and includes the LOCKED value
  • ONYXKEYS.BANK_ACCOUNT_LIST entries have accountData.state which can be LOCKED
  • Both are populated by existing OpenApp/ReconnectApp API responses

For the unlock CTA:

  • The InitiateBankAccountUnlock write command already exists on the backend (apiCommandTypes.php) — it needs to be integrated into the NewDot API layer
  • It accepts a bankAccountID parameter and creates a Concierge conversation escalated to Operations

Implementation Details

Architecture

Backend (OpenApp) → Onyx (COLLECTION.POLICY, BANK_ACCOUNT_LIST)
                          ↓
              TimeSensitiveSection detects locked state
                          ↓
              UnlockBankAccount widget renders
                          ↓
           CTA → InitiateBankAccountUnlock API → Concierge chat

Files to Change

  1. src/pages/home/TimeSensitiveSection/items/UnlockBankAccount.tsx (new)

    • Widget component using BaseWidgetItem (same pattern as FixCompanyCardConnection.tsx)
    • Props: bankAccountID, policyName?, maskedAccountNumber?
  2. src/pages/home/TimeSensitiveSection/index.tsx

    • Add detection logic: iterate adminPolicies checking policy.achAccount?.state === CONST.BANK_ACCOUNT.STATE.LOCKED
    • Add useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST) for personal locked accounts
    • Add condition to hasAnyTimeSensitiveContent guard
    • Render widget at appropriate priority (suggest between broken card connections and discount offers)
  3. src/libs/actions/BankAccounts.ts

    • Add initiateBankAccountUnlock(bankAccountID: number) function calling the InitiateBankAccountUnlock write command
  4. src/libs/API/types.ts

    • Add InitiateBankAccountUnlock to write commands and parameter types
  5. src/languages/en.ts, src/languages/pt-BR.ts, src/languages/zh-hans.ts

    • Add translation strings under homePage.timeSensitiveSection.unlockBankAccount

OldDot Reference

Key OldDot files for reference:

  • Web-Expensify/site/app/modals/settings/unlockWithdrawalBankAccount.jsx — unlock modal UI
  • Web-Expensify/site/app/settings/reimbursement/bankAccountView.jsx — locked state detection + "Fix" button
  • Web-Expensify/lib/BankAccountAPI.phpsendEmailToUnlock() backend logic
  • Web-Expensify/api.phpInitiateBankAccountUnlock command routing

Open Questions

  • Should the masked account number be displayed in the widget? The ACHAccount type has accountNumber (may be masked from backend) and personal BankAccount has description ("Account ending in XXXX"). Need to verify actual API response format.
  • Priority order within Time Sensitive section: suggested after card fraud but before broken card connections, since a locked VBA blocks reimbursements.

EXFY-18795

Issue OwnerCurrent Issue Owner: @ZhenjaHorbach

Metadata

Metadata

Labels

Type

No type
No fields configured for issues without a type.

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions