Mapcn
blockuptime-monitorsource: ok · hit
Uptime Monitor
Status-page style map of edge network locations with live health, latency, and uptime.
npx shadcn add "https://mapcn.dev/r/uptime-monitor.json"free
Where it goes
stats
Interaction
animated-counter
Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.
Dependencies
Bundle weight · lightRegistry dependencies
@mapcn/map
Files4 · source cached 10/8/2026, 10:25:08 PM
src/registry/blocks/uptime-monitor/page.tsx
"use client";
import { Map, MapControls, MapGeoJSON } from "@/components/ui/map";
import { EdgeNodeMarker } from "./components/edge-node-marker";
import { StatusSidebar } from "./components/status-sidebar";
import { edgeNodes, mapView, WORLD_GEOJSON } from "./data";
export default function Page() {
return (
<div className="flex min-h-screen items-center justify-center p-4">
<div className="bg-card flex h-[500px] w-full max-w-4xl overflow-hidden rounded-xl border shadow-sm">
<StatusSidebar nodes={edgeNodes} />
<div className="relative min-w-0 flex-1">
<Map
blank
center={mapView.center}
zoom={mapView.zoom}
minZoom={mapView.minZoom}
maxZoom={mapView.maxZoom}
scrollZoom={false}
dragRotate={false}
pitchWithRotate={false}
>
<MapGeoJSON data={WORLD_GEOJSON} linePaint={false} />
{edgeNodes.map((node) => (
<EdgeNodeMarker key={node.id} node={node} />
))}
<MapControls className="bottom-2" />
</Map>
</div>
</div>
</div>
);
}
src/registry/blocks/uptime-monitor/data.ts
export type EdgeStatus = "operational" | "degraded" | "down";
export interface EdgeNode {
id: string;
city: string;
region: string;
lng: number;
lat: number;
status: EdgeStatus;
latency: number;
uptime: number;
}
export const statusMeta: Record<EdgeStatus, { dot: string; text: string }> = {
operational: {
dot: "bg-emerald-500",
text: "text-emerald-600 dark:text-emerald-400",
},
degraded: {
dot: "bg-amber-500",
text: "text-amber-600 dark:text-amber-400",
},
down: {
dot: "bg-red-500",
text: "text-red-600 dark:text-red-400",
},
};
/** Country borders served from a public CDN — swap in your own GeoJSON. */
export const WORLD_GEOJSON =
"https://cdn.jsdelivr.net/gh/nvkelso/natural-earth-vector@v5.1.2/geojson/ne_110m_admin_0_countries.geojson";
export const mapView = {
center: [5, 28] as [number, number],
zoom: 0.45,
minZoom: 0.45,
maxZoom: 2,
};
/** Edge network points of presence. */
export const edgeNodes: EdgeNode[] = [
{
id: "iad",
city: "Washington, D.C.",
region: "North America",
lng: -77.0369,
lat: 38.9072,
status: "operational",
latency: 24,
uptime: 99.99,
},
{
id: "sfo",
city: "San Francisco",
region: "North America",
lng: -122.4194,
lat: 37.7749,
status: "operational",
latency: 31,
uptime: 99.98,
},
{
id: "yyz",
city: "Toronto",
region: "North America",
lng: -79.3832,
lat: 43.6532,
status: "degraded",
latency: 88,
uptime: 99.21,
},
{
id: "gru",
city: "São Paulo",
region: "South America",
lng: -46.6333,
lat: -23.5505,
status: "operational",
latency: 47,
uptime: 99.95,
},
{
id: "lhr",
city: "London",
region: "Europe",
lng: -0.1276,
lat: 51.5074,
status: "operational",
latency: 19,
uptime: 99.99,
},
{
id: "fra",
city: "Frankfurt",
region: "Europe",
lng: 8.6821,
lat: 50.1109,
status: "operational",
latency: 22,
uptime: 99.97,
},
{
id: "cdg",
city: "Paris",
region: "Europe",
lng: 2.3522,
lat: 48.8566,
status: "down",
latency: 0,
uptime: 97.84,
},
{
id: "jnb",
city: "Johannesburg",
region: "Africa",
lng: 28.0473,
lat: -26.2041,
status: "operational",
latency: 64,
uptime: 99.92,
},
{
id: "bom",
city: "Mumbai",
region: "Asia Pacific",
lng: 72.8777,
lat: 19.076,
status: "operational",
latency: 41,
uptime: 99.96,
},
{
id: "sin",
city: "Singapore",
region: "Asia Pacific",
lng: 103.8198,
lat: 1.3521,
status: "degraded",
latency: 73,
uptime: 99.45,
},
{
id: "nrt",
city: "Tokyo",
region: "Asia Pacific",
lng: 139.6917,
lat: 35.6895,
status: "operational",
latency: 28,
uptime: 99.98,
},
{
id: "syd",
city: "Sydney",
region: "Asia Pacific",
lng: 151.2093,
lat: -33.8688,
status: "operational",
latency: 52,
uptime: 99.94,
},
{
id: "lax",
city: "Los Angeles",
region: "North America",
lng: -118.2437,
lat: 34.0522,
status: "operational",
latency: 33,
uptime: 99.97,
},
{
id: "ord",
city: "Chicago",
region: "North America",
lng: -87.6298,
lat: 41.8781,
status: "operational",
latency: 27,
uptime: 99.98,
},
{
id: "scl",
city: "Santiago",
region: "South America",
lng: -70.6693,
lat: -33.4489,
status: "operational",
latency: 58,
uptime: 99.91,
},
{
id: "ams",
city: "Amsterdam",
region: "Europe",
lng: 4.9041,
lat: 52.3676,
status: "operational",
latency: 21,
uptime: 99.98,
},
{
id: "mad",
city: "Madrid",
region: "Europe",
lng: -3.7038,
lat: 40.4168,
status: "degraded",
latency: 79,
uptime: 99.32,
},
{
id: "arn",
city: "Stockholm",
region: "Europe",
lng: 18.0686,
lat: 59.3293,
status: "operational",
latency: 26,
uptime: 99.96,
},
{
id: "dxb",
city: "Dubai",
region: "Middle East",
lng: 55.2708,
lat: 25.2048,
status: "operational",
latency: 44,
uptime: 99.95,
},
{
id: "hkg",
city: "Hong Kong",
region: "Asia Pacific",
lng: 114.1694,
lat: 22.3193,
status: "operational",
latency: 36,
uptime: 99.97,
},
{
id: "icn",
city: "Seoul",
region: "Asia Pacific",
lng: 126.978,
lat: 37.5665,
status: "operational",
latency: 30,
uptime: 99.98,
},
{
id: "los",
city: "Lagos",
region: "Africa",
lng: 3.3792,
lat: 6.5244,
status: "down",
latency: 0,
uptime: 98.12,
},
];
export interface NetworkSummary {
status: EdgeStatus;
label: string;
operational: number;
total: number;
avgUptime: number;
}
/** Derive the overall network health from the individual edge nodes. */
export function getNetworkSummary(nodes: EdgeNode[]): NetworkSummary {
const total = nodes.length;
const operational = nodes.filter((n) => n.status === "operational").length;
const hasDown = nodes.some((n) => n.status === "down");
const hasDegraded = nodes.some((n) => n.status === "degraded");
const status: EdgeStatus = hasDown
? "down"
: hasDegraded
? "degraded"
: "operational";
const label = hasDown
? "Partial outage"
: hasDegraded
? "Degraded performance"
: "All systems operational";
const avgUptime =
nodes.reduce((sum, n) => sum + n.uptime, 0) / Math.max(total, 1);
return { status, label, operational, total, avgUptime };
}
src/registry/blocks/uptime-monitor/components/edge-node-marker.tsx
"use client";
import { cn } from "@/lib/utils";
import { MapMarker, MarkerContent, MarkerTooltip } from "@/components/ui/map";
import { statusMeta, type EdgeNode } from "../data";
interface EdgeNodeMarkerProps {
node: EdgeNode;
}
export function EdgeNodeMarker({ node }: EdgeNodeMarkerProps) {
const meta = statusMeta[node.status];
return (
<MapMarker longitude={node.lng} latitude={node.lat}>
<MarkerContent>
<div className="group flex size-4 items-center justify-center">
<span
className={cn(
"inline-flex size-2 rounded-full transition-transform group-hover:scale-125",
meta.dot,
)}
/>
</div>
</MarkerContent>
<MarkerTooltip
offset={10}
className="bg-popover text-popover-foreground min-w-28 border p-2"
>
<div className="flex items-center gap-1.5">
<span className={cn("size-1.5 rounded-full", meta.dot)} />
<span className="text-[11px] font-medium">{node.city}</span>
<span className="text-muted-foreground ml-auto font-mono text-[11px] uppercase">
{node.id}
</span>
</div>
<div className="text-muted-foreground mt-1.5 flex items-center justify-between gap-3 text-[11px]">
<span>{node.status === "down" ? "—" : `${node.latency} ms`}</span>
<span className="tabular-nums">{node.uptime.toFixed(2)}%</span>
</div>
</MarkerTooltip>
</MapMarker>
);
}
src/registry/blocks/uptime-monitor/components/status-sidebar.tsx
"use client";
import { cn } from "@/lib/utils";
import { getNetworkSummary, statusMeta, type EdgeNode } from "../data";
interface StatusSidebarProps {
nodes: EdgeNode[];
}
export function StatusSidebar({ nodes }: StatusSidebarProps) {
const summary = getNetworkSummary(nodes);
const summaryMeta = statusMeta[summary.status];
return (
<div className="bg-card flex w-56 shrink-0 flex-col overflow-hidden border-r">
<div className="border-b p-3">
<p className="text-foreground text-sm font-semibold">Edge Network</p>
<div className="mt-2 flex items-center gap-1.5">
<span className={cn("size-2 rounded-full", summaryMeta.dot)} />
<span className={cn("text-xs font-medium", summaryMeta.text)}>
{summary.label}
</span>
</div>
<div className="mt-3 grid grid-cols-2 gap-2">
<div className="bg-background/60 rounded-md border p-2">
<p className="text-muted-foreground text-[10px] tracking-wide uppercase">
Uptime
</p>
<p className="text-foreground text-sm font-semibold tabular-nums">
{summary.avgUptime.toFixed(2)}%
</p>
</div>
<div className="bg-background/60 rounded-md border p-2">
<p className="text-muted-foreground text-[10px] tracking-wide uppercase">
Edges up
</p>
<p className="text-foreground text-sm font-semibold tabular-nums">
{summary.operational}
<span className="text-muted-foreground font-normal">
/{summary.total}
</span>
</p>
</div>
</div>
</div>
<ul className="flex-1 divide-y overflow-y-auto">
{nodes.map((node) => {
const meta = statusMeta[node.status];
return (
<li key={node.id} className="flex items-center gap-2.5 px-3 py-2">
<span className={cn("size-2 shrink-0 rounded-full", meta.dot)} />
<div className="min-w-0 flex-1">
<p className="text-foreground truncate text-xs font-medium">
{node.city}
</p>
<p className="text-muted-foreground truncate text-[10px]">
{node.region}
</p>
</div>
<div className="flex shrink-0 flex-col items-end">
<span className="text-muted-foreground font-mono text-[10px] uppercase">
{node.id}
</span>
<span className="text-foreground font-mono text-[10px] tabular-nums">
{node.status === "down" ? "—" : `${node.latency}ms`}
</span>
</div>
</li>
);
})}
</ul>
</div>
);
}