All checks were successful
Build and Push Docker Image to Gitea / build-and-push (push) Successful in 1m11s
Features added: - Created SSE (Server-Sent Events) endpoint at /api/supervisor/events - Polls supervisor every 2 seconds for state changes - Sends process-update events when state changes detected - Sends heartbeat events to keep connection alive - Includes error handling with error events - Created useEventSource hook for managing SSE connections - Automatic reconnection with exponential backoff - Configurable max reconnection attempts (default 10) - Connection status tracking (connecting, connected, disconnected, error) - Clean event listener management with proper cleanup - Heartbeat monitoring for connection health - Created ConnectionStatus component - Visual status indicator with icons (Wifi, WifiOff, Loader, AlertCircle) - Color-coded states (green=connected, yellow=connecting, red=error) - Shows reconnection attempt count - Manual reconnect button when disconnected/error - Integrated real-time updates into dashboard and processes pages - Auto-refresh process data when state changes occur - Connection status indicator in page headers - No manual refresh needed for live updates - Implemented proper cleanup on unmount - EventSource properly closed - Reconnection timeouts cleared - No memory leaks 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
82 lines
2.2 KiB
TypeScript
82 lines
2.2 KiB
TypeScript
'use client';
|
|
|
|
import { Wifi, WifiOff, Loader2, AlertCircle } from 'lucide-react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { ConnectionStatus as Status } from '@/lib/hooks/useEventSource';
|
|
import { cn } from '@/lib/utils/cn';
|
|
|
|
interface ConnectionStatusProps {
|
|
status: Status;
|
|
reconnectAttempts?: number;
|
|
onReconnect?: () => void;
|
|
}
|
|
|
|
export function ConnectionStatus({ status, reconnectAttempts = 0, onReconnect }: ConnectionStatusProps) {
|
|
const getStatusConfig = () => {
|
|
switch (status) {
|
|
case 'connected':
|
|
return {
|
|
icon: Wifi,
|
|
text: 'Live',
|
|
color: 'text-success',
|
|
bgColor: 'bg-success/10',
|
|
borderColor: 'border-success/20',
|
|
};
|
|
case 'connecting':
|
|
return {
|
|
icon: Loader2,
|
|
text: 'Connecting...',
|
|
color: 'text-warning',
|
|
bgColor: 'bg-warning/10',
|
|
borderColor: 'border-warning/20',
|
|
animate: true,
|
|
};
|
|
case 'error':
|
|
return {
|
|
icon: AlertCircle,
|
|
text: reconnectAttempts > 0 ? `Retrying (${reconnectAttempts})` : 'Error',
|
|
color: 'text-destructive',
|
|
bgColor: 'bg-destructive/10',
|
|
borderColor: 'border-destructive/20',
|
|
};
|
|
case 'disconnected':
|
|
default:
|
|
return {
|
|
icon: WifiOff,
|
|
text: 'Offline',
|
|
color: 'text-muted-foreground',
|
|
bgColor: 'bg-muted',
|
|
borderColor: 'border-muted',
|
|
};
|
|
}
|
|
};
|
|
|
|
const config = getStatusConfig();
|
|
const Icon = config.icon;
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
'flex items-center gap-2 px-3 py-1.5 rounded-md border text-sm',
|
|
config.bgColor,
|
|
config.borderColor
|
|
)}
|
|
>
|
|
<Icon
|
|
className={cn('h-4 w-4', config.color, config.animate && 'animate-spin')}
|
|
/>
|
|
<span className={cn('font-medium', config.color)}>{config.text}</span>
|
|
{(status === 'error' || status === 'disconnected') && onReconnect && (
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={onReconnect}
|
|
className="h-6 px-2 ml-1"
|
|
>
|
|
Reconnect
|
|
</Button>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|