import * as React from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import type { Agent } from '@sentryagent/idp-sdk';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { ConfirmDialog } from '@/components/ui/dialog';
import { getClient } from '@/lib/client';
type BadgeVariant = 'success' | 'warning' | 'danger';
/** Maps AgentStatus to a Badge variant. */
function statusVariant(status: Agent['status']): BadgeVariant {
switch (status) {
case 'active': return 'success';
case 'suspended': return 'warning';
case 'decommissioned': return 'danger';
}
}
/** Formats an ISO timestamp to a readable local date-time string. */
function formatDateTime(iso: string): string {
return new Date(iso).toLocaleString(undefined, {
year: 'numeric', month: 'short', day: 'numeric',
hour: '2-digit', minute: '2-digit',
});
}
interface DetailRowProps {
label: string;
value: string;
}
/** Single label/value row in the detail card. */
function DetailRow({ label, value }: DetailRowProps): React.JSX.Element {
return (
{label}
{value}
);
}
type DialogAction = 'suspend' | 'reactivate';
/**
* Agent Detail page — shows all agent fields and provides suspend/reactivate actions.
* Route: /dashboard/agents/:agentId
*/
export default function AgentDetail(): React.JSX.Element {
const { agentId } = useParams<{ agentId: string }>();
const navigate = useNavigate();
const [agent, setAgent] = React.useState(null);
const [loading, setLoading] = React.useState(true);
const [error, setError] = React.useState(null);
const [actionLoading, setActionLoading] = React.useState(false);
const [dialog, setDialog] = React.useState(null);
React.useEffect(() => {
if (!agentId) return;
let cancelled = false;
setLoading(true);
setError(null);
const fetchAgent = async (): Promise => {
try {
const result = await getClient().agents.getAgent(agentId);
if (!cancelled) setAgent(result);
} catch (err) {
if (!cancelled) setError(err instanceof Error ? err.message : 'Failed to load agent.');
} finally {
if (!cancelled) setLoading(false);
}
};
void fetchAgent();
return () => { cancelled = true; };
}, [agentId]);
const handleAction = React.useCallback(
async (action: DialogAction): Promise => {
if (!agentId) return;
setActionLoading(true);
setDialog(null);
try {
const newStatus = action === 'suspend' ? 'suspended' : 'active';
const updated = await getClient().agents.updateAgent(agentId, { status: newStatus });
setAgent(updated);
} catch (err) {
setError(err instanceof Error ? err.message : 'Action failed.');
} finally {
setActionLoading(false);
}
},
[agentId],
);
if (loading) {
return (
{Array.from({ length: 6 }).map((_, i) => (
))}
);
}
if (error || !agent) {
return (
{error ?? 'Agent not found.'}
);
}
const dialogConfig = dialog === 'suspend'
? {
title: `Suspend agent ${agent.email}?`,
description: `Suspending ${agent.email} means it will no longer be able to authenticate.`,
confirmLabel: 'Suspend',
variant: 'destructive' as const,
}
: {
title: `Reactivate agent ${agent.email}?`,
description: `Reactivating ${agent.email} will allow it to authenticate again.`,
confirmLabel: 'Reactivate',
variant: 'default' as const,
};
return (
{/* Back navigation */}
{ navigate('/dashboard/agents'); }}
className="mb-6 flex items-center gap-1 text-sm text-brand-600 hover:text-brand-800"
>
← Back to Agents
{agent.email}
Agent ID: {agent.agentId}
{agent.status}
{error && (
{error}
)}
{/* Detail card */}
{/* Actions */}
{agent.status !== 'decommissioned' && (
{agent.status === 'active' && (
{ setDialog('suspend'); }}
>
Suspend Agent
)}
{agent.status === 'suspended' && (
{ setDialog('reactivate'); }}
>
Reactivate Agent
)}
)}
{/* Credentials section */}
Credentials
Manage client secrets for this agent. Rotate or revoke credentials as needed.
{ navigate(`/dashboard/agents/${agent.agentId}/credentials`); }}
>
View Credentials
{/* Confirm dialog */}
{dialog !== null && (
{ void handleAction(dialog); }}
onCancel={() => { setDialog(null); }}
/>
)}
);
}