diff options
Diffstat (limited to 'src/frontend/app/components/PlannerOverlay.tsx')
| -rw-r--r-- | src/frontend/app/components/PlannerOverlay.tsx | 108 |
1 files changed, 57 insertions, 51 deletions
diff --git a/src/frontend/app/components/PlannerOverlay.tsx b/src/frontend/app/components/PlannerOverlay.tsx index 0320d45..cbb4ac1 100644 --- a/src/frontend/app/components/PlannerOverlay.tsx +++ b/src/frontend/app/components/PlannerOverlay.tsx @@ -1,4 +1,4 @@ -import { MapPin } from "lucide-react"; +import { ChevronUp, Map, MapPin } from "lucide-react"; import React, { useCallback, useEffect, @@ -7,6 +7,7 @@ import React, { useState, } from "react"; import { useTranslation } from "react-i18next"; +import { useNavigate } from "react-router"; import PlaceListItem from "~/components/PlaceListItem"; import { reverseGeocode, @@ -45,9 +46,21 @@ export const PlannerOverlay: React.FC<PlannerOverlayProps> = ({ autoLoad = true, }) => { const { t } = useTranslation(); - const { origin, setOrigin, destination, setDestination, loading, error } = - usePlanner({ autoLoad }); - const [isExpanded, setIsExpanded] = useState(false); + const navigate = useNavigate(); + const { + origin, + setOrigin, + destination, + setDestination, + loading, + error, + setPickingMode, + isExpanded, + setIsExpanded, + recentPlaces, + addRecentPlace, + clearRecentPlaces, + } = usePlanner({ autoLoad }); const [originQuery, setOriginQuery] = useState(origin?.name || ""); const [destQuery, setDestQuery] = useState(""); @@ -61,14 +74,6 @@ export const PlannerOverlay: React.FC<PlannerOverlayProps> = ({ const [favouriteStops, setFavouriteStops] = useState<PlannerSearchResult[]>( [] ); - const [recentPlaces, setRecentPlaces] = useState<PlannerSearchResult[]>([]); - const RECENT_KEY = `recentPlaces`; - const clearRecentPlaces = useCallback(() => { - setRecentPlaces([]); - try { - localStorage.removeItem(RECENT_KEY); - } catch {} - }, []); const pickerInputRef = useRef<HTMLInputElement | null>(null); @@ -130,43 +135,6 @@ export const PlannerOverlay: React.FC<PlannerOverlayProps> = ({ .catch(() => setFavouriteStops([])); }, []); - // Load recent places from localStorage - useEffect(() => { - try { - const raw = localStorage.getItem(RECENT_KEY); - if (raw) { - const parsed = JSON.parse(raw) as PlannerSearchResult[]; - setRecentPlaces(parsed.slice(0, 20)); - } - } catch { - setRecentPlaces([]); - } - }, []); - - const addRecentPlace = useCallback( - (p: PlannerSearchResult) => { - const key = `${p.lat.toFixed(5)},${p.lon.toFixed(5)}`; - const existing = recentPlaces.filter( - (rp) => `${rp.lat.toFixed(5)},${rp.lon.toFixed(5)}` !== key - ); - const updated = [ - { - name: p.name, - label: p.label, - lat: p.lat, - lon: p.lon, - layer: p.layer, - }, - ...existing, - ].slice(0, 20); - setRecentPlaces(updated); - try { - localStorage.setItem(RECENT_KEY, JSON.stringify(updated)); - } catch {} - }, - [recentPlaces] - ); - const filteredFavouriteStops = useMemo(() => { const q = pickerQuery.trim().toLowerCase(); if (!q) return favouriteStops; @@ -350,7 +318,6 @@ export const PlannerOverlay: React.FC<PlannerOverlayProps> = ({ className="block w-full px-2 py-1 text-left hover:bg-slate-50 dark:hover:bg-slate-800 transition-colors duration-200" onClick={() => { setIsExpanded(true); - openPicker("destination"); }} > <div className="text-small font-semibold text-slate-900 dark:text-slate-100"> @@ -364,7 +331,7 @@ export const PlannerOverlay: React.FC<PlannerOverlayProps> = ({ <div className="flex items-center gap-2"> <button type="button" - className="w-full rounded-lg bg-surface border border-slate-200 dark:border-slate-700 px-4 py-2.5 text-left text-sm text-slate-900 dark:text-slate-100 hover:bg-slate-50 dark:hover:bg-slate-800 transition-colors duration-150 focus:outline-none focus:border-primary-500 shadow-sm" + className="grow rounded-lg bg-surface border border-slate-200 dark:border-slate-700 px-4 py-2.5 text-left text-sm text-slate-900 dark:text-slate-100 hover:bg-slate-50 dark:hover:bg-slate-800 transition-colors duration-150 focus:outline-none focus:border-primary-500 shadow-sm" onClick={() => openPicker("origin")} > <span @@ -375,6 +342,16 @@ export const PlannerOverlay: React.FC<PlannerOverlayProps> = ({ {originQuery || t("planner.origin")} </span> </button> + {!forceExpanded && ( + <button + type="button" + className="p-2 text-slate-400 hover:text-slate-600 dark:hover:text-slate-200 transition-colors" + onClick={() => setIsExpanded(false)} + aria-label={t("planner.collapse", "Collapse")} + > + <ChevronUp className="w-5 h-5" /> + </button> + )} </div> <div> @@ -610,6 +587,35 @@ export const PlannerOverlay: React.FC<PlannerOverlayProps> = ({ </li> )} + <li className="border-t border-slate-100 dark:border-slate-700"> + <button + type="button" + className="flex w-full items-center justify-between px-4 py-3 text-left hover:bg-slate-50 dark:hover:bg-slate-800 transition-colors duration-200" + onClick={() => { + setPickingMode(pickerField); + setPickerOpen(false); + navigate("/map"); + }} + > + <div className="flex items-center gap-2"> + <span className="inline-flex items-center justify-center w-4 h-4"> + <Map className="w-4 h-4 text-slate-600 dark:text-slate-400" /> + </span> + <div> + <div className="text-sm font-semibold text-slate-900 dark:text-slate-100"> + {t("planner.pick_on_map", "Pick on map")} + </div> + <div className="text-xs text-slate-500 dark:text-slate-400"> + {t( + "planner.pick_on_map_desc", + "Select a point visually" + )} + </div> + </div> + </div> + </button> + </li> + {(remoteLoading || sortedRemoteResults.length > 0) && ( <li className="border-t border-slate-100 dark:border-slate-700 px-4 py-2 text-xs font-bold uppercase tracking-wider text-slate-500 dark:text-slate-400 bg-slate-50 dark:bg-slate-800/70"> {remoteLoading |
