Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions apps/web/app/status/_components/StatusDashboard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"use client";

import { Dial, MetricCard, TimeSeriesChart } from "@konjoai/ui";
import type { TimeSeriesPoint } from "@konjoai/ui";
import { useLiveTimeSeries } from "@/lib/hooks";

/** Deterministic 30-day uptime seed — toki incident on days 20–22. */
const UPTIME_SEED: TimeSeriesPoint[] = Array.from({ length: 30 }, (_, i) => {
const incident = i >= 20 && i <= 22;
const base = incident
? 88 + Math.sin(i * 1.3) * 1.5
: 99.3 + Math.sin(i * 0.7) * 0.5;
return { t: i, value: base };
});

/** Live portfolio health monitoring — dial, KPI cards, 30-day uptime sparkline. */
export function StatusDashboard() {
const uptime = useLiveTimeSeries(UPTIME_SEED, 4000, 32, (t) =>
99.2 + Math.sin(t * 0.0008) * 0.4,
);

return (
<section className="mx-auto max-w-6xl px-6 pb-12">
<div className="glass-konjo rounded-konjo-xl p-6 sm:p-8">
<div className="grid grid-cols-1 gap-8 lg:grid-cols-[200px_1fr]">

<div className="flex flex-col items-center justify-center gap-2">
<Dial
value={89}
min={0}
max={100}
size={180}
label="Portfolio Health"
unit="%"
severity="ok"
sublabel="7 of 9 nominal"
/>
</div>

<div className="flex flex-col gap-5">
<div className="grid grid-cols-2 gap-3 sm:grid-cols-4">
<MetricCard
value={98.7}
label="30d Uptime"
unit="%"
severity="ok"
delta={0.2}
deltaLabel="vs prior 30d"
/>
<MetricCard
value={8.2}
label="p99 Latency"
unit="ms"
severity="warn"
delta={-1.1}
deltaLabel="vs prior"
/>
<MetricCard
value={9}
label="Endpoints"
severity="info"
/>
<MetricCard
value={1}
label="Incidents"
unit="/ 30d"
severity="warn"
delta={-2}
deltaLabel="vs prior"
/>
</div>

<div>
<p className="text-konjo-mono mb-3 text-[10px] uppercase tracking-widest text-konjo-fg-faint">
30-day uptime · toki incident d20–22
</p>
<TimeSeriesChart
data={uptime}
label="Uptime"
unit="%"
severity="ok"
height={120}
/>
</div>
</div>

</div>
</div>
</section>
);
}
50 changes: 40 additions & 10 deletions apps/web/app/status/page.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import type { Metadata } from "next";
import { StatusBadge } from "@konjoai/ui";
import { StatusBadge, severity as sevColor } from "@konjoai/ui";
import { Footer } from "@/app/_components/Footer";
import { Breadcrumbs } from "@/app/_components/Breadcrumbs";
import { StatusDashboard } from "@/app/status/_components/StatusDashboard";
import { PRODUCTS } from "@/lib/products";

export const metadata: Metadata = {
title: "Status — KonjoAI",
description: "Live health of the KonjoAI portfolio.",
};

// Build-time pinned timestamp — every render of this static page shows when
// the page was last regenerated. Replace with a live probe when an actual
// health endpoint exists for each subdomain.
const BUILD_TIME = new Date().toISOString();

function overallLevel(): "operational" | "degraded" | "outage" {
Expand Down Expand Up @@ -42,9 +40,16 @@ export default function StatusPage() {
</div>

<section className="mx-auto max-w-6xl px-6 pt-12 pb-8 sm:pt-16">
<p className="text-konjo-mono mb-3 text-xs uppercase tracking-widest text-konjo-fg-faint">
Portfolio health
</p>
<div className="mb-4 flex items-center gap-2">
<span
className="inline-block size-2.5 animate-pulse rounded-full"
style={{ background: overall === "operational" ? sevColor.ok : sevColor.warn }}
aria-hidden
/>
<p className="text-konjo-mono text-xs uppercase tracking-widest text-konjo-fg-faint">
Portfolio health
</p>
</div>
<h1 className="text-konjo-display text-konjo-gradient text-5xl font-semibold tracking-tight sm:text-6xl">
{OVERALL_COPY[overall]}
</h1>
Expand All @@ -60,11 +65,16 @@ export default function StatusPage() {
</dl>
</section>

<StatusDashboard />

<section className="mx-auto max-w-6xl px-6 pb-24">
<p className="text-konjo-mono mb-4 text-[10px] uppercase tracking-widest text-konjo-fg-faint">
Product endpoints
</p>
<ul role="list" className="grid grid-cols-1 gap-3">
{PRODUCTS.map((p) => (
<li key={p.slug}>
<article className="glass-konjo rounded-konjo-lg flex items-center justify-between gap-6 p-5">
<article className="glass-konjo rounded-konjo-lg flex items-center justify-between gap-4 p-5 transition-colors hover:bg-konjo-surface/60">
<div className="flex items-center gap-4">
<span
aria-hidden
Expand All @@ -83,13 +93,33 @@ export default function StatusPage() {
</div>
</div>

<div className="flex shrink-0 items-center gap-3">
<div className="flex shrink-0 items-center gap-4">
{/* Live headline metric */}
<div
className="text-konjo-mono hidden flex-col items-end sm:flex"
aria-label={`${p.metric.label}: ${p.metric.value}${p.metric.unit}`}
>
<span
className="text-lg font-semibold tabular-nums leading-none"
style={{ color: sevColor[p.metric.severity] }}
>
{p.metric.value}
<span className="ml-0.5 text-xs text-konjo-fg-muted">
{p.metric.unit}
</span>
</span>
<span className="mt-0.5 text-[10px] uppercase tracking-widest text-konjo-fg-faint">
{p.metric.label}
</span>
</div>

<StatusBadge level={p.status} lastCheckedAt={BUILD_TIME} />

<a
href={`https://${p.slug}.konjo.ai`}
target="_blank"
rel="noreferrer"
className="text-konjo-mono hidden rounded-konjo border border-konjo-line/60 px-2.5 py-1 text-[11px] text-konjo-fg-muted transition-colors hover:border-konjo-line hover:text-konjo-fg sm:inline-block"
className="text-konjo-mono hidden rounded-konjo border border-konjo-line/60 px-2.5 py-1 text-[11px] text-konjo-fg-muted transition-colors hover:border-konjo-line hover:text-konjo-fg lg:inline-block"
>
{p.slug}.konjo.ai ↗
</a>
Expand Down
Loading