diff options
| author | Copilot <198982749+Copilot@users.noreply.github.com> | 2026-02-25 16:15:16 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-02-25 16:15:16 +0100 |
| commit | 7d86c66be248861f440089f37765778c69deaaa7 (patch) | |
| tree | 6181be67a333c7afc1b39f81b49457dbc3fa1819 /src/frontend/app/routes | |
| parent | 191d36dbbea5fab6141d9a144f154c98757e284f (diff) | |
[WIP] Implement UX improvements for map styles and feature selection (#136)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: arielcostas <94913521+arielcostas@users.noreply.github.com>
Diffstat (limited to 'src/frontend/app/routes')
| -rw-r--r-- | src/frontend/app/routes/map.tsx | 87 |
1 files changed, 84 insertions, 3 deletions
diff --git a/src/frontend/app/routes/map.tsx b/src/frontend/app/routes/map.tsx index 2686222..af94509 100644 --- a/src/frontend/app/routes/map.tsx +++ b/src/frontend/app/routes/map.tsx @@ -1,4 +1,4 @@ -import { Check, X } from "lucide-react"; +import { Check, MapPin, X } from "lucide-react"; import type { FilterSpecification } from "maplibre-gl"; import { useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; @@ -37,6 +37,9 @@ export default function StopMap() { StopSheetProps["stop"] | null >(null); const [isSheetOpen, setIsSheetOpen] = useState(false); + const [disambiguationStops, setDisambiguationStops] = useState< + Array<StopSheetProps["stop"]> + >([]); const mapRef = useRef<MapRef>(null); const { @@ -105,9 +108,42 @@ export default function StopMap() { ); return; } - const feature = features[0]; - handlePointClick(feature); + // Collect only stop-layer features with valid properties + const stopFeatures = features.filter( + (f) => f.layer?.id?.startsWith("stops") && f.properties?.id + ); + + if (stopFeatures.length === 0) return; + + if (stopFeatures.length === 1) { + // Single unambiguous stop – open the sheet directly + handlePointClick(stopFeatures[0]); + return; + } + + // Multiple overlapping stops – deduplicate by stop id and ask the user + const seen = new Set<string>(); + const candidates: Array<StopSheetProps["stop"]> = []; + for (const f of stopFeatures) { + const id: string = f.properties!.id; + if (!seen.has(id)) { + seen.add(id); + candidates.push({ + stopId: id, + stopCode: f.properties!.code, + name: f.properties!.name || "Unknown Stop", + }); + } + } + + if (candidates.length === 1) { + // After deduplication only one stop remains + setSelectedStop(candidates[0]); + setIsSheetOpen(true); + } else { + setDisambiguationStops(candidates); + } }; const stopLayerFilter = useMemo(() => { @@ -350,6 +386,51 @@ export default function StopMap() { stop={selectedStop} /> )} + + {disambiguationStops.length > 1 && ( + <div className="fixed inset-x-0 bottom-0 z-30 flex justify-center pointer-events-none pb-safe"> + <div className="pointer-events-auto w-full max-w-md bg-white dark:bg-slate-900 rounded-t-2xl shadow-2xl border border-slate-200 dark:border-slate-700 p-4"> + <div className="flex items-center justify-between mb-3"> + <h3 className="font-semibold text-slate-900 dark:text-slate-100 text-base"> + {t("map.select_nearby_stop", "Seleccionar parada")} + </h3> + <button + onClick={() => setDisambiguationStops([])} + className="p-1 rounded-full hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors" + aria-label={t("planner.close", "Cerrar")} + > + <X className="w-5 h-5 text-slate-500" /> + </button> + </div> + <ul className="divide-y divide-slate-100 dark:divide-slate-800"> + {disambiguationStops.map((stop) => ( + <li key={stop.stopId}> + <button + className="w-full flex items-center gap-3 py-3 text-left hover:bg-slate-50 dark:hover:bg-slate-800 transition-colors rounded-lg px-2" + onClick={() => { + setDisambiguationStops([]); + setSelectedStop(stop); + setIsSheetOpen(true); + }} + > + <MapPin className="w-4 h-4 flex-shrink-0 text-primary-600" /> + <div> + <div className="font-medium text-slate-900 dark:text-slate-100 text-sm"> + {stop.name} + </div> + {stop.stopCode && ( + <div className="text-xs text-slate-500 dark:text-slate-400"> + {stop.stopCode} + </div> + )} + </div> + </button> + </li> + ))} + </ul> + </div> + </div> + )} </AppMap> </div> ); |
