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 => (
))}
)}
{/* 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'}
>
);
}