Display

SLA Report

A contract-side availability settlement — real period length, overlap-merged downtime, a weighted or worst-service composite, an error budget allowed to read past 100%, and the service-credit rung the period landed on.

Preview in your theme

Loading preview…

"use client"

import * as React from "react"
import {
  Ban,
  CalendarCheck,
  CalendarClock,
  CircleAlert,
  CircleCheck,
  CircleSlash,
  Radio,
  TriangleAlert,
} from "lucide-react"
import { cn } from "@/lib/utils"

Installation

npx shadcn@latest add https://ui.zyeon.ai/r/sla-report.json

Prompt

Build a React + TypeScript + Tailwind "SlaReport" panel with zod and
lucide-react. This is the contract view of a billing period, not a live status
page: every figure on it is something a customer can re-derive with a
calculator, so arithmetic honesty outranks everything else.

Contract
- A zod schema (`slaReportSchema` in a sibling contract file) is the single
  source of truth: { status; periodLabel; periodStart; periodEnd; asOf;
  targetPercent; services[]; incidents[]; creditTiers[]; errorMessage? }.
  `status` is "loading" | "empty" | "error" | "ready" and is the panel's own
  render state, unrelated to whether the SLA was met.
- Every time is an ISO instant with an offset, never a pre-formatted label.
  periodEnd is EXCLUSIVE: Apr 1 -> May 1 is April, and its length is April's
  real length. A "30 days" constant is how a report is wrong by 3.3% every
  January; an object-level refine rejects periodEnd <= periodStart (guard the
  fields inside the refine — refinements still run after other checks fail).
- `asOf` is when the numbers were computed. asOf < periodEnd means the period
  is still open and every figure is a running total, not a settlement.
- service = { id; name; weight? } — weight is the composite's currency
  (revenue share, seats, traffic), defaulting to 1, with negative/non-finite
  read as 0.
- incident = { id; title; startedAt; endedAt (null = still open); severity;
  serviceIds (at least one); excluded? }. `excluded` is an announced
  maintenance window: listed and labelled, contributes zero downtime.
- creditTier = { belowPercent; creditPercent } — thresholds are exclusive and
  the ladder is NOT assumed sorted or monotonic.
- Panel props: rollup ("weighted" | "worst", default "weighted"); locale
  ("en-US"); timeZone ("UTC"); label; onRetry; emptyState; skeletonRows
  (clamped 1-12); showCreditTiers / showServices / showIncidents. Render never
  reads the clock, Math.random or any browser API — `asOf` is injected, so SSR
  and hydration are byte-identical.
- Export `summarizeSlaReport(data, rollup)` and the duration formatter, so a
  PDF/email/alert can reuse the same arithmetic. Two implementations of "what
  was our availability" is two answers.

Behavior
- Availability = (measured window - downtime) / measured window. The measured
  window is periodStart -> min(asOf, periodEnd); once settled it equals the
  full period. A 31-day month with 43m 12s of downtime reads 99.90%; the SAME
  downtime in a 28-day February reads 99.89%, because the denominator is the
  real month.
- Downtime is a UNION of intervals, never a sum. Clip each incident to the
  measured window, sort by start, and merge while the next start is at or
  before the current end (touching windows are contiguous). Two 30-minute
  incidents that overlap by 20 are 40 minutes, not 60 — measured on the
  shipped component, the naive sum turns a period that MET 99.9% into one
  that breached it and owes a 10% credit. When merging removed anything, say
  so in the incidents header: "2h 6m of incident time = 1h 58m impaired".
- Multi-service composites are never a plain mean. "weighted" averages
  per-service downtime by weight (a nightly batch job must not cancel out an
  API outage); "worst" reports the least-available service and tags the row
  that set it. Whichever is in force is printed on the report. All weights
  zero falls back to an unweighted mean rather than dividing by zero.
- Error budget = (1 - target) x FULL period, always — that is the contract's
  allowance, even mid-period. 99.9% over 30 days is 43m 12s (= 43.2 min);
  99.95% is 21m 36s. Consumption is NEVER clamped: "135% consumed" is the
  most important state this component can show, and clamping it hides the
  breach the report exists to surface. Past 100% the track rescales to what
  was actually spent so the overage gets its own hatched segment instead of
  being painted outside the track; aria-valuenow stays inside 0-100 (spec) and
  the unclamped figure travels in aria-valuetext.
- Credit ladder: apply the LARGEST credit among every rung the availability
  fell below, so a badly ordered ladder can't under-credit. 98.5% against
  (below 99.9% -> 10%, below 99% -> 25%, below 95% -> 50%) owes 25%, not 10%.
  The matching row gets aria-current and the headline says what is due.
- An open period must never read like a settled one: the tile is titled
  "Availability to date", the caption is "As of <instant> · <elapsed> of
  <period> elapsed" (settled says "Settled · full period measured"), the
  verdict chip is "On track"/"Below target" instead of "Met"/"Breached", and
  the credit is "Projected N% at the current availability", not money owed.
- Rounding may not contradict the verdict beside it. Precision grows until the
  printed figure agrees with the raw comparison: 99.8999% prints as "99.8999%"
  next to "Breached" rather than rounding to a target-matching "99.90%", and a
  period with 400 ms of downtime never prints "100.00%".
