import { RefreshCw } from "lucide-react"; import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { Sheet } from "react-modal-sheet"; import { Link } from "react-router"; import { ConsolidatedCirculationList } from "~/components/Stops/ConsolidatedCirculationList"; import { REGION_DATA } from "~/config/RegionConfig"; import type { Stop } from "~/data/StopDataProvider"; import { type ConsolidatedCirculation } from "../routes/stops-$id"; import { ErrorDisplay } from "./ErrorDisplay"; import LineIcon from "./LineIcon"; import { StopAlert } from "./StopAlert"; import "./StopSummarySheet.css"; import { StopSummarySheetSkeleton } from "./StopSummarySheetSkeleton"; interface StopSheetProps { isOpen: boolean; onClose: () => void; stop: Stop; } interface ErrorInfo { type: "network" | "server" | "unknown"; status?: number; message?: string; } const loadConsolidatedData = async ( stopId: string ): Promise => { const resp = await fetch( `${REGION_DATA.consolidatedCirculationsEndpoint}?stopId=${stopId}`, { headers: { Accept: "application/json", }, } ); if (!resp.ok) { throw new Error(`HTTP ${resp.status}: ${resp.statusText}`); } return await resp.json(); }; export const StopSheet: React.FC = ({ isOpen, onClose, stop, }) => { const { t } = useTranslation(); const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [lastUpdated, setLastUpdated] = useState(null); const parseError = (error: any): ErrorInfo => { if (!navigator.onLine) { return { type: "network", message: "No internet connection" }; } if ( error.message?.includes("Failed to fetch") || error.message?.includes("NetworkError") ) { return { type: "network" }; } if (error.message?.includes("HTTP")) { const statusMatch = error.message.match(/HTTP (\d+):/); const status = statusMatch ? parseInt(statusMatch[1]) : undefined; return { type: "server", status }; } return { type: "unknown", message: error.message }; }; const loadData = async () => { try { setLoading(true); setError(null); setData(null); const stopData = await loadConsolidatedData(stop.stopId); setData(stopData); setLastUpdated(new Date()); } catch (err) { console.error("Failed to load stop data:", err); setError(parseError(err)); } finally { setLoading(false); } }; useEffect(() => { if (isOpen && stop.stopId) { loadData(); } }, [isOpen, stop.stopId]); // Show only the next 4 arrivals const sortedData = data ? [...data].sort( (a, b) => (a.realTime?.minutes ?? a.schedule?.minutes ?? 999) - (b.realTime?.minutes ?? b.schedule?.minutes ?? 999) ) : []; const limitedEstimates = sortedData.slice(0, 4); return (

{stop.name.original}

({stop.stopId})
= 10 ? "scrollable" : ""}`} > {stop.lines.map((line) => (
))}
{loading ? ( ) : error ? ( ) : data ? ( <>

{t("estimates.next_arrivals", "Next arrivals")}

{limitedEstimates.length === 0 ? (
{t("estimates.none", "No hay estimaciones disponibles")}
) : ( )}
) : null}
{lastUpdated && (
{t("estimates.last_updated", "Actualizado a las")}{" "} {lastUpdated.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit", second: "2-digit", })}
)}
{t("map.view_all_estimates", "Ver todas las estimaciones")}
); };