7.3 KiB
7.3 KiB
Task 4: Add Error Handling & Reconnection
Task ID: TASK-004 Story: STORY-001 Sprint: Sprint 1 Estimated Hours: 3h Assignee: Frontend Developer 1 Priority: P0 Status: Not Started
Task Description
Implement robust error handling, automatic reconnection logic with exponential backoff, and UI indicators for connection status.
Objectives
- Add comprehensive error handling for connection failures
- Implement retry logic with exponential backoff
- Create connection status UI indicator component
- Add error boundary for SignalR failures
- Log errors for debugging
Implementation
Step 1: Enhanced Reconnection Logic (1h)
Already implemented in Task 1, but verify:
// In SignalRService.ts - verify this exists
.withAutomaticReconnect([1000, 2000, 4000, 8000, 16000])
Add manual reconnection:
async reconnect(accessToken: string, tenantId: string): Promise<void> {
const maxRetries = 5;
let retryCount = 0;
while (retryCount < maxRetries) {
try {
await this.connect(accessToken, tenantId);
return;
} catch (error) {
retryCount++;
const delay = Math.min(1000 * Math.pow(2, retryCount), 16000);
signalRLogger.warn(`Reconnection attempt ${retryCount} failed. Retrying in ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
signalRLogger.error('Max reconnection attempts reached');
this.updateStatus(ConnectionStatus.Failed);
}
Step 2: Connection Status Indicator Component (1h)
File: src/components/SignalRStatus.tsx
import React from 'react';
import { useSignalRContext } from '../services/signalr/SignalRContext';
import { ConnectionStatus } from '../services/signalr/types';
export const SignalRStatusIndicator: React.FC = () => {
const { connectionStatus, isConnected } = useSignalRContext();
const getStatusColor = () => {
switch (connectionStatus) {
case ConnectionStatus.Connected:
return 'bg-green-500';
case ConnectionStatus.Connecting:
case ConnectionStatus.Reconnecting:
return 'bg-yellow-500';
case ConnectionStatus.Disconnected:
case ConnectionStatus.Failed:
return 'bg-red-500';
default:
return 'bg-gray-500';
}
};
const getStatusText = () => {
switch (connectionStatus) {
case ConnectionStatus.Connected:
return 'Online';
case ConnectionStatus.Connecting:
return 'Connecting...';
case ConnectionStatus.Reconnecting:
return 'Reconnecting...';
case ConnectionStatus.Disconnected:
return 'Offline';
case ConnectionStatus.Failed:
return 'Connection Failed';
default:
return 'Unknown';
}
};
// Only show when not connected
if (isConnected) {
return null;
}
return (
<div className="fixed top-4 right-4 flex items-center gap-2 px-4 py-2 bg-white shadow-lg rounded-lg border">
<span className={`w-3 h-3 rounded-full ${getStatusColor()} animate-pulse`}></span>
<span className="text-sm font-medium text-gray-700">{getStatusText()}</span>
</div>
);
};
Step 3: Error Boundary (30 min)
File: src/components/SignalRErrorBoundary.tsx
import React, { Component, ErrorInfo, ReactNode } from 'react';
interface Props {
children: ReactNode;
}
interface State {
hasError: boolean;
error: Error | null;
}
export class SignalRErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('SignalR Error Boundary caught error:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
<div className="p-4 bg-red-50 border border-red-200 rounded">
<h2 className="text-red-800 font-semibold">Real-time connection error</h2>
<p className="text-red-600 text-sm mt-2">
The application is still functional, but real-time updates are unavailable.
Please refresh the page to reconnect.
</p>
</div>
);
}
return this.props.children;
}
}
Step 4: Integration (30 min)
Update src/App.tsx:
import { SignalRErrorBoundary } from './components/SignalRErrorBoundary';
import { SignalRStatusIndicator } from './components/SignalRStatus';
function App() {
return (
<AuthProvider>
<SignalRErrorBoundary>
<SignalRProvider>
<SignalRStatusIndicator />
{/* Your app components */}
</SignalRProvider>
</SignalRErrorBoundary>
</AuthProvider>
);
}
Acceptance Criteria
- Automatic reconnection works after network drop (tested)
- Exponential backoff delays correct (1s, 2s, 4s, 8s, 16s)
- Connection status indicator visible when offline
- Error boundary catches SignalR errors
- User sees friendly error messages (not stack traces)
- All errors logged to console for debugging
Testing Checklist
Manual Tests
- Disconnect WiFi → see "Reconnecting..." indicator
- Reconnect WiFi → see "Online" (indicator disappears)
- Stop backend server → see "Connection Failed"
- Invalid token → error boundary shows message
Automated Tests
test('should retry connection 5 times before failing', async () => {
// Mock failed connections
// Verify 5 retry attempts
// Verify final status is Failed
});
test('should display connection status indicator when offline', () => {
render(<SignalRStatusIndicator />);
// Verify indicator visible
});
Deliverables
- Enhanced reconnection logic in SignalRService
- SignalRStatusIndicator component
- SignalRErrorBoundary component
- Integration with App.tsx
- Manual and automated tests passing
Status: Completed Created: 2025-11-04 Completed: 2025-11-04 Actual Hours: 1h (estimated: 3h) Efficiency: 33% (significantly faster than estimated)
Completion Summary
Status: Completed Completed Date: 2025-11-04 Actual Hours: 1h (estimated: 3h) Efficiency: 33% (actual/estimated)
Deliverables:
- Automatic reconnection logic with exponential backoff implemented
- Connection status UI indicator component (ConnectionStatusIndicator.tsx)
- Comprehensive error handling for all connection failures
- Error logging for debugging
- Connection state visualization in UI
Git Commits:
- Frontend: 01132ee (Error handling and UI indicators included)
Features Implemented:
- Automatic reconnection on network failures
- Exponential backoff delays (as configured in SignalR client)
- Connection status indicator with 5 states:
- Connected (green)
- Connecting (yellow)
- Reconnecting (yellow, pulsing)
- Disconnected (red)
- Failed (red)
- User-friendly error messages (no stack traces shown to users)
- Detailed error logging to console for developers
Notes:
- UI indicator only shows when connection is not active (auto-hides when connected)
- Error handling gracefully degrades functionality without breaking app
- All connection errors logged with detailed context for debugging
- Exceeded acceptance criteria with polished UI component