import { useState } from 'react';
import { Link, useNavigate, useParams } from 'react-router-dom';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { ArrowLeft, Edit, Trash2 } from 'lucide-react';
import { toast } from 'sonner';
import {
Button,
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
Separator,
Skeleton,
} from '@vector/ui';
import { deleteHost, getHost, listHostDeployedParts } from '../lib/api/hosts.js';
import { ApiRequestError } from '../lib/api/client.js';
import { queryKeys } from '../lib/queryKeys.js';
import { useAuth } from '../contexts/AuthContext.js';
import { HostStateBadge, HostStackBadge } from '../components/hosts/HostStateBadge.js';
import { HostTimeline } from '../components/hosts/HostTimeline.js';
import { HostFormDialog } from '../components/hosts/HostFormDialog.js';
import { PartStateBadge } from '../components/parts/PartStateBadge.js';
import { ConfirmDialog } from '../components/ConfirmDialog.js';
function DetailRow({ label, value }: { label: string; value: React.ReactNode }) {
return (
{label}
{value}
);
}
export default function HostDetail() {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
const queryClient = useQueryClient();
const { user } = useAuth();
const isAdmin = user?.role === 'ADMIN';
const [editOpen, setEditOpen] = useState(false);
const [confirmDelete, setConfirmDelete] = useState(false);
const { data: host, isPending, isError, error } = useQuery({
queryKey: queryKeys.hosts.detail(id!),
queryFn: () => getHost(id!),
enabled: Boolean(id),
});
const deployedPartsQuery = useQuery({
queryKey: queryKeys.hosts.deployedParts(id!),
queryFn: () => listHostDeployedParts(id!),
enabled: Boolean(id),
});
const deleteMutation = useMutation({
mutationFn: () => deleteHost(id!),
onSuccess: () => {
toast.success('Host deleted');
queryClient.invalidateQueries({ queryKey: queryKeys.hosts.all });
navigate('/hosts', { replace: true });
},
onError: (err) => {
toast.error(err instanceof ApiRequestError ? err.body.message : 'Delete failed');
},
});
if (isPending) {
return (
);
}
if (isError || !host) {
const msg = error instanceof ApiRequestError ? error.body.message : 'Host not found.';
return (
Host unavailable
{msg}
);
}
const deployedParts = deployedPartsQuery.data ?? [];
return (
{host.name}
{host.assetId}
{host.location ? ` · ${host.location}` : ''}
{isAdmin && (
)}
Summary
{host.assetId}}
/>
—
}
/>
{host.notes && (
<>
>
)}
History
FMs, repairs, part swaps, and host field changes.
Deployed parts
Parts currently installed on this host.
{deployedPartsQuery.isPending ? (
) : deployedParts.length === 0 ? (
No parts deployed here.
) : (
| Serial |
MPN |
Manufacturer |
State |
{deployedParts.map((p) => (
|
{p.serialNumber}
|
{p.partModel.mpn} |
{p.manufacturer.name}
|
|
))}
)}
deleteMutation.mutate()}
/>
);
}