Approval Card
AI · ApprovalCard
The human-in-the-loop decision: what an agent wants to do, the exact payload, and the four answers A–D.
Props: title (the action as one sentence), text (why), children (the payload, verbatim), agent, project, family ("money" | "infra" | "comms" | "marketing" | "apps" | "legal") or familyLabel, age, escalation, trace (node), note, heading, onDecide(key, text), decision ({key, by, at, text}: renders the one-line record), options (override A–D; each {key, label, input}), approveLabel (relabels A), compact. Legacy onApprove / onDeny / onEdit and state still work.
- Always four answers in this order: A yes, as shown · B yes, with a change · C no, discard · D different instruction. B and D open a text field; ⌘/Ctrl+Enter sends.
- A–D work as keys while the card has focus.
- The payload is exactly what will run: the full mail, the amount and recipient, the command, the DNS change before and after. Never a summary.
- Show the risk family, the age and the escalation time. Nothing runs automatically when time is up.
- After a decision the card collapses to one line: letter, who, when (and the change or instruction).
- The A–D wording can be changed through
options.
Approval neededAtlas · SupportCommunication4 min
Send this email to ida@northwind.io
Atlas is set to drafts only, so it needs your OK.
Hi Ida,
happy to add 40 seats before your renewal on May 1. The quote is attached.
Best,
Mara
Escalates to the owner in 1:52 hOpen trace
C · No, discardRefund €10.00 to the card ending 4242Lukas · Yesterday 18:02
B · Yes, with a changeAdd TXT record ops-testMara Ellis · 08:10“Use TTL 300 instead”
Props
ApprovalCard
| Prop | Type | Notes |
|---|---|---|
title | ReactNode | Required |
text? | ReactNode | |
children? | ReactNode | |
agent? | string | |
project? | string | |
family? | RiskFamily | |
familyLabel? | string | |
age? | string | |
escalation? | ReactNode | |
trace? | ReactNode | |
note? | ReactNode | |
heading? | string | |
options? | ApprovalOption[] | |
approveLabel? | string | |
onDecide? | (key: ApprovalKey, text?: string) => void | |
decision? | ApprovalDecision | null | |
compact? | boolean | |
onApprove? | () => void | |
onDeny? | () => void | |
onEdit? | (text: string) => void | |
state? | "approved" | "denied" | |
className? | string | Extra class on the root element. |
style? | CSSProperties | Inline style on the root element. |