import { useState } from 'react'; import { X, Download, CheckCircle, AlertCircle, RefreshCw, Info, Users, Package, Hash, } from 'lucide-react'; import { useMutation, useQuery } from '@tanstack/react-query'; import { agentApi, updateApi } from '@/lib/api'; import toast from 'react-hot-toast'; import { cn } from '@/lib/utils'; import { Agent } from '@/types'; interface AgentUpdatesModalProps { isOpen: boolean; onClose: () => void; selectedAgentIds: string[]; onAgentsUpdated: () => void; } export function AgentUpdatesModal({ isOpen, onClose, selectedAgentIds, onAgentsUpdated, }: AgentUpdatesModalProps) { const [selectedVersion, setSelectedVersion] = useState(''); const [selectedPlatform, setSelectedPlatform] = useState(''); const [isUpdating, setIsUpdating] = useState(false); // Fetch selected agents details const { data: agents = [] } = useQuery({ queryKey: ['agents-details', selectedAgentIds], queryFn: async (): Promise => { const promises = selectedAgentIds.map(id => agentApi.getAgent(id)); const results = await Promise.all(promises); return results; }, enabled: isOpen && selectedAgentIds.length > 0, }); // Fetch available update packages const { data: packagesResponse, isLoading: packagesLoading } = useQuery({ queryKey: ['update-packages'], queryFn: () => updateApi.getPackages(), enabled: isOpen, }); const packages = packagesResponse?.packages || []; // Group packages by version const versions = [...new Set(packages.map(pkg => pkg.version))].sort((a, b) => b.localeCompare(a)); const platforms = [...new Set(packages.map(pkg => pkg.platform))].sort(); // Filter packages based on selection const availablePackages = packages.filter( pkg => (!selectedVersion || pkg.version === selectedVersion) && (!selectedPlatform || pkg.platform === selectedPlatform) ); // Get unique platform for selected agents (simplified - assumes all agents same platform) const agentPlatform = agents[0]?.os_type || 'linux'; const agentArchitecture = agents[0]?.os_architecture || 'amd64'; // Update agents mutation const updateAgentsMutation = useMutation({ mutationFn: async (packageId: string) => { const pkg = packages.find(p => p.id === packageId); if (!pkg) throw new Error('Package not found'); // For single agent updates, use individual update with nonce for security if (selectedAgentIds.length === 1) { const agentId = selectedAgentIds[0]; // Generate nonce for security const nonceData = await agentApi.generateUpdateNonce(agentId, pkg.version); console.log('[UI] Update nonce generated for single agent:', nonceData); // Use individual update endpoint with nonce return agentApi.updateAgent(agentId, { version: pkg.version, platform: pkg.platform, nonce: nonceData.update_nonce }); } // For multiple agents, use bulk update const updateData = { agent_ids: selectedAgentIds, version: pkg.version, platform: pkg.platform, }; return agentApi.updateMultipleAgents(updateData); }, onSuccess: (data) => { const count = selectedAgentIds.length; const message = count === 1 ? 'Update initiated for agent' : `Update initiated for ${count} agents`; toast.success(message); setIsUpdating(false); onAgentsUpdated(); onClose(); }, onError: (error: any) => { toast.error(`Failed to update agents: ${error.message}`); setIsUpdating(false); }, }); const handleUpdateAgents = async (packageId: string) => { setIsUpdating(true); updateAgentsMutation.mutate(packageId); }; const canUpdate = selectedAgentIds.length > 0 && availablePackages.length > 0 && !isUpdating; const hasUpdatingAgents = agents.some(agent => agent.is_updating); if (!isOpen) return null; return (
{/* Header */}

Agent Updates

Update {selectedAgentIds.length} agent{selectedAgentIds.length !== 1 ? 's' : ''}

{/* Content */}
{/* Selected Agents */}

Selected Agents

{agents.map((agent) => (
{agent.hostname}
{agent.os_type}/{agent.os_architecture} • Current: {agent.current_version || 'Unknown'}
{agent.is_updating && (
Updating to {agent.updating_to_version}
)}
))}
{hasUpdatingAgents && (
Some agents are currently updating
)}
{/* Package Selection */}

Update Package Selection

{/* Filters */}
{/* Available Packages */}
{packagesLoading ? (
Loading packages...
) : availablePackages.length === 0 ? (
No packages available for the selected criteria
) : ( availablePackages.map((pkg) => (
handleUpdateAgents(pkg.id)} >
Version {pkg.version}
{pkg.platform} • {(pkg.file_size / 1024 / 1024).toFixed(1)} MB
{pkg.checksum.slice(0, 8)}...
)) )}
{/* Platform Compatibility Info */}
Detected platform: {agentPlatform}/{agentArchitecture}. Only compatible packages will be shown.
{/* Footer */}
); }