● Zion Boggan
repos/Darwin/face/src/components/health/HealthPanel.tsx
zionboggan.com ↗
94 lines · tsx
History for this file →
1
"use client";
2
 
3
import type { HealthEngine } from "@/lib/types";
4
 
5
interface HealthPanelProps {
6
  engines: HealthEngine[];
7
  ok: boolean;
8
}
9
 
10
function stalenessLabel(h: number): string {
11
  if (h < 1) return `${Math.round(h * 60)}m`;
12
  if (h < 24) return `${h.toFixed(1)}h`;
13
  return `${Math.round(h / 24)}d`;
14
}
15
 
16
function stalenessClass(h: number, status: "up" | "down"): string {
17
  if (status === "down") return "text-red-400";
18
  if (h < 2) return "text-teal-bio";
19
  if (h < 12) return "text-text-secondary";
20
  return "text-text-muted";
21
}
22
 
23
function EngineRow({ engine }: { engine: HealthEngine }) {
24
  const isUp = engine.status === "up";
25
  const stale = engine.staleness_h > 12;
26
 
27
  return (
28
    <div className="flex items-center gap-3 py-2 border-b border-surface-2 last:border-0">
29
      {}
30
      <div
31
        className={[
32
          "w-2 h-2 rounded-full flex-shrink-0",
33
          isUp && !stale ? "bg-teal-bio animate-pulse-slow" : "",
34
          isUp && stale ? "bg-text-muted" : "",
35
          !isUp ? "bg-red-500" : "",
36
        ].join(" ")}
37
      />
38
 
39
      {}
40
      <span
41
        className={[
42
          "font-mono text-label-sm uppercase tracking-widest flex-1",
43
          isUp ? "text-text-primary" : "text-text-muted",
44
        ].join(" ")}
45
      >
46
        {engine.engine}
47
      </span>
48
 
49
      {}
50
      <span className={`font-mono text-label-sm ${stalenessClass(engine.staleness_h, engine.status)}`}>
51
        {isUp ? stalenessLabel(engine.staleness_h) : "down"}
52
      </span>
53
    </div>
54
  );
55
}
56
 
57
export default function HealthPanel({ engines, ok }: HealthPanelProps) {
58
  const upCount = engines.filter((e) => e.status === "up").length;
59
  const freshCount = engines.filter((e) => e.status === "up" && e.staleness_h < 2).length;
60
 
61
  return (
62
    <div className="border border-surface-2 p-4">
63
      {}
64
      <div className="flex items-center justify-between mb-4">
65
        <div className="flex items-center gap-2">
66
          <div className={`w-2 h-2 rounded-full ${ok ? "bg-teal-bio animate-pulse-slow" : "bg-red-500"}`} />
67
          <span className="font-mono text-label-sm uppercase tracking-widest text-text-primary">
68
            Organism health
69
          </span>
70
        </div>
71
        <span className="font-mono text-label-sm text-text-muted">
72
          {upCount}/{engines.length} up
73
        </span>
74
      </div>
75
 
76
      {}
77
      <div>
78
        {engines.map((eng) => (
79
          <EngineRow key={eng.engine} engine={eng} />
80
        ))}
81
      </div>
82
 
83
      {}
84
      <div className="mt-4 pt-3 border-t border-surface-2 flex justify-between items-center">
85
        <span className="font-mono text-label-sm text-text-muted">
86
          {freshCount} nodes fresh
87
        </span>
88
        <span className={`font-mono text-label-sm ${ok ? "text-teal-bio" : "text-red-400"}`}>
89
          {ok ? "HEALTHY" : "DEGRADED"}
90
        </span>
91
      </div>
92
    </div>
93
  );
94
}