All checks were successful
React + Vite + TypeScript SPA: scaffold, operations-terminal design system, Bearer-token auth, and the Machines view. - Design system: OKLCH-tinted dark theme (ink-slate + signal-cyan), Hanken Grotesk + JetBrains Mono, status-color language (online/offline/granted/pending/denied/not_required), motion with prefers-reduced-motion honored. - Auth: token in sessionStorage via ref (never React state), protected routes, 401 session teardown, admin-gated per-agent-key UI. - Machines view: data table (sticky header, keyboard-activated rows, skeleton loading, actionable empty/error states), non-blocking detail drawer, delete confirm, admin key management with copy-once reveal. - UI primitives: Modal (focus trap + inert + portal + dialogStack), Drawer, Table, Badge/StatusDot, toast, states. - Typed API client normalizing the two error-envelope shapes. Passed Code Review (no blockers), impeccable critique-and-polish, and local gates (tsc/lint/build green). Dev-only Vite proxy to :3002. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
52 lines
1.5 KiB
TypeScript
52 lines
1.5 KiB
TypeScript
import { useAuth } from "../../auth/AuthContext";
|
|
import { useRelayStatus } from "../../lib/useRelayStatus";
|
|
import { Badge } from "../ui/Badge";
|
|
import { Button } from "../ui/Button";
|
|
import { LogoutIcon } from "./icons";
|
|
|
|
function roleTone(role: string | undefined): "accent" | "ok" | "neutral" {
|
|
if (role === "admin") return "accent";
|
|
if (role === "operator") return "ok";
|
|
return "neutral";
|
|
}
|
|
|
|
export function Topbar() {
|
|
const { user, logout } = useAuth();
|
|
const { live, checking } = useRelayStatus();
|
|
|
|
const relayClass = live ? "relay relay--live" : "relay relay--down";
|
|
const relayLabel = checking ? "probing" : live ? "live" : "offline";
|
|
|
|
return (
|
|
<header className="topbar">
|
|
<div
|
|
className={relayClass}
|
|
title="GuruConnect relay connection"
|
|
aria-label={`Relay ${relayLabel}`}
|
|
>
|
|
<span className="relay__pip" aria-hidden="true" />
|
|
<span>Relay</span>
|
|
<span className="relay__label mono">{relayLabel}</span>
|
|
</div>
|
|
|
|
<div className="topbar__spacer" />
|
|
|
|
<div className="topbar__user">
|
|
<div className="topbar__id">
|
|
<span className="topbar__username">{user?.username}</span>
|
|
</div>
|
|
<Badge tone={roleTone(user?.role)}>{user?.role ?? "—"}</Badge>
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={() => void logout()}
|
|
aria-label="Log out"
|
|
>
|
|
<LogoutIcon width={15} height={15} />
|
|
Logout
|
|
</Button>
|
|
</div>
|
|
</header>
|
|
);
|
|
}
|