Skip to main content

WORKFLOW MONITOR UI - REAL-TIME WEBSOCKET INTEGRATION ⚡

Date Created: October 6, 2025
Priority: HIGH
Status: 🚧 READY TO IMPLEMENT


GOAL: Christmas Tree Effect for Live Workflow Execution 🎄

Create a real-time workflow monitoring UI that displays each ExecModule's execution status as it runs, creating a visual "Christmas tree" effect with glowing status indicators. Include play/pause/stop controls and OasServer picker for dynamic API targeting.


1. WebSocket Integration for Live EventLog Streaming

Backend WebSocket Handler

File: valkyrai/src/main/java/com/valkyrlabs/workflow/websocket/WorkflowWebSocketHandler.java

Requirements:

  • Spring WebSocket support with STOMP protocol
  • Endpoint: /ws/workflows/{workflowId}
  • Subscribe to workflow execution events per workflow ID
  • Stream Flux<EventLog> from executeReactive() to connected clients
  • Handle reconnection gracefully (Spring WebSocket auto-reconnect)
  • Backpressure handling with onBackpressureDrop strategy
  • Session management with concurrent client tracking

Protocol:

Client → Server (Subscribe):

{
"action": "subscribe",
"workflowId": "uuid",
"clientId": "uuid"
}

Server → Client (EventLog):

{
"type": "event_log",
"workflowId": "uuid",
"execModuleId": "uuid",
"eventLog": {
"status": "OK|ERROR",
"eventDetails": "message",
"createdDate": "ISO8601"
}
}

Server → Client (Status Update):

{
"type": "status_update",
"workflowId": "uuid",
"execModuleId": "uuid",
"status": "PENDING|RUNNING|SUCCESS|ERROR|PAUSED",
"duration": 1234
}

Server → Client (WorkflowState Update):

{
"type": "state_update",
"workflowId": "uuid",
"state": {
"key": "value"
}
}

2. Live ExecModule Status Display (Christmas Tree Effect 🎄)

Visual Design Requirements

Status Indicators:

  • 🔵 PENDING - Not yet started (dim blue, no animation)
  • 🟡 RUNNING - Currently executing (pulsing yellow/orange, animated)
  • 🟢 SUCCESS - Completed successfully (bright green glow, celebrate animation)
  • 🔴 ERROR - Failed with error (pulsing red, shake animation)
  • ⏸️ PAUSED - Execution paused (gray, static)
  • ⏭️ SKIPPED - Module skipped (light gray, strikethrough)

Module Card Components:

  • Module name and type badge
  • Real-time status indicator (color + animation)
  • Progress bar (for RUNNING state)
  • Duration counter (live update)
  • Last 3-5 EventLog messages (scrollable)
  • Click to expand full details

Animations (CSS):

@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; }
}

@keyframes glow {
0%, 100% { box-shadow: 0 0 5px currentColor; }
50% { box-shadow: 0 0 20px currentColor; }
}

@keyframes celebrate {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}

@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px); }
75% { transform: translateX(5px); }
}

3. Workflow Controls

Play Button (▶️)

Function: Start or resume workflow execution

Requirements:

  • Enabled when: workflow.status === 'PENDING' or 'PAUSED'
  • Disabled when: workflow.status === 'RUNNING' or 'COMPLETED' or 'ERROR'
  • REST endpoint: POST /api/workflows/{id}/start
  • Mutation: useStartWorkflowMutation() (ThorAPI generated)
  • Optimistic update: set status to RUNNING immediately
  • Toast notification on success/error

Response:

{
"workflowId": "uuid",
"status": "RUNNING",
"startedAt": "ISO8601"
}

Pause Button (⏸️)

Function: Pause workflow execution after current module completes

Requirements:

  • Enabled when: workflow.status === 'RUNNING'
  • Disabled when: workflow.status !== 'RUNNING'
  • REST endpoint: POST /api/workflows/{id}/pause
  • Mutation: usePauseWorkflowMutation()
  • Graceful pause: wait for current ExecModule to complete
  • WorkflowState marker: "workflow.paused=true"
  • Toast notification: "Workflow will pause after current module"

