import React, { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { Package, Download, Trash2, Search, Filter, CheckCircle, AlertCircle, Loader2, Plug, Wrench, Database, Sparkles, } from 'lucide-react'; import { Badge } from '@/components/ui/Badge'; import { classNames } from '@/utils/helpers'; interface Plugin { id: string; name: string; category: string; description: string; version: string; size: string; installed: boolean; requires_key: boolean; } interface PluginsResponse { plugins: Record; categories: string[]; stats: { total: number; installed: number; available: number; }; } interface PluginsPageProps { className?: string; } const getCategoryIcon = (category: string) => { switch (category) { case 'apis': return ; case 'mcps': return ; case 'processors': return ; default: return ; } }; const getCategoryLabel = (category: string) => { const labels: Record = { apis: 'API Providers', mcps: 'MCP Tools', processors: 'Data Processors', }; return labels[category] || category; }; const getCategoryColor = (category: string) => { const colors: Record = { apis: 'from-cyan-500/20 to-blue-500/10 border-cyan-500/30', mcps: 'from-amber-500/20 to-orange-500/10 border-amber-500/30', processors: 'from-pink-500/20 to-rose-500/10 border-pink-500/30', }; return colors[category] || 'from-gray-500/20 to-gray-500/10 border-gray-500/30'; }; export const PluginsPage: React.FC = ({ className }) => { const queryClient = useQueryClient(); const [searchQuery, setSearchQuery] = useState(''); const [selectedCategory, setSelectedCategory] = useState(null); const [showInstalled, setShowInstalled] = useState(false); // Fetch plugins const { data: pluginsData, isLoading } = useQuery({ queryKey: ['plugins'], queryFn: async () => { const res = await fetch('/api/plugins/'); return res.json(); }, }); // Install plugin mutation const installMutation = useMutation({ mutationFn: async (pluginId: string) => { const res = await fetch('/api/plugins/install', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ plugin_id: pluginId }), }); return res.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['plugins'] }); }, }); // Uninstall plugin mutation const uninstallMutation = useMutation({ mutationFn: async (pluginId: string) => { const res = await fetch('/api/plugins/uninstall', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ plugin_id: pluginId }), }); if (!res.ok) { const error = await res.json(); throw new Error(error.detail); } return res.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['plugins'] }); }, }); // Filter plugins const getFilteredPlugins = () => { if (!pluginsData?.plugins) return {}; const result: Record = {}; for (const [category, plugins] of Object.entries(pluginsData.plugins)) { if (selectedCategory && category !== selectedCategory) continue; const filtered = plugins.filter((plugin) => { const matchesSearch = !searchQuery || plugin.name.toLowerCase().includes(searchQuery.toLowerCase()) || plugin.description.toLowerCase().includes(searchQuery.toLowerCase()); const matchesInstalled = !showInstalled || plugin.installed; return matchesSearch && matchesInstalled; }); if (filtered.length > 0) { result[category] = filtered; } } return result; }; const filteredPlugins = getFilteredPlugins(); return (
{/* Header */}

Plugins

Extend ScrapeRL with APIs, MCP tools, and processors

{/* Stats */} {pluginsData?.stats && (
{pluginsData.stats.installed}
Installed
{pluginsData.stats.available}
Available
{pluginsData.stats.total}
Total
)}
{/* Filters */}
{/* Search */}
setSearchQuery(e.target.value)} className="w-full pl-10 pr-4 py-2.5 bg-gray-900/50 border border-gray-700/50 rounded-lg text-gray-200 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/50 focus:border-cyan-500/50 transition-all" />
{/* Category Filter */}
{(pluginsData?.categories || ['apis', 'mcps', 'processors']).map((cat) => ( ))}
{/* Show Installed Toggle */}
{/* Plugin List */} {isLoading ? (

Loading plugins...

) : (
{Object.entries(filteredPlugins).map(([category, plugins]) => (
{getCategoryIcon(category)}

{getCategoryLabel(category)}

{plugins.length}
{plugins.map((plugin) => (

{plugin.name}

{plugin.installed && ( )}
{plugin.installed ? 'Installed' : 'Available'}

{plugin.description}

v{plugin.version} {plugin.size} {plugin.requires_key && ( API Key )}
{plugin.installed ? ( ) : ( )}
))}
))} {Object.keys(filteredPlugins).length === 0 && (

No plugins found

Try adjusting your search or filter criteria

)}
)} {/* Error Messages */} {uninstallMutation.isError && (
{(uninstallMutation.error as Error).message}
)}
); }; export default PluginsPage;