← All work

Fintech · business banking · 2026

Kova

Making a six-figure payroll feel boring.

Business banking for teams that pay people in 38 countries. I redesigned payouts, the scariest button in the product, around one question: is anything unusual in this batch?

  • Product
  • UI/UX
  • Frontend
  • QA
Role
Concept project · product design + React
Year
2026
Disciplines
Product · UI/UX · Frontend · QA
app.kova.money

Good afternoon, Nadia

Sun 11 Oct · 3 approvals waiting

Total balance · USD equivalent
$4,826,310.42▲ 13.4% · 30D
$4.50M$5.00MFORECAST12 Sept27 SeptToday25 Oct
Payroll Money inProjected low $4.78M · 23 OctHover or ← →
Accounts5 currencies
  • $USD44% of balance$2,140,880.10
  • €EUR27% of balance€1,204,310.00
  • £GBP16% of balance£612,040.55
  • ◎USDC10% of balance482,900.00
  • ৳BDT3% of balance৳16,410,000.00
Cash flow · last 6 weeksNet $89k
In $2.08MOut $1.99M
Week of 5 Oct−$79k
Waiting for you3
  • October payroll42 people · 6 countriesPriya’s bank details changed 2 days ago$412,600.00
  • Figma · annual seatsVendor · due Tue$18,240.00
  • Convert €200,000 → USDRate locked 14:05 · 1.0871$217,420.00

01 The problem

Finance leads approve large payroll batches by exporting them to a spreadsheet first. The product shows everything, so it highlights nothing. The one recipient whose bank details changed is buried in row 31.

02 Key decisions

01

Show what changed, not what’s there

The batch highlights the one recipient whose bank details changed this week, and holds them, so the other 41 can go.

02

One approval bar, always in reach

Who approved, who’s left, and the exact action, pinned to the bottom. “Hold Priya & send 41” is a first-class button.

03

Routing you can read

Every payout shows its rail (ACH, SEPA, BEFTN or USDC) and arrival time before you sign.

03 Built in code

payouts/ApprovalBar.tsx
 1export function ApprovalBar({ batch }: Props) {
 2  const held = batch.items.filter(changedBankDetails({ days: 7 }))
 3  const ready = batch.items.length - held.length
 4  return (
 5    <Bar tone={held.length ? "review" : "ready"} aria-live="polite">
 6      {held.length > 0 && <Button>Hold {names(held)} & send {ready}</Button>}
 7      <Button primary holdMs={1200}>Approve {money(batch.total)}</Button>
 8    </Bar>
 9  )
10}

04 Then I tried to break it

CaseDesktopiPadScreen readerSlow 3G
Batch of 42, one changed✓✓✓✓
Approver goes offline mid-sign✓✓!✓
FX rate expires during review✓✓✓✕
RTL recipient names✓!✓✓
  • Double-click must never approve twice
  • Amounts are read with their currency
  • ৳ symbol never clips in the bar
  • Held recipients survive a refresh

✦ What I’d measure

Time to approvea batch, inside the product
Misrouted payoutsthe number that must stay at zero
Spreadsheet exportsshould drop as trust goes up

Concept project. These are the success measures I designed for, not reported results.

→ What I’d do next

“Recall within 10 minutes”: people trust a button more when they know they can undo it.

Next case studyRelayAn agent you actually let drive.
→