From b3b20bc1360ea67de6a1c837bb24c2b55541d3ac Mon Sep 17 00:00:00 2001 From: Ariel Costas Guerrero Date: Fri, 13 Mar 2026 12:51:01 +0100 Subject: feat: enhance navigation and planner functionality in NavBar and map components --- src/frontend/app/components/layout/NavBar.tsx | 7 +- src/frontend/app/routes/map.tsx | 22 ++----- src/frontend/app/routes/planner.tsx | 92 +++++++++++++++------------ 3 files changed, 64 insertions(+), 57 deletions(-) diff --git a/src/frontend/app/components/layout/NavBar.tsx b/src/frontend/app/components/layout/NavBar.tsx index 5822ce7..e66c388 100644 --- a/src/frontend/app/components/layout/NavBar.tsx +++ b/src/frontend/app/components/layout/NavBar.tsx @@ -1,4 +1,4 @@ -import { Home, Map, Route } from "lucide-react"; +import { Home, Map, Navigation, Route } from "lucide-react"; import type { LngLatLike } from "maplibre-gl"; import { useTranslation } from "react-i18next"; import { Link, useLocation, useNavigate } from "react-router"; @@ -52,6 +52,11 @@ export default function NavBar({ orientation = "horizontal" }: NavBarProps) { ); }, }, + { + name: t("navbar.planner", "Planificador"), + icon: Navigation, + path: "/planner", + }, { name: t("navbar.routes", "Rutas"), icon: Route, diff --git a/src/frontend/app/routes/map.tsx b/src/frontend/app/routes/map.tsx index 6d1fc9f..efc97e4 100644 --- a/src/frontend/app/routes/map.tsx +++ b/src/frontend/app/routes/map.tsx @@ -1,6 +1,6 @@ import { Check, MapPin, Navigation, Search, X } from "lucide-react"; import type { FilterSpecification } from "maplibre-gl"; -import { useEffect, useRef, useState, useMemo } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { Layer, @@ -16,7 +16,11 @@ import { } from "~/components/map/StopSummarySheet"; import { AppMap } from "~/components/shared/AppMap"; import { usePageTitle } from "~/contexts/PageTitleContext"; -import { reverseGeocode, searchPlaces, type PlannerSearchResult } from "~/data/PlannerApi"; +import { + reverseGeocode, + searchPlaces, + type PlannerSearchResult, +} from "~/data/PlannerApi"; import { usePlanner } from "~/hooks/usePlanner"; import StopDataProvider from "../data/StopDataProvider"; import "../tailwind-full.css"; @@ -170,15 +174,6 @@ function MapSearchBar({ mapRef }: MapSearchBarProps) { )} - - {/* Plan a trip – always visible */} - ); @@ -667,10 +662,7 @@ export default function StopMap() { {contextMenu && ( <> {/* Dismiss backdrop */} -
+
{/* Context menu */}
{ return 2 * R * Math.asin(Math.sqrt(h)); }; +const shouldSkipWalkLeg = (leg: Itinerary["legs"][number]): boolean => { + if (leg.mode !== "WALK") return false; + const durationMinutes = + (new Date(leg.endTime).getTime() - new Date(leg.startTime).getTime()) / + 60000; + return durationMinutes <= 2 || leg.distanceMeters < 50; +}; + const sumWalkMetrics = (legs: Itinerary["legs"]) => { let meters = 0; let minutes = 0; @@ -129,44 +137,44 @@ const ItinerarySummary = ({
- {itinerary.legs.map((leg, idx) => { - const isWalk = leg.mode === "WALK"; - const legDurationMinutes = Math.max( - 1, - Math.round( - (new Date(leg.endTime).getTime() - - new Date(leg.startTime).getTime()) / - 60000 - ) - ); - - const isFirstBusLeg = - !isWalk && - itinerary.legs.findIndex((l) => l.mode !== "WALK") === idx; - - return ( - - {idx > 0 && } - {isWalk ? ( -
- - - {formatDuration(legDurationMinutes, t)} - -
- ) : ( -
- -
- )} -
- ); - })} + {itinerary.legs + .filter((leg) => !shouldSkipWalkLeg(leg)) + .map((leg, idx) => { + const isWalk = leg.mode === "WALK"; + const legDurationMinutes = Math.max( + 1, + Math.round( + (new Date(leg.endTime).getTime() - + new Date(leg.startTime).getTime()) / + 60000 + ) + ); + + return ( + + {idx > 0 && } + {isWalk ? ( +
+ + + {formatDuration(legDurationMinutes, t)} + +
+ ) : ( +
+ +
+ )} +
+ ); + })}
@@ -284,6 +292,8 @@ const ItineraryDetail = ({ }, [itinerary]); // Get origin and destination coordinates + const visibleLegs = itinerary.legs.filter((leg) => !shouldSkipWalkLeg(leg)); + const origin = itinerary.legs[0]?.from; const destination = itinerary.legs[itinerary.legs.length - 1]?.to; @@ -495,7 +505,7 @@ const ItineraryDetail = ({
- {itinerary.legs.map((leg, idx) => ( + {visibleLegs.map((leg, idx) => (
{leg.mode === "WALK" ? ( @@ -513,7 +523,7 @@ const ItineraryDetail = ({ textColour={leg.routeTextColor || undefined} /> )} - {idx < itinerary.legs.length - 1 && ( + {idx < visibleLegs.length - 1 && (
)}
@@ -574,7 +584,7 @@ const ItineraryDetail = ({ const currentLine = leg.routeShortName || leg.routeName; const previousLeg = - idx > 0 ? itinerary.legs[idx - 1] : null; + idx > 0 ? visibleLegs[idx - 1] : null; const previousLine = previousLeg?.mode !== "WALK" ? previousLeg?.routeShortName || -- cgit v1.3