# WebSocket Real-Time Notifications - Usage Guide This guide shows you how to use the WebSocket real-time notification system in your React components. ## Quick Start ### 1. Enable Global Notifications Add the `NotificationToastHandler` to your root `App.tsx`: ```tsx // src/App.tsx import { Toaster } from 'react-hot-toast'; import { NotificationToastHandler } from './components/NotificationToastHandler'; function App() { return ( <> {/* React Hot Toast container */} {/* WebSocket notification handler (renders nothing, handles side effects) */} {/* Your app routes and components */} ); } ``` ### 2. Add Notification Bell to Header ```tsx // src/components/Header.tsx import { NotificationBell } from './components/NotificationBell'; import { useNavigate } from 'react-router-dom'; function Header() { const navigate = useNavigate(); return (

Flyer Crawler

{/* Notification bell with unread count */} navigate('/notifications')} showConnectionStatus={true} />
); } ``` ### 3. Listen for Notifications in Components ```tsx // src/pages/DealsPage.tsx import { useEventBus } from '../hooks/useEventBus'; import { useCallback, useState } from 'react'; import type { DealNotificationData } from '../types/websocket'; function DealsPage() { const [deals, setDeals] = useState([]); // Listen for new deal notifications const handleDealNotification = useCallback((data: DealNotificationData) => { console.log('New deals received:', data.deals); // Update your deals list setDeals((prev) => [...data.deals, ...prev]); // Or refetch from API // refetchDeals(); }, []); useEventBus('notification:deal', handleDealNotification); return (

Deals

