localgreenchain/components/realtime/ConnectionStatus.tsx
Claude 7098335ce7
Add real-time updates system with Socket.io
Implement Agent 6: Real-Time Updates feature for LocalGreenChain:

- Add Socket.io server with room-based subscriptions
- Create client-side hooks (useSocket, useLiveFeed, usePlantUpdates)
- Add SocketProvider context for application-wide state
- Implement UI components:
  - ConnectionStatus: Shows WebSocket connection state
  - LiveFeed: Real-time event feed display
  - NotificationToast: Toast notifications with auto-dismiss
  - LiveChart: Real-time data visualization
- Add event type definitions and formatting utilities
- Create socket API endpoint for WebSocket initialization
- Add socket stats endpoint for monitoring
- Extend tailwind with fadeIn/slideIn animations

Integrates with existing EventStream SSE system for fallback.
2025-11-23 03:51:51 +00:00

167 lines
3.9 KiB
TypeScript

/**
* Connection Status Indicator Component
*
* Shows the current WebSocket connection status with visual feedback.
*/
import React from 'react';
import classNames from 'classnames';
import { useConnectionStatus } from '../../lib/realtime/useSocket';
import type { ConnectionStatus as ConnectionStatusType } from '../../lib/realtime/types';
interface ConnectionStatusProps {
showLabel?: boolean;
showLatency?: boolean;
size?: 'sm' | 'md' | 'lg';
className?: string;
}
/**
* Get status color classes
*/
function getStatusColor(status: ConnectionStatusType): string {
switch (status) {
case 'connected':
return 'bg-green-500';
case 'connecting':
case 'reconnecting':
return 'bg-yellow-500 animate-pulse';
case 'disconnected':
return 'bg-gray-400';
case 'error':
return 'bg-red-500';
default:
return 'bg-gray-400';
}
}
/**
* Get status label
*/
function getStatusLabel(status: ConnectionStatusType): string {
switch (status) {
case 'connected':
return 'Connected';
case 'connecting':
return 'Connecting...';
case 'reconnecting':
return 'Reconnecting...';
case 'disconnected':
return 'Disconnected';
case 'error':
return 'Connection Error';
default:
return 'Unknown';
}
}
/**
* Get size classes
*/
function getSizeClasses(size: 'sm' | 'md' | 'lg'): { dot: string; text: string } {
switch (size) {
case 'sm':
return { dot: 'w-2 h-2', text: 'text-xs' };
case 'md':
return { dot: 'w-3 h-3', text: 'text-sm' };
case 'lg':
return { dot: 'w-4 h-4', text: 'text-base' };
default:
return { dot: 'w-3 h-3', text: 'text-sm' };
}
}
/**
* Connection Status component
*/
export function ConnectionStatus({
showLabel = true,
showLatency = false,
size = 'md',
className,
}: ConnectionStatusProps) {
const { status, latency } = useConnectionStatus();
const sizeClasses = getSizeClasses(size);
return (
<div
className={classNames(
'inline-flex items-center gap-2',
className
)}
title={getStatusLabel(status)}
>
{/* Status dot */}
<span
className={classNames(
'rounded-full',
sizeClasses.dot,
getStatusColor(status)
)}
/>
{/* Label */}
{showLabel && (
<span className={classNames('text-gray-600', sizeClasses.text)}>
{getStatusLabel(status)}
</span>
)}
{/* Latency */}
{showLatency && status === 'connected' && latency !== undefined && (
<span className={classNames('text-gray-400', sizeClasses.text)}>
({latency}ms)
</span>
)}
</div>
);
}
/**
* Compact connection indicator (dot only)
*/
export function ConnectionDot({ className }: { className?: string }) {
const { status } = useConnectionStatus();
return (
<span
className={classNames(
'inline-block w-2 h-2 rounded-full',
getStatusColor(status),
className
)}
title={getStatusLabel(status)}
/>
);
}
/**
* Connection banner for showing reconnection status
*/
export function ConnectionBanner() {
const { status } = useConnectionStatus();
if (status === 'connected') {
return null;
}
const bannerClasses = classNames(
'fixed top-0 left-0 right-0 py-2 px-4 text-center text-sm font-medium z-50',
{
'bg-yellow-100 text-yellow-800': status === 'connecting' || status === 'reconnecting',
'bg-red-100 text-red-800': status === 'error',
'bg-gray-100 text-gray-800': status === 'disconnected',
}
);
return (
<div className={bannerClasses}>
{status === 'connecting' && 'Connecting to real-time updates...'}
{status === 'reconnecting' && 'Connection lost. Reconnecting...'}
{status === 'error' && 'Connection error. Please check your network.'}
{status === 'disconnected' && 'Disconnected from real-time updates.'}
</div>
);
}
export default ConnectionStatus;