From e2fc2478ccdfa9ae9d7957a7d0f1051d19802ad6 Mon Sep 17 00:00:00 2001 From: Soumyadas15 Date: Sun, 15 Jun 2025 16:10:58 +0530 Subject: [PATCH] chore(dev): workflow ui improvement --- backend/workflows/converter.py | 13 +- .../src/app/(dashboard)/workflows/page.tsx | 176 ++++++++++-------- .../src/components/workflows/NodePalette.tsx | 31 +-- .../components/workflows/WorkflowBuilder.tsx | 1 + .../workflows/nodes/ToolConnectionNode.tsx | 60 +++++- 5 files changed, 185 insertions(+), 96 deletions(-) diff --git a/backend/workflows/converter.py b/backend/workflows/converter.py index fbe1843d..18e40539 100644 --- a/backend/workflows/converter.py +++ b/backend/workflows/converter.py @@ -231,7 +231,13 @@ class WorkflowConverter: tool_data = tool_node.get('data', {}) tool_name = tool_data.get('nodeId', tool_data.get('label', 'Unknown Tool')) tool_desc = tool_data.get('description', 'No description available') - prompt_parts.append(f"- **{tool_name}**: {tool_desc}") + tool_instructions = tool_data.get('instructions', '') + + # Build tool description with instructions if provided + tool_description = f"- **{tool_name}**: {tool_desc}" + if tool_instructions: + tool_description += f" - Instructions: {tool_instructions}" + prompt_parts.append(tool_description) # Collect tool ID for XML examples tool_id = tool_data.get('nodeId') @@ -327,6 +333,7 @@ class WorkflowConverter: data = node.get('data', {}) name = data.get('label', 'Tool') tool_id = data.get('nodeId', 'unknown_tool') + instructions = data.get('instructions', '') input_connections = self._find_node_inputs(node.get('id'), edges) output_connections = self._find_node_outputs(node.get('id'), edges) @@ -337,6 +344,10 @@ class WorkflowConverter: f"**Purpose**: Provides {name.lower()} functionality to the workflow", ] + # Add instructions if provided + if instructions: + description.append(f"**Instructions**: {instructions}") + if input_connections: description.append(f"**Connected to agents**: {', '.join(input_connections)}") diff --git a/frontend/src/app/(dashboard)/workflows/page.tsx b/frontend/src/app/(dashboard)/workflows/page.tsx index c65912c1..ce70aced 100644 --- a/frontend/src/app/(dashboard)/workflows/page.tsx +++ b/frontend/src/app/(dashboard)/workflows/page.tsx @@ -4,7 +4,7 @@ import { useState, useEffect } from "react"; import Link from "next/link"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Plus, Play, Edit, Trash2, Clock, CheckCircle, XCircle, AlertCircle, Check, X } from "lucide-react"; +import { Plus, Play, Edit, Trash2, Clock, CheckCircle, XCircle, AlertCircle, Check, X, Workflow as WorkflowIcon } from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { @@ -234,6 +234,23 @@ export default function WorkflowsPage() { } }; + const getWorkflowColor = (status: string) => { + switch (status) { + case "active": + return "#10b981"; // green-500 + case "draft": + return "#f59e0b"; // amber-500 + case "paused": + return "#6b7280"; // gray-500 + case "disabled": + return "#ef4444"; // red-500 + case "archived": + return "#9ca3af"; // gray-400 + default: + return "#8b5cf6"; // violet-500 + } + }; + if (loading) { return (
@@ -322,91 +339,96 @@ export default function WorkflowsPage() { ) : (
{workflows.map((workflow) => ( - - -
-
- {editingWorkflowId === workflow.id ? ( -
- setEditingName(e.target.value)} - className="text-lg font-semibold h-8" - onKeyDown={(e) => { - if (e.key === 'Enter') { - handleSaveEditName(workflow.id); - } else if (e.key === 'Escape') { - handleCancelEditName(); - } - }} - autoFocus - /> -
- - -
-
- ) : ( - handleStartEditName(workflow)} - > - {workflow.definition.name || workflow.name} - - )} - {workflow.definition.description || workflow.description} -
- {getStatusBadge(workflow.status)} +
+ {/* Colorful Header */} +
+
+
- - -
+
+ {getStatusIcon(workflow.status)} +
+
+ + {/* Content */} +
+
+ {/* Title and Status */} +
+
+ {editingWorkflowId === workflow.id ? ( +
+ setEditingName(e.target.value)} + className="text-lg font-semibold h-8" + onKeyDown={(e) => { + if (e.key === 'Enter') { + handleSaveEditName(workflow.id); + } else if (e.key === 'Escape') { + handleCancelEditName(); + } + }} + autoFocus + /> +
+ + +
+
+ ) : ( +

handleStartEditName(workflow)} + > + {workflow.definition.name || workflow.name} +

+ )} +
+ {getStatusBadge(workflow.status)} +
+

