All files / frontend/src/components/Map WebSDRMarkers.tsx

5.19% Statements 4/77
100% Branches 0/0
0% Functions 0/3
5.19% Lines 4/77

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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137            1x 1x                                                                                                                                               1x                                                                                                               1x  
/**
 * WebSDRMarkers Component
 * 
 * Displays WebSDR receiver locations on the map with status-based colors
 */
 
import { useEffect, useRef } from 'react';
import mapboxgl from 'mapbox-gl';
import type { WebSDRConfig, WebSDRHealthStatus } from '@/services/api/types';
 
export interface WebSDRMarkersProps {
    map: mapboxgl.Map;
    websdrs: WebSDRConfig[];
    healthStatus: Record<number, WebSDRHealthStatus>;
}
 
/**
 * Get marker color based on WebSDR health status
 */
function getStatusColor(status?: 'online' | 'offline' | 'unknown'): string {
    switch (status) {
        case 'online':
            return '#10b981'; // Green
        case 'offline':
            return '#ef4444'; // Red
        case 'unknown':
        default:
            return '#f59e0b'; // Yellow
    }
}
 
/**
 * Create popup HTML for WebSDR receiver
 */
function createPopupHTML(websdr: WebSDRConfig, health?: WebSDRHealthStatus): string {
    const status = health?.status || 'unknown';
    const statusBadge =
        status === 'online'
            ? '<span class="badge bg-success">Online</span>'
            : status === 'offline'
            ? '<span class="badge bg-danger">Offline</span>'
            : '<span class="badge bg-warning">Unknown</span>';
 
    return `
        <div style="min-width: 200px;">
            <h6 class="mb-2">${websdr.name}</h6>
            <div class="mb-2">${statusBadge}</div>
            <table class="table table-sm table-borderless mb-0">
                <tbody>
                    <tr>
                        <td class="text-muted">Location:</td>
                        <td>${websdr.location_name}</td>
                    </tr>
                    <tr>
                        <td class="text-muted">Coordinates:</td>
                        <td>${websdr.latitude.toFixed(4)}, ${websdr.longitude.toFixed(4)}</td>
                    </tr>
                    ${
                        health?.response_time_ms
                            ? `<tr>
                            <td class="text-muted">Response:</td>
                            <td>${health.response_time_ms}ms</td>
                        </tr>`
                            : ''
                    }
                    ${
                        health?.avg_snr !== undefined && health?.avg_snr !== null
                            ? `<tr>
                            <td class="text-muted">Avg SNR:</td>
                            <td>${health.avg_snr.toFixed(1)} dB</td>
                        </tr>`
                            : ''
                    }
                </tbody>
            </table>
        </div>
    `;
}
 
const WebSDRMarkers: React.FC<WebSDRMarkersProps> = ({ map, websdrs, healthStatus }) => {
    const markersRef = useRef<mapboxgl.Marker[]>([]);
 
    useEffect(() => {
        // Clear existing markers
        markersRef.current.forEach((marker) => marker.remove());
        markersRef.current = [];
 
        // Add markers for each WebSDR
        websdrs.forEach((websdr) => {
            const health = healthStatus[websdr.id];
            const color = getStatusColor(health?.status);
 
            // Create custom marker element
            const el = document.createElement('div');
            el.className = 'websdr-marker';
            el.style.width = '24px';
            el.style.height = '24px';
            el.style.borderRadius = '50%';
            el.style.backgroundColor = color;
            el.style.border = '2px solid white';
            el.style.boxShadow = '0 2px 4px rgba(0,0,0,0.3)';
            el.style.cursor = 'pointer';
            el.title = websdr.name;
 
            // Add pulsing animation for online receivers
            if (health?.status === 'online') {
                el.style.animation = 'pulse 2s infinite';
            }
 
            // Create popup
            const popup = new mapboxgl.Popup({
                offset: 25,
                closeButton: true,
                closeOnClick: false,
            }).setHTML(createPopupHTML(websdr, health));
 
            // Create marker
            const marker = new mapboxgl.Marker(el)
                .setLngLat([websdr.longitude, websdr.latitude])
                .setPopup(popup)
                .addTo(map);
 
            markersRef.current.push(marker);
        });
 
        // Cleanup function
        return () => {
            markersRef.current.forEach((marker) => marker.remove());
            markersRef.current = [];
        };
    }, [map, websdrs, healthStatus]);
 
    return null; // This component doesn't render anything directly
};
 
export default WebSDRMarkers;