Massive update

This commit is contained in:
Logan
2026-04-11 13:44:08 -04:00
parent fd6c2fd8bf
commit 3b3a136d04
31 changed files with 1919 additions and 94 deletions
+56 -4
View File
@@ -1,9 +1,9 @@
"use client";
import { useEffect } from "react";
import { MapContainer, TileLayer, Marker, Popup, useMap } from "react-leaflet";
import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet";
import L from "leaflet";
import type { NodeRecord, CallRecord } from "@/lib/types";
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<string, unknown>)._getIconUrl;
L.Icon.Default.mergeOptions({
@@ -25,16 +25,45 @@ const nodeIcon = (status: string) =>
iconAnchor: [7, 7],
});
const INCIDENT_COLORS: Record<string, string> = {
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: `<div style="
width:16px;height:16px;border-radius:3px;
background:${color};border:2px solid #111827;
display:flex;align-items:center;justify-content:center;
font-size:9px;color:#111827;font-weight:bold;line-height:1;
">!</div>`,
iconSize: [16, 16],
iconAnchor: [8, 8],
});
};
interface Props {
nodes: NodeRecord[];
activeCalls: CallRecord[];
incidents?: IncidentRecord[];
}
export default function MapView({ nodes, activeCalls }: Props) {
export default function MapView({ nodes, activeCalls, incidents = [] }: Props) {
const activeByNode = Object.fromEntries(
activeCalls.map((c) => [c.node_id, c])
);
// Only show incidents that have coordinates
const mappableIncidents = incidents.filter(
(i) => i.location && i.location.lat != null && i.location.lng != null
);
const center: [number, number] =
nodes.length > 0 ? [nodes[0].lat, nodes[0].lon] : [39.5, -98.35];
@@ -49,6 +78,8 @@ export default function MapView({ nodes, activeCalls }: Props) {
url="https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png"
attribution='&copy; <a href="https://carto.com/">CARTO</a>'
/>
{/* Node markers */}
{nodes.map((node) => (
<Marker
key={node.node_id}
@@ -70,6 +101,27 @@ export default function MapView({ nodes, activeCalls }: Props) {
</Popup>
</Marker>
))}
{/* Incident markers */}
{mappableIncidents.map((inc) => (
<Marker
key={inc.incident_id}
position={[inc.location!.lat, inc.location!.lng]}
icon={incidentIcon(inc.type)}
>
<Popup className="font-mono">
<div className="text-gray-900">
<p className="font-bold">{inc.title ?? "Incident"}</p>
<p className="text-xs capitalize" style={{ color: INCIDENT_COLORS[inc.type ?? "other"] }}>
{inc.type ?? "other"}
</p>
<p className="text-xs mt-1 capitalize">{inc.status}</p>
<p className="text-xs text-gray-500">{inc.call_ids.length} call{inc.call_ids.length !== 1 ? "s" : ""}</p>
{inc.summary && <p className="text-xs mt-1">{inc.summary}</p>}
</div>
</Popup>
</Marker>
))}
</MapContainer>
);
}