From b6ff57ddab98ff3878ab7971f0dd642801a03c5a Mon Sep 17 00:00:00 2001 From: viettranx Date: Thu, 19 Mar 2026 11:24:15 +0700 Subject: [PATCH] fix(ui): heartbeat logs crash + header/card improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix HeartbeatLog interface field names (snake_case → camelCase) to match backend JSON tags — fixes white screen crash on logs dialog open - Format duration with formatDuration(), show token usage per log entry - Fix logs dialog padding to match standard pattern + close button overlap - Show model override and last error on heartbeat card - Replace status badge with colored dot in agent header - Compact header badges on mobile (icon-only for evolving/static) - Switch sidebar to tablet breakpoint (1024px) for earlier collapse --- ui/web/src/components/layout/app-layout.tsx | 4 +- .../agents/agent-detail/agent-header.tsx | 39 ++++++++++++------- .../agent-detail/heartbeat-logs-dialog.tsx | 31 ++++++++------- .../overview-sections/heartbeat-card.tsx | 6 +++ .../pages/agents/hooks/use-agent-heartbeat.ts | 10 ++--- 5 files changed, 55 insertions(+), 35 deletions(-) diff --git a/ui/web/src/components/layout/app-layout.tsx b/ui/web/src/components/layout/app-layout.tsx index 6a144484..8a6bacd3 100644 --- a/ui/web/src/components/layout/app-layout.tsx +++ b/ui/web/src/components/layout/app-layout.tsx @@ -6,7 +6,7 @@ import { Topbar } from "./topbar"; import { ErrorBoundary } from "@/components/shared/error-boundary"; import { useUiStore } from "@/stores/use-ui-store"; import { useAuthStore } from "@/stores/use-auth-store"; -import { useIsMobile } from "@/hooks/use-media-query"; +import { useIsTablet } from "@/hooks/use-media-query"; import { cn } from "@/lib/utils"; export function AppLayout() { @@ -16,7 +16,7 @@ export function AppLayout() { const mobileSidebarOpen = useUiStore((s) => s.mobileSidebarOpen); const setMobileSidebarOpen = useUiStore((s) => s.setMobileSidebarOpen); const connected = useAuthStore((s) => s.connected); - const isMobile = useIsMobile(); + const isMobile = useIsTablet(); return (
diff --git a/ui/web/src/pages/agents/agent-detail/agent-header.tsx b/ui/web/src/pages/agents/agent-detail/agent-header.tsx index daf2b96f..5ad42c3e 100644 --- a/ui/web/src/pages/agents/agent-detail/agent-header.tsx +++ b/ui/web/src/pages/agents/agent-detail/agent-header.tsx @@ -7,6 +7,7 @@ import type { AgentData } from "@/types/agent"; import type { HeartbeatConfig } from "@/pages/agents/hooks/use-agent-heartbeat"; import { useCountdown } from "@/hooks/use-countdown"; import { agentDisplayName, agentKeyDisplay } from "./agent-display-utils"; +import { cn } from "@/lib/utils"; interface AgentHeaderProps { agent: AgentData; @@ -51,21 +52,29 @@ export function AgentHeader({ agent, heartbeat, onBack, onDelete, onAdvanced, on {agent.is_default && ( )} - - {agent.status === "summon_failed" ? t("detail.summonFailed") : agent.status} - - {agent.agent_type} + + + + {agent.status === "summon_failed" ? t("detail.summonFailed") : agent.status} + + + + + + {agent.agent_type} + {agent.agent_type === "predefined" ? "P" : "O"} + {agent.agent_type === "predefined" ? t("card.predefinedTooltip") : t("card.openTooltip")} @@ -78,8 +87,8 @@ export function AgentHeader({ agent, heartbeat, onBack, onDelete, onAdvanced, on variant={selfEvolve ? "default" : "outline"} className={`text-[10px] ${selfEvolve ? "bg-violet-100 text-violet-700 hover:bg-violet-100 dark:bg-violet-900/30 dark:text-violet-300" : "text-muted-foreground"}`} > - - {selfEvolve ? t("detail.evolving") : t("detail.static")} + + {selfEvolve ? t("detail.evolving") : t("detail.static")} diff --git a/ui/web/src/pages/agents/agent-detail/heartbeat-logs-dialog.tsx b/ui/web/src/pages/agents/agent-detail/heartbeat-logs-dialog.tsx index 802f3412..21010fc2 100644 --- a/ui/web/src/pages/agents/agent-detail/heartbeat-logs-dialog.tsx +++ b/ui/web/src/pages/agents/agent-detail/heartbeat-logs-dialog.tsx @@ -8,7 +8,7 @@ import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { useMinLoading } from "@/hooks/use-min-loading"; import type { HeartbeatLog } from "@/pages/agents/hooks/use-agent-heartbeat"; -import { formatRelativeTime, formatDate } from "@/lib/format"; +import { formatRelativeTime, formatDate, formatDuration } from "@/lib/format"; const PAGE_SIZE = 20; @@ -69,8 +69,8 @@ export function HeartbeatLogsDialog({ return ( - - + + {t("heartbeat.logsTitle")}