import { History } from "lucide-react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router"; import { PlannerOverlay } from "~/components/PlannerOverlay"; import { usePageTitle } from "~/contexts/PageTitleContext"; import { usePlanner } from "~/hooks/usePlanner"; import StopGallery from "../components/StopGallery"; import StopItem from "../components/StopItem"; import StopDataProvider, { type Stop } from "../data/StopDataProvider"; import "../tailwind-full.css"; export default function StopList() { const { t } = useTranslation(); usePageTitle(t("navbar.stops", "Paradas")); const navigate = useNavigate(); const { history, searchRoute, loadRoute } = usePlanner({ autoLoad: false }); const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [searchResults, setSearchResults] = useState(null); const [favouriteStops, setFavouriteStops] = useState([]); const [recentStops, setRecentStops] = useState([]); const [userLocation, setUserLocation] = useState<{ latitude: number; longitude: number; } | null>(null); const searchTimeout = useRef(null); const randomPlaceholder = useMemo( () => t("stoplist.search_placeholder"), [t] ); const requestUserLocation = useCallback(() => { if (typeof window === "undefined" || !("geolocation" in navigator)) { return; } navigator.geolocation.getCurrentPosition( (position) => { setUserLocation({ latitude: position.coords.latitude, longitude: position.coords.longitude, }); }, (error) => { console.warn("Unable to obtain user location", error); }, { enableHighAccuracy: false, maximumAge: 5 * 60 * 1000, } ); }, []); useEffect(() => { if (typeof window === "undefined" || !("geolocation" in navigator)) { return; } let permissionStatus: PermissionStatus | null = null; const handlePermissionChange = () => { if (permissionStatus?.state === "granted") { requestUserLocation(); } }; const checkPermission = async () => { try { if (navigator.permissions?.query) { permissionStatus = await navigator.permissions.query({ name: "geolocation", }); if (permissionStatus.state === "granted") { requestUserLocation(); } permissionStatus.addEventListener("change", handlePermissionChange); } else { requestUserLocation(); } } catch (error) { console.warn("Geolocation permission check failed", error); requestUserLocation(); } }; checkPermission(); return () => { permissionStatus?.removeEventListener("change", handlePermissionChange); }; }, [requestUserLocation]); // Load stops from network const loadStops = useCallback(async () => { try { setLoading(true); const favouriteIds = StopDataProvider.getFavouriteIds(); const recentIds = StopDataProvider.getRecent(); const allIds = Array.from(new Set([...favouriteIds, ...recentIds])); const stopsMap = await StopDataProvider.fetchStopsByIds(allIds); const favStops = favouriteIds .map((id) => stopsMap[id]) .filter(Boolean) .map((stop) => ({ ...stop, favourite: true })); setFavouriteStops(favStops); const recStops = recentIds .map((id) => stopsMap[id]) .filter(Boolean) .map((stop) => ({ ...stop, favourite: favouriteIds.includes(stop.stopId), })); setRecentStops(recStops); setData(Object.values(stopsMap)); } catch (error) { console.error("Failed to load stops:", error); } finally { setLoading(false); } }, []); useEffect(() => { loadStops(); }, [loadStops]); const handleStopSearch = (event: React.ChangeEvent) => { const searchQuery = event.target.value || ""; if (searchTimeout.current) { clearTimeout(searchTimeout.current); } searchTimeout.current = setTimeout(async () => { if (searchQuery.length === 0) { setSearchResults(null); return; } try { const response = await fetch( `/api/stops/search?q=${encodeURIComponent(searchQuery)}` ); if (response.ok) { const results = await response.json(); setSearchResults(results); } else { setSearchResults([]); } } catch (error) { console.error("Search failed:", error); setSearchResults([]); } }, 300); }; return (
{/* Planner Section */}
{t("planner.where_to", "¿A dónde quieres ir?")}
{ searchRoute(origin, destination, time, arriveBy); }} onNavigateToPlanner={() => navigate("/planner")} />
{history.length > 0 && (

{t("planner.recent_routes", "Rutas recientes")}

{history.map((route, idx) => ( ))}
)}
{/* Search Section */}

{t("stoplist.search_label", "Buscar paradas")}

{/* Search Results */} {searchResults && searchResults.length > 0 ? (

{t("stoplist.search_results", "Resultados de la búsqueda")}

    {searchResults.map((stop: Stop) => ( ))}
) : searchResults !== null ? (

{t("stoplist.no_results", "No se encontraron resultados")}

) : ( <> {/* Favourites Gallery */} {!loading && ( a.stopId.localeCompare(b.stopId) )} title={t("stoplist.favourites")} emptyMessage={t("stoplist.no_favourites")} /> )} {/* Recent Stops Gallery - only show if no favourites */} {!loading && favouriteStops.length === 0 && ( )} {/**/} )}
); }