diff --git a/apps/planner/app/components/PlannerMap.tsx b/apps/planner/app/components/PlannerMap.tsx index 1597cf5..3c81524 100644 --- a/apps/planner/app/components/PlannerMap.tsx +++ b/apps/planner/app/components/PlannerMap.tsx @@ -54,6 +54,27 @@ function MapExposer() { return null; } +function RouteFitter({ coordinates }: { coordinates: [number, number, number][] | null }) { + const map = useMap(); + const hasFitted = useRef(false); + + useEffect(() => { + if (hasFitted.current || !coordinates || coordinates.length < 2) return; + + // Coordinates are in [lon, lat, elevation] GeoJSON format + const bounds = L.latLngBounds( + coordinates.filter((c) => c.length >= 2).map((c) => [c[1]!, c[0]!] as [number, number]), + ); + + if (bounds.isValid()) { + map.fitBounds(bounds, { padding: [50, 50], maxZoom: 16 }); + hasFitted.current = true; + } + }, [coordinates, map]); + + return null; +} + function MapClickHandler({ onAdd, suppressRef }: { onAdd: (lat: number, lng: number) => void; suppressRef: React.RefObject }) { useMapEvents({ click(e) { @@ -314,6 +335,7 @@ export function PlannerMap({ yjs, onRouteRequest, highlightPosition }: PlannerMa + {} : addWaypoint} suppressRef={suppressMapClickRef} /> diff --git a/e2e/planner.test.ts b/e2e/planner.test.ts index 2ace23e..759c9ec 100644 --- a/e2e/planner.test.ts +++ b/e2e/planner.test.ts @@ -151,6 +151,30 @@ test.describe("Planner", () => { await expect(page.getByTitle("Draw no-go area")).toBeVisible(); }); + test("map zooms to fit the route when opened with initial waypoints", async ({ page, request }) => { + const sessionResp = await request.post("/api/sessions", { data: {} }); + const { url } = await sessionResp.json(); + + const waypoints = [ + { lat: 52.520, lon: 13.405 }, + { lat: 52.515, lon: 13.351 }, + ]; + await page.goto(`${url}?waypoints=${encodeURIComponent(JSON.stringify(waypoints))}`); + + await expect(page.locator(".leaflet-container")).toBeVisible({ timeout: 10000 }); + await expect(page.getByText("Connected")).toBeVisible({ timeout: 15000 }); + // Wait for route to compute and fit + await expect(page.getByText(/\d+\.\d+ km/)).toBeVisible({ timeout: 20000 }); + + // The map should have zoomed in to the route bounds (zoom > default 6) + const zoom = await page.evaluate(() => { + const map = (window as any).__leafletMap; + return map ? map.getZoom() : null; + }); + expect(zoom).not.toBeNull(); + expect(zoom).toBeGreaterThan(6); + }); + test("can create session with initial waypoints", async ({ request }) => { const response = await request.post("/api/sessions", { data: {