Web InnoventixFreeCode

Dashboard Stat Widgets

Original · free

assorted dashboard stat widgets

byWeb InnoventixReact + Tailwind
dashstatwidgetsdashboards
Open

Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.

npx shadcn@latest add https://webinnoventix.com/r/dash-stat-widgets.json
dash-stat-widgets.tsx
"use client";

import {
  useCallback,
  useId,
  useMemo,
  useRef,
  useState,
  type KeyboardEvent,
  type ReactNode,
} from "react";
import { motion, useReducedMotion } from "motion/react";

/* ------------------------------------------------------------------ */
/* Deterministic sample data (stable across server + client renders)   */
/* ------------------------------------------------------------------ */

const DAY_MS = 86_400_000;
const ANCHOR_MS = Date.UTC(2026, 6, 17);
const HISTORY = 180;

const MONTHS = [
  "Jan",
  "Feb",
  "Mar",
  "Apr",
  "May",
  "Jun",
  "Jul",
  "Aug",
  "Sep",
  "Oct",
  "Nov",
  "Dec",
] as const;

function rng(seed: number): () => number {
  let t = seed >>> 0;
  return () => {
    t += 0x6d2b79f5;
    let x = Math.imul(t ^ (t >>> 15), 1 | t);
    x ^= x + Math.imul(x ^ (x >>> 7), 61 | x);
    return ((x ^ (x >>> 14)) >>> 0) / 4294967296;
  };
}

function dayAt(index: number): Date {
  return new Date(ANCHOR_MS - (HISTORY - 1 - index) * DAY_MS);
}

const LABELS: string[] = Array.from({ length: HISTORY }, (_, i) => {
  const d = dayAt(i);
  return `${MONTHS[d.getUTCMonth()]} ${d.getUTCDate()}`;
});

interface SeriesSpec {
  seed: number;
  base: number;
  drift: number;
  noise: number;
  weekend: number;
  floor: number;
}

function makeSeries(spec: SeriesSpec): number[] {
  const rand = rng(spec.seed);
  const out: number[] = [];
  for (let i = 0; i < HISTORY; i += 1) {
    const dow = dayAt(i).getUTCDay();
    const weekend = dow === 0 || dow === 6 ? spec.weekend : 1;
    const raw =
      (spec.base + spec.drift * i + (rand() - 0.5) * 2 * spec.noise) * weekend;
    out.push(Math.max(spec.floor, raw));
  }
  return out;
}

const REVENUE = makeSeries({
  seed: 20_260_717,
  base: 26_800,
  drift: 118,
  noise: 1_450,
  weekend: 0.78,
  floor: 8_000,
});

const LATENCY = makeSeries({
  seed: 44_107,
  base: 331,
  drift: -0.21,
  noise: 24,
  weekend: 0.94,
  floor: 120,
});

const ERRORS = makeSeries({
  seed: 90_211,
  base: 0.61,
  drift: -0.0011,
  noise: 0.11,
  weekend: 0.8,
  floor: 0.03,
});

const DEPLOYS = makeSeries({
  seed: 31_337,
  base: 3.4,
  drift: 0.004,
  noise: 2.6,
  weekend: 0.28,
  floor: 0,
}).map((v) => Math.round(v));

const RANGES = [
  { key: "7D", days: 7, label: "Last 7 days" },
  { key: "30D", days: 30, label: "Last 30 days" },
  { key: "90D", days: 90, label: "Last 90 days" },
] as const;

type RangeKey = (typeof RANGES)[number]["key"];

const usd = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  maximumFractionDigits: 0,
});

const nf = new Intl.NumberFormat("en-US");

function sum(values: number[]): number {
  return values.reduce((a, b) => a + b, 0);
}

function avg(values: number[]): number {
  return values.length === 0 ? 0 : sum(values) / values.length;
}

function pctChange(current: number, prior: number): number {
  if (prior === 0) return 0;
  return ((current - prior) / prior) * 100;
}

/* ------------------------------------------------------------------ */
/* Static widget content                                               */
/* ------------------------------------------------------------------ */

interface Channel {
  id: string;
  name: string;
  share: number;
  sessions: number;
  bar: string;
  dot: string;
}

