KQ-1769 · SEAT/PLANNER
Seat Planner
A seat management panel where every seat is a chair in a row: seated members sit on theirs as initials on a disc, empty paid seats are outlines and seats added in the draft are dashed accent chairs until confirmed. Adding a seat sets a new chair into the row from 16px above, landing on springs.recoil while its shadow spreads, a cascade apart when several arrive at once, and today's prorated charge rolls up; removing one lifts the chair away on the exit ease and opens the credit on springs.glide. Waiting members drag 1:1 onto an empty chair or seat through a menu; the chairs are one roving group (Left, Right, Home, End, Enter opens a chair's menu) and the count is a spinbutton driven by the arrow and Page keys that never falls below the seats in use.
10 seats · 8 taken · no changes
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/seat-planner
Props
- seats
- number
- default 10
- Seats paid for today.
- value / defaultValue
- number
- default — / seats
- The draft seat count, controlled or initial; the planner follows a controlled value whenever it changes.
- onSeatsChange
- (count) => void
- default —
- Fires from the stepper, a key or a menu with the new draft count.
- members
- SeatMember[]
- default defaultSeatMembers
- Everyone in the workspace: { id, name, role?, seated }.
- onAssign
- (memberId, chair | null) => void
- default —
- Fires when someone is seated (with the chair's index) or stood up (null).
- pricing
- SeatPricing
- default defaultSeatPricing
- Price per seat and the billing period: { perSeat, periodStart, periodEnd }.
- now
- number
- default Oct 17 2026, 14:00 UTC
- The moment proration counts from, in ms.
- maxSeats
- number
- default 40
- The most seats the plan allows.
- onConfirm
- (change) => void | Promise
- default —
- Confirm was pressed with { seats, added, removed, charge, credit, assignments }; a rejection keeps the draft and shows its message.
- onDiscard
- () => void
- default —
- The draft was thrown away.
- workspace
- string
- default "Coldbrook"
- The workspace's name.
- status
- "ready" | "loading" | "error"
- default "ready"
- Whether the seats have arrived.
- onRetry
- () => void
- default —
- Try again was pressed after the seats failed to load.
- chairStyle
- "chair" | "stool" | "dot"
- default "chair"
- How each seat is drawn.
- gap
- number
- default 10
- Space between chairs, in px.
- accent
- "cobalt" | "plum" | "mint" | "ink" | string
- default "cobalt"
- New chairs, the drop target and Confirm: a named pigment or any CSS colour.
- formatMoney
- (amount) => string
- default Intl USD
- How money is written.
- disabled
- boolean
- default false
- Seats and assignments are read-only.
- className
- string
- default —
- Classes for the panel; it caps at 36rem tall and scrolls inside past that.
| Prop | Type | Default | Description |
|---|---|---|---|
| seats | number | 10 | Seats paid for today. |
| value / defaultValue | number | — / seats | The draft seat count, controlled or initial; the planner follows a controlled value whenever it changes. |
| onSeatsChange | (count) => void | — | Fires from the stepper, a key or a menu with the new draft count. |
| members | SeatMember[] | defaultSeatMembers | Everyone in the workspace: { id, name, role?, seated }. |
| onAssign | (memberId, chair | null) => void | — | Fires when someone is seated (with the chair's index) or stood up (null). |
| pricing | SeatPricing | defaultSeatPricing | Price per seat and the billing period: { perSeat, periodStart, periodEnd }. |
| now | number | Oct 17 2026, 14:00 UTC | The moment proration counts from, in ms. |
| maxSeats | number | 40 | The most seats the plan allows. |
| onConfirm | (change) => void | Promise | — | Confirm was pressed with { seats, added, removed, charge, credit, assignments }; a rejection keeps the draft and shows its message. |
| onDiscard | () => void | — | The draft was thrown away. |
| workspace | string | "Coldbrook" | The workspace's name. |
| status | "ready" | "loading" | "error" | "ready" | Whether the seats have arrived. |
| onRetry | () => void | — | Try again was pressed after the seats failed to load. |
| chairStyle | "chair" | "stool" | "dot" | "chair" | How each seat is drawn. |
| gap | number | 10 | Space between chairs, in px. |
| accent | "cobalt" | "plum" | "mint" | "ink" | string | "cobalt" | New chairs, the drop target and Confirm: a named pigment or any CSS colour. |
| formatMoney | (amount) => string | Intl USD | How money is written. |
| disabled | boolean | false | Seats and assignments are read-only. |
| className | string | — | Classes for the panel; it caps at 36rem tall and scrolls inside past that. |