Skip to main content

Workflow Monitor Component Library πŸŽ„

Real-time workflow execution monitoring with WebSocket streaming and animated status indicators.


Installation​

npm install react-toastify @stomp/stompjs

Quick Start​

import { WorkflowMonitor } from '@/components/WorkflowMonitor';
import { useGetWorkflowQuery } from '@thor/redux/services/WorkflowService';
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

function MyWorkflowPage({ workflowId }: { workflowId: string }) {
const { data: workflow } = useGetWorkflowQuery(workflowId);

if (!workflow) return <div>Loading...</div>;

return (
<>
<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' })}
/>
<ToastContainer position="bottom-right" />
</>
);
}

Components​

WorkflowMonitor​

Main container component that orchestrates all workflow monitoring features.

Props​

PropTypeRequiredDescription
workflowWorkflowβœ…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​

PropTypeRequiredDescription
moduleExecModuleβœ…ThorAPI ExecModule model
statusModuleStatus❌Current status (default: 'PENDING')
eventLogsEventLog[]❌Recent event logs
durationnumber❌Execution duration in ms
onExpand(id: string) => void❌Expand handler

Status Values​

  • PENDING - πŸ”΅ Blue, no animation
  • RUNNING - 🟑 Yellow, pulsing animation
  • SUCCESS - 🟒 Green, glow animation
  • ERROR - πŸ”΄ Red, shake animation
  • PAUSED - ⏸️ Gray, static
  • SKIPPED - ⏭️ 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​

PropTypeRequiredDescription
workflowWorkflowβœ…ThorAPI Workflow model
onStart() => Promise<void>βœ…Start handler
onPause() => Promise<void>βœ…Pause handler
onStop() => Promise<void>βœ…Stop handler
onRefresh() => void❌Refresh handler
disabledboolean❌Disable all buttons

Button States​

Workflow StatusPlayPauseStop
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​

PropTypeRequiredDescription
stateRecord<string, any>βœ…State key-value pairs
collapsedboolean❌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​

PropTypeRequiredDescription
currentServerstring❌Current server URL
onChange(url: string) => voidβœ…Change handler
disabledboolean❌Disable picker

Built-in Servers​

  1. Development - http://localhost:8080
  2. Staging - https://staging.valkyr.ai
  3. Production - https://api.valkyr.ai
  4. 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​

ParamTypeDescription
workflowIdstring | undefinedWorkflow UUID

Returns​

PropertyTypeDescription
liveStatusRecord<string, ModuleStatus>Module statuses by ID
liveEventsRecord<string, EventLog[]>Event logs by module ID
workflowStateRecord<string, any>Current workflow state
durationsRecord<string, number>Execution durations (ms)
connectedbooleanWebSocket connection status
errorError | nullConnection error
reconnect() => voidManual 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​

Import ToastContainer:

import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

<ToastContainer position="bottom-right" />

Examples​

With Redux Toolkit Query​

import { useGetWorkflowQuery, useStartWorkflowMutation } from '@thor/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: