Worked productFinance · a payments desk for a platform

Every figure names the rows it came from.

A payments desk for a platform: what came in, what is waiting to go out, what we spent on our own cards, and how much of the balance is actually ours to move today. Looking at it should settle whether a number on screen can be checked.

Preview interactive design
The operatorTreasury operationstwo admins, an approver, two members
The windowRolling 30 daysnever a calendar month
Declared statusSix fields, 28 valuesseven derived flags, none stored
The dataOne fixture set, in cents20 payments, 8 payouts, 22 expenses
Five figures, then the queues that owe them. · $162,232.15 net over the last 30 days, and $12,905.60 of payouts you can release right now.the screen the day starts on
The screensIn the order the work happens

Five screens, each answering the last one’s question.

Thirty registered routes, eleven of them top-level. Two destinations deliberately have no record screen — a payment and a payout are only ever read on the list that holds them, so a notification about one lands on that list rather than on a route with nothing behind it.

02The status set →
Screen 01

The queue that knows which payouts you can finish

The dashboard promises an approvable figure; this screen owes an answer for it. Three payouts are waiting on us and one can be released: another team holds the second for a dispute running on a different screen, and the third is above the operator’s own approval limit. The queue hides neither and offers neither.

  • $12,905.60 ready to approve — 1 of the 3 payouts waiting on us
  • One reads “Held by Risk — Compliance review — open dispute”
  • One reads “Above your $50,000.00 limit”, and names who can release it
/payoutsqueue · gated
Screen 02

Card spend, and the receipts holding it up

Our own money going out. Three expenses can be approved now; two more are waiting on a receipt and are recessed with the name of the person who owes it. Approve exists on those rows and is disabled with its reason, because the precondition is visible — not omitted, and not a sentence beside a live button.

  • 3 you can approve now, $2,332.00 across them
  • 2 waiting on a receipt, $1,183.00 — required over $75.00, and on meals and travel spend
  • A queried row names who is being waited on, and what was asked
/expensesqueue · gated
Screen 03

One card, its limit, and the window it is measured over

A meter rather than a row, because the question is how much of the limit is left. One card is past its limit and another is near it — and “near” is the policy’s 80% share rather than a number inside the component. The card’s whole ledger sits under the chart, so the meter is checkable against the rows that filled it.

  • $5,120.00 of $5,000.00 — over, at 102.4%
  • Another reads near at 82.6%, against the policy’s 80% share
  • Rolling 30 days, stated in words beside every figure it scopes
/cards/:idrecord · meter
Screen 04

The money the network is holding

Three open disputes hold $9,650.10 out of the balance. Two need evidence from us and one is under review, where the clock belongs to the network — so that record is offered no action at all, and the screen says who is being waited on instead.

  • $9,650.10 held back against the 3 still open
  • One is due in 22 hours, another in four days
  • The one under review is owed nothing — that clock runs up to 75 days
/disputes/:idlist-detail · thread
Screen 05

What of the balance is actually yours to move today

The three-part answer, and the end of the argument the other four screens started. Available is the only stored money figure in the product; committed is the payouts that have not landed and reserved is the open disputes, and both are summed from the two tables printed directly below them.

  • $412,806.44 available · $102,835.60 committed · $9,650.10 reserved
  • $525,292.14 on the platform — available plus everything claiming it
  • Each table’s footer states the figure it sums to
/balancesrecord · reconciliation
StatusA closed set with defined transitions

Six fields, and the words they refuse to hold.

The value everybody looks for in the payout enum is blocked, and it is not there: a held payout is awaiting_approval with a blocking record attached, and the block is computed at render. Late is absent for the same reason, from this field and from payments.

03The awkward cases →
awaiting_approvalapprovedin_transitpaidrejectedreturned
Six values, three of them terminal. Rejected is deliberately neutral rather than danger — turning a payout down is an ending we chose and can defend, while returned is the network failing us, and the two must not share a colour. Across the product: six status fields, twenty-eight values, and seven derived flag families beside them.
The dataFixtures that break a screen

Three records the desk has to be right about.

All three are entries in the fixture rather than hypotheticals, and each one is why a rule in the product exists.

04The audit →
Case 01A payout held by another team for a dispute on another screen
NaivelyIt sits in the queue with a live Approve button — or it drops out of both the queue and the count, and the operator never learns why the total moved.
Here$6,300.00, still in the queue at full weight and still badged awaiting approval, with the holding team, the reason and the dispute under the badge, and “Waiting on Risk” where the button would be. It is outside the approvable figure.
Case 02A card past a limit the settings screen owns
NaivelyThe meter clamps at 100%, or the limit and the warning share are constants inside the component, so the settings form edits a number nothing reads.
Here$5,120.00 of $5,000.00 at 102.4%, over. The 80% near share and the $75.00 receipt threshold live in one policy object, every predicate takes it as an argument, and the settings screen runs the real evaluator over the real expenses with the draft values.
Case 03An expense that cannot be approved yet
NaivelyApprove is hidden, so the row looks finished — or it is live, and the receipt rule is a sentence printed next to it.
Here$965.00 at a hotel. Approve is present and disabled, reading “A receipt is required before this can be approved — travel spend, 75 dollar threshold”, the row is recessed and names who is being waited on, and a “No receipt” badge sits beside the status.
The auditWhat checking it turned up

A figure on screen that nothing read.

There is no audit view and there should not be one — the audit is a design-time discipline. It found this: every payout row and the team screen printed an approval limit per person, and no payout predicate read either one. Approvability asked only whether the payout was held, so one at $61,480.00 counted as approvable for everybody.

05Back to the top →
Three of five can approve anything, together up to $575,000.00 a run. · the operator’s own limit is $50,000.00, so a $61,480.00 payout sits in her queue named, badged and unapprovable.checkable

Reckon

The chain travels.
The desk does not.

Available plus committed plus reserved is arithmetic any product can borrow. Which team is being waited on, and what the screen says instead of a button, is the part you have to decide.

Built for Claude Design · plain markdown · nothing to run