diff options
Diffstat (limited to 'src/frontend/app/routes')
| -rw-r--r-- | src/frontend/app/routes/estimates-$id.css | 8 | ||||
| -rw-r--r-- | src/frontend/app/routes/estimates-$id.tsx | 89 | ||||
| -rw-r--r-- | src/frontend/app/routes/map.tsx | 13 | ||||
| -rw-r--r-- | src/frontend/app/routes/settings.css | 8 | ||||
| -rw-r--r-- | src/frontend/app/routes/settings.tsx | 45 | ||||
| -rw-r--r-- | src/frontend/app/routes/stoplist.tsx | 57 |
6 files changed, 133 insertions, 87 deletions
diff --git a/src/frontend/app/routes/estimates-$id.css b/src/frontend/app/routes/estimates-$id.css index 66e7fb6..fde4099 100644 --- a/src/frontend/app/routes/estimates-$id.css +++ b/src/frontend/app/routes/estimates-$id.css @@ -74,8 +74,12 @@ } @keyframes spin { - from { transform: rotate(0deg); } - to { transform: rotate(360deg); } + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } } @media (max-width: 640px) { diff --git a/src/frontend/app/routes/estimates-$id.tsx b/src/frontend/app/routes/estimates-$id.tsx index 21186fb..c8c52b5 100644 --- a/src/frontend/app/routes/estimates-$id.tsx +++ b/src/frontend/app/routes/estimates-$id.tsx @@ -7,8 +7,14 @@ import { RegularTable } from "../components/RegularTable"; import { useApp } from "../AppContext"; import { GroupedTable } from "../components/GroupedTable"; import { useTranslation } from "react-i18next"; -import { SchedulesTable, type ScheduledTable } from "~/components/SchedulesTable"; -import { SchedulesTableSkeleton, EstimatesGroupedSkeleton } from "~/components/SchedulesTableSkeleton"; +import { + SchedulesTable, + type ScheduledTable, +} from "~/components/SchedulesTable"; +import { + SchedulesTableSkeleton, + EstimatesGroupedSkeleton, +} from "~/components/SchedulesTableSkeleton"; import { TimetableSkeleton } from "~/components/TimetableSkeleton"; import { ErrorDisplay } from "~/components/ErrorDisplay"; import { PullToRefresh } from "~/components/PullToRefresh"; @@ -24,12 +30,15 @@ export interface Estimate { } interface ErrorInfo { - type: 'network' | 'server' | 'unknown'; + type: "network" | "server" | "unknown"; status?: number; message?: string; } -const loadData = async (region: RegionId, stopId: string): Promise<Estimate[]> => { +const loadData = async ( + region: RegionId, + stopId: string, +): Promise<Estimate[]> => { const regionConfig = getRegionConfig(region); const resp = await fetch(`${regionConfig.estimatesEndpoint}?id=${stopId}`, { headers: { @@ -44,7 +53,10 @@ const loadData = async (region: RegionId, stopId: string): Promise<Estimate[]> = return await resp.json(); }; -const loadTimetableData = async (region: RegionId, stopId: string): Promise<ScheduledTable[]> => { +const loadTimetableData = async ( + region: RegionId, + stopId: string, +): Promise<ScheduledTable[]> => { const regionConfig = getRegionConfig(region); // Check if timetable is available for this region @@ -52,12 +64,15 @@ const loadTimetableData = async (region: RegionId, stopId: string): Promise<Sche throw new Error("Timetable not available for this region"); } - const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD format - const resp = await fetch(`${regionConfig.timetableEndpoint}?date=${today}&stopId=${stopId}`, { - headers: { - Accept: "application/json", + const today = new Date().toISOString().split("T")[0]; // YYYY-MM-DD format + const resp = await fetch( + `${regionConfig.timetableEndpoint}?date=${today}&stopId=${stopId}`, + { + headers: { + Accept: "application/json", + }, }, - }); + ); if (!resp.ok) { throw new Error(`HTTP ${resp.status}: ${resp.statusText}`); @@ -91,20 +106,23 @@ export default function Estimates() { const parseError = (error: any): ErrorInfo => { if (!navigator.onLine) { - return { type: 'network', message: 'No internet connection' }; + 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("Failed to fetch") || + error.message?.includes("NetworkError") + ) { + return { type: "network" }; } - if (error.message?.includes('HTTP')) { + 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: "server", status }; } - return { type: 'unknown', message: error.message }; + return { type: "unknown", message: error.message }; }; const loadEstimatesData = useCallback(async () => { @@ -121,7 +139,7 @@ export default function Estimates() { setStopData(stop); setCustomName(StopDataProvider.getCustomName(region, stopIdNum)); } catch (error) { - console.error('Error loading estimates data:', error); + console.error("Error loading estimates data:", error); setEstimatesError(parseError(error)); setData(null); setDataDate(null); @@ -144,7 +162,7 @@ export default function Estimates() { const timetableBody = await loadTimetableData(region, params.id!); setTimetableData(timetableBody); } catch (error) { - console.error('Error loading timetable data:', error); + console.error("Error loading timetable data:", error); setTimetableError(parseError(error)); setTimetableData([]); } finally { @@ -153,10 +171,7 @@ export default function Estimates() { }, [params.id, region, regionConfig.timetableEndpoint]); const refreshData = useCallback(async () => { - await Promise.all([ - loadEstimatesData(), - loadTimetableDataAsync() - ]); + await Promise.all([loadEstimatesData(), loadTimetableDataAsync()]); }, [loadEstimatesData, loadTimetableDataAsync]); // Manual refresh function for pull-to-refresh and button @@ -183,7 +198,9 @@ export default function Estimates() { loadTimetableDataAsync(); StopDataProvider.pushRecent(region, parseInt(params.id ?? "")); - setFavourited(StopDataProvider.isFavourite(region, parseInt(params.id ?? ""))); + setFavourited( + StopDataProvider.isFavourite(region, parseInt(params.id ?? "")), + ); }, [params.id, region, loadEstimatesData, loadTimetableDataAsync]); const toggleFavourite = () => { @@ -273,7 +290,9 @@ export default function Estimates() { disabled={isManualRefreshing || estimatesLoading} title={t("estimates.reload", "Recargar estimaciones")} > - <RefreshCw className={`refresh-icon ${isManualRefreshing ? 'spinning' : ''}`} /> + <RefreshCw + className={`refresh-icon ${isManualRefreshing ? "spinning" : ""}`} + /> </button> </div> @@ -290,13 +309,24 @@ export default function Estimates() { <ErrorDisplay error={estimatesError} onRetry={loadEstimatesData} - title={t("errors.estimates_title", "Error al cargar estimaciones")} + title={t( + "errors.estimates_title", + "Error al cargar estimaciones", + )} /> ) : data ? ( tableStyle === "grouped" ? ( - <GroupedTable data={data} dataDate={dataDate} regionConfig={regionConfig} /> + <GroupedTable + data={data} + dataDate={dataDate} + regionConfig={regionConfig} + /> ) : ( - <RegularTable data={data} dataDate={dataDate} regionConfig={regionConfig} /> + <RegularTable + data={data} + dataDate={dataDate} + regionConfig={regionConfig} + /> ) ) : null} </div> @@ -318,10 +348,7 @@ export default function Estimates() { currentTime={new Date().toTimeString().slice(0, 8)} // HH:MM:SS /> <div className="timetable-actions"> - <Link - to={`/timetable/${params.id}`} - className="view-all-link" - > + <Link to={`/timetable/${params.id}`} className="view-all-link"> <ExternalLink className="external-icon" /> {t("timetable.viewAll", "Ver todos los horarios")} </Link> diff --git a/src/frontend/app/routes/map.tsx b/src/frontend/app/routes/map.tsx index f705617..d3288e9 100644 --- a/src/frontend/app/routes/map.tsx +++ b/src/frontend/app/routes/map.tsx @@ -145,10 +145,11 @@ export default function StopMap() { zoom: mapState.zoom, }} attributionControl={false} - maxBounds={REGIONS[region].bounds ? [ - REGIONS[region].bounds!.sw, - REGIONS[region].bounds!.ne, - ] : undefined} + maxBounds={ + REGIONS[region].bounds + ? [REGIONS[region].bounds!.sw, REGIONS[region].bounds!.ne] + : undefined + } > <NavigationControl position="top-right" /> <GeolocateControl position="top-right" trackUserLocation={true} /> @@ -188,12 +189,12 @@ export default function StopMap() { "text-offset": [0, 3], "text-anchor": "center", "text-justify": "center", - "text-size": ["interpolate", ["linear"], ["zoom"], 11, 8, 22, 16] + "text-size": ["interpolate", ["linear"], ["zoom"], 11, 8, 22, 16], }} paint={{ "text-color": `${REGIONS[region].textColour || "#000"}`, "text-halo-color": "#FFF", - "text-halo-width": 1 + "text-halo-width": 1, }} /> diff --git a/src/frontend/app/routes/settings.css b/src/frontend/app/routes/settings.css index c08ed68..d82cf8b 100644 --- a/src/frontend/app/routes/settings.css +++ b/src/frontend/app/routes/settings.css @@ -179,8 +179,12 @@ } @keyframes spin { - from { transform: rotate(0deg); } - to { transform: rotate(360deg); } + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } } /* Modal styles */ diff --git a/src/frontend/app/routes/settings.tsx b/src/frontend/app/routes/settings.tsx index cb64f4e..d9efa2e 100644 --- a/src/frontend/app/routes/settings.tsx +++ b/src/frontend/app/routes/settings.tsx @@ -159,30 +159,31 @@ export default function Settings() { </a> </p> {region === "vigo" && ( - <p> - {t("about.data_source_prefix")}{" "} - <a - href="https://datos.vigo.org" - className="about-link" - rel="nofollow noreferrer noopener" - > - datos.vigo.org - </a>{" "} - {t("about.data_source_middle")}{" "} - <a - href="https://opendefinition.org/licenses/odc-by/" - className="about-link" - rel="nofollow noreferrer noopener" - > - Open Data Commons Attribution License - </a>. - </p> + <p> + {t("about.data_source_prefix")}{" "} + <a + href="https://datos.vigo.org" + className="about-link" + rel="nofollow noreferrer noopener" + > + datos.vigo.org + </a>{" "} + {t("about.data_source_middle")}{" "} + <a + href="https://opendefinition.org/licenses/odc-by/" + className="about-link" + rel="nofollow noreferrer noopener" + > + Open Data Commons Attribution License + </a> + . + </p> )} {region === "santiago" && ( <p> - Datos obtenidos de app MaisBus (Concello de Santiago/TUSSA), - gracias a la documentación de [TP Galicia](https://tpgalicia.github.io/urban/santiago/) - en GitHub. + Datos obtenidos de app MaisBus (Concello de Santiago/TUSSA), gracias a + la documentación de [TP + Galicia](https://tpgalicia.github.io/urban/santiago/) en GitHub. </p> )} @@ -193,7 +194,7 @@ export default function Settings() { <p> {t( "about.region_change_message", - "¿Estás seguro de que quieres cambiar la región? Serás redirigido a la lista de paradas." + "¿Estás seguro de que quieres cambiar la región? Serás redirigido a la lista de paradas.", )} </p> <div className="modal-buttons"> diff --git a/src/frontend/app/routes/stoplist.tsx b/src/frontend/app/routes/stoplist.tsx index 80267ea..fe6d2f1 100644 --- a/src/frontend/app/routes/stoplist.tsx +++ b/src/frontend/app/routes/stoplist.tsx @@ -32,10 +32,11 @@ export default function StopList() { ); const fuse = useMemo( - () => new Fuse(data || [], { - threshold: 0.3, - keys: ["name.original", "name.intersect", "stopId"] - }), + () => + new Fuse(data || [], { + threshold: 0.3, + keys: ["name.original", "name.intersect", "stopId"], + }), [data], ); @@ -77,7 +78,9 @@ export default function StopList() { const checkPermission = async () => { try { if (navigator.permissions?.query) { - permissionStatus = await navigator.permissions.query({ name: "geolocation" }); + permissionStatus = await navigator.permissions.query({ + name: "geolocation", + }); if (permissionStatus.state === "granted") { requestUserLocation(); } @@ -109,7 +112,12 @@ export default function StopList() { } const toRadians = (value: number) => (value * Math.PI) / 180; - const getDistance = (lat1: number, lon1: number, lat2: number, lon2: number) => { + const getDistance = ( + lat1: number, + lon1: number, + lat2: number, + lon2: number, + ) => { const R = 6371000; // meters const dLat = toRadians(lat2 - lat1); const dLon = toRadians(lon2 - lon1); @@ -125,7 +133,10 @@ export default function StopList() { return data .map((stop) => { - if (typeof stop.latitude !== "number" || typeof stop.longitude !== "number") { + if ( + typeof stop.latitude !== "number" || + typeof stop.longitude !== "number" + ) { return { stop, distance: Number.POSITIVE_INFINITY }; } @@ -162,25 +173,24 @@ export default function StopList() { // Add favourite flags to stops const favouriteStopsIds = StopDataProvider.getFavouriteIds(region); - const stopsWithFavourites = stops.map(stop => ({ + const stopsWithFavourites = stops.map((stop) => ({ ...stop, - favourite: favouriteStopsIds.includes(stop.stopId) + favourite: favouriteStopsIds.includes(stop.stopId), })); setData(stopsWithFavourites); // Update favourite and recent stops with full data - const favStops = stopsWithFavourites.filter(stop => - favouriteStopsIds.includes(stop.stopId) + const favStops = stopsWithFavourites.filter((stop) => + favouriteStopsIds.includes(stop.stopId), ); setFavouriteStops(favStops); const recIds = StopDataProvider.getRecent(region); const recStops = recIds - .map(id => stopsWithFavourites.find(stop => stop.stopId === id)) + .map((id) => stopsWithFavourites.find((stop) => stop.stopId === id)) .filter(Boolean) as Stop[]; setRecentStops(recStops.reverse()); - } catch (error) { console.error("Failed to load stops:", error); } finally { @@ -212,12 +222,12 @@ export default function StopList() { // Check if search query is a number (stop code search) const isNumericSearch = /^\d+$/.test(searchQuery.trim()); - + let items: Stop[]; if (isNumericSearch) { // Direct match for stop codes const stopId = parseInt(searchQuery.trim(), 10); - const exactMatch = data.filter(stop => stop.stopId === stopId); + const exactMatch = data.filter((stop) => stop.stopId === stopId); if (exactMatch.length > 0) { items = exactMatch; } else { @@ -230,14 +240,14 @@ export default function StopList() { const results = fuse.search(searchQuery); items = results.map((result) => result.item); } - + setSearchResults(items); }, 300); }; return ( <div className="page-container stoplist-page"> - <h1 className="page-title">BusUrbano - {REGIONS[region].name}</h1> + <h1 className="page-title">BusUrbano - {REGIONS[region].name}</h1> <form className="search-form"> <div className="form-group"> @@ -286,10 +296,9 @@ export default function StopList() { <div className="list-container"> <h2 className="page-subtitle"> - {userLocation - ? t("stoplist.nearby_stops", "Nearby stops") - : t("stoplist.all_stops", "Paradas") - } + {userLocation + ? t("stoplist.nearby_stops", "Nearby stops") + : t("stoplist.all_stops", "Paradas")} </h2> <ul className="list"> @@ -301,9 +310,9 @@ export default function StopList() { </> )} {!loading && data - ? (userLocation ? sortedAllStops.slice(0, 6) : sortedAllStops).map((stop) => ( - <StopItem key={stop.stopId} stop={stop} /> - )) + ? (userLocation ? sortedAllStops.slice(0, 6) : sortedAllStops).map( + (stop) => <StopItem key={stop.stopId} stop={stop} />, + ) : null} </ul> </div> |
