import React, { useState, useEffect, useCallback } from 'react'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '../ui/dialog'; import { Button } from '../ui/Button'; import Typography from '../ui/Typography'; import { Badge } from '../ui/badge'; import { FiSearch, FiX, FiRefreshCw, FiUsers, FiCode, FiMessageSquare, FiStar, FiGlobe, FiZap, FiShield, FiFilter, FiExternalLink, FiUserPlus, } from 'react-icons/fi'; import { cn } from '../../lib/utils'; import { useHRLImageUrl } from '../../hooks/useHRLImageUrl'; import { NetworkType } from '@hashgraphonline/standards-sdk'; import ConnectionConfirmDialog from '../shared/ConnectionConfirmDialog'; interface AgentProfile { accountId: string; profile?: { display_name?: string; alias?: string; bio?: string; profileImage?: string; aiAgent?: { type?: number; capabilities?: number[]; model?: string; creator?: string; }; socials?: Array<{ platform: string; url?: string; handle?: string; }>; }; metadata?: any; rating?: number; ratingCount?: number; network?: string; createdAt?: string; isRegistryBroker?: boolean; } interface AgentSelectorModalProps { isOpen: boolean; onClose: () => void; onConnect: (agent: AgentProfile) => void; currentNetwork: string; } interface FilterState { capabilities: number[]; rating: number; agentType: 'all' | 'ai' | 'human' | 'registry'; } const capabilityMap: { [key: number]: { name: string; icon: React.ComponentType<{ className?: string }>; }; } = { 0: { name: 'Text Generation', icon: FiCode }, 1: { name: 'Image Generation', icon: FiZap }, 2: { name: 'Audio Generation', icon: FiZap }, 3: { name: 'Video Generation', icon: FiZap }, 4: { name: 'Code Generation', icon: FiCode }, 5: { name: 'Translation', icon: FiGlobe }, 6: { name: 'Summarization', icon: FiMessageSquare }, 7: { name: 'Knowledge Retrieval', icon: FiShield }, 8: { name: 'Data Integration', icon: FiUsers }, 9: { name: 'Market Intelligence', icon: FiStar }, 10: { name: 'Transaction Analytics', icon: FiShield }, 11: { name: 'Smart Contract Audit', icon: FiShield }, 12: { name: 'Governance', icon: FiUsers }, 13: { name: 'Security Monitoring', icon: FiShield }, 14: { name: 'Compliance Analysis', icon: FiShield }, 15: { name: 'Fraud Detection', icon: FiShield }, 16: { name: 'Multi-Agent Coordination', icon: FiUsers }, 17: { name: 'API Integration', icon: FiCode }, 18: { name: 'Workflow Automation', icon: FiZap }, }; /** * Agent selector modal for discovering and connecting to agents */ export const AgentSelectorModal: React.FC = ({ isOpen, onClose, onConnect, currentNetwork, }) => { const [agents, setAgents] = useState([]); const [isLoading, setIsLoading] = useState(false); const [searchTerm, setSearchTerm] = useState(''); const [sortBy, setSortBy] = useState<'name' | 'rating' | 'recent'>('rating'); const [showFilters, setShowFilters] = useState(false); const [filters, setFilters] = useState({ capabilities: [], rating: 0, agentType: 'all', }); const [currentPage, setCurrentPage] = useState(1); const [error, setError] = useState(null); const pageSize = 10; const loadAgents = useCallback(async () => { if (!isOpen) return; setIsLoading(true); setError(null); try { const result = await window.electron.invoke('hcs10:discover-agents', { search: searchTerm, network: currentNetwork, page: currentPage, limit: pageSize, sortBy, filters, }); if (result.success) { setAgents(result.agents || []); } else { setError(result.error || 'Failed to load agents'); } } catch (err) { setError('Failed to discover agents'); } finally { setIsLoading(false); } }, [isOpen, searchTerm, currentNetwork, currentPage, sortBy, filters]); useEffect(() => { loadAgents(); }, [loadAgents]); useEffect(() => { setCurrentPage(1); }, [searchTerm, filters, sortBy]); const [isConnecting, setIsConnecting] = useState(false); const [showConfirmDialog, setShowConfirmDialog] = useState(false); const [selectedAgentToConnect, setSelectedAgentToConnect] = useState(null); const handleConnect = useCallback(async (agent: AgentProfile) => { setSelectedAgentToConnect(agent); setShowConfirmDialog(true); }, []); const handleConfirmConnect = useCallback(async () => { if (!selectedAgentToConnect) return; try { setIsConnecting(true); setShowConfirmDialog(false); await onConnect(selectedAgentToConnect); onClose(); } catch (err) { setError('Failed to send connection request. Please try again.'); } finally { setIsConnecting(false); setSelectedAgentToConnect(null); } }, [selectedAgentToConnect, onConnect, onClose]); const handleCancelConnect = useCallback(() => { setShowConfirmDialog(false); setSelectedAgentToConnect(null); }, []); const handleFilterChange = useCallback((newFilters: Partial) => { setFilters((prev) => ({ ...prev, ...newFilters })); }, []); const filteredAgents = agents.filter((agent) => { if (searchTerm) { const searchLower = searchTerm.toLowerCase(); const name = agent.profile?.display_name || agent.profile?.alias || ''; const bio = agent.profile?.bio || ''; return ( name.toLowerCase().includes(searchLower) || bio.toLowerCase().includes(searchLower) || agent.accountId.includes(searchLower) ); } return true; }); const AgentCard: React.FC<{ agent: AgentProfile }> = ({ agent }) => { const displayName = agent.profile?.display_name || agent.profile?.alias || 'Unknown Agent'; const bio = agent.profile?.bio || 'No description available'; const capabilities = agent.profile?.aiAgent?.capabilities || []; const rating = agent.rating || 0; const ratingCount = agent.ratingCount || 0; const isAI = agent.profile?.aiAgent?.type !== undefined; const { resolvedUrl: imageUrl, isLoading: imageLoading } = useHRLImageUrl( agent.profile?.profileImage || '', currentNetwork as NetworkType ); return (
{/* Avatar */}
{imageLoading ? ( ) : imageUrl ? ( {`${displayName} {}} /> ) : isAI ? ( ) : ( )}
{/* Content */}
{displayName} {agent.accountId}
{rating > 0 && (
{rating.toFixed(1)} ({ratingCount})
)}
{bio} {/* Badges */}
{isAI && ( AI Agent )} {agent.isRegistryBroker && ( External )} {currentNetwork.toUpperCase()}
{/* Capabilities */} {capabilities.length > 0 && (
{capabilities.slice(0, 3).map((capId) => { const cap = capabilityMap[capId]; if (!cap) return null; const IconComponent = cap.icon; return (
{cap.name}
); })} {capabilities.length > 3 && (
+{capabilities.length - 3} more
)}
)} {/* Actions */}
); }; return ( Discover Agents
{/* Search and filters */}
{/* Search bar */}
setSearchTerm(e.target.value)} className='w-full pl-10 pr-4 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500' /> {searchTerm && ( )}
{/* Sort and filter controls */}
Sort by:
{filteredAgents.length} agents found
{/* Advanced filters */} {showFilters && (
Agent Type
{(['all', 'ai', 'human', 'registry'] as const).map( (type) => ( ) )}
Minimum Rating
{[0, 3, 4, 4.5].map((rating) => ( ))}
)}
{/* Results */}
{error ? (
{error}
) : isLoading ? (
Discovering agents...
) : filteredAgents.length === 0 ? (
No agents found Try adjusting your search terms or filters
) : (
{filteredAgents.map((agent) => ( ))}
)}
{/* Pagination */} {filteredAgents.length >= pageSize && (
Page {currentPage}
)}
); }; export default AgentSelectorModal;