KQ-1775 · TEAM/CHARGEBACK

Team Chargeback

A cost-allocation view for a cloud account: the month's total is one long bar already divided into its teams, with what no rule claims hatched at its end and every billed month drawn below it. Pressing the total splits it: the segments part on springs.snap, then each slides down into its own row of a ranked list on springs.glide in one cascade while its share, trend and owner arrive beside it, and the unallocated remainder stays behind in the bar; pressing again folds the rows back up. Allocation is computed from tag rules and shared-cost splits, rows open with Enter to their spend by service and the rules that claimed it, arrow keys walk the rows and the month tabs, and a narrow container shortens each bar to its percentage.

KQ-1775 · TEAM/CHARGEBACK

Basinworks · cost by team

Chargeback for October · 5 teams

Press the total to split it into a ranked list of teams. Arrow keys walk the rows; Enter opens one.

  • Data platform28%
  • Payments28%
  • Search27%
  • Mobile8%
  • Growth4%

Month by month

  • Aug$44,739 in August, $2,822 unallocated
  • Sep$47,090 in September, $2,970 unallocated
  • Oct$50,320 in October, $3,140 unallocated

october · press the total to split it by team

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/team-chargeback

Props

barStyle
"solid" | "soft" | "outline"
default "soft"
How team bars are drawn: pigment fills, a tint with a lead edge, or outlines that fill on hover and focus.
density
"compact" | "cozy" | "roomy"
default "cozy"
Row height and bar thickness; roomy rows also show the owner's role.
palette
"basin" | "ember" | "spectrum" | "mono"
default "basin"
Team colours as pigments at fixed lightness: cool blues and teals, warm, spread hues, or ink steps.
costs
Record<string, ChargebackCost[]>
default defaultChargebackCosts
Costs per month id: { id, label (resource), service, amount, previous, tags }.
months
ChargebackMonth[]
default defaultChargebackMonths
The months as tabs, oldest first: { id, label, long }.
month / defaultMonth
string
default — / "2026-10"
The month shown, controlled or initial.
onMonthChange
(month: string) => void
default —
A month tab was chosen.
teams
ChargebackTeam[]
default defaultChargebackTeams
The teams costs are charged back to: { id, name, owner, role? }.
rules / defaultRules
ChargebackRule[]
default — / defaultChargebackRules
Allocation rules, controlled or initial: tag rules { kind: "tag", key, value, team, enabled } (key "resource" matches a cost's label) and split rules { kind: "split", key, value, method: "usage" | "even", enabled } for shared costs.
onRuleChange
(rule: ChargebackRule, rules: ChargebackRule[]) => void
default —
A rule was switched, re-methoded or added, with every rule.
onAllocate
(costId: string, teamId: string) => void
default —
An unallocated cost was assigned to a team; uncontrolled, a resource rule is added so the amount moves.
onExport
(csv: string, rows: ChargebackRow[]) => void
default —
Export was pressed: the month's allocation as CSV text and as rows.
split / defaultSplit
boolean
default — / false
Whether the total bar is split into team rows, controlled or initial. A host that flips it gets the same choreography as a press.
onSplitChange
(split: boolean) => void
default —
The total bar was pressed.
format
(amount: number) => string
default US dollars, no cents
Formats every amount.
status
"ready" | "loading" | "error"
default "ready"
Whether the costs have arrived; a month without costs says so on its own.
onRetry
() => void
default —
Try again was pressed after the costs failed to load.
account
string
default "Basinworks"
The account, in the heading.
height
number
default 540
Height of the surface in px; its body scrolls inside.
disabled
boolean
default false
Nothing can be split, switched or assigned.
className
string
default —
Classes for the outer surface.