All files / src/components/features/notifications/ConnectionIndicator index.tsx

45.45% Statements 45/99
100% Branches 0/0
0% Functions 0/1
45.45% Lines 45/99

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 1001x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x                                                                                                             1x 1x  
'use client';
 
/**
 * ConnectionIndicator Component
 *
 * Visual indicator for real-time connection status.
 * Shows whether the client is connected via WebSocket or using polling fallback.
 */
 
import { cn } from '@/lib/core';
import type { NotificationMode } from '@/hooks/useNotificationsWithFallback';
 
export interface ConnectionIndicatorProps {
  /** Whether connected to real-time server */
  isConnected: boolean;
  /** Current notification mode */
  mode: NotificationMode;
  /** Additional CSS classes */
  className?: string;
  /** Whether to show the label text */
  showLabel?: boolean;
  /** Size variant */
  size?: 'sm' | 'md' | 'lg';
  /** Whether to show animation when connected */
  animated?: boolean;
  /** Whether to show tooltip on hover */
  showTooltip?: boolean;
  /** Custom tooltip content */
  tooltipContent?: string;
}
 
const sizeClasses = {
  sm: 'h-1.5 w-1.5',
  md: 'h-2 w-2',
  lg: 'h-2.5 w-2.5',
};
 
const labelSizeClasses = {
  sm: 'text-[10px]',
  md: 'text-xs',
  lg: 'text-sm',
};
 
export function ConnectionIndicator({
  isConnected,
  mode,
  className,
  showLabel = false,
  size = 'md',
  animated = true,
  showTooltip = true,
  tooltipContent,
}: ConnectionIndicatorProps) {
  const dotClasses = cn(
    'rounded-full flex-shrink-0',
    sizeClasses[size],
    isConnected
      ? 'bg-green-500 dark:bg-green-400'
      : 'bg-yellow-500 dark:bg-yellow-400',
    animated && isConnected && 'animate-pulse'
  );

  const labelText = mode === 'realtime' ? 'Live' : 'Polling';
  const tooltip =
    tooltipContent ||
    (mode === 'realtime'
      ? 'Connected - Real-time updates active'
      : 'Polling mode - Updates every 30 seconds');

  return (
    <div
      className={cn('flex items-center gap-1.5', className)}
      role="status"
      aria-live="polite"
      aria-label={`Connection status: ${mode === 'realtime' ? 'connected' : 'polling mode'}`}
      title={showTooltip ? tooltip : undefined}
    >
      <span className={dotClasses} aria-hidden="true" />
      {showLabel && (
        <span
          className={cn(
            'text-gray-500 dark:text-gray-400 font-medium',
            labelSizeClasses[size]
          )}
        >
          {labelText}
        </span>
      )}
      {/* Screen reader text */}
      <span className="sr-only">
        {mode === 'realtime'
          ? 'Connected to real-time updates'
          : 'Using polling for updates'}
      </span>
    </div>
  );
}
 
export default ConnectionIndicator;