+ {workflow.definition.description || workflow.description || 'No description provided'} +

-
- - +
+
))}
)} diff --git a/frontend/src/components/workflows/NodePalette.tsx b/frontend/src/components/workflows/NodePalette.tsx index 1c625f4b..7d3b85fb 100644 --- a/frontend/src/components/workflows/NodePalette.tsx +++ b/frontend/src/components/workflows/NodePalette.tsx @@ -213,15 +213,15 @@ export default function NodePalette() { - + Input - + Agents - + Tools @@ -257,8 +257,8 @@ export default function NodePalette() { variables: {} }} > - - + +
@@ -268,12 +268,12 @@ export default function NodePalette() { {node.name} Required - + {node.description}
-
+
); @@ -310,9 +310,9 @@ export default function NodePalette() { config: {}, }} > - - -
+ + +
@@ -320,12 +320,12 @@ export default function NodePalette() { {node.name} - + {node.description}
-
+
); @@ -361,10 +361,11 @@ export default function NodePalette() { label: node.name, nodeId: node.id, config: {}, + instructions: "", }} > - - + +
@@ -378,7 +379,7 @@ export default function NodePalette() {
-
+
); diff --git a/frontend/src/components/workflows/WorkflowBuilder.tsx b/frontend/src/components/workflows/WorkflowBuilder.tsx index bc19fb6f..fe9ef851 100644 --- a/frontend/src/components/workflows/WorkflowBuilder.tsx +++ b/frontend/src/components/workflows/WorkflowBuilder.tsx @@ -84,6 +84,7 @@ const initialNodes: Node[] = [ data: { label: 'Web Search', nodeId: 'web_search_tool', + instructions: '', outputConnections: [ { id: 'agent-1', diff --git a/frontend/src/components/workflows/nodes/ToolConnectionNode.tsx b/frontend/src/components/workflows/nodes/ToolConnectionNode.tsx index 0e303000..09c2a4af 100644 --- a/frontend/src/components/workflows/nodes/ToolConnectionNode.tsx +++ b/frontend/src/components/workflows/nodes/ToolConnectionNode.tsx @@ -1,9 +1,14 @@ "use client"; -import { memo } from "react"; +import { memo, useState } from "react"; import { Handle, Position, NodeProps } from "@xyflow/react"; import { Card, CardContent, CardHeader } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Textarea } from "@/components/ui/textarea"; +import { Label } from "@/components/ui/label"; +import { Separator } from "@/components/ui/separator"; +import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; import { Database, FileText, @@ -14,18 +19,24 @@ import { Cloud, Send, Settings, - Wrench + Wrench, + ChevronDown, + ChevronUp } from "lucide-react"; +import { useWorkflow } from "../WorkflowContext"; interface ToolConnectionNodeData { label: string; nodeId?: string; toolType?: string; config?: any; + instructions?: string; } -const ToolConnectionNode = memo(({ data, selected }: NodeProps) => { +const ToolConnectionNode = memo(({ data, selected, id }: NodeProps) => { const nodeData = data as unknown as ToolConnectionNodeData; + const [isConfigOpen, setIsConfigOpen] = useState(false); + const { updateNodeData } = useWorkflow(); const getToolConfig = () => { const toolId = nodeData.nodeId || nodeData.toolType; @@ -134,6 +145,49 @@ const ToolConnectionNode = memo(({ data, selected }: NodeProps) => { Ready
+ + {/* Show instructions preview if available */} + {nodeData.instructions && !isConfigOpen && ( +
+ Instructions: {nodeData.instructions.length > 50 + ? `${nodeData.instructions.substring(0, 50)}...` + : nodeData.instructions} +
+ )} + + + + {/* Configuration Toggle */} + + + + + + + {/* Instructions Configuration */} +
+ +