Files
meshstream/web/src/routes/map.tsx
Daniel Pupius 26a2bba441 Fix network map mobile overflow and infer MQTT hops from SNR=0 (#2)
* Fix network map mobile overflow and infer MQTT hops from SNR=0

- Add min-h-0 and flex-shrink-0 to map layout so legend/actions don't
  overflow the viewport on mobile
- Infer viaMqtt=true for traceroute hops with SNR exactly 0
- Change MQTT hop color from purple to orange across map, legend, and
  node detail badges

https://claude.ai/code/session_01Ffqq7YPCJE28uUFR88eK7C

* Revert MQTT color to purple; keep SNR=0 MQTT inference

The color change was unintended — MQTT hops should stay purple. The
SNR=0 inference in traceroute processing correctly marks those hops
as viaMqtt so they render as purple dashed lines.

https://claude.ai/code/session_01Ffqq7YPCJE28uUFR88eK7C

* Fix test setup: mock URL.createObjectURL for maplibre-gl

maplibre-gl calls URL.createObjectURL during import for its worker
setup, which doesn't exist in jsdom. Add the mock to test setup.

https://claude.ai/code/session_01Ffqq7YPCJE28uUFR88eK7C

* Run go fmt on unformatted files

https://claude.ai/code/session_01Ffqq7YPCJE28uUFR88eK7C

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-15 23:06:20 -07:00

96 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React from "react";
import { createFileRoute } from "@tanstack/react-router";
import { PageWrapper } from "../components";
import { NetworkMap } from "../components/dashboard";
import { Button } from "../components/ui";
import { Locate, GitBranch } from "lucide-react";
import { getNodeColors, ActivityLevel } from "../lib/activity";
export const Route = createFileRoute("/map")({
component: MapPage,
});
function MapPage() {
// State to track if auto-zoom is enabled (forwarded from the NetworkMap component)
const [autoZoomEnabled, setAutoZoomEnabled] = React.useState(true);
const [showLinks, setShowLinks] = React.useState(true);
const mapRef = React.useRef<{ resetAutoZoom?: () => void }>({});
// Function to reset auto-zoom, will be called by the button
const handleResetZoom = () => {
if (mapRef.current.resetAutoZoom) {
mapRef.current.resetAutoZoom();
}
};
return (
<PageWrapper>
<div className="flex flex-col h-[calc(100vh-7rem)] md:h-[calc(100vh-5rem)]">
<div className="flex-1 flex flex-col min-h-0">
<NetworkMap
fullHeight
ref={mapRef as any}
onAutoZoomChange={setAutoZoomEnabled}
showLinks={showLinks}
/>
<div className="mt-2 rounded-lg p-2 text-xs flex items-center justify-between effect-inset flex-shrink-0">
<div className="flex flex-wrap gap-2">
<div className="flex items-center gap-x-3 gap-y-1 flex-wrap">
<span className={`inline-flex items-center px-2 py-0.5 rounded ${getNodeColors(ActivityLevel.RECENT, false).textClass} ${getNodeColors(ActivityLevel.RECENT, false).background}`}>
<span className={`w-2 h-2 ${getNodeColors(ActivityLevel.RECENT, false).statusDot} rounded-full mr-1.5`}></span>
Nodes
</span>
<span className={`inline-flex items-center px-2 py-0.5 rounded ${getNodeColors(ActivityLevel.RECENT, true).textClass} ${getNodeColors(ActivityLevel.RECENT, true).background}`}>
<span className={`w-2 h-2 ${getNodeColors(ActivityLevel.RECENT, true).statusDot} rounded-full mr-1.5`}></span>
Gateways
</span>
<span className="text-neutral-500">·</span>
<span className="inline-flex items-center gap-1.5 text-neutral-300">
<span className="inline-block w-5 h-0.5 rounded-full bg-[#22c55e]"></span>SNR 5
</span>
<span className="inline-flex items-center gap-1.5 text-neutral-300">
<span className="inline-block w-5 h-0.5 rounded-full bg-[#eab308]"></span>SNR 04
</span>
<span className="inline-flex items-center gap-1.5 text-neutral-300">
<span className="inline-block w-5 h-0.5 rounded-full bg-[#ef4444]"></span>SNR &lt; 0
</span>
<span className="inline-flex items-center gap-1.5 text-neutral-300">
<span className="inline-block w-5 h-0.5 rounded-full bg-[#6b7280]"></span>Unknown
</span>
<span className="inline-flex items-center gap-1.5 text-neutral-300">
<span className="inline-block w-5 border-t-2 border-dashed border-[#a855f7] opacity-80"></span>MQTT
</span>
</div>
</div>
<div className="flex items-center gap-2">
<Button
size="sm"
variant={showLinks ? "primary" : "secondary"}
onClick={() => setShowLinks((v) => !v)}
icon={GitBranch}
title={showLinks ? "Hide link polylines" : "Show link polylines"}
>
Links
</Button>
{/* Always show the button, but disable it when auto-zoom is enabled */}
<Button
size="sm"
variant="secondary"
onClick={handleResetZoom}
icon={Locate}
disabled={autoZoomEnabled}
title={autoZoomEnabled ? "Auto-zoom is already enabled" : "Enable auto-zoom to fit all nodes"}
>
Auto-zoom
</Button>
</div>
</div>
</div>
</div>
</PageWrapper>
);
}