"use client"; import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet"; import L from "leaflet"; import type { NodeRecord, CallRecord, IncidentRecord } from "@/lib/types"; // Fix Leaflet default icon paths broken by webpack delete (L.Icon.Default.prototype as unknown as Record)._getIconUrl; L.Icon.Default.mergeOptions({ iconRetinaUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png", iconUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png", shadowUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png", }); const nodeIcon = (status: string) => L.divIcon({ className: "", html: `
`, iconSize: [14, 14], iconAnchor: [7, 7], }); const INCIDENT_COLORS: Record = { fire: "#ef4444", police: "#3b82f6", ems: "#eab308", accident: "#f97316", other: "#6b7280", }; const incidentIcon = (type: string | null) => { const color = INCIDENT_COLORS[type ?? "other"] ?? INCIDENT_COLORS.other; return L.divIcon({ className: "", html: `
!
`, iconSize: [16, 16], iconAnchor: [8, 8], }); }; interface Props { nodes: NodeRecord[]; activeCalls: CallRecord[]; incidents?: IncidentRecord[]; } export default function MapView({ nodes, activeCalls, incidents = [] }: Props) { const activeByNode = Object.fromEntries( activeCalls.map((c) => [c.node_id, c]) ); // Only show incidents that have been geocoded (location_coords set by the server). const plottedIncidents = incidents.flatMap((inc) => inc.location_coords ? [{ inc, pos: [inc.location_coords.lat, inc.location_coords.lng] as [number, number] }] : [] ); const center: [number, number] = nodes.length > 0 ? [nodes[0].lat, nodes[0].lon] : plottedIncidents.length > 0 ? plottedIncidents[0].pos : [39.5, -98.35]; const zoom = nodes.length > 0 ? 10 : plottedIncidents.length > 0 ? 14 : 4; return ( {/* Node markers */} {nodes.map((node) => (

{node.name}

{node.node_id}

{node.status}

{activeByNode[node.node_id] && (

● TG {activeByNode[node.node_id].talkgroup_id ?? "—"}{" "} {activeByNode[node.node_id].talkgroup_name}

)}
))} {/* Incident markers — positioned at the node covering the incident's system */} {plottedIncidents.map(({ inc, pos }) => (

{inc.title ?? "Incident"}

{inc.type ?? "other"}

{inc.status}

{inc.location &&

{inc.location}

}

{inc.call_ids.length} call{inc.call_ids.length !== 1 ? "s" : ""}

{inc.summary &&

{inc.summary}

} View incident →
))}
); }