import { useParams, useNavigate } from 'react-router-dom'; import { useQuery } from '@tanstack/react-query'; import { getAgent, getJobs } from '../api/client'; import PageHeader from '../components/PageHeader'; import StatusBadge from '../components/StatusBadge'; import ErrorState from '../components/ErrorState'; import { formatDateTime, timeAgo } from '../api/utils'; function InfoRow({ label, value }: { label: string; value: React.ReactNode }) { return (
{label} {value}
); } function heartbeatStatus(lastHeartbeat: string): string { if (!lastHeartbeat) return 'Offline'; const ago = Date.now() - new Date(lastHeartbeat).getTime(); if (ago < 5 * 60 * 1000) return 'Online'; if (ago < 15 * 60 * 1000) return 'Stale'; return 'Offline'; } export default function AgentDetailPage() { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const { data: agent, isLoading, error, refetch } = useQuery({ queryKey: ['agent', id], queryFn: () => getAgent(id!), enabled: !!id, refetchInterval: 10000, }); const { data: jobs } = useQuery({ queryKey: ['agent-jobs', id], queryFn: () => getJobs({ per_page: '10' }), enabled: !!id, }); // Filter jobs related to this agent (deployment jobs) const agentJobs = jobs?.data?.slice(0, 10) || []; if (isLoading) { return ( <>
Loading...
); } if (error || !agent) { return ( <> refetch()} /> ); } const health = agent.status || heartbeatStatus(agent.last_heartbeat_at); return ( <> navigate('/agents')} className="btn btn-ghost text-xs">Back } />
{/* Agent Info */}

Agent Details

} /> {agent.hostname || '—'}} /> {agent.ip_address || '—'}} /> {timeAgo(agent.last_heartbeat_at)} {formatDateTime(agent.last_heartbeat_at)} ) : '—' } />
{/* System Info */}

System Information

{agent.ip_address || '—'}} /> {agent.capabilities?.length ? (

Capabilities

{agent.capabilities.map((c) => ( {c} ))}
) : null} {agent.tags && Object.keys(agent.tags).length > 0 ? (

Tags

{Object.entries(agent.tags).map(([k, v]) => ( {k}: {v} ))}
) : null}
{/* Recent Jobs */}

Recent Jobs

{!agentJobs.length ? (

No recent jobs

) : (
{agentJobs.map(j => (
{j.type}
{j.id}
{j.certificate_id}
))}
)}
{/* Heartbeat Timeline */}

Heartbeat Status

{health}

{health === 'Online' && 'Agent is responding to heartbeat checks'} {health === 'Stale' && 'Agent has not sent a heartbeat recently'} {health === 'Offline' && 'Agent is not responding'}

); }