aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/app/routes
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend/app/routes')
-rw-r--r--src/frontend/app/routes/estimates-$id.css8
-rw-r--r--src/frontend/app/routes/estimates-$id.tsx89
-rw-r--r--src/frontend/app/routes/map.tsx13
-rw-r--r--src/frontend/app/routes/settings.css8
-rw-r--r--src/frontend/app/routes/settings.tsx45
-rw-r--r--src/frontend/app/routes/stoplist.tsx57
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>