Stop Button (⏹️)

Function: Stop workflow execution immediately

Requirements:

  • Enabled when: workflow.status === 'RUNNING' or 'PAUSED'
  • Disabled when: workflow.status === 'PENDING' or 'COMPLETED'
  • Confirm dialog before stopping (destructive action)
  • REST endpoint: POST /api/workflows/{id}/stop
  • Mutation: useStopWorkflowMutation()
  • Immediate termination with cleanup
  • EventLog: "Workflow stopped by user"
  • Toast notification with undo option (5 second window)

Keyboard Shortcuts:

  • Space: Play/Pause toggle
  • Shift+S: Stop (with confirmation)
  • R: Refresh workflow state
  • Esc: Close expanded module details

4. OasServer Picker

Function: Select OpenAPI server for workflow execution

Component: <OasServerPicker />

Server Options

Predefined Servers:

  1. Development - http://localhost:8080
  2. Staging - https://staging.valkyr.ai
  3. Production - https://api.valkyr.ai
  4. Custom - User-defined URL (text input + validation)

Server Configuration:

interface OasServer {
id: string;
name: string;
url: string;
description?: string;
variables?: Record<string, string>;
}

Requirements

  • Dropdown select with server list from OpenAPI spec
  • Current server displayed prominently (badge + URL)
  • Change confirmation when workflow is RUNNING
  • Persist selection in WorkflowState: "workflow.server.url"
  • REST endpoint: PUT /api/workflows/{id}/server
  • Mutation: useUpdateWorkflowServerMutation()
  • Validate URL format for custom servers
  • Test connection button (ping /actuator/health)

Custom Server Input:

<Input
type="url"
placeholder="https://custom.api.com"
pattern="https?://.+"
onBlur={validateAndSave}
/>

5. WorkflowState Display Panel

Component: <WorkflowStatePanel />

Features

  • Collapsible side panel (default: collapsed)
  • Real-time updates via WebSocket
  • Key-value pairs with syntax highlighting (JSON)
  • Search/filter by key
  • Copy individual values (click to copy)
  • Export all state (JSON download)
  • History/diff view (show changes over time)
  • Namespace grouping (e.g., "build.", "deploy.", "ig.publish.*")

Layout:

<Panel collapsed={collapsed} onToggle={toggle}>
<Header>
<h3>Workflow State</h3>
<SearchInput onChange={handleSearch} />
<ExportButton onClick={exportJSON} />
</Header>
<StateTree>
{Object.entries(groupedState).map(([namespace, values]) => (
<Namespace key={namespace} name={namespace}>
{Object.entries(values).map(([key, value]) => (
<StateEntry
key={key}
name={key}
value={value}
onCopy={copyValue}
/>
))}
</Namespace>
))}
</StateTree>
</Panel>

6. Performance & UX Requirements

Performance

  • Smooth 60fps animations (CSS transitions, not JavaScript)
  • Debounce WebSocket updates (max 10 updates/sec per module)
  • Virtual scrolling for workflows with 20+ modules
  • Lazy load module details (expand on demand)
  • Memoized components with React.memo()
  • Use useCallback for event handlers

Responsive Design

  • Desktop: Grid layout (3-4 columns)
  • Tablet: 2 columns
  • Mobile: Single column, stacked
  • Touch-friendly buttons (min 44x44px)
  • Swipe gestures for module cards

Accessibility

  • ARIA labels on all interactive elements
  • Keyboard navigation (Tab, Arrow keys)
  • Screen reader announcements for status changes
  • High contrast mode support
  • Focus indicators

Error Handling

  • Error boundary for graceful degradation
  • WebSocket reconnection with exponential backoff
  • Fallback to polling if WebSocket fails
  • Clear error messages with recovery actions
  • Toast notifications for transient errors

7. Component Structure

