Workflow Monitor Component
Real-time workflow execution monitoring with WebSocket streaming and animated status indicators.
Installation
npm install @stomp/stompjs
Quick Start
import { WorkflowMonitor } from '@/components/WorkflowMonitor';
import { ToastProvider } from '@/providers/ToastProvider';
import { useGetWorkflowQuery } from '@thorapi/redux/services/WorkflowService';
function MyWorkflowPage({ workflowId }: { workflowId: string }) {
const { data: workflow } = useGetWorkflowQuery(workflowId);
if (!workflow) return <div>Loading...</div>;
return (
<ToastProvider>
<WorkflowMonitor
workflow={workflow}
onStart={() => fetch(`/api/workflows/${workflowId}/start`, { method: 'POST' })}
onPause={() => fetch(`/api/workflows/${workflowId}/pause`, { method: 'POST' })}
onStop={() => fetch(`/api/workflows/${workflowId}/stop`, { method: 'POST' })}
/>
</ToastProvider>
);
}
Components
WorkflowMonitor
Main container component that orchestrates all workflow monitoring features.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
workflow | Workflow | ✅ | ThorAPI Workflow model |
onStart | () => Promise<void> | ✅ | Start workflow handler |
onPause | () => Promise<void> | ✅ | Pause workflow handler |
onStop | () => Promise<void> | ✅ | Stop workflow handler |
onServerChange | (url: string) => void | ❌ | Server URL change handler |
Example
<WorkflowMonitor
workflow={workflow}
onStart={async () => {
const response = await startWorkflowMutation(workflowId);
console.log('Started:', response);
}}
onPause={async () => {
await pauseWorkflowMutation(workflowId);
}}
onStop={async () => {
await stopWorkflowMutation(workflowId);
}}
onServerChange={(url) => {
console.log('Server changed to:', url);
}}
/>
ExecModuleCard
Animated card displaying individual module execution status.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
module | ExecModule | ✅ | ThorAPI ExecModule model |
status | ModuleStatus | ❌ | Current status (default: 'PENDING') |
eventLogs | EventLog[] | ❌ | Recent event logs |
duration | number | ❌ | Execution duration in ms |
onExpand | (id: string) => void | ❌ | Expand handler |
Status Values
PENDING- 🔵 Blue, no animationRUNNING- 🟡 Yellow, pulsing animationSUCCESS- 🟢 Green, glow animationERROR- 🔴 Red, shake animationPAUSED- ⏸️ Gray, staticSKIPPED- ⏭️ Light gray
Example
<ExecModuleCard
module={execModule}
status="RUNNING"
eventLogs={recentLogs}
duration={1500}
onExpand={(id) => console.log('Expanded:', id)}
/>
WorkflowControls
Play/Pause/Stop buttons with smart enable/disable logic.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
workflow | Workflow | ✅ | ThorAPI Workflow model |
onStart | () => Promise<void> | ✅ | Start handler |
onPause | () => Promise<void> | ✅ | Pause handler |
onStop | () => Promise<void> | ✅ | Stop handler |
onRefresh | () => void | ❌ | Refresh handler |
disabled | boolean | ❌ | Disable all buttons |
Button States
| Workflow Status | Play | Pause | Stop |
|---|---|---|---|
| READY | ✅ | ❌ | ❌ |
| RUNNING | ❌ | ✅ | ✅ |
| STOPPED | ✅ | ❌ | ❌ |
| ERROR | ✅ | ❌ | ❌ |
Example
<WorkflowControls
workflow={workflow}
onStart={handleStart}
onPause={handlePause}
onStop={handleStop}
onRefresh={() => refetch()}
/>
WorkflowStatePanel
Collapsible panel for viewing live workflow state.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
state | Record<string, any> | ✅ | State key-value pairs |
collapsed | boolean | ❌ | Collapsed state |
onToggle | () => void | ❌ | Toggle handler |
Features
- ✅ Namespace grouping (e.g., "build.*")
- ✅ Search/filter by key or value
- ✅ Click to copy individual values
- ✅ Export all state as JSON
- ✅ Type badges (string, number, boolean, object)
Example
const [collapsed, setCollapsed] = useState(true);
<WorkflowStatePanel
state={{
'build.version': '1.0.0',
'deploy.target': 'production',
'ig.publish.status': 'pending'
}}
collapsed={collapsed}
onToggle={() => setCollapsed(!collapsed)}
/>
OasServerPicker
Dropdown for selecting API server endpoint.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
currentServer | string | ❌ | Current server URL |
onChange | (url: string) => void | ✅ | Change handler |
disabled | boolean | ❌ | Disable picker |
Built-in Servers
- Development -
http://localhost:8080 - Staging -
https://staging.valkyr.ai - Production -
https://api.valkyr.ai - Custom - User-defined URL
Example
<OasServerPicker
currentServer="http://localhost:8080"
onChange={(url) => {
console.log('Server changed to:', url);
updateWorkflowServer(workflowId, url);
}}
disabled={workflow.status === 'running'}
/>
Hooks
useWorkflowWebSocket
Custom hook for WebSocket connection and real-time updates.
Parameters
| Param | Type | Description |
|---|---|---|
workflowId | string | undefined | Workflow UUID |
Returns
| Property | Type | Description |
|---|---|---|
liveStatus | Record<string, ModuleStatus> | Module statuses by ID |
liveEvents | Record<string, EventLog[]> | Event logs by module ID |
workflowState | Record<string, any> | Current workflow state |
durations | Record<string, number> | Execution durations (ms) |
connected | boolean | WebSocket connection status |
error | Error | null | Connection error |
reconnect | () => void | Manual reconnect function |
Example
const {
liveStatus,
liveEvents,
workflowState,
durations,
connected,
error,
reconnect
} = useWorkflowWebSocket(workflowId);
if (!connected) {
return <div>Connecting... <button onClick={reconnect}>Retry</button></div>;
}
if (error) {
return <div>Error: {error.message}</div>;
}
// Use live data...
Styling
Custom Animations
The component library includes GPU-accelerated CSS animations:
.module-status-running {
animation: pulse 2s ease-in-out infinite;
}
.module-status-success {
animation: glow 1s ease-in-out;
}
.module-status-error {
animation: shake 0.5s ease-in-out;
}
Theming
Override default styles:
.workflow-monitor {
--status-pending: #6c757d;
--status-running: #ffc107;
--status-success: #28a745;
--status-error: #dc3545;
--status-paused: #6c757d;
}
Accessibility
All components follow WCAG 2.1 AA guidelines:
- ✅ Keyboard navigation (Tab, Enter, Space, Esc)
- ✅ ARIA labels on interactive elements
- ✅ Screen reader announcements
- ✅ High contrast mode support
- ✅ Focus indicators
- ✅ Reduced motion support
Performance
- 🚀 60fps animations via CSS GPU acceleration
- 🚀 Debounced WebSocket updates (max 10/sec)
- 🚀 Memoized components with React.memo()
- 🚀 Lazy loading for module details
- 🚀 Virtual scrolling for 20+ modules
Browser Support
- ✅ Chrome 90+
- ✅ Firefox 88+
- ✅ Safari 14+
- ✅ Edge 90+
Troubleshooting
WebSocket Connection Fails
// Check WebSocket URL
const wsUrl = `${window.location.protocol === "https:" ? "wss:" : "ws:"}//${window.location.host}/ws`;
console.log("WebSocket URL:", wsUrl);
TypeScript Errors
Enable skipLibCheck in tsconfig.json:
{
"compilerOptions": {
"skipLibCheck": true
}
}
Missing Toast Notifications
Make sure your React tree is wrapped in ToastProvider (see src/providers/ToastProvider.tsx) so toast.success(...) calls are rendered.
Examples
With Redux Toolkit Query
import { useGetWorkflowQuery, useStartWorkflowMutation } from '@thorapi/redux/services/WorkflowService';
function WorkflowPage({ id }: { id: string }) {
const { data: workflow, refetch } = useGetWorkflowQuery(id);
const [start] = useStartWorkflowMutation();
const [pause] = usePauseWorkflowMutation();
const [stop] = useStopWorkflowMutation();
return (
<WorkflowMonitor
workflow={workflow!}
onStart={() => start(id).unwrap()}
onPause={() => pause(id).unwrap()}
onStop={() => stop(id).unwrap()}
/>
);
}
With Custom WebSocket Endpoint
// Modify useWorkflowWebSocket hook
const wsUrl = process.env.REACT_APP_WS_URL || `ws://${window.location.host}/ws`;
const client = new Client({
brokerURL: wsUrl,
// ...
});
API Reference
For complete API documentation, see: