/ web / src / components / SidebarStatusStrip.tsx
SidebarStatusStrip.tsx
 1  import { Link } from "react-router-dom";
 2  import type { StatusResponse } from "@/lib/api";
 3  import { useSidebarStatus } from "@/hooks/useSidebarStatus";
 4  import { cn } from "@/lib/utils";
 5  import { useI18n } from "@/i18n";
 6  
 7  /** Gateway + session summary for the System sidebar block (no separate strip chrome). */
 8  export function SidebarStatusStrip() {
 9    const status = useSidebarStatus();
10    const { t } = useI18n();
11  
12    if (status === null) {
13      return (
14        <div className="px-5 py-1.5" aria-hidden>
15          <div className="h-2 w-[80%] max-w-full animate-pulse rounded-sm bg-midground/10" />
16        </div>
17      );
18    }
19  
20    const gw = gatewayLine(status, t);
21    const { activeSessionsLabel, gatewayStatusLabel } = t.app;
22  
23    return (
24      <Link
25        to="/sessions"
26        title={t.app.statusOverview}
27        className={cn(
28          "block text-left",
29          "px-5 pb-2 pt-0.5",
30          "text-muted-foreground/70",
31          "transition-colors hover:text-muted-foreground/90",
32          "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-midground/40",
33          "focus-visible:ring-inset",
34        )}
35      >
36        <div className="flex flex-col gap-1 font-mondwest text-[0.55rem] leading-snug tracking-[0.12em]">
37          <p className="break-words">
38            <span className="text-muted-foreground/50">{gatewayStatusLabel}</span>{" "}
39            <span className={cn("font-medium", gw.tone)}>{gw.label}</span>
40          </p>
41  
42          <p className="break-words">
43            <span className="text-muted-foreground/50">{activeSessionsLabel}</span>{" "}
44            <span className="tabular-nums text-muted-foreground/70">
45              {status.active_sessions}
46            </span>
47          </p>
48        </div>
49      </Link>
50    );
51  }
52  
53  function gatewayLine(
54    status: StatusResponse,
55    t: ReturnType<typeof useI18n>["t"],
56  ): { label: string; tone: string } {
57    const g = t.app.gatewayStrip;
58    const byState: Record<string, { label: string; tone: string }> = {
59      running: { label: g.running, tone: "text-success" },
60      starting: { label: g.starting, tone: "text-warning" },
61      startup_failed: { label: g.failed, tone: "text-destructive" },
62      stopped: { label: g.stopped, tone: "text-muted-foreground" },
63    };
64    if (status.gateway_state && byState[status.gateway_state]) {
65      return byState[status.gateway_state];
66    }
67    return status.gateway_running
68      ? { label: g.running, tone: "text-success" }
69      : { label: g.off, tone: "text-muted-foreground" };
70  }