diff options
| author | copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> | 2026-03-01 10:21:52 +0000 |
|---|---|---|
| committer | copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> | 2026-03-01 10:21:52 +0000 |
| commit | 2f2261f764e0a0a52652bceda306f39f6f568b87 (patch) | |
| tree | e5672168b542f773204d0cf8937e79e8b5ff37b4 /src | |
| parent | e5ab68b158558e0f6577bf0fdd95e652fb269e6a (diff) | |
Implement route-specific realtime filtering and route detail UI updates
Co-authored-by: arielcostas <94913521+arielcostas@users.noreply.github.com>
Diffstat (limited to 'src')
| -rw-r--r-- | src/frontend/app/api/schema.ts | 1 | ||||
| -rw-r--r-- | src/frontend/app/routes/routes-$id.tsx | 102 |
2 files changed, 86 insertions, 17 deletions
diff --git a/src/frontend/app/api/schema.ts b/src/frontend/app/api/schema.ts index c0c97a4..57f34b1 100644 --- a/src/frontend/app/api/schema.ts +++ b/src/frontend/app/api/schema.ts @@ -1,6 +1,7 @@ import { z } from "zod"; export const RouteInfoSchema = z.object({ + gtfsId: z.string().optional().nullable(), shortName: z.string(), colour: z.string(), textColour: z.string(), diff --git a/src/frontend/app/routes/routes-$id.tsx b/src/frontend/app/routes/routes-$id.tsx index 97f6337..6cc872d 100644 --- a/src/frontend/app/routes/routes-$id.tsx +++ b/src/frontend/app/routes/routes-$id.tsx @@ -79,6 +79,28 @@ export default function RouteDetailsPage() { true, Boolean(selectedStopId) && isTodaySelectedDate ); + const filteredRealtimeArrivals = useMemo(() => { + const arrivals = selectedStopRealtime?.arrivals ?? []; + if (arrivals.length === 0) { + return []; + } + + const routeId = id?.trim(); + const routeShortName = route?.shortName?.trim().toLowerCase(); + + return arrivals.filter((arrival) => { + const arrivalGtfsId = arrival.route.gtfsId?.trim(); + if (routeId && arrivalGtfsId) { + return arrivalGtfsId === routeId; + } + + if (routeShortName) { + return arrival.route.shortName.trim().toLowerCase() === routeShortName; + } + + return true; + }); + }, [selectedStopRealtime?.arrivals, id, route?.shortName]); usePageTitle( route?.shortName @@ -321,14 +343,33 @@ export default function RouteDetailsPage() { {selectedPattern?.geometry && ( <Source type="geojson" data={geojson}> <Layer - id="route-line" + id="route-line-border" + type="line" + paint={{ + "line-color": + route.textColor && route.textColor.trim() + ? formatHex(route.textColor) + : "#111827", + "line-width": 7, + "line-opacity": 0.75, + }} + layout={{ + "line-cap": "round", + "line-join": "round", + }} + /> + <Layer + id="route-line-inner" type="line" paint={{ "line-color": route.color ? formatHex(route.color) : "#3b82f6", - "line-width": 4, - "line-opacity": 0.8, + "line-width": 5, + }} + layout={{ + "line-cap": "round", + "line-join": "round", }} /> </Source> @@ -639,22 +680,49 @@ export default function RouteDetailsPage() { <div className="text-[11px] text-muted"> {t("routes.loading_realtime", "Cargando...")} </div> - ) : ( - <div className="flex flex-wrap gap-1"> - {(selectedStopRealtime?.arrivals ?? []).map( - (arrival, i) => ( - <span - key={`${arrival.tripId}-${i}`} - className="text-[11px] px-2 py-0.5 bg-primary/10 text-primary rounded" - > - {arrival.estimate.minutes} - {arrival.delay?.minutes - ? formatDelayMinutes(arrival.delay.minutes) - : ""} - </span> - ) + ) : filteredRealtimeArrivals.length === 0 ? ( + <div className="text-[11px] text-muted"> + {t( + "routes.realtime_no_route_estimates", + "Sin estimaciones para esta línea" )} </div> + ) : ( + <> + <div className="flex items-center justify-between gap-2 rounded-lg border border-green-500/30 bg-green-500/10 px-2.5 py-2"> + <span className="text-[11px] font-semibold uppercase tracking-wide text-green-700 dark:text-green-300"> + {t("routes.next_arrival", "Próximo")} + </span> + <span className="inline-flex min-w-16 items-center justify-center rounded-xl bg-green-600 px-3 py-1.5 text-base font-bold leading-none text-white"> + {filteredRealtimeArrivals[0].estimate.minutes}′ + {filteredRealtimeArrivals[0].delay?.minutes + ? formatDelayMinutes( + filteredRealtimeArrivals[0].delay.minutes + ) + : ""} + </span> + </div> + + {filteredRealtimeArrivals.length > 1 && ( + <div className="mt-2 flex flex-wrap justify-end gap-1"> + {filteredRealtimeArrivals + .slice(1) + .map((arrival, i) => ( + <span + key={`${arrival.tripId}-${i}`} + className="text-[11px] px-2 py-0.5 bg-primary/10 text-primary rounded" + > + {arrival.estimate.minutes}′ + {arrival.delay?.minutes + ? formatDelayMinutes( + arrival.delay.minutes + ) + : ""} + </span> + ))} + </div> + )} + </> )} </div> )} |