{/* Render deals */}
); } ``` ## Available Components ### `NotificationBell` A notification bell icon with unread count and connection status indicator. **Props:** - `onClick?: () => void` - Callback when bell is clicked - `showConnectionStatus?: boolean` - Show green/red/yellow connection dot (default: `true`) - `className?: string` - Custom CSS classes **Example:** ```tsx navigate('/notifications')} showConnectionStatus={true} className="mr-4" /> ``` ### `ConnectionStatus` A simple status indicator showing if WebSocket is connected (no bell icon). **Example:** ```tsx ``` ### `NotificationToastHandler` Global handler that listens for WebSocket events and displays toasts. Should be rendered once at app root. **Props:** - `enabled?: boolean` - Enable/disable toast notifications (default: `true`) - `playSound?: boolean` - Play sound on notifications (default: `false`) - `soundUrl?: string` - Custom notification sound URL **Example:** ```tsx ``` ## Available Hooks ### `useWebSocket` Connect to the WebSocket server and manage connection state. **Options:** - `autoConnect?: boolean` - Auto-connect on mount (default: `true`) - `maxReconnectAttempts?: number` - Max reconnect attempts (default: `5`) - `reconnectDelay?: number` - Base reconnect delay in ms (default: `1000`) - `onConnect?: () => void` - Callback on connection - `onDisconnect?: () => void` - Callback on disconnect - `onError?: (error: Event) => void` - Callback on error **Returns:** - `isConnected: boolean` - Connection status - `isConnecting: boolean` - Connecting state - `error: string | null` - Error message if any - `connect: () => void` - Manual connect function - `disconnect: () => void` - Manual disconnect function - `send: (message: WebSocketMessage) => void` - Send message to server **Example:** ```tsx const { isConnected, error, connect, disconnect } = useWebSocket({ autoConnect: true, maxReconnectAttempts: 3, onConnect: () => console.log('Connected!'), onDisconnect: () => console.log('Disconnected!'), }); return (

Status: {isConnected ? 'Connected' : 'Disconnected'}

{error &&

Error: {error}

}
); ``` ### `useEventBus` Subscribe to event bus events (used with WebSocket integration). **Parameters:** - `event: string` - Event name to listen for - `callback: (data?: T) => void` - Callback function **Available Events:** - `'notification:deal'` - Deal notifications (`DealNotificationData`) - `'notification:system'` - System messages (`SystemMessageData`) - `'notification:error'` - Error messages (`{ message: string; code?: string }`) **Example:** ```tsx import { useEventBus } from '../hooks/useEventBus'; import type { DealNotificationData } from '../types/websocket'; function MyComponent() { useEventBus('notification:deal', (data) => { console.log('Received deal:', data); }); return
Listening for deals...
; } ``` ## Message Types ### Deal Notification ```typescript interface DealNotificationData { notification_id?: string; deals: Array<{ item_name: string; best_price_in_cents: number; store_name: string; store_id: string; }>; user_id: string; message: string; } ``` ### System Message ```typescript interface SystemMessageData { message: string; severity: 'info' | 'warning' | 'error'; } ``` ## Advanced Usage ### Custom Notification Handling If you don't want to use the default `NotificationToastHandler`, you can create your own: ```tsx import { useWebSocket } from '../hooks/useWebSocket'; import { useEventBus } from '../hooks/useEventBus'; import type { DealNotificationData } from '../types/websocket'; function CustomNotificationHandler() { const { isConnected } = useWebSocket({ autoConnect: true }); useEventBus('notification:deal', (data) => { // Custom handling - e.g., update Redux store dispatch(addDeals(data.deals)); // Show custom UI showCustomNotification(data.message); }); return null; // Or return your custom UI } ``` ### Conditional WebSocket Connection ```tsx import { useWebSocket } from '../hooks/useWebSocket'; import { useAuth } from '../hooks/useAuth'; function ConditionalWebSocket() { const { user } = useAuth(); // Only connect if user is logged in useWebSocket({ autoConnect: !!user, }); return null; } ``` ### Send Messages to Server ```tsx import { useWebSocket } from '../hooks/useWebSocket'; function PingComponent() { const { send, isConnected } = useWebSocket(); const sendPing = () => { send({ type: 'ping', data: {}, timestamp: new Date().toISOString(), }); }; return ( ); } ``` ## Admin Monitoring ### Get WebSocket Stats Admin users can check WebSocket connection statistics: ```bash # Get connection stats curl -H "Authorization: Bearer " \ http://localhost:3001/api/admin/websocket/stats ``` **Response:** ```json { "success": true, "data": { "totalUsers": 42, "totalConnections": 67 } } ``` ### Admin Dashboard Integration ```tsx import { useEffect, useState } from 'react'; function AdminWebSocketStats() { const [stats, setStats] = useState({ totalUsers: 0, totalConnections: 0 }); useEffect(() => { const fetchStats = async () => { const response = await fetch('/api/admin/websocket/stats', { headers: { Authorization: `Bearer ${token}` }, }); const data = await response.json(); setStats(data.data); }; fetchStats(); const interval = setInterval(fetchStats, 5000); // Poll every 5s return () => clearInterval(interval); }, []); return (

WebSocket Stats

Connected Users: {stats.totalUsers}

Total Connections: {stats.totalConnections}

); } ``` ## Troubleshooting ### Connection Issues 1. **Check JWT Token**: WebSocket requires a valid JWT token in cookies or query string 2. **Check Server Logs**: Look for WebSocket connection errors in server logs 3. **Check Browser Console**: WebSocket errors are logged to console 4. **Verify Path**: WebSocket server is at `ws://localhost:3001/ws` (or `wss://` for HTTPS) ### Not Receiving Notifications 1. **Check Connection Status**: Use `` to verify connection 2. **Verify Event Name**: Ensure you're listening to the correct event (`notification:deal`, etc.) 3. **Check User ID**: Notifications are sent to specific users - verify JWT user_id matches ### High Memory Usage 1. **Connection Leaks**: Ensure components using `useWebSocket` are properly unmounting 2. **Event Listeners**: `useEventBus` automatically cleans up, but verify no manual listeners remain 3. **Check Stats**: Use `/api/admin/websocket/stats` to monitor connection count ## Testing ### Unit Tests ```typescript import { renderHook } from '@testing-library/react'; import { useWebSocket } from '../hooks/useWebSocket'; describe('useWebSocket', () => { it('should connect automatically', () => { const { result } = renderHook(() => useWebSocket({ autoConnect: true })); expect(result.current.isConnecting).toBe(true); }); }); ``` ### Integration Tests See [src/tests/integration/websocket.integration.test.ts](../src/tests/integration/websocket.integration.test.ts) for comprehensive integration tests. ## Related Documentation - [ADR-022: Real-time Notification System](./adr/0022-real-time-notification-system.md) - [ADR-036: Event Bus and Pub/Sub Pattern](./adr/0036-event-bus-and-pub-sub-pattern.md) - [ADR-042: Email and Notification Architecture](./adr/0042-email-and-notification-architecture.md)