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>fromexecuteReactive()to connected clients - Handle reconnection gracefully (Spring WebSocket auto-reconnect)
- Backpressure handling with
onBackpressureDropstrategy - 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:
- Development - http://localhost:8080
- Staging - https://staging.valkyr.ai
- Production - https://api.valkyr.ai
- 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
- ✅ WorkflowMonitor.tsx - Main container
- ✅ ExecModuleCard.tsx - Individual module card with animations
- ✅ WorkflowControls.tsx - Play/Pause/Stop buttons
- ✅ OasServerPicker.tsx - Server selection dropdown
- ✅ WorkflowStatePanel.tsx - State display panel
- ✅ useWorkflowWebSocket.ts - WebSocket hook
- ✅ workflowAnimations.css - CSS animations
Backend Components
- ✅ WorkflowWebSocketHandler.java - WebSocket handler
- ✅ WorkflowControlController.java - Control endpoints
- ✅ WebSocketConfig.java - WebSocket configuration
Documentation
- ✅ Component Library Docs - Usage examples and API
- ✅ WebSocket Protocol Docs - Message formats
- ✅ 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! 🎄✨