diff --git a/app/w/[id]/workflow.tsx b/app/w/[id]/workflow.tsx index 1a76e1b4a2..c50528af22 100644 --- a/app/w/[id]/workflow.tsx +++ b/app/w/[id]/workflow.tsx @@ -26,6 +26,8 @@ import { initializeStateLogger } from '@/stores/workflow/state-logger' import { Serializer } from '@/serializer' import { Executor } from '@/executor' import { BlockState, SubBlockState } from '@/stores/workflow/types' +import { NotificationList } from '@/app/w/components/notifications/notifications' +import { useNotificationStore } from '@/stores/notifications/notifications-store' /** * Represents the data structure for a workflow node @@ -104,6 +106,7 @@ function WorkflowCanvas() { } = useWorkflowStore() const [isExecuting, setIsExecuting] = useState(false) const [executionResult, setExecutionResult] = useState(null) + const { addNotification } = useNotificationStore() // Convert blocks to ReactFlow nodes const nodes = Object.values(blocks).map((block) => ({ @@ -240,24 +243,26 @@ function WorkflowCanvas() { * @throws {Error} If no initial block is found or block configuration is invalid */ const getInitialNode = () => { - const initialBlockId = Object.values(blocks).find(block => - !edges.some(edge => edge.target === block.id) - )?.id; + const initialBlockId = Object.values(blocks).find( + (block) => !edges.some((edge) => edge.target === block.id) + )?.id if (!initialBlockId) { - throw new Error('Could not determine the initial block in the workflow'); + throw new Error('Could not determine the initial block in the workflow') } - const blockConfig = getBlock(blocks[initialBlockId].type); + const blockConfig = getBlock(blocks[initialBlockId].type) if (!blockConfig) { - throw new Error(`Block configuration not found for type: ${blocks[initialBlockId].type}`); + throw new Error( + `Block configuration not found for type: ${blocks[initialBlockId].type}` + ) } return { block: blocks[initialBlockId], - config: blockConfig - }; - }; + config: blockConfig, + } + } /** * Determines the initial input parameters based on the block type @@ -272,18 +277,18 @@ function WorkflowCanvas() { systemPrompt: block.subBlocks?.['systemPrompt']?.value, temperature: block.subBlocks?.['temperature']?.value, apiKey: block.subBlocks?.['apiKey']?.value, - prompt: block.subBlocks?.['systemPrompt']?.value - }; + prompt: block.subBlocks?.['systemPrompt']?.value, + } } else if (block.type === 'api') { return { url: block.subBlocks?.['url']?.value, method: block.subBlocks?.['method']?.value, headers: block.subBlocks?.['headers']?.value, - body: block.subBlocks?.['body']?.value - }; + body: block.subBlocks?.['body']?.value, + } } - return {}; - }; + return {} + } /** * Serializes a block into the format expected by the executor @@ -292,23 +297,23 @@ function WorkflowCanvas() { * @throws {Error} If block configuration or tools are not properly defined */ const serializeBlock = (block: BlockState) => { - const blockConfig = getBlock(block.type); + const blockConfig = getBlock(block.type) if (!blockConfig) { - throw new Error(`Block configuration not found for type: ${block.type}`); + throw new Error(`Block configuration not found for type: ${block.type}`) } - const tools = blockConfig.workflow.tools; + const tools = blockConfig.workflow.tools if (!tools || !tools.access || tools.access.length === 0) { - throw new Error(`No tools specified for block type: ${block.type}`); + throw new Error(`No tools specified for block type: ${block.type}`) } // Get the values from subBlocks - const params: Record = {}; + const params: Record = {} Object.entries(block.subBlocks || {}).forEach(([id, subBlock]) => { if (subBlock) { - params[id] = subBlock.value; + params[id] = subBlock.value } - }); + }) return { id: block.id, @@ -320,15 +325,15 @@ function WorkflowCanvas() { interface: { inputs: block.type === 'agent' ? { prompt: 'string' } : {}, outputs: { - [block.type === 'agent' ? 'response' : 'output']: - typeof blockConfig.workflow.outputType === 'string' - ? blockConfig.workflow.outputType - : blockConfig.workflow.outputType.default - } - } + [block.type === 'agent' ? 'response' : 'output']: + typeof blockConfig.workflow.outputType === 'string' + ? blockConfig.workflow.outputType + : blockConfig.workflow.outputType.default, + }, + }, }, - }; - }; + } + } /** * Handles the execution of the workflow @@ -340,19 +345,20 @@ function WorkflowCanvas() { setExecutionResult(null) // 1. Get initial node - const { block: initialBlock, config: initialBlockConfig } = getInitialNode(); + const { block: initialBlock, config: initialBlockConfig } = + getInitialNode() // 2. Serialize the workflow const serializer = new Serializer() const serializedWorkflow = serializer.serializeWorkflow( Object.values(blocks).map(serializeBlock), edges - ); + ) // 3. Create executor and run workflow const executor = new Executor(serializedWorkflow) - const initialInput = getInitialInput(initialBlock, initialBlockConfig); - + const initialInput = getInitialInput(initialBlock, initialBlockConfig) + const result = await executor.execute( window.location.pathname.split('/').pop() || 'workflow', initialInput @@ -360,63 +366,37 @@ function WorkflowCanvas() { // 4. Handle result setExecutionResult(result) - + if (result.success) { console.log('Workflow executed successfully:', result.data) + addNotification('console', 'Workflow completed successfully') } else { console.error('Workflow execution failed:', result.error) + addNotification('error', `Failed to execute workflow: ${result.error}`) } - } catch (error) { + } catch (error: any) { console.error('Error executing workflow:', error) setExecutionResult({ success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred' + error: + error instanceof Error ? error.message : 'Unknown error occurred', }) + addNotification('error', `Failed to execute workflow: ${error.message}`) } finally { setIsExecuting(false) } - }; + } + + const testNotification = () => { + addNotification('console', 'Response: hey') + } return (
+ - - {/* Run Button */} -
- -
- - {/* Execution Result */} - {executionResult && ( -
- {executionResult.success ? ( -
-

Success

-
-                {JSON.stringify(executionResult.data, null, 2)}
-              
-
- ) : ( -
-

Error

-

{executionResult.error}

-
- )} -
- )} + 50 ? `${message.slice(0, 50)}...` : message + + return ( + showNotification(id)} + > + +
+
+ + {type === 'error' ? 'Error' : 'Console'} + + {timeAgo} +
+

{truncatedMessage}

+
+
+ ) +} diff --git a/app/w/components/control-bar/control-bar.tsx b/app/w/components/control-bar/control-bar.tsx index f20cb8de99..3429d8fbcf 100644 --- a/app/w/components/control-bar/control-bar.tsx +++ b/app/w/components/control-bar/control-bar.tsx @@ -1,3 +1,5 @@ +'use client' + import { Button } from '@/components/ui/button' import { DropdownMenu, @@ -6,8 +8,12 @@ import { DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import { History, Bell, Play } from 'lucide-react' +import { useNotificationStore } from '@/stores/notifications/notifications-store' +import { NotificationDropdownItem } from './components/notification-dropdown-item' export function ControlBar() { + const { notifications } = useNotificationStore() + return (
{/* Left Section - Workflow Info */} @@ -41,9 +47,28 @@ export function ControlBar() { Notifications - - No new notifications - + + {notifications.length === 0 ? ( + + + No new notifications + + + ) : ( + + {[...notifications] + .sort((a, b) => b.timestamp - a.timestamp) + .map((notification) => ( + + ))} + + )}