const CHANNELS: Channel[] = [
  {
    id: "organic",
    name: "Organic search",
    share: 41,
    sessions: 184_920,
    bar: "bg-indigo-500 dark:bg-indigo-400",
    dot: "bg-indigo-500 dark:bg-indigo-400",
  },
  {
    id: "direct",
    name: "Direct",
    share: 23,
    sessions: 103_740,
    bar: "bg-violet-500 dark:bg-violet-400",
    dot: "bg-violet-500 dark:bg-violet-400",
  },
  {
    id: "referral",
    name: "Referral",
    share: 16,
    sessions: 72_160,
    bar: "bg-sky-500 dark:bg-sky-400",
    dot: "bg-sky-500 dark:bg-sky-400",
  },
  {
    id: "paid",
    name: "Paid social",
    share: 12,
    sessions: 54_120,
    bar: "bg-emerald-500 dark:bg-emerald-400",
    dot: "bg-emerald-500 dark:bg-emerald-400",
  },
  {
    id: "email",
    name: "Lifecycle email",
    share: 8,
    sessions: 36_080,
    bar: "bg-amber-500 dark:bg-amber-400",
    dot: "bg-amber-500 dark:bg-amber-400",
  },
];

const TOTAL_SESSIONS = sum(CHANNELS.map((c) => c.sessions));

interface Goal {
  id: string;
  name: string;
  detail: string;
  progress: number;
  bar: string;
}

const GOALS: Goal[] = [
  {
    id: "arr",
    name: "Enterprise ARR",
    detail: "$4.21M of $5.40M target",
    progress: 78,
    bar: "bg-indigo-500 dark:bg-indigo-400",
  },
  {
    id: "csat",
    name: "Support CSAT",
    detail: "4.61 of 4.80 target",
    progress: 96,
    bar: "bg-emerald-500 dark:bg-emerald-400",
  },
  {
    id: "onboard",
    name: "Median onboarding",
    detail: "9.1 days against a 7.0 day target",
    progress: 77,
    bar: "bg-amber-500 dark:bg-amber-400",
  },
];

const BUDGET_USED = 63.4;
const HEAT_DAYS = 28;
const HEAT_COLS = 7;

function heatClass(count: number): string {
  if (count <= 0) return "bg-zinc-100 dark:bg-zinc-800";
  if (count <= 2) return "bg-emerald-200 dark:bg-emerald-900";
  if (count <= 4) return "bg-emerald-400 dark:bg-emerald-700";
  return "bg-emerald-600 dark:bg-emerald-400";
}

/* ------------------------------------------------------------------ */
/* Small presentational pieces                                         */
/* ------------------------------------------------------------------ */

function TrendIcon({ up }: { up: boolean }) {
  return (
    <svg
      viewBox="0 0 12 12"
      className={`h-3 w-3 ${up ? "" : "rotate-180"}`}
      aria-hidden="true"
      fill="none"
      stroke="currentColor"
      strokeWidth="1.6"
      strokeLinecap="round"
      strokeLinejoin="round"
    >
      <path d="M6 9.5V2.5" />
      <path d="M2.75 5.75 6 2.5l3.25 3.25" />
    </svg>
  );
}

function Delta({ value, good }: { value: number; good: boolean }) {
  const up = value >= 0;
  return (
    <span
      className={`inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-semibold tabular-nums ${
        good
          ? "bg-emerald-50 text-emerald-700 dark:bg-emerald-500/10 dark:text-emerald-300"
          : "bg-rose-50 text-rose-700 dark:bg-rose-500/10 dark:text-rose-300"
      }`}
    >
      <TrendIcon up={up} />
      <span className="sr-only">{up ? "Up" : "Down"} </span>
      {Math.abs(value).toFixed(1)}%
    </span>
  );
}

function Card({
  className = "",
  children,
}: {
  className?: string;
  children: ReactNode;
}) {
  return (
    <div
      className={`rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-800 dark:bg-zinc-900/60 ${className}`}
    >
      {children}
    </div>
  );
}

/* ------------------------------------------------------------------ */
/* Component                                                           */
/* ------------------------------------------------------------------ */

