diff --git a/webui/react-ui/src/App.css b/webui/react-ui/src/App.css index 98f6b46..622c220 100644 --- a/webui/react-ui/src/App.css +++ b/webui/react-ui/src/App.css @@ -1933,3 +1933,124 @@ select.form-control { background-color: var(--light-bg); color: var(--primary); } + +.import-agent-container { + max-width: 800px; + margin: 0 auto; + padding: 20px; +} + +.import-agent-content { + margin-top: 20px; +} + +.file-dropzone { + border: 2px dashed var(--border); + border-radius: 8px; + padding: 40px; + text-align: center; + margin-bottom: 20px; + transition: all 0.3s ease; + background: rgba(30, 30, 30, 0.7); + box-shadow: 0 0 15px rgba(0, 0, 0, 0.2); +} + +.file-dropzone:hover { + border-color: var(--primary); + background: rgba(30, 30, 30, 0.8); +} + +.dropzone-content { + color: var(--text); +} + +.dropzone-content i { + font-size: 48px; + color: var(--primary); + margin-bottom: 20px; +} + +.dropzone-content h2 { + margin-bottom: 10px; + color: var(--primary); + text-shadow: var(--neon-glow); +} + +.dropzone-content p { + margin: 10px 0; + color: var(--text); +} + +.file-button { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 12px 24px; + background: var(--primary); + color: var(--dark-bg); + border-radius: 4px; + cursor: pointer; + transition: all 0.3s ease; + text-decoration: none; +} + +.file-button:hover { + background: rgba(0, 255, 149, 0.8); + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); +} + +.file-button i { + font-size: 16px; +} + +.selected-file-info { + margin-top: 20px; + padding: 20px; + background: rgba(30, 30, 30, 0.7); + border-radius: 8px; + box-shadow: 0 0 15px rgba(0, 0, 0, 0.2); +} + +.selected-file-info p { + margin-bottom: 15px; + color: var(--text); +} + +.import-button { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 12px 24px; + background: var(--primary); + color: var(--dark-bg); + border: none; + border-radius: 4px; + cursor: pointer; + transition: all 0.3s ease; + font-size: 16px; +} + +.import-button:hover:not(:disabled) { + background: rgba(0, 255, 149, 0.8); + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); +} + +.import-button:disabled { + background: rgba(0, 255, 149, 0.3); + cursor: not-allowed; +} + +.import-button i { + font-size: 16px; +} + +.import-button .fa-spinner { + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} diff --git a/webui/react-ui/src/pages/AgentsList.jsx b/webui/react-ui/src/pages/AgentsList.jsx index 5189282..6472c71 100644 --- a/webui/react-ui/src/pages/AgentsList.jsx +++ b/webui/react-ui/src/pages/AgentsList.jsx @@ -112,9 +112,14 @@ function AgentsList() {

Manage Agents

- - Create New Agent - +
+ + Create Agent + + + Import Agent + +
{agents.length > 0 ? ( diff --git a/webui/react-ui/src/pages/Home.jsx b/webui/react-ui/src/pages/Home.jsx index 81440b0..3eaf87a 100644 --- a/webui/react-ui/src/pages/Home.jsx +++ b/webui/react-ui/src/pages/Home.jsx @@ -99,9 +99,18 @@ function Home() {

Create Group

-

Create agent groups for collaborative intelligence.

+

Create a group of agents with shared configurations and behaviors.

+ + {/* Card for Import Agent */} + +
+

Import Agent

+

Import an existing agent configuration from a file.

+
+ +
{stats.agents.length > 0 && ( diff --git a/webui/react-ui/src/pages/ImportAgent.jsx b/webui/react-ui/src/pages/ImportAgent.jsx new file mode 100644 index 0000000..ea87b18 --- /dev/null +++ b/webui/react-ui/src/pages/ImportAgent.jsx @@ -0,0 +1,102 @@ +import { useState } from 'react'; +import { useNavigate, useOutletContext } from 'react-router-dom'; +import { agentApi } from '../utils/api'; + +function ImportAgent() { + const navigate = useNavigate(); + const { showToast } = useOutletContext(); + const [file, setFile] = useState(null); + const [loading, setLoading] = useState(false); + + const handleFileChange = (e) => { + const selectedFile = e.target.files[0]; + if (selectedFile) { + setFile(selectedFile); + } + }; + + const handleImport = async () => { + if (!file) { + showToast('Please select a file to import', 'error'); + return; + } + + setLoading(true); + try { + const formData = new FormData(); + formData.append('file', file); + + await agentApi.importAgentConfig(formData); + showToast('Agent imported successfully', 'success'); + navigate('/agents'); + } catch (err) { + showToast(`Error importing agent: ${err.message}`, 'error'); + } finally { + setLoading(false); + } + }; + + return ( +
+
+

+ Import Agent +

+
+ +
+
+
{ + e.preventDefault(); + const droppedFile = e.dataTransfer.files[0]; + if (droppedFile) { + setFile(droppedFile); + } + }} + onDragOver={(e) => e.preventDefault()}> +
+ +

Drop your agent file here

+

or

+ + +
+
+ + {file && ( +
+

Selected file: {file.name}

+ +
+ )} +
+
+
+ ); +} + +export default ImportAgent; diff --git a/webui/react-ui/src/router.jsx b/webui/react-ui/src/router.jsx index c0f5a74..f7d5c34 100644 --- a/webui/react-ui/src/router.jsx +++ b/webui/react-ui/src/router.jsx @@ -8,6 +8,7 @@ import Chat from './pages/Chat'; import ActionsPlayground from './pages/ActionsPlayground'; import GroupCreate from './pages/GroupCreate'; import AgentStatus from './pages/AgentStatus'; +import ImportAgent from './pages/ImportAgent'; // Get the base URL from Vite's environment variables or default to '/app/' const BASE_URL = import.meta.env.BASE_URL || '/app'; @@ -46,6 +47,10 @@ export const router = createBrowserRouter([ path: 'group-create', element: }, + { + path: 'import', + element: + }, { path: 'status/:name', element: diff --git a/webui/react-ui/src/utils/api.js b/webui/react-ui/src/utils/api.js index 36fd7eb..b4f0444 100644 --- a/webui/react-ui/src/utils/api.js +++ b/webui/react-ui/src/utils/api.js @@ -142,11 +142,11 @@ export const agentApi = { }, // Import agent configuration - importAgentConfig: async (configData) => { + importAgent: async (formData) => { const response = await fetch(buildUrl(API_CONFIG.endpoints.importAgent), { method: 'POST', headers: API_CONFIG.headers, - body: JSON.stringify(configData), + body: formData, }); return handleResponse(response); },