KQ-1655 · PAGER/ROTA
Pager Rota
An on-call week drawn as a dial — Monday at twelve o'clock, the week running clockwise, one ring per layer cut into wedges in each engineer's colour — with a now-hand, who is on call now, next and after that, and the selected shift's Page, Override and Swap. Adding an override opens a notch in the ring and the new wedge, in the covering engineer's colour, slides into it from 18px out along its bisector on springs.snap; when now moves the hand ticks there on springs.snap, unwrapped so it never runs backwards round the week, and time nobody covers is hatched and flagged. The dial is a grid with one tab stop: Left and Right walk the week, Up and Down change layer, Home and End jump, O overrides, S swaps and P pages, and narrow containers get a linear schedule with the same keys.
Gaugeworks platform on call
Week of Mon 12 · 3 layers
Left and Right move through the week, Up and Down change layer. O adds an override, S swaps with the next shift, P pages.
Wed 10:00 · Ravi Okonjo · until Wed 18:00 · 1 gap
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/pager-rota
Props
- dialStyle
- "rings" | "solid" | "line"
- default "rings"
- How the week is drawn: separate rings with gaps between shifts, rings that touch, or thin arcs with a dot at each handoff.
- layerCount
- number
- default 3
- How many layers are shown, outermost first (1–3).
- accent
- string
- default "var(--accent-bright)"
- The now-hand, the selection and Override, any CSS colour.
- rota / defaultRota
- Rota
- default — / defaultRota
- The rota, controlled or initial: { people: { id, name, color }[], layers: { id, label, shifts }[], overrides }.
- onRotaChange
- (rota: Rota) => void
- default —
- Fires from the override or swap that changed the rota, with the whole rota.
- onOverride
- (override: RotaOverride) => void
- default —
- An override was added: { id, layer, person, start, end }.
- onSwap
- (a: RotaShift, b: RotaShift) => void
- default —
- Two shifts traded people; a and b as they are after the swap.
- onPage
- (person: RotaPerson, segment: RotaSegment) => void
- default —
- Page was pressed (or P) for the person on the selected segment.
- now
- number
- default defaultRotaNow
- The current time in ms; the hand points at it. Never read from the clock.
- weekStart
- number
- default defaultRotaWeekStart
- Monday 00:00 of the week drawn, in ms.
- selected / defaultSelected
- string | null
- default — / the primary segment at now
- The selected segment id, controlled or initial.
- onSelectedChange
- (id: string) => void
- default —
- Fires from the key or press that moved the selection.
- status
- "ready" | "loading" | "error"
- default "ready"
- Whether the rota has loaded. Loading draws an empty dial; error adds a Retry banner.
- onRetry
- () => void
- default —
- Retry was pressed after the rota failed to load.
- team
- string
- default "Gaugeworks platform"
- The team, in the header and the grid's name.
- height
- number
- default 540
- Height of the rota in px; its body scrolls inside it.
- disabled
- boolean
- default false
- Nothing can be overridden, swapped or paged.
- className
- string
- default —
- Classes for the outer surface.
| Prop | Type | Default | Description |
|---|---|---|---|
| dialStyle | "rings" | "solid" | "line" | "rings" | How the week is drawn: separate rings with gaps between shifts, rings that touch, or thin arcs with a dot at each handoff. |
| layerCount | number | 3 | How many layers are shown, outermost first (1–3). |
| accent | string | "var(--accent-bright)" | The now-hand, the selection and Override, any CSS colour. |
| rota / defaultRota | Rota | — / defaultRota | The rota, controlled or initial: { people: { id, name, color }[], layers: { id, label, shifts }[], overrides }. |
| onRotaChange | (rota: Rota) => void | — | Fires from the override or swap that changed the rota, with the whole rota. |
| onOverride | (override: RotaOverride) => void | — | An override was added: { id, layer, person, start, end }. |
| onSwap | (a: RotaShift, b: RotaShift) => void | — | Two shifts traded people; a and b as they are after the swap. |
| onPage | (person: RotaPerson, segment: RotaSegment) => void | — | Page was pressed (or P) for the person on the selected segment. |
| now | number | defaultRotaNow | The current time in ms; the hand points at it. Never read from the clock. |
| weekStart | number | defaultRotaWeekStart | Monday 00:00 of the week drawn, in ms. |
| selected / defaultSelected | string | null | — / the primary segment at now | The selected segment id, controlled or initial. |
| onSelectedChange | (id: string) => void | — | Fires from the key or press that moved the selection. |
| status | "ready" | "loading" | "error" | "ready" | Whether the rota has loaded. Loading draws an empty dial; error adds a Retry banner. |
| onRetry | () => void | — | Retry was pressed after the rota failed to load. |
| team | string | "Gaugeworks platform" | The team, in the header and the grid's name. |
| height | number | 540 | Height of the rota in px; its body scrolls inside it. |
| disabled | boolean | false | Nothing can be overridden, swapped or paged. |
| className | string | — | Classes for the outer surface. |