OSAGI

Monitoring Page

Dashboards

About

Monitoring page: the platform's status in one banner, a server filter, server cards with CPU, memory and disk, the latest service checks and the incidents, open ones first, with refresh. From the Ops Dashboards Pack → ai.osagi.finance/kits/ops-dashboards

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Monitoring Page to your project and fits it to your theme.

Code

How Monitoring Page is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

"use client";

import { useCallback, useEffect, useMemo, useState } from "react";
import {
  type MonitoringIncident,
  MonitoringPage,
  type MonitoringServer,
  type MonitoringService,
} from "@/components/ui/monitoring-page";

const settings = {
  failRefresh: false,
  incident: true,
  live: true,
};

/** A fixed clock to start from (the same on the server and in the browser). */
const BASE = Date.parse("2026-10-17T09:30:00Z");
const MINUTE = 60_000;
const TICK_MS = 2000;

const iso = (ms: number) => new Date(ms).toISOString();

const SERVERS: MonitoringServer[] = [
  {
    cpu: 42,
    disk: 61,
    id: "api",
    memory: 58,
    name: "api-eu-01",
    p95Ms: 182,
    region: "Frankfurt",
    role: "API",
    state: "healthy",
    uptimePct: 99.98,
  },
  {
    cpu: 71,
    disk: 78,
    id: "db",
    memory: 83,
    name: "db-eu-01",
    p95Ms: 24,
    region: "Frankfurt",
    role: "Database",
    state: "healthy",
    uptimePct: 100,
  },
  {
    cpu: 36,
    disk: 44,
    id: "worker",
    memory: 49,
    name: "worker-us-01",
    p95Ms: 640,
    region: "Virginia",
    role: "Worker",
    state: "healthy",
    uptimePct: 99.91,
  },
];

const SERVICES: MonitoringService[] = [
  {
    id: "web",
    message: "200 OK, 18 KB",
    name: "Web app",
    responseMs: 142,
    serverId: "api",
    state: "healthy",
  },
  {
    id: "public-api",
    message: "200 OK",
    name: "Public API",
    responseMs: 96,
    serverId: "api",
    state: "healthy",
  },
  {
    id: "postgres",
    message: "Connections 41 of 200",
    name: "Postgres",
    responseMs: 12,
    serverId: "db",
    state: "healthy",
  },
  {
    id: "queue",
    message: "212 jobs waiting",
    name: "Job queue",
    responseMs: 220,
    serverId: "worker",
    state: "healthy",
  },
  {
    id: "email",
    message: "Delivered 1,204 in the last hour",
    name: "Email sender",
    responseMs: 310,
    serverId: "worker",
    state: "healthy",
  },
];

const RESOLVED: MonitoringIncident[] = [
  {
    id: "inc-2",
    resolvedAt: iso(BASE - 26 * 60 * MINUTE + 9 * MINUTE),
    serverId: "db",
    severity: "warning",
    startedAt: iso(BASE - 26 * 60 * MINUTE),
    title: "Replica lag above 30 s",
  },
  {
    id: "inc-3",
    resolvedAt: iso(BASE - 3 * 24 * 60 * MINUTE + 41 * MINUTE),
    serverId: "api",
    severity: "critical",
    startedAt: iso(BASE - 3 * 24 * 60 * MINUTE),
    title: "Public API returned 502 after a deploy",
  },
];

/** A small value that moves a little with every tick, the same way every time. */
const wobble = (seed: number, tick: number) =>
  Math.round(Math.sin(seed * 12.9898 + tick * 0.7) * 6);

/** A fleet of three servers: switch an incident on, let the numbers move, make Refresh fail. */
export default function Demo(props: Partial<typeof settings>) {
  const s = { ...settings, ...props };
  const [tick, setTick] = useState(0);
  const [checkedAt, setCheckedAt] = useState(BASE);
  const now = BASE + tick * TICK_MS;

  useEffect(() => {
    if (!s.live) {
      return;
    }
    const timer = setInterval(() => setTick((t) => t + 1), TICK_MS);
    return () => clearInterval(timer);
  }, [s.live]);

  const servers = useMemo(
    () =>
      SERVERS.map((server, i) => ({
        ...server,
        cpu: Math.min(99, Math.max(3, server.cpu + wobble(i + 1, tick))),
        memory: Math.min(99, Math.max(3, server.memory + wobble(i + 7, tick))),
        ...(s.incident && server.id === "worker"
          ? { cpu: 96, p95Ms: 4200, state: "down" as const, uptimePct: 97.2 }
          : {}),
      })),
    [s.incident, tick]
  );
  const services = useMemo(
    () =>
      s.incident
        ? SERVICES.map((service) =>
            service.id === "queue"
              ? {
                  ...service,
                  message: "No answer within 10 s; 4,812 jobs waiting",
                  responseMs: null,
                  state: "down" as const,
                }
              : service.id === "email"
                ? {
                    ...service,
                    message: "Delivering slowly: 2.4 s per message",
                    responseMs: 2400,
                    state: "degraded" as const,
                  }
                : service
          )
        : SERVICES,
    [s.incident]
  );
  const incidents = useMemo(
    () =>
      s.incident
        ? [
            {
              id: "inc-1",
              resolvedAt: null,
              serverId: "worker",
              severity: "critical" as const,
              startedAt: iso(BASE - 12 * MINUTE),
              title: "Job queue stopped answering on worker-us-01",
            },
            ...RESOLVED,
          ]
        : RESOLVED,
    [s.incident]
  );

  const refresh = useCallback(async () => {
    await new Promise((resolve) => setTimeout(resolve, 800));
    if (s.failRefresh) {
      throw new Error("Monitoring did not answer. These are the last results.");
    }
    setCheckedAt(now);
  }, [now, s.failRefresh]);

  return (
    <div className="flex min-h-[720px] w-full justify-center bg-background p-4 sm:p-8">
      <MonitoringPage
        checkedAt={iso(checkedAt)}
        className="max-w-6xl"
        incidents={incidents}
        now={now}
        onRefresh={refresh}
        servers={servers}
        services={services}
      />
    </div>
  );
}

Dependencies

More by OSAGI

View all
  • AI Orb

    AI Orb

    83
  • KPI Tile

    KPI Tile

    28
  • OTP Input

    OTP Input

    12
  • Reasoning Thread

    Reasoning Thread

    8
  • Assistant Composer

    Assistant Composer

    8
  • Release Notes

    Release Notes

  • Docs TOC

    Docs TOC

  • Docs Search

    Docs Search

More in Dashboards

View all
  • Dashboard with Collapsible Sidebar

    Dashboard with Collapsible Sidebar

    2.4K
  • Sidebar

    Sidebar

    235
  • SaaS Template

    SaaS Template

    2.4K
  • Stats Bento

    Stats Bento

    1.1K
  • Financial Bento Grid

    Financial Bento Grid

    71
  • Stats cards with links

    Stats cards with links

    33
  • Course Design Cards

    Course Design Cards

    1.1K
  • Chat template

    Chat template

    598