import { useState } from "react"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { AlertTriangle, Clock, RefreshCw } from "lucide-react"; import { toast } from "sonner"; import { Card } from "@/components/ui"; import { HostHeader } from "@/components/hosts/HostHeader"; import { StacksTable } from "@/components/stacks/StacksTable"; import { AttentionStrip } from "@/components/dashboard/AttentionStrip"; import { FleetKpiRow } from "@/components/dashboard/FleetKpiRow"; import { StackStatusBar } from "@/components/dashboard/StackStatusBar"; import { HostResourceTable } from "@/components/dashboard/HostResourceTable"; import { stacksApi } from "@/api/stacks"; import { systemApi } from "@/api/system"; import { agentsApi } from "@/api/agents"; import { dashboardApi } from "@/api/dashboard"; import { apiErrorMessage } from "@/api/client"; import { cn, relativeTime } from "@/lib/utils"; import { useAuthStore } from "@/store/auth"; import { useStackActions } from "@/hooks/useStackActions"; import type { Agent } from "@/types"; export function Dashboard() { const isAdmin = useAuthStore((s) => s.user?.role === "admin"); const { isBusy, statusFor, dismissStatus, start, stop, restart } = useStackActions(); const qc = useQueryClient(); const [refreshing, setRefreshing] = useState(false); const fleet = useQuery({ queryKey: ["dashboard-fleet"], queryFn: () => dashboardApi.fleet(), refetchInterval: 20000, }); const stacks = useQuery({ queryKey: ["stacks"], queryFn: stacksApi.list, refetchInterval: 5000 }); const stats = useQuery({ queryKey: ["stack-stats"], queryFn: stacksApi.stats, refetchInterval: 5000 }); const info = useQuery({ queryKey: ["system"], queryFn: systemApi.info, refetchInterval: 30000 }); const audit = useQuery({ queryKey: ["audit"], queryFn: () => systemApi.audit(10), refetchInterval: 10000 }); const agents = useQuery({ queryKey: ["agents"], queryFn: () => agentsApi.list(), refetchInterval: 15000 }); const hasAgents = (agents.data?.length ?? 0) > 0; const refreshFleet = async () => { setRefreshing(true); try { const data = await dashboardApi.fleet(true); qc.setQueryData(["dashboard-fleet"], data); } catch (e) { toast.error(apiErrorMessage(e)); } finally { setRefreshing(false); } }; return (
{/* ---- Header ---- */}

Overview

{/* ---- Fleet cockpit (attention + KPIs + status + hosts) ---- */} {fleet.isError && !fleet.data ? (
Couldn’t load fleet overview: {apiErrorMessage(fleet.error)}
) : ( <> {/* Needs attention */} {/* Fleet KPIs */} {fleet.data ? ( ) : (
{Array.from({ length: 6 }).map((_, i) => (
))}
)} {/* Status + hosts */}
{fleet.data ? ( ) : (
)} {fleet.data ? ( ) : (
)}
)} {/* ---- Local host stacks ---- */}
{hasAgents ? :

This host

}
{/* ---- Remote host stacks ---- */} {agents.data?.map((agent) => ( ))} {/* ---- Recent activity ---- */}

Recent activity

{audit.data && audit.data.length > 0 ? (
    {audit.data.map((a) => (
  • {a.user}{" "} {a.action}{" "} {a.target} {relativeTime(a.timestamp)}
  • ))}
) : (

No activity yet.

)}
); } /* ---------------------------------------------------------------------- */ /* Remote host stacks section */ /* ---------------------------------------------------------------------- */ function AgentDashboardSection({ agent, isAdmin }: { agent: Agent; isAdmin: boolean }) { const qc = useQueryClient(); const online = agent.status === "online"; const [busyId, setBusyId] = useState(null); const stacks = useQuery({ queryKey: ["agent-stacks", agent.id], queryFn: () => agentsApi.stacks(agent.id), enabled: online, refetchInterval: 8000, }); const stats = useQuery({ queryKey: ["agent-stack-stats", agent.id], queryFn: () => agentsApi.stackStats(agent.id), enabled: online, refetchInterval: 5000, }); const sys = useQuery({ queryKey: ["agent-system", agent.id], queryFn: () => agentsApi.system(agent.id), enabled: online, refetchInterval: 30000, }); const run = async (action: string, label: string, id: string) => { setBusyId(id); const t = toast.loading(`${label} ${id} on ${agent.name}…`); try { await agentsApi.action(agent.id, id, action); toast.success(`${label} ${id} ✓`, { id: t }); qc.invalidateQueries({ queryKey: ["agent-stacks", agent.id] }); qc.invalidateQueries({ queryKey: ["agent-stack-stats", agent.id] }); } catch (e) { toast.error(apiErrorMessage(e), { id: t }); } finally { setBusyId(null); } }; return (
{!online ? (

Host is {agent.status}. Check it under Settings → Remote hosts.

) : ( busyId === id} loading={stacks.isLoading} linkBase={`/hosts/${agent.id}/stacks`} onStart={(id) => run("start", "Starting", id)} onStop={(id) => run("stop", "Stopping", id)} onRestart={(id) => run("restart", "Restarting", id)} emptyText="No stacks on this host." /> )}
); }