- ONE duration format everywhere (downtime, budget, remaining, per incident):
  every non-zero unit largest-first, zeros dropped — "43m 12s", "1h 5m",
  "1d 1h 5m 3s"; sub-second reads `<1s`, never "0s". Tone and breach decisions
  are made on raw milliseconds, never on the formatted string.
- Degenerate inputs are branches, not crashes: a zero-length measured window
  shows an em dash plus "No data"; a 100% target (zero budget) says so instead
  of dividing by zero; incidents naming an unmeasured service are listed but
  not counted.
- Four first-class branches on `status`. The header (period, target, window,
  open/closed) renders in ALL of them, because those are contract terms known
  before any data arrives. "empty" means nothing was MEASURED — deliberately
  not the same as a period with zero downtime, which is a perfect period and
  belongs in "ready".

Rendering & styling
- Semantic tokens only, no hex/oklch: bg-card, bg-muted (track, skeleton,
  section headers at /30), bg-primary (fill within budget), bg-destructive
  (+ /10 chips) past it, text-muted-foreground for secondary text, border for
  the hairline grid, focus-visible:ring-2 ring-ring. Do NOT reach for
  var(--chart-*) here — nothing is a series, and chart tokens are not text
  colours.
- Colour is never the only channel: every verdict is a chip with its own icon
  AND its own word, a service below target also gets a glyph plus an sr-only
  phrase, and the over-budget segment carries a diagonal texture
  (color-mix over --background) on top of the destructive fill.
- Two colour traps worth knowing: a bg-destructive/10 chip inside a
  bg-destructive/10 row stacks the tint and drops text-destructive from
  4.70:1 to 3.91:1 (make the inner chip transparent), and a green tick on the
  "period closed" chip reads as "the SLA was met" before any figure has been
  shown (use a neutral calendar glyph).
- Every figure is tabular-nums. Density keys off @container, not viewport
  breakpoints — the same report is a page section in one app and a 360px
  drawer in another. Rows are flex-wrap with min-w-0 + wrap-anywhere labels
  and figure groups that wrap inside themselves, so an 80-character service
  name cannot push the card wider than its container.
- Root is role="group" + aria-labelledby (a section element would add a
  landmark to every report on the page); the budget bar is a real
  role="progressbar"; loading exposes one sr-only role="status".
- Only the skeleton pulse animates, with motion-reduce:animate-none.

Customization levers
- Composite definition: `rollup` is the switch. Add a third rule (e.g. "any
  service down = platform down", the union across all services) by adding one
  branch in summarizeSlaReport — every tile and section reads the result, not
  the rule.
- Budget denominator: it is the full period by design. Switch it to the
  measured window for a "pace so far" reading; keep the label honest either
  way.
- Ladder: creditTiers is data. Two rungs, five rungs, or none (the section
  disappears). Add a `label` per rung if your contract names them.
- Sub-blocks: showCreditTiers / showServices / showIncidents each drop a
  section; single-service SLAs usually drop the service table.
- Exclusions: `excluded` currently means "announced maintenance". Widen it to
  a reason string and print it on the row when your contract distinguishes
  force majeure from planned work.
- Precision: AVAILABILITY_MAX_DECIMALS caps how far the figure may grow;
  raise it for four-nines contracts. The duration formatter is one function —
  swap it for Intl.DurationFormat where available, but keep it single.
- Density: px-4 py-2.5 rows read as a statement; drop to py-1.5 and h-1.5
  tracks for an internal SRE review board.

Concepts

  • Downtime is a union, not a sum — clipped incident windows are sorted and merged while the next start is at or before the current end, so overlapping outages are counted once. Measured on this component: two 30-minute incidents overlapping by 20 minutes are 40 minutes, and adding them up instead flips a period that met 99.9% into a breach owing a 10% credit.
  • The denominator is a real month — the period is the distance between two instants, so 43m 12s of downtime is 99.90% in a 31-day March and 99.89% in a 28-day February. Whenever the report can't measure the whole period yet it says so rather than quietly shrinking the denominator.
  • Error budget consumption is unclamped — the budget is (1 − target) × full period (43m 12s for 99.9% over 30 days, 21m 36s for 99.95%), and consumption is allowed to print 135%. Past 100% the track rescales to what was spent so the overage becomes a hatched segment inside the bar, instead of the number being pinned at exactly the value that would hide the breach.
  • Composite by weight or by worst, never by mean — a plain average across services lets a healthy nightly export cancel out an hour of API downtime. The rule in force is printed on the report, and under worst the row that set the number is tagged.
  • Verdict-safe rounding — precision grows until the printed percentage agrees with the met/breached comparison beside it, so 99.8999% never prints as a target-matching "99.90%", and a period with 400 ms of downtime never prints "100.00%".
  • To-date is not settled — an open period changes the tile title, the caption, the verdict wording and the credit line all at once; a running total that reads like a settlement is the one mistake in this component that ends up in an invoice.
  • Empty means unmeasured — a period with no incidents is a perfect period and belongs in ready at 100.00%. The empty branch is for the case where nothing was measured at all, which cannot be settled and cannot be credited.

On This Page