Portal Community

Real-Time Status Panel

import { useState, useCallback } from 'react';
import { useSubscription, useConnectionStatus } from 'edge-stream-js-react';

interface ServerStatus {
  serverId: string;
  status: 'healthy' | 'degraded' | 'error';
  lastCheck: string;
}

function ServerStatusPanel() {
  const [servers, setServers] = useState<Map<string, ServerStatus>>(new Map());
  const { isReady } = useConnectionStatus();

  const handler = useCallback((envelope) => {
    const status: ServerStatus = envelope.body;
    setServers(prev => new Map(prev).set(status.serverId, status));
  }, []);

  useSubscription('bas', 'monitor.server.*', handler);

  return (
    <div>
      {!isReady && <p>Connecting to EdgeStream...</p>}
      {Array.from(servers.values()).map(s => (
        <div key={s.serverId} className={`server-card status-${s.status}`}>
          <strong>{s.serverId}</strong>
          <span>{s.status}</span>
          <small>Last check: {s.lastCheck}</small>
        </div>
      ))}
    </div>
  );
}

Live Log Feed

import { useState, useCallback, useRef } from 'react';
import { useSubscription } from 'edge-stream-js-react';

interface LogEntry {
  id: string;
  level: 'info' | 'warn' | 'error';
  message: string;
  timestamp: string;
}

function LiveLogFeed({ maxLines = 200 }: { maxLines?: number }) {
  const [logs, setLogs] = useState<LogEntry[]>([]);
  const bottomRef = useRef<HTMLDivElement>(null);

  const handler = useCallback((envelope) => {
    const entry: LogEntry = {
      id: envelope.meta.id,
      level: envelope.body.level,
      message: envelope.body.message,
      timestamp: envelope.meta.receivedAt.toISOString(),
    };

    setLogs(prev => {
      const updated = [...prev, entry];
      return updated.length > maxLines
        ? updated.slice(updated.length - maxLines) // keep most recent
        : updated;
    });

    // Auto-scroll to bottom
    setTimeout(() => bottomRef.current?.scrollIntoView({ behavior: 'smooth' }), 10);
  }, [maxLines]);

  useSubscription('bas', 'logs.*', handler);

  return (
    <div style={{ height: '400px', overflow: 'auto', fontFamily: 'monospace', fontSize: '12px' }}>
      {logs.map(log => (
        <div key={log.id} style={{ color: log.level === 'error' ? '#e74c3c' : log.level === 'warn' ? '#f39c12' : '#b0c4d8' }}>
          [{log.timestamp}] [{log.level.toUpperCase()}] {log.message}
        </div>
      ))}
      <div ref={bottomRef} />
    </div>
  );
}

Workflow Execution Tracker

import { useState, useCallback } from 'react';
import { useSubscription } from 'edge-stream-js-react';

interface WorkflowState {
  id: string;
  name: string;
  status: 'running' | 'completed' | 'failed' | 'suspended';
  currentNode: string;
  progress: number;
  startedAt: string;
}

function WorkflowTracker({ workflowId }: { workflowId: string }) {
  const [workflow, setWorkflow] = useState<WorkflowState | null>(null);
  const [events, setEvents] = useState<string[]>([]);

  const statusHandler = useCallback((envelope) => {
    setWorkflow(envelope.body);
  }, []);

  const eventHandler = useCallback((envelope) => {
    const { nodeId, event, timestamp } = envelope.body;
    setEvents(prev => [`${timestamp}: ${nodeId} — ${event}`, ...prev].slice(0, 50));
  }, []);

  // Subscribe to workflow-specific topics
  useSubscription('bas', `workflow.${workflowId}.status`, statusHandler);
  useSubscription('bas', `workflow.${workflowId}.node.*`, eventHandler);

  if (!workflow) return <div>Waiting for workflow data...</div>;

  return (
    <div>
      <h2>{workflow.name}</h2>
      <p>Status: <strong>{workflow.status}</strong></p>
      <p>Current node: {workflow.currentNode}</p>
      <progress value={workflow.progress} max={100} />
      <p>{workflow.progress}% complete</p>

      <h3>Event Log</h3>
      <ul>
        {events.map((e, i) => <li key={i}>{e}</li>)}
      </ul>
    </div>
  );
}
Next: Message Plugins Learn about the EdgeStream message plugin system — hooks that transform, filter, encrypt, or forward messages at the pipeline level. See Guide 9: Message Plugins.