import React, { useCallback, useEffect, useState } from 'react'; import { X, Radio, Boxes, Copy, Check, RefreshCw, CheckCircle } from 'lucide-react'; export function ServerDetailDrawer({ server, catalogTools, backendUrl, apiKey, onClose, onRepaired }) { const isGateway = server.kind === 'gateway'; const Icon = isGateway ? Radio : Boxes; const [copied, setCopied] = React.useState(false); const [repairing, setRepairing] = useState(false); const [repairResult, setRepairResult] = useState(null); const [serverTools, setServerTools] = useState(null); const [loadingTools, setLoadingTools] = useState(false); // Derive status consistently with InstalledServerCard const hasTools = server.toolCount > 0 || (serverTools !== null && serverTools.length > 0); const isEnabled = server.enabled === true; const status = !isEnabled ? 'disconnected' : server.kind === 'server' && !hasTools ? 'empty' : 'connected'; const copyId = () => { navigator.clipboard.writeText(server.id).catch(() => { }); setCopied(true); setTimeout(() => setCopied(false), 1500); }; // For virtual servers, fetch full tool objects from the proxy endpoint const fetchServerTools = useCallback(async () => { if (isGateway) return; setLoadingTools(true); try { const headers = {}; if (apiKey) headers['x-api-key'] = apiKey; const res = await fetch(`${backendUrl}/v1/agentic/servers/${server.id}/tools`, { headers }); if (res.ok) { const data = await res.json(); if (Array.isArray(data)) { setServerTools(data.map((t) => ({ id: t.id || t.name || '', name: t.name || t.original_name || t.id || '', description: t.description || '', enabled: t.enabled !== false, }))); setLoadingTools(false); return; } } } catch { // fall through to catalog resolution } // Fallback: resolve tool IDs from catalog if (server.toolIds.length > 0) { const idSet = new Set(server.toolIds); const resolved = catalogTools .filter((t) => idSet.has(t.id)) .map((t) => ({ id: t.id, name: t.name, description: t.description, enabled: t.enabled !== false })); setServerTools(resolved); } else { setServerTools([]); } setLoadingTools(false); }, [isGateway, backendUrl, apiKey, server.id, server.toolIds, catalogTools]); useEffect(() => { fetchServerTools(); }, [fetchServerTools]); // Resolve tools for gateways from catalog by matching IDs const gatewayResolvedTools = React.useMemo(() => { if (!isGateway || server.toolIds.length === 0) return []; const idSet = new Set(server.toolIds); return catalogTools.filter((t) => idSet.has(t.id)); }, [isGateway, server.toolIds, catalogTools]); const handleRepair = async () => { setRepairing(true); setRepairResult(null); try { const headers = { 'Content-Type': 'application/json' }; if (apiKey) headers['x-api-key'] = apiKey; const res = await fetch(`${backendUrl}/v1/agentic/sync`, { method: 'POST', headers }); if (res.ok) { const data = await res.json(); if (data?.sync) setRepairResult(data.sync); await fetchServerTools(); onRepaired?.(); } } catch { // non-fatal } finally { setRepairing(false); } }; const effectiveTools = isGateway ? gatewayResolvedTools : (serverTools || []); const toolCount = isGateway ? gatewayResolvedTools.length : (serverTools?.length ?? server.toolCount); const statusDot = { connected: 'bg-emerald-400', empty: 'bg-amber-400', disconnected: 'bg-yellow-400', }[status]; const statusLabel = { connected: `Active \u00b7 ${toolCount} tool${toolCount !== 1 ? 's' : ''}`, empty: 'No tools linked', disconnected: 'Disconnected', }[status]; const statusTextColor = { connected: 'text-emerald-300', empty: 'text-amber-300', disconnected: 'text-yellow-300', }[status]; return (
{server.description}
)} {/* Endpoint (gateways only) */} {isGateway && server.url && (
{server.url}
No tools linked yet. Try Sync All below.
) : null}