diff --git a/components/settings/PluginManagerTab.tsx b/components/settings/PluginManagerTab.tsx new file mode 100644 index 0000000..c9e911a --- /dev/null +++ b/components/settings/PluginManagerTab.tsx @@ -0,0 +1,838 @@ +import React, { useState, useEffect } from 'react'; +import { + PluginWorkflowProfile, + PluginRoleInstance, + RolePlugin, + ConfigField +} from '../../types/rolePlugin'; +import { PluginManagerService } from '../../services/pluginManagerService'; + +interface PluginManagerTabProps { + disabled?: boolean; +} + +const PluginManagerTab: React.FC = ({ disabled = false }) => { + const [profiles, setProfiles] = useState([]); + const [selectedProfile, setSelectedProfile] = useState(null); + const [editingProfile, setEditingProfile] = useState(null); + const [editingRole, setEditingRole] = useState(null); + const [availablePlugins, setAvailablePlugins] = useState([]); + const [showRoleModal, setShowRoleModal] = useState(false); + const [showPluginModal, setShowPluginModal] = useState(false); + const [selectedPlugin, setSelectedPlugin] = useState(null); + const [error, setError] = useState(null); + const [success, setSuccess] = useState(null); + const [showImportModal, setShowImportModal] = useState(false); + const [importData, setImportData] = useState(''); + + const service = PluginManagerService.getInstance(); + + useEffect(() => { + loadData(); + }, []); + + const loadData = () => { + try { + const loadedProfiles = service.getAllProfiles(); + const plugins = service.getAllPlugins(); + + setProfiles(loadedProfiles); + setAvailablePlugins(plugins); + + if (!selectedProfile && loadedProfiles.length > 0) { + setSelectedProfile(loadedProfiles[0]); + } + + setError(null); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to load data'); + } + }; + + const handleCreateProfile = () => { + const newProfile: PluginWorkflowProfile = { + id: `plugin-profile-${Date.now()}`, + name: 'New Plugin Workflow', + description: 'Custom plugin-based workflow', + version: '1.0.0', + roles: [], + executionOrder: [], + globalSettings: { + maxTotalIterations: 15, + timeoutMinutes: 30, + allowParallelExecution: false + }, + metadata: { + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + author: 'User', + tags: [] + } + }; + setEditingProfile(newProfile); + }; + + const handleEditProfile = (profile: PluginWorkflowProfile) => { + setEditingProfile({ ...profile }); + }; + + const handleSaveProfile = () => { + if (!editingProfile) return; + + try { + if (profiles.some(p => p.id === editingProfile.id && p !== selectedProfile)) { + service.addProfile(editingProfile); + setSuccess('Profile created successfully'); + } else { + service.updateProfile(editingProfile.id, editingProfile); + setSuccess('Profile updated successfully'); + } + + loadData(); + setSelectedProfile(editingProfile); + setEditingProfile(null); + setError(null); + + // Clear success message after 3 seconds + setTimeout(() => setSuccess(null), 3000); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to save profile'); + } + }; + + const handleDeleteProfile = (profileId: string) => { + try { + service.deleteProfile(profileId); + loadData(); + if (selectedProfile?.id === profileId) { + setSelectedProfile(profiles.find(p => p.id !== profileId) || null); + } + setSuccess('Profile deleted successfully'); + setTimeout(() => setSuccess(null), 3000); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to delete profile'); + } + }; + + const handleAddRole = (plugin: RolePlugin) => { + if (!editingProfile) return; + + const schema = plugin.getConfigSchema(); + const defaultConfig = schema.defaultValues || {}; + + const newRole: PluginRoleInstance = { + id: `role-${Date.now()}`, + pluginId: plugin.metadata.id, + name: plugin.metadata.name, + description: plugin.metadata.description, + config: defaultConfig, + executionSettings: { + enabled: true, + timeout: 180, + maxRetries: 1, + maxLoops: plugin.metadata.category === 'reviewer' ? 2 : 1 + } + }; + + setEditingRole(newRole); + setSelectedPlugin(plugin); + setShowRoleModal(true); + }; + + const handleEditRole = (role: PluginRoleInstance) => { + const plugin = availablePlugins.find(p => p.metadata.id === role.pluginId); + if (!plugin) return; + + setEditingRole({ ...role }); + setSelectedPlugin(plugin); + setShowRoleModal(true); + }; + + const handleSaveRole = () => { + if (!editingProfile || !editingRole || !selectedPlugin) return; + + try { + const validation = selectedPlugin.validateConfig(editingRole.config); + if (!validation.isValid) { + setError(`Role configuration errors: ${validation.errors?.join(', ')}`); + return; + } + + const existingIndex = editingProfile.roles.findIndex(r => r.id === editingRole.id); + if (existingIndex >= 0) { + editingProfile.roles[existingIndex] = editingRole; + } else { + editingProfile.roles.push(editingRole); + editingProfile.executionOrder.push(editingRole.id); + } + + setEditingProfile({ ...editingProfile }); + setShowRoleModal(false); + setEditingRole(null); + setSelectedPlugin(null); + setError(null); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to save role'); + } + }; + + const handleRemoveRole = (roleId: string) => { + if (!editingProfile) return; + + setEditingProfile({ + ...editingProfile, + roles: editingProfile.roles.filter(r => r.id !== roleId), + executionOrder: editingProfile.executionOrder.filter(id => id !== roleId) + }); + }; + + const renderConfigField = (field: ConfigField, value: any, onChange: (value: any) => void) => { + switch (field.type) { + case 'string': + return ( + onChange(e.target.value)} + className="w-full px-3 py-2 border border-theme rounded-md bg-theme-elevated text-theme-primary text-sm" + disabled={disabled} + /> + ); + + case 'textarea': + return ( +