import { useState, useEffect } from 'react'; import WorldMap from './world-map'; import { motion } from 'framer-motion'; // Interface for the simplified data passed to WorldMap interface GeoNode { lat: number; lng: number; label?: string; color?: string; } // Interface for the raw data structure expected from the gridproxy API interface RawNode { node_id: number; location: { latitude: string; // API often returns these as strings longitude: string; // API often returns these as strings city: string; country: string; }; // ... other raw fields you don't need } const clusterNodes = (nodeList: GeoNode[], cellSize = 2) => { const buckets = new Map< string, { latSum: number; lngSum: number; count: number } >(); nodeList.forEach((node) => { const latBucket = Math.round(node.lat / cellSize) * cellSize; const lngBucket = Math.round(node.lng / cellSize) * cellSize; const key = `${latBucket}|${lngBucket}`; const bucket = buckets.get(key); if (bucket) { bucket.latSum += node.lat; bucket.lngSum += node.lng; bucket.count += 1; } else { buckets.set(key, { latSum: node.lat, lngSum: node.lng, count: 1, }); } }); return Array.from(buckets.values()).map((bucket) => { const avgLat = bucket.latSum / bucket.count; const avgLng = bucket.lngSum / bucket.count; const count = bucket.count; let color = "#06b6d4"; if (count > 20) { color = "#0891b2"; } else if (count > 5) { color = "#22d3ee"; } return { lat: avgLat, lng: avgLng, color, label: `${count} nodes`, }; }); }; function DynamicMapContainer() { const [loading, setLoading] = useState(true); const [nodes, setNodes] = useState([]); const API_URL = "https://gridproxy.grid.tf/nodes?healthy=true&size=99999"; useEffect(() => { async function fetchNodeData() { try { const response = await fetch(API_URL); const data: RawNode[] = await response.json(); // Type the incoming data // 🚨 Map the API response to your component's expected GeoNode format const geoNodes: GeoNode[] = data .filter((node: RawNode) => node.location && node.location.latitude && node.location.longitude) .map((node: RawNode) => ({ // Convert string coordinates to numbers lat: parseFloat(node.location.latitude), lng: parseFloat(node.location.longitude), label: `${node.location.city}, ${node.location.country} (${node.node_id})`, // Optionally set color based on some node property if available })); const clusteredNodes = clusterNodes(geoNodes); setNodes(clusteredNodes); setLoading(false); } catch (error) { console.error("Failed to fetch node data:", error); setLoading(false); } } fetchNodeData(); }, []); // --- RENDERING --- if (loading) { // Show a loading state while data is being fetched return (
🌎

Loading nodes...

); } // Pass the dynamically fetched nodes to your WorldMap component return ( ); } export default DynamicMapContainer;