Spend approvalPro
The agent asks to spend over your limit: what, how far over, why, and a clear approve or decline that expires.
On this page
Installation
Usage
import { SpendApproval } from "@/components/ui/chatcn/pro/spend-approval"
<SpendApproval
agent="Shopping agent"
item={{ title: "Tarn 22 L daypack", merchant: "Northfold Supply" }}
amount={128}
limit={100}
reason="The only waterproof one that arrives before your trip."
expiresIn={300}
onApprove={({ raiseLimit }) => wallet.approve(request.id, raiseLimit)}
onDecline={() => wallet.decline(request.id)}
/>The limit meter is scaled to the request: the part inside your limit in quiet gray, the overrun hatched in amber, with a tick and label at the limit. The over amount is also written out, so the hatch is never the only signal. The agent's reason sits in a quote in its own words. Approval covers this purchase only; with suggestedLimit, a checkbox raises the limit at the same time. With expiresIn, a countdown runs in the corner and the request lapses on its own, saying nothing was charged. Pass status to drive it from your server instead.
Examples
States
Props
| Prop | Type | Notes |
|---|---|---|
agent | string | Who asks. Default "The agent" |
item | { title, merchant, image?, detail? } | What it wants to buy |
amount, limit, suggestedLimit | number | The request, the limit and an optional raise |
currency, locale | string | Prices format with Intl, tabular figures. Default USD, en-US |
reason | ReactNode | The agent's words |
expiresIn, onExpire | number, () => void | Seconds until it lapses |
status | "pending" | "approving" | "approved" | "declined" | "expired" | "error" | Controlled; otherwise it runs itself |
onApprove, onDecline | ({ raiseLimit }) => unknown | Promise<unknown>, () => void | Reject to show the error |
labels | Partial<SpendApprovalLabels> | Every string, for translation |
Keyboard
| Key | Action |
|---|---|
| Tab | The raise checkbox, Decline, then Approve |
| Enter or Space | Press the focused button |
Accessibility
- The card is named by its request line, which includes the amount.
- The countdown is read as Expires in, not as a bare clock.
- The outcome is a status message; errors are alerts with what to do.
Motion
The outcome line fades up 4 px in 180 ms. The countdown ticks without animation. Reduced motion keeps the fade only.