export default function DashStatWidgets() {
  const reduced = useReducedMotion();
  const uid = useId();
  const areaId = `${uid}-area`;
  const gaugeId = `${uid}-gauge`;

  const [range, setRange] = useState<RangeKey>("30D");
  const [compare, setCompare] = useState<boolean>(true);
  const [scrub, setScrub] = useState<number | null>(null);
  const [hidden, setHidden] = useState<string[]>([]);
  const [heatSel, setHeatSel] = useState<number>(HEAT_DAYS - 1);
  const [heatFocus, setHeatFocus] = useState<number>(HEAT_DAYS - 1);

  const rangeRefs = useRef<Array<HTMLButtonElement | null>>([]);
  const heatRefs = useRef<Array<HTMLButtonElement | null>>([]);

  const days = RANGES.find((r) => r.key === range)?.days ?? 30;

  const win = useMemo(() => {
    const slice = (arr: number[]) => ({
      now: arr.slice(HISTORY - days),
      prior: arr.slice(HISTORY - days * 2, HISTORY - days),
    });
    return {
      revenue: slice(REVENUE),
      latency: slice(LATENCY),
      errors: slice(ERRORS),
      deploys: slice(DEPLOYS),
      labels: LABELS.slice(HISTORY - days),
    };
  }, [days]);

  const revTotal = sum(win.revenue.now);
  const revPrior = sum(win.revenue.prior);
  const revDelta = pctChange(revTotal, revPrior);

  const chart = useMemo(() => {
    const pool = compare
      ? [...win.revenue.now, ...win.revenue.prior]
      : win.revenue.now;
    const rawLo = Math.min(...pool);
    const rawHi = Math.max(...pool);
    const pad = (rawHi - rawLo) * 0.14 || 1;
    const lo = rawLo - pad;
    const hi = rawHi + pad;
    const norm = (v: number) => (v - lo) / (hi - lo);

    const path = (values: number[]): string =>
      values
        .map((v, i) => {
          const x = (i / (values.length - 1)) * 100;
          const y = (1 - norm(v)) * 100;
          return `${i === 0 ? "M" : "L"}${x.toFixed(3)},${y.toFixed(3)}`;
        })
        .join(" ");

    return {
      line: path(win.revenue.now),
      area: `${path(win.revenue.now)} L100,100 L0,100 Z`,
      prior: path(win.revenue.prior),
      norm,
    };
  }, [compare, win.revenue]);

  const activeIndex = scrub ?? win.revenue.now.length - 1;
  const activeValue = win.revenue.now[activeIndex] ?? 0;
  const peak = Math.max(...win.revenue.now);
  const low = Math.min(...win.revenue.now);

  const tiles = useMemo(() => {
    const latencyNow = avg(win.latency.now);
    const latencyWas = avg(win.latency.prior);
    const errorNow = avg(win.errors.now);
    const errorWas = avg(win.errors.prior);
    const deployNow = sum(win.deploys.now);
    const deployWas = sum(win.deploys.prior);
    return [
      {
        id: "latency",
        label: "p95 latency",
        value: `${Math.round(latencyNow)} ms`,
        delta: pctChange(latencyNow, latencyWas),
        goodWhenDown: true,
        spark: win.latency.now.slice(-14),
        note: `Prior period ${Math.round(latencyWas)} ms`,
      },
      {
        id: "errors",
        label: "Error rate",
        value: `${errorNow.toFixed(2)}%`,
        delta: pctChange(errorNow, errorWas),
        goodWhenDown: true,
        spark: win.errors.now.slice(-14),
        note: `Prior period ${errorWas.toFixed(2)}%`,
      },
      {
        id: "deploys",
        label: "Deploys shipped",
        value: `${deployNow}`,
        delta: pctChange(deployNow, deployWas),
        goodWhenDown: false,
        spark: win.deploys.now.slice(-14),
        note: `Prior period ${deployWas} deploys`,
      },
    ];
  }, [win]);

  const visible = CHANNELS.filter((c) => !hidden.includes(c.id));
  const visibleTotal = sum(visible.map((c) => c.share));
  const visibleSessions = sum(visible.map((c) => c.sessions));

  const heat = useMemo(() => {
    const counts = DEPLOYS.slice(HISTORY - HEAT_DAYS);
    const labels = LABELS.slice(HISTORY - HEAT_DAYS);
    return counts.map((count, i) => ({ count, label: labels[i] }));
  }, []);

  const onRangeKeys = useCallback(
    (event: KeyboardEvent<HTMLDivElement>) => {
      const index = RANGES.findIndex((r) => r.key === range);
      let next = index;
      if (event.key === "ArrowRight" || event.key === "ArrowDown") {
        next = (index + 1) % RANGES.length;
      } else if (event.key === "ArrowLeft" || event.key === "ArrowUp") {
        next = (index - 1 + RANGES.length) % RANGES.length;
      } else if (event.key === "Home") {
        next = 0;
      } else if (event.key === "End") {
        next = RANGES.length - 1;
      } else {
        return;
      }
      event.preventDefault();
      setRange(RANGES[next].key);
      setScrub(null);
      rangeRefs.current[next]?.focus();
    },
    [range],
  );

  const onHeatKeys = useCallback(
    (event: KeyboardEvent<HTMLButtonElement>, index: number) => {
      let next = index;
      if (event.key === "ArrowRight") next = Math.min(HEAT_DAYS - 1, index + 1);
      else if (event.key === "ArrowLeft") next = Math.max(0, index - 1);
      else if (event.key === "ArrowDown")
        next = Math.min(HEAT_DAYS - 1, index + HEAT_COLS);
      else if (event.key === "ArrowUp") next = Math.max(0, index - HEAT_COLS);
      else if (event.key === "Home") next = 0;
      else if (event.key === "End") next = HEAT_DAYS - 1;
      else return;
      event.preventDefault();
      setHeatFocus(next);
      heatRefs.current[next]?.focus();
    },
    [],
  );

  const toggleChannel = useCallback((id: string) => {
    setHidden((prev) => {
      if (prev.includes(id)) return prev.filter((x) => x !== id);
      if (prev.length >= CHANNELS.length - 1) return prev;
      return [...prev, id];
    });
  }, []);

  const gaugeR = 54;
  const gaugeC = 2 * Math.PI * gaugeR;
  const gaugeArc = gaugeC * 0.75;
  const gaugeTarget = gaugeArc * (1 - BUDGET_USED / 100);

  return (
    <section className="relative w-full bg-zinc-50 px-4 py-20 sm:px-6 sm:py-28 lg:px-8 dark:bg-zinc-950">
      <style>{`
        @keyframes dsw-pulse {
          0%   { transform: scale(1);   opacity: 0.65; }
          70%  { transform: scale(2.4); opacity: 0; }
          100% { transform: scale(2.4); opacity: 0; }
        }
        .dsw-pulse-ring { animation: dsw-pulse 2.4s cubic-bezier(0.4, 0, 0.2, 1) infinite; }
        @keyframes dsw-sheen {
          0%   { transform: translateX(-100%); }
          55%  { transform: translateX(320%); }
          100% { transform: translateX(320%); }
        }
        .dsw-sheen { animation: dsw-sheen 3.6s ease-in-out infinite; }
        @media (prefers-reduced-motion: reduce) {
          .dsw-pulse-ring, .dsw-sheen { animation: none !important; }
        }
      `}</style>

      <div className="mx-auto max-w-6xl">
        {/* Header */}
        <div className="flex flex-col gap-6 sm:flex-row sm:items-end sm:justify-between">
          <div>
            <p className="flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.18em] text-indigo-600 dark:text-indigo-400">
              <span className="relative inline-flex h-2 w-2">
                <span className="dsw-pulse-ring absolute inline-flex h-full w-full rounded-full bg-emerald-500" />
                <span className="relative inline-flex h-2 w-2 rounded-full bg-emerald-500" />
              </span>
              Reliability &amp; growth
            </p>
            <h2 className="mt-3 text-3xl font-semibold tracking-tight text-zinc-900 sm:text-4xl dark:text-zinc-50">
              Platform overview
            </h2>
            <p className="mt-3 max-w-xl text-sm leading-relaxed text-zinc-600 dark:text-zinc-400">
              Every widget reads from one deterministic series. Change the range
              or scrub the chart and the totals, deltas and micro-charts all
              recompute together.
            </p>
          </div>

          <div className="flex flex-wrap items-center gap-3">
            <div
              role="radiogroup"
              aria-label="Reporting range"
              onKeyDown={onRangeKeys}
              className="inline-flex rounded-xl border border-zinc-200 bg-white p-1 shadow-sm dark:border-zinc-800 dark:bg-zinc-900"
            >
              {RANGES.map((r, i) => {
                const active = r.key === range;
                return (
                  <button
                    key={r.key}
                    ref={(el) => {
                      rangeRefs.current[i] = el;
                    }}
                    type="button"
                    role="radio"
                    aria-checked={active}
                    aria-label={r.label}
                    tabIndex={active ? 0 : -1}
                    onClick={() => {
                      setRange(r.key);
                      setScrub(null);
                    }}
                    className={`rounded-lg px-3 py-1.5 text-sm font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-zinc-900 ${
                      active
                        ? "bg-zinc-900 text-white dark:bg-zinc-100 dark:text-zinc-900"
                        : "text-zinc-600 hover:bg-zinc-100 dark:text-zinc-400 dark:hover:bg-zinc-800"
                    }`}
                  >
                    {r.key}
                  </button>
                );
              })}
            </div>

            <button
              type="button"
              role="switch"
              aria-checked={compare}
              onClick={() => setCompare((v) => !v)}
              className="group inline-flex items-center gap-2.5 rounded-xl border border-zinc-200 bg-white px-3 py-2 text-sm font-medium text-zinc-700 shadow-sm transition-colors hover:bg-zinc-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-50 dark:border-zinc-800 dark:bg-zinc-900 dark:text-zinc-300 dark:hover:bg-zinc-800 dark:focus-visible:ring-offset-zinc-950"
            >
              <span
                aria-hidden="true"
                className={`relative h-5 w-9 shrink-0 rounded-full transition-colors ${
                  compare
                    ? "bg-indigo-600 dark:bg-indigo-500"
                    : "bg-zinc-300 dark:bg-zinc-700"
                }`}
              >
                <span
                  className={`absolute top-0.5 h-4 w-4 rounded-full bg-white shadow transition-all ${
                    compare ? "left-[1.125rem]" : "left-0.5"
                  }`}
                />
              </span>
              Compare prior period
            </button>
          </div>
        </div>

        {/* Grid */}
        <div className="mt-10 grid grid-cols-1 gap-4 lg:grid-cols-6">
          {/* Revenue KPI + sparkline */}
          <Card className="lg:col-span-4">
            <div className="flex flex-wrap items-start justify-between gap-4">
              <div>
                <h3 className="text-sm font-medium text-zinc-600 dark:text-zinc-400">
                  Net revenue
                </h3>
                <p className="mt-1.5 text-3xl font-semibold tabular-nums tracking-tight text-zinc-900 dark:text-zinc-50">
                  {usd.format(revTotal)}
                </p>
                <p className="mt-1 text-xs text-zinc-500 dark:text-zinc-500">
                  {RANGES.find((r) => r.key === range)?.label} · prior period{" "}
                  {usd.format(revPrior)}
                </p>
              </div>
              <Delta value={revDelta} good={revDelta >= 0} />
            </div>

            <div className="relative mt-6">
              <div className="relative h-44 w-full rounded-xl border border-zinc-200 bg-zinc-50/60 sm:h-52 dark:border-zinc-800 dark:bg-zinc-950/40">
                <div className="absolute inset-0 overflow-hidden rounded-xl">
                  {[0, 25, 50, 75, 100].map((y) => (
                    <div
                      key={y}
                      aria-hidden="true"
                      className="absolute inset-x-0 border-t border-dashed border-zinc-200/80 dark:border-zinc-800/80"
                      style={{ top: `${y}%` }}
                    />
                  ))}

                  <svg
                    viewBox="0 0 100 100"
                    preserveAspectRatio="none"
                    className="absolute inset-0 h-full w-full"
                    aria-hidden="true"
                    focusable="false"
                  >
                    <defs>
                      <linearGradient id={areaId} x1="0" y1="0" x2="0" y2="1">
                        <stop
                          offset="0%"
                          stopColor="rgb(99 102 241)"
                          stopOpacity="0.34"
                        />
                        <stop
                          offset="100%"
                          stopColor="rgb(99 102 241)"
                          stopOpacity="0"
                        />
                      </linearGradient>
                    </defs>

                    {compare ? (
                      <path
                        d={chart.prior}
                        fill="none"
                        stroke="currentColor"
                        className="text-zinc-400 dark:text-zinc-600"
                        strokeWidth="1.5"
                        strokeDasharray="4 4"
                        vectorEffect="non-scaling-stroke"
                      />
                    ) : null}

                    <path d={chart.area} fill={`url(#${areaId})`} />
                    <motion.path
                      d={chart.line}
                      fill="none"
                      stroke="currentColor"
                      className="text-indigo-600 dark:text-indigo-400"
                      strokeWidth="2"
                      strokeLinecap="round"
                      strokeLinejoin="round"
                      vectorEffect="non-scaling-stroke"
                      initial={reduced ? false : { pathLength: 0 }}
                      animate={{ pathLength: 1 }}
                      transition={{
                        duration: reduced ? 0 : 0.9,
                        ease: "easeInOut",
                      }}
                    />
                  </svg>
                </div>

                {/* Scrub cursor */}
                <div
                  aria-hidden="true"
                  className="pointer-events-none absolute inset-y-0 w-px bg-indigo-500/70 dark:bg-indigo-400/70"
                  style={{
                    left: `${(activeIndex / Math.max(1, win.revenue.now.length - 1)) * 100}%`,
                  }}
                />
                <div
                  aria-hidden="true"
                  className="pointer-events-none absolute h-2.5 w-2.5 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white bg-indigo-600 shadow dark:border-zinc-900 dark:bg-indigo-400"
                  style={{
                    left: `${(activeIndex / Math.max(1, win.revenue.now.length - 1)) * 100}%`,
                    top: `${(1 - chart.norm(activeValue)) * 100}%`,
                  }}
                />
              </div>

              <input
                type="range"
                min={0}
                max={win.revenue.now.length - 1}
                step={1}
                value={activeIndex}
                onChange={(e) => setScrub(Number(e.target.value))}
                aria-label="Scrub daily net revenue"
                aria-valuetext={`${win.labels[activeIndex]}: ${usd.format(activeValue)}`}
                className="peer absolute inset-0 h-full w-full cursor-crosshair appearance-none bg-transparent opacity-0 focus:outline-none"
              />
              <div className="pointer-events-none absolute inset-0 rounded-xl ring-indigo-500 ring-offset-2 ring-offset-white peer-focus-visible:ring-2 dark:ring-offset-zinc-900" />
            </div>

            <div className="mt-4 flex flex-wrap items-center justify-between gap-3">
              <p className="text-sm text-zinc-600 dark:text-zinc-400">
                <span className="font-semibold text-zinc-900 dark:text-zinc-100">
                  {win.labels[activeIndex]}
                </span>{" "}
                · {usd.format(activeValue)}
              </p>
              <div className="flex flex-wrap gap-2 text-xs tabular-nums">
                <span className="rounded-md bg-zinc-100 px-2 py-1 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400">
                  Peak {usd.format(peak)}
                </span>
                <span className="rounded-md bg-zinc-100 px-2 py-1 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400">
                  Low {usd.format(low)}
                </span>
                <span className="rounded-md bg-zinc-100 px-2 py-1 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400">
                  Avg/day {usd.format(avg(win.revenue.now))}
                </span>
              </div>
            </div>
          </Card>

          {/* Error budget gauge */}
          <Card className="flex flex-col lg:col-span-2">
            <h3 className="text-sm font-medium text-zinc-600 dark:text-zinc-400">
              Error budget consumed
            </h3>
            <p className="mt-1 text-xs text-zinc-500 dark:text-zinc-500">
              Quarter to date · 99.9% SLO
            </p>

            <div className="relative mx-auto mt-4 h-40 w-40">
              <svg viewBox="0 0 140 140" className="h-full w-full">
                <title>{`Error budget consumed: ${BUDGET_USED}% of quarter`}</title>
                <defs>
                  <linearGradient id={gaugeId} x1="0" y1="0" x2="1" y2="1">
                    <stop offset="0%" stopColor="rgb(129 140 248)" />
                    <stop offset="55%" stopColor="rgb(139 92 246)" />
                    <stop offset="100%" stopColor="rgb(244 63 94)" />
                  </linearGradient>
                </defs>
                <circle
                  cx="70"
                  cy="70"
                  r={gaugeR}
                  fill="none"
                  stroke="currentColor"
                  className="text-zinc-200 dark:text-zinc-800"
                  strokeWidth="12"
                  strokeLinecap="round"
                  strokeDasharray={`${gaugeArc} ${gaugeC}`}
                  transform="rotate(-225 70 70)"
                />
                <motion.circle
                  cx="70"
                  cy="70"
                  r={gaugeR}
                  fill="none"
                  stroke={`url(#${gaugeId})`}
                  strokeWidth="12"
                  strokeLinecap="round"
                  strokeDasharray={`${gaugeArc} ${gaugeC}`}
                  transform="rotate(-225 70 70)"
                  initial={{
                    strokeDashoffset: reduced ? gaugeTarget : gaugeArc,
                  }}
                  animate={{ strokeDashoffset: gaugeTarget }}
                  transition={{
                    duration: reduced ? 0 : 1.1,
                    ease: "easeOut",
                  }}
                />
              </svg>
              <div className="pointer-events-none absolute inset-0 flex flex-col items-center justify-center">
                <span className="text-3xl font-semibold tabular-nums tracking-tight text-zinc-900 dark:text-zinc-50">
                  {BUDGET_USED}%
                </span>
                <span className="mt-0.5 text-xs text-zinc-500 dark:text-zinc-500">
                  of budget
                </span>
              </div>
            </div>

            <dl className="mt-auto grid grid-cols-2 gap-3 border-t border-zinc-200 pt-4 text-sm dark:border-zinc-800">
              <div>
                <dt className="text-xs text-zinc-500 dark:text-zinc-500">
                  Remaining
                </dt>
                <dd className="font-semibold tabular-nums text-zinc-900 dark:text-zinc-100">
                  {(100 - BUDGET_USED).toFixed(1)}%
                </dd>
              </div>
              <div>
                <dt className="text-xs text-zinc-500 dark:text-zinc-500">
                  Resets
                </dt>
                <dd className="font-semibold text-zinc-900 dark:text-zinc-100">
                  Oct 1
                </dd>
              </div>
            </dl>
          </Card>

          {/* Micro stat tiles */}
          {tiles.map((tile) => {
            const good = tile.goodWhenDown ? tile.delta < 0 : tile.delta >= 0;
            const sparkHi = Math.max(...tile.spark);
            const sparkLo = Math.min(...tile.spark);
            return (
              <Card key={tile.id} className="lg:col-span-2">
                <div className="flex items-start justify-between gap-3">
                  <h3 className="text-sm font-medium text-zinc-600 dark:text-zinc-400">
                    {tile.label}
                  </h3>
                  <Delta value={tile.delta} good={good} />
                </div>
                <p className="mt-2 text-2xl font-semibold tabular-nums tracking-tight text-zinc-900 dark:text-zinc-50">
                  {tile.value}
                </p>
                <div
                  className="mt-4 flex h-10 items-end gap-1"
                  aria-hidden="true"
                >
                  {tile.spark.map((v, i) => {
                    const h =
                      sparkHi === sparkLo
                        ? 55
                        : 18 + ((v - sparkLo) / (sparkHi - sparkLo)) * 82;
                    return (
                      <motion.span
                        key={`${tile.id}-${i}`}
                        className={`flex-1 rounded-sm ${
                          i === tile.spark.length - 1
                            ? "bg-indigo-500 dark:bg-indigo-400"
                            : "bg-zinc-200 dark:bg-zinc-700"
                        }`}
                        initial={reduced ? false : { scaleY: 0 }}
                        animate={{ scaleY: 1 }}
                        style={{ height: `${h}%`, transformOrigin: "bottom" }}
                        transition={{
                          duration: reduced ? 0 : 0.4,
                          delay: reduced ? 0 : i * 0.02,
                          ease: "easeOut",
                        }}
                      />
                    );
                  })}
                </div>
                <p className="mt-3 text-xs text-zinc-500 dark:text-zinc-500">
                  {tile.note}
                </p>
              </Card>
            );
          })}

          {/* Channel mix */}
          <Card className="flex flex-col lg:col-span-3">
            <div className="flex items-start justify-between gap-3">
              <div>
                <h3 className="text-sm font-medium text-zinc-600 dark:text-zinc-400">
                  Acquisition mix
                </h3>
                <p className="mt-1 text-xs text-zinc-500 dark:text-zinc-500">
                  {nf.format(TOTAL_SESSIONS)} sessions · toggle a channel to
                  re-weight the split
                </p>
              </div>
            </div>

            <div className="mt-5 flex h-3 w-full gap-0.5 overflow-hidden rounded-full">
              {visible.map((c) => (
                <motion.div
                  key={c.id}
                  className={`h-full first:rounded-l-full last:rounded-r-full ${c.bar}`}
                  initial={false}
                  animate={{ width: `${(c.share / visibleTotal) * 100}%` }}
                  transition={{ duration: reduced ? 0 : 0.45, ease: "easeOut" }}
                />
              ))}
            </div>

            <ul className="mt-5 space-y-1">
              {CHANNELS.map((c) => {
                const off = hidden.includes(c.id);
                const share = off ? 0 : (c.share / visibleTotal) * 100;
                return (
                  <li key={c.id}>
                    <button
                      type="button"
                      aria-pressed={!off}
                      onClick={() => toggleChannel(c.id)}
                      className="flex w-full items-center gap-3 rounded-lg px-2 py-1.5 text-left transition-colors hover:bg-zinc-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:hover:bg-zinc-800/60"
                    >
                      <span
                        aria-hidden="true"
                        className={`h-2.5 w-2.5 shrink-0 rounded-full transition-opacity ${c.dot} ${
                          off ? "opacity-25" : "opacity-100"
                        }`}
                      />
                      <span
                        className={`flex-1 text-sm transition-colors ${
                          off
                            ? "text-zinc-400 line-through dark:text-zinc-600"
                            : "text-zinc-800 dark:text-zinc-200"
                        }`}
                      >
                        {c.name}
                      </span>
                      <span className="text-xs tabular-nums text-zinc-500 dark:text-zinc-500">
                        {nf.format(c.sessions)}
                      </span>
                      <span
                        className={`w-12 text-right text-sm font-semibold tabular-nums ${
                          off
                            ? "text-zinc-400 dark:text-zinc-600"
                            : "text-zinc-900 dark:text-zinc-100"
                        }`}
                      >
                        {share.toFixed(1)}%
                      </span>
                    </button>
                  </li>
                );
              })}
            </ul>

            <div className="mt-auto flex items-center justify-between gap-4 border-t border-zinc-200 pt-4 dark:border-zinc-800">
              <p className="text-sm text-zinc-700 dark:text-zinc-300">
                <span className="font-semibold tabular-nums text-zinc-900 dark:text-zinc-100">
                  {nf.format(visibleSessions)}
                </span>{" "}
                sessions shown
              </p>
              <p className="text-xs tabular-nums text-zinc-500 dark:text-zinc-500">
                {visible.length} of {CHANNELS.length} channels
              </p>
            </div>
          </Card>

          {/* Deploy heatmap */}
          <Card className="lg:col-span-3">
            <div className="flex items-start justify-between gap-3">
              <div>
                <h3 className="text-sm font-medium text-zinc-600 dark:text-zinc-400">
                  Deploy activity
                </h3>
                <p className="mt-1 text-xs text-zinc-500 dark:text-zinc-500">
                  Last 28 days · {sum(heat.map((h) => h.count))} releases to
                  production
                </p>
              </div>
            </div>

            <div
              role="group"
              aria-label="Deploys per day, last 28 days"
              className="mt-5 grid grid-cols-7 gap-1.5"
            >
              {heat.map((cell, i) => {
                const selected = i === heatSel;
                return (
                  <button
                    key={cell.label}
                    ref={(el) => {
                      heatRefs.current[i] = el;
                    }}
                    type="button"
                    tabIndex={i === heatFocus ? 0 : -1}
                    aria-pressed={selected}
                    aria-label={`${cell.label}: ${cell.count} ${
                      cell.count === 1 ? "deploy" : "deploys"
                    }`}
                    onClick={() => {
                      setHeatSel(i);
                      setHeatFocus(i);
                    }}
                    onFocus={() => setHeatFocus(i)}
                    onKeyDown={(e) => onHeatKeys(e, i)}
                    className={`aspect-square rounded-md transition-transform hover:scale-110 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-zinc-900 ${heatClass(
                      cell.count,
                    )} ${
                      selected
                        ? "ring-2 ring-zinc-900 ring-offset-2 ring-offset-white dark:ring-zinc-100 dark:ring-offset-zinc-900"
                        : ""
                    }`}
                  />
                );
              })}
            </div>

            <div className="mt-5 flex items-center justify-between gap-4 border-t border-zinc-200 pt-4 dark:border-zinc-800">
              <p className="text-sm text-zinc-700 dark:text-zinc-300">
                <span className="font-semibold text-zinc-900 dark:text-zinc-100">
                  {heat[heatSel]?.label}
                </span>{" "}
                · {heat[heatSel]?.count}{" "}
                {heat[heatSel]?.count === 1 ? "deploy" : "deploys"}
              </p>
              <div className="flex items-center gap-1.5">
                <span className="text-xs text-zinc-500 dark:text-zinc-500">
                  Less
                </span>
                {[0, 2, 4, 6].map((n) => (
                  <span
                    key={n}
                    aria-hidden="true"
                    className={`h-3 w-3 rounded-sm ${heatClass(n)}`}
                  />
                ))}
                <span className="text-xs text-zinc-500 dark:text-zinc-500">
                  More
                </span>
              </div>
            </div>
          </Card>

          {/* Quarterly targets */}
          <Card className="lg:col-span-6">
            <h3 className="text-sm font-medium text-zinc-600 dark:text-zinc-400">
              Quarterly targets
            </h3>
            <div className="mt-5 grid grid-cols-1 gap-6 sm:grid-cols-3">
              {GOALS.map((goal, i) => (
                <div key={goal.id}>
                  <div className="flex items-baseline justify-between gap-2">
                    <p className="text-sm font-medium text-zinc-800 dark:text-zinc-200">
                      {goal.name}
                    </p>
                    <p className="text-sm font-semibold tabular-nums text-zinc-900 dark:text-zinc-100">
                      {goal.progress}%
                    </p>
                  </div>
                  <div
                    className="relative mt-2 h-2 w-full overflow-hidden rounded-full bg-zinc-100 dark:bg-zinc-800"
                    role="progressbar"
                    aria-valuemin={0}
                    aria-valuemax={100}
                    aria-valuenow={goal.progress}
                    aria-label={`${goal.name}: ${goal.detail}`}
                  >
                    <motion.div
                      className={`relative h-full overflow-hidden rounded-full ${goal.bar}`}
                      initial={reduced ? false : { width: 0 }}
                      animate={{ width: `${goal.progress}%` }}
                      transition={{
                        duration: reduced ? 0 : 0.8,
                        delay: reduced ? 0 : 0.1 + i * 0.1,
                        ease: "easeOut",
                      }}
                    >
                      <span
                        aria-hidden="true"
                        className="dsw-sheen absolute inset-y-0 -left-1/3 w-1/3 bg-white/40"
                      />
                    </motion.div>
                  </div>
                  <p className="mt-2 text-xs text-zinc-500 dark:text-zinc-500">
                    {goal.detail}
                  </p>
                </div>
              ))}
            </div>
          </Card>
        </div>
      </div>
    </section>
  );
}

Dependencies

motion

Licence

Built by Web Innoventix. Free for personal and commercial use, no attribution required.

Built by Web Innoventix

Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.

Get a free quote

Similar components

Browse all →