Portal Community

How SignalR Updates Flow In

// useExecutionSignalR.ts
connection.on("NodeStatusChanged", (event: NodeStatusChangedEvent) => {
    // Update executionStore (for Observer Panel tabs)
    useExecutionStore.getState().setNodeStatus(event.nodeId, {
        nodeId      : event.nodeId,
        status      : event.status,
        startedAt   : event.startedAt,
        completedAt : event.completedAt,
        durationMs  : event.durationMs,
        errorMessage: event.errorMessage,
        retryCount  : event.retryCount
    });

    // Also update designerModeStore (for canvas overlays)
    useDesignerModeStore.getState().setNodeStatus(event.nodeId, event);
});

setNodeStatus Implementation

setNodeStatus: (nodeId, status) => set(state => ({
    nodeStatuses: {
        ...state.nodeStatuses,
        [nodeId]: status
    }
}));

// Immutable update — creates a new object for nodeStatuses
// Only components subscribing to state.nodeStatuses[specificNodeId] re-render

Consuming Node Statuses in the Nodes Tab

// NodeListTabContent.tsx — shows all nodes with their statuses
function NodeListTabContent() {
    const nodeStatuses = useExecutionStore(state => state.nodeStatuses);
    const { nodes }    = useWorkflowStore(state => ({ nodes: state.nodes }));

    return (
        <ul>
            {nodes.map(node => {
                const status = nodeStatuses[node.id];
                return (
                    <li key={node.id}>
                        <StatusDot status={status?.status ?? 'pending'} />
                        <span>{node.data.label}</span>
                        {status?.durationMs != null &&
                            <span>{status.durationMs}ms</span>}
                    </li>
                );
            })}
        </ul>
    );
}
nodeStatuses is the Observer Panel view — not the authority. The authoritative status lives in the backend Process_Executions table. The frontend store is a real-time mirror. If a user reloads mid-execution, the statuses are re-fetched from the API.