fix: use grid/cell components

This commit is contained in:
Austin Pickett 2026-04-19 15:21:57 -04:00
parent 923539a46b
commit 60fd4b7d16
12 changed files with 434 additions and 181 deletions

View file

@ -9,6 +9,7 @@ import {
Wifi,
WifiOff,
} from "lucide-react";
import { Cell, Grid } from "@nous-research/ui/ui/components/grid/index";
import { api } from "@/lib/api";
import type { PlatformStatus, SessionInfo, StatusResponse } from "@/lib/api";
import { timeAgo, isoTimeAgo } from "@/lib/utils";
@ -23,8 +24,14 @@ export default function StatusPage() {
useEffect(() => {
const load = () => {
api.getStatus().then(setStatus).catch(() => {});
api.getSessions(50).then((resp) => setSessions(resp.sessions)).catch(() => {});
api
.getStatus()
.then(setStatus)
.catch(() => {});
api
.getSessions(50)
.then((resp) => setSessions(resp.sessions))
.catch(() => {});
};
load();
const interval = setInterval(load, 5000);
@ -39,13 +46,19 @@ export default function StatusPage() {
);
}
const PLATFORM_STATE_BADGE: Record<string, { variant: "success" | "warning" | "destructive"; label: string }> = {
const PLATFORM_STATE_BADGE: Record<
string,
{ variant: "success" | "warning" | "destructive"; label: string }
> = {
connected: { variant: "success", label: t.status.connected },
disconnected: { variant: "warning", label: t.status.disconnected },
fatal: { variant: "destructive", label: t.status.error },
};
const GATEWAY_STATE_DISPLAY: Record<string, { badge: "success" | "warning" | "destructive" | "outline"; label: string }> = {
const GATEWAY_STATE_DISPLAY: Record<
string,
{ badge: "success" | "warning" | "destructive" | "outline"; label: string }
> = {
running: { badge: "success", label: t.status.running },
starting: { badge: "warning", label: t.status.starting },
startup_failed: { badge: "destructive", label: t.status.failed },
@ -53,15 +66,19 @@ export default function StatusPage() {
};
function gatewayValue(): string {
if (status!.gateway_running && status!.gateway_health_url) return status!.gateway_health_url;
if (status!.gateway_running && status!.gateway_pid) return `${t.status.pid} ${status!.gateway_pid}`;
if (status!.gateway_running && status!.gateway_health_url)
return status!.gateway_health_url;
if (status!.gateway_running && status!.gateway_pid)
return `${t.status.pid} ${status!.gateway_pid}`;
if (status!.gateway_running) return t.status.runningRemote;
if (status!.gateway_state === "startup_failed") return t.status.startFailed;
return t.status.notRunning;
}
function gatewayBadge() {
const info = status!.gateway_state ? GATEWAY_STATE_DISPLAY[status!.gateway_state] : null;
const info = status!.gateway_state
? GATEWAY_STATE_DISPLAY[status!.gateway_state]
: null;
if (info) return info;
return status!.gateway_running
? { badge: "success" as const, label: t.status.running }
@ -88,9 +105,14 @@ export default function StatusPage() {
{
icon: Activity,
label: t.status.activeSessions,
value: status.active_sessions > 0 ? `${status.active_sessions} ${t.status.running.toLowerCase()}` : t.status.noneRunning,
value:
status.active_sessions > 0
? `${status.active_sessions} ${t.status.running.toLowerCase()}`
: t.status.noneRunning,
badgeText: status.active_sessions > 0 ? t.common.live : t.common.off,
badgeVariant: (status.active_sessions > 0 ? "success" : "outline") as "success" | "outline",
badgeVariant: (status.active_sessions > 0 ? "success" : "outline") as
| "success"
| "outline",
},
];
@ -106,9 +128,14 @@ export default function StatusPage() {
detail: status.gateway_exit_reason ?? undefined,
});
}
const failedPlatforms = platforms.filter(([, info]) => info.state === "fatal" || info.state === "disconnected");
const failedPlatforms = platforms.filter(
([, info]) => info.state === "fatal" || info.state === "disconnected",
);
for (const [name, info] of failedPlatforms) {
const stateLabel = info.state === "fatal" ? t.status.platformError : t.status.platformDisconnected;
const stateLabel =
info.state === "fatal"
? t.status.platformError
: t.status.platformDisconnected;
alerts.push({
message: `${name.charAt(0).toUpperCase() + name.slice(1)} ${stateLabel}`,
detail: info.error_message ?? undefined,
@ -117,7 +144,6 @@ export default function StatusPage() {
return (
<div className="flex flex-col gap-6">
{/* Alert banner — breaks grid monotony for critical states */}
{alerts.length > 0 && (
<div className="border border-destructive/30 bg-destructive/[0.06] p-4">
<div className="flex items-start gap-3">
@ -125,9 +151,13 @@ export default function StatusPage() {
<div className="flex flex-col gap-2 min-w-0">
{alerts.map((alert, i) => (
<div key={i}>
<p className="text-sm font-medium text-destructive">{alert.message}</p>
<p className="text-sm font-medium text-destructive">
{alert.message}
</p>
{alert.detail && (
<p className="text-xs text-destructive/70 mt-0.5">{alert.detail}</p>
<p className="text-xs text-destructive/70 mt-0.5">
{alert.detail}
</p>
)}
</div>
))}
@ -136,32 +166,41 @@ export default function StatusPage() {
</div>
)}
<div className="grid gap-4 sm:grid-cols-3">
<Grid className="border-b lg:!grid-cols-3">
{items.map(({ icon: Icon, label, value, badgeText, badgeVariant }) => (
<Card key={label} className="min-w-0 overflow-hidden">
<CardHeader className="flex flex-row items-center justify-between pb-2">
<Cell
key={label}
className="flex min-w-0 flex-col gap-2 overflow-hidden"
>
<div className="flex items-center justify-between">
<CardTitle className="text-sm font-medium">{label}</CardTitle>
<Icon className="h-4 w-4 text-muted-foreground" />
</CardHeader>
</div>
<CardContent>
<div className="text-2xl font-bold font-mondwest truncate" title={value}>{value}</div>
<div
className="truncate text-2xl font-bold font-mondwest"
title={value}
>
{value}
</div>
{badgeText && (
<Badge variant={badgeVariant} className="mt-2">
{badgeVariant === "success" && (
<span className="mr-1 inline-block h-1.5 w-1.5 animate-pulse rounded-full bg-current" />
)}
{badgeText}
</Badge>
)}
</CardContent>
</Card>
{badgeText && (
<Badge variant={badgeVariant} className="self-start">
{badgeVariant === "success" && (
<span className="mr-1 inline-block h-1.5 w-1.5 animate-pulse rounded-full bg-current" />
)}
{badgeText}
</Badge>
)}
</Cell>
))}
</div>
</Grid>
{platforms.length > 0 && (
<PlatformsCard platforms={platforms} platformStateBadge={PLATFORM_STATE_BADGE} />
<PlatformsCard
platforms={platforms}
platformStateBadge={PLATFORM_STATE_BADGE}
/>
)}
{activeSessions.length > 0 && (
@ -169,7 +208,9 @@ export default function StatusPage() {
<CardHeader>
<div className="flex items-center gap-2">
<Activity className="h-5 w-5 text-success" />
<CardTitle className="text-base">{t.status.activeSessions}</CardTitle>
<CardTitle className="text-base">
{t.status.activeSessions}
</CardTitle>
</div>
</CardHeader>
@ -181,7 +222,9 @@ export default function StatusPage() {
>
<div className="flex flex-col gap-1 min-w-0 w-full">
<div className="flex items-center gap-2">
<span className="font-medium text-sm truncate">{s.title ?? t.common.untitled}</span>
<span className="font-medium text-sm truncate">
{s.title ?? t.common.untitled}
</span>
<Badge variant="success" className="text-[10px] shrink-0">
<span className="mr-1 inline-block h-1.5 w-1.5 animate-pulse rounded-full bg-current" />
@ -190,7 +233,11 @@ export default function StatusPage() {
</div>
<span className="text-xs text-muted-foreground truncate">
<span className="font-mono-ui">{(s.model ?? t.common.unknown).split("/").pop()}</span> · {s.message_count} {t.common.msgs} · {timeAgo(s.last_active)}
<span className="font-mono-ui">
{(s.model ?? t.common.unknown).split("/").pop()}
</span>{" "}
· {s.message_count} {t.common.msgs} ·{" "}
{timeAgo(s.last_active)}
</span>
</div>
</div>
@ -204,7 +251,9 @@ export default function StatusPage() {
<CardHeader>
<div className="flex items-center gap-2">
<Clock className="h-5 w-5 text-muted-foreground" />
<CardTitle className="text-base">{t.status.recentSessions}</CardTitle>
<CardTitle className="text-base">
{t.status.recentSessions}
</CardTitle>
</div>
</CardHeader>
@ -215,10 +264,16 @@ export default function StatusPage() {
className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 border border-border p-3 w-full"
>
<div className="flex flex-col gap-1 min-w-0 w-full">
<span className="font-medium text-sm truncate">{s.title ?? t.common.untitled}</span>
<span className="font-medium text-sm truncate">
{s.title ?? t.common.untitled}
</span>
<span className="text-xs text-muted-foreground truncate">
<span className="font-mono-ui">{(s.model ?? t.common.unknown).split("/").pop()}</span> · {s.message_count} {t.common.msgs} · {timeAgo(s.last_active)}
<span className="font-mono-ui">
{(s.model ?? t.common.unknown).split("/").pop()}
</span>{" "}
· {s.message_count} {t.common.msgs} ·{" "}
{timeAgo(s.last_active)}
</span>
{s.preview && (
@ -228,7 +283,10 @@ export default function StatusPage() {
)}
</div>
<Badge variant="outline" className="text-[10px] shrink-0 self-start sm:self-center">
<Badge
variant="outline"
className="text-[10px] shrink-0 self-start sm:self-center"
>
<Database className="mr-1 h-3 w-3" />
{s.source ?? "local"}
</Badge>
@ -249,7 +307,9 @@ function PlatformsCard({ platforms, platformStateBadge }: PlatformsCardProps) {
<CardHeader>
<div className="flex items-center gap-2">
<Radio className="h-5 w-5 text-muted-foreground" />
<CardTitle className="text-base">{t.status.connectedPlatforms}</CardTitle>
<CardTitle className="text-base">
{t.status.connectedPlatforms}
</CardTitle>
</div>
</CardHeader>
@ -259,7 +319,12 @@ function PlatformsCard({ platforms, platformStateBadge }: PlatformsCardProps) {
variant: "outline" as const,
label: info.state,
};
const IconComponent = info.state === "connected" ? Wifi : info.state === "fatal" ? AlertTriangle : WifiOff;
const IconComponent =
info.state === "connected"
? Wifi
: info.state === "fatal"
? AlertTriangle
: WifiOff;
return (
<div
@ -267,19 +332,25 @@ function PlatformsCard({ platforms, platformStateBadge }: PlatformsCardProps) {
className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 border border-border p-3 w-full"
>
<div className="flex items-center gap-3 min-w-0 w-full">
<IconComponent className={`h-4 w-4 shrink-0 ${
info.state === "connected"
? "text-success"
: info.state === "fatal"
? "text-destructive"
: "text-warning"
}`} />
<IconComponent
className={`h-4 w-4 shrink-0 ${
info.state === "connected"
? "text-success"
: info.state === "fatal"
? "text-destructive"
: "text-warning"
}`}
/>
<div className="flex flex-col gap-0.5 min-w-0">
<span className="text-sm font-medium capitalize truncate">{name}</span>
<span className="text-sm font-medium capitalize truncate">
{name}
</span>
{info.error_message && (
<span className="text-xs text-destructive">{info.error_message}</span>
<span className="text-xs text-destructive">
{info.error_message}
</span>
)}
{info.updated_at && (
@ -290,7 +361,10 @@ function PlatformsCard({ platforms, platformStateBadge }: PlatformsCardProps) {
</div>
</div>
<Badge variant={display.variant} className="shrink-0 self-start sm:self-center">
<Badge
variant={display.variant}
className="shrink-0 self-start sm:self-center"
>
{display.variant === "success" && (
<span className="mr-1 inline-block h-1.5 w-1.5 animate-pulse rounded-full bg-current" />
)}
@ -306,5 +380,8 @@ function PlatformsCard({ platforms, platformStateBadge }: PlatformsCardProps) {
interface PlatformsCardProps {
platforms: [string, PlatformStatus][];
platformStateBadge: Record<string, { variant: "success" | "warning" | "destructive"; label: string }>;
platformStateBadge: Record<
string,
{ variant: "success" | "warning" | "destructive"; label: string }
>;
}