<WorkflowMonitor workflowId={id}>
{/* Top Bar */}
<WorkflowHeader>
<WorkflowTitle>{workflow.name}</WorkflowTitle>
<WorkflowControls
status={workflow.status}
onPlay={handleStart}
onPause={handlePause}
onStop={handleStop}
/>
<OasServerPicker
servers={oasServers}
selected={currentServer}
onChange={handleServerChange}
/>
</WorkflowHeader>

{/* Main Content */}
<WorkflowContent>
{/* Left: Module Grid */}
<ExecModuleGrid>
{workflow.execModules.map(module => (
<ExecModuleCard
key={module.id}
module={module}
status={liveStatus[module.id]}
eventLogs={liveEvents[module.id]}
duration={durations[module.id]}
onExpand={handleModuleExpand}
/>
))}
</ExecModuleGrid>

{/* Right: State Panel */}
<WorkflowStatePanel
state={workflowState}
collapsed={stateCollapsed}
onToggle={toggleStatePanel}
/>
</WorkflowContent>

{/* Bottom: Timeline (optional) */}
<WorkflowTimeline
modules={workflow.execModules}
statuses={liveStatus}
/>
</WorkflowMonitor>

8. Custom Hook: useWorkflowWebSocket

File: web/typescript/valkyr_labs_com/src/hooks/useWorkflowWebSocket.ts

export function useWorkflowWebSocket(workflowId: string) {
const [liveStatus, setLiveStatus] = useState<Record<string, ModuleStatus>>({});
const [liveEvents, setLiveEvents] = useState<Record<string, EventLog[]>>({});
const [workflowState, setWorkflowState] = useState<Record<string, any>>({});
const [connected, setConnected] = useState(false);
const [error, setError] = useState<Error | null>(null);

useEffect(() => {
const client = new StompClient({
brokerURL: `ws://${window.location.host}/ws`,
reconnectDelay: 5000,
heartbeatIncoming: 4000,
heartbeatOutgoing: 4000,
});

client.onConnect = () => {
setConnected(true);

// Subscribe to workflow events
client.subscribe(`/topic/workflows/${workflowId}/events`, (message) => {
const data = JSON.parse(message.body);
handleEvent(data);
});
};

client.onStompError = (frame) => {
setError(new Error(frame.headers['message']));
};

client.activate();

return () => {
client.deactivate();
};
}, [workflowId]);

const handleEvent = (data: any) => {
switch (data.type) {
case 'status_update':
setLiveStatus(prev => ({
...prev,
[data.execModuleId]: data.status,
}));
break;

case 'event_log':
setLiveEvents(prev => ({
...prev,
[data.execModuleId]: [
...(prev[data.execModuleId] || []).slice(-4),
data.eventLog,
],
}));
break;

case 'state_update':
setWorkflowState(prev => ({
...prev,
...data.state,
}));
break;
}
};

return { liveStatus, liveEvents, workflowState, connected, error };
}

9. Backend Implementation

WebSocket Configuration

File: valkyrai/src/main/java/com/valkyrlabs/config/WebSocketConfig.java

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/app");
}

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setAllowedOriginPatterns("*")
.withSockJS();
}
}

WebSocket Handler

File: valkyrai/src/main/java/com/valkyrlabs/workflow/websocket/WorkflowWebSocketHandler.java

@Component
public class WorkflowWebSocketHandler {

private final SimpMessagingTemplate messagingTemplate;

public void broadcastEventLog(String workflowId, String execModuleId, EventLog eventLog) {
Map<String, Object> payload = Map.of(
"type", "event_log",
"workflowId", workflowId,
"execModuleId", execModuleId,
"eventLog", eventLog
);

messagingTemplate.convertAndSend(
"/topic/workflows/" + workflowId + "/events",
payload
);
}

public void broadcastStatusUpdate(String workflowId, String execModuleId,
ExecModule.StatusEnum status, long duration) {
Map<String, Object> payload = Map.of(
"type", "status_update",
"workflowId", workflowId,
"execModuleId", execModuleId,
"status", status.name(),
"duration", duration
);

messagingTemplate.convertAndSend(
"/topic/workflows/" + workflowId + "/events",
payload
);
}

public void broadcastStateUpdate(String workflowId, Map<String, String> state) {
Map<String, Object> payload = Map.of(
"type", "state_update",
"workflowId", workflowId,
"state", state
);

messagingTemplate.convertAndSend(
"/topic/workflows/" + workflowId + "/events",
payload
);
}
}

Workflow Control Endpoints

File: valkyrai/src/main/java/com/valkyrlabs/workflow/controller/WorkflowControlController.java

@RestController
@RequestMapping("/api/workflows")
public class WorkflowControlController {

@PostMapping("/{id}/start")
public ResponseEntity<WorkflowControlResponse> startWorkflow(@PathVariable String id) {
// Implementation
}

@PostMapping("/{id}/pause")
public ResponseEntity<WorkflowControlResponse> pauseWorkflow(@PathVariable String id) {
// Implementation
}

@PostMapping("/{id}/stop")
public ResponseEntity<WorkflowControlResponse> stopWorkflow(@PathVariable String id) {
// Implementation
}

@PutMapping("/{id}/server")
public ResponseEntity<Void> updateServer(
@PathVariable String id,
@RequestBody ServerUpdateRequest request) {
// Implementation
}
}

10. Testing Requirements

Unit Tests

  • WebSocket connection/disconnection
  • Message serialization/deserialization
  • Event handler dispatch
  • Status transitions
  • Error recovery

Integration Tests

  • Full workflow execution with WebSocket streaming
  • Multiple concurrent clients
  • Reconnection scenarios
  • Backpressure handling

UI Tests

  • Module card rendering with different statuses
  • Animation performance (60fps target)
  • Responsive layout on different screen sizes
  • Keyboard navigation
  • Accessibility (screen reader)

Performance Tests

  • 20+ modules in single workflow
  • 10+ concurrent workflows
  • WebSocket message throughput
  • Memory usage over long-running workflows

11. Deliverables

Frontend Components

  1. WorkflowMonitor.tsx - Main container
  2. ExecModuleCard.tsx - Individual module card with animations
  3. WorkflowControls.tsx - Play/Pause/Stop buttons
  4. OasServerPicker.tsx - Server selection dropdown
  5. WorkflowStatePanel.tsx - State display panel
  6. useWorkflowWebSocket.ts - WebSocket hook
  7. workflowAnimations.css - CSS animations

Backend Components

  1. WorkflowWebSocketHandler.java - WebSocket handler
  2. WorkflowControlController.java - Control endpoints
  3. WebSocketConfig.java - WebSocket configuration

Documentation

  1. Component Library Docs - Usage examples and API
  2. WebSocket Protocol Docs - Message formats
  3. Deployment Guide - WebSocket server configuration

12. Implementation Timeline

Phase 1: Backend Foundation (Day 1)

  • WebSocket configuration
  • WorkflowWebSocketHandler
  • WorkflowControlController
  • Testing with Postman/wscat

Phase 2: Frontend Core (Day 2)

  • useWorkflowWebSocket hook
  • WorkflowMonitor container
  • ExecModuleCard with animations
  • Basic WebSocket integration

Phase 3: Controls & Polish (Day 3)

  • WorkflowControls component
  • OasServerPicker component
  • WorkflowStatePanel component
  • Keyboard shortcuts

Phase 4: Testing & Documentation (Day 4)

  • Unit and integration tests
  • Performance testing and optimization
  • Component library documentation
  • Deployment guide

13. Success Criteria

✅ Real-time status updates with < 500ms latency
✅ Smooth 60fps animations on standard hardware
✅ Graceful WebSocket reconnection
✅ Support for 20+ modules without performance degradation
✅ Accessible (WCAG 2.1 AA compliance)
✅ Mobile-responsive design
✅ Comprehensive test coverage (>80%)
✅ Complete documentation


Status: 🚀 READY TO CRUSH
Next Action: Begin Phase 1 - Backend Foundation

Let's make this workflow monitor shine like a Christmas tree! 🎄✨