From 7b8594debceb93a1fa400d48fe1dcff943bd5af6 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 26 Jun 2025 23:44:25 +0200 Subject: Implement stop sheet modal for map stop interactions (#27) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: arielcostas <94913521+arielcostas@users.noreply.github.com> Co-authored-by: Ariel Costas Guerrero --- src/frontend/app/components/GroupedTable.tsx | 124 +++++++++++---------- src/frontend/app/components/LineIcon.css | 5 +- src/frontend/app/components/LineIcon.tsx | 4 +- src/frontend/app/components/NavBar.tsx | 30 +++--- src/frontend/app/components/RegularTable.tsx | 129 ++++++++++++---------- src/frontend/app/components/StopItem.css | 2 +- src/frontend/app/components/StopItem.tsx | 15 ++- src/frontend/app/components/StopSheet.css | 146 +++++++++++++++++++++++++ src/frontend/app/components/StopSheet.tsx | 154 +++++++++++++++++++++++++++ 9 files changed, 465 insertions(+), 144 deletions(-) create mode 100644 src/frontend/app/components/StopSheet.css create mode 100644 src/frontend/app/components/StopSheet.tsx (limited to 'src/frontend/app/components') diff --git a/src/frontend/app/components/GroupedTable.tsx b/src/frontend/app/components/GroupedTable.tsx index 3a16d89..47c2d31 100644 --- a/src/frontend/app/components/GroupedTable.tsx +++ b/src/frontend/app/components/GroupedTable.tsx @@ -2,73 +2,79 @@ import { type StopDetails } from "../routes/estimates-$id"; import LineIcon from "./LineIcon"; interface GroupedTable { - data: StopDetails; - dataDate: Date | null; + data: StopDetails; + dataDate: Date | null; } export const GroupedTable: React.FC = ({ data, dataDate }) => { - const formatDistance = (meters: number) => { - if (meters > 1024) { - return `${(meters / 1000).toFixed(1)} km`; - } else { - return `${meters} m`; - } + const formatDistance = (meters: number) => { + if (meters > 1024) { + return `${(meters / 1000).toFixed(1)} km`; + } else { + return `${meters} m`; } + }; - const groupedEstimates = data.estimates.reduce((acc, estimate) => { - if (!acc[estimate.line]) { - acc[estimate.line] = []; - } - acc[estimate.line].push(estimate); - return acc; - }, {} as Record); + const groupedEstimates = data.estimates.reduce( + (acc, estimate) => { + if (!acc[estimate.line]) { + acc[estimate.line] = []; + } + acc[estimate.line].push(estimate); + return acc; + }, + {} as Record, + ); - const sortedLines = Object.keys(groupedEstimates).sort((a, b) => { - const firstArrivalA = groupedEstimates[a][0].minutes; - const firstArrivalB = groupedEstimates[b][0].minutes; - return firstArrivalA - firstArrivalB; - }); + const sortedLines = Object.keys(groupedEstimates).sort((a, b) => { + const firstArrivalA = groupedEstimates[a][0].minutes; + const firstArrivalB = groupedEstimates[b][0].minutes; + return firstArrivalA - firstArrivalB; + }); - return - + return ( +
Estimaciones de llegadas a las {dataDate?.toLocaleTimeString()}
+ - - - - - - - - - - - {sortedLines.map((line) => ( - groupedEstimates[line].map((estimate, idx) => ( - - {idx === 0 && ( - - )} - - - - - )) - ))} - + + + + + + + + - {data?.estimates.length === 0 && ( - - - - - + + {sortedLines.map((line) => + groupedEstimates[line].map((estimate, idx) => ( + + {idx === 0 && ( + + )} + + + + + )), )} + + + {data?.estimates.length === 0 && ( + + + + + + )}
+ Estimaciones de llegadas a las {dataDate?.toLocaleTimeString()} +
LíneaRutaLlegadaDistancia
- - {estimate.route}{`${estimate.minutes} min`} - {estimate.meters > -1 - ? formatDistance(estimate.meters) - : "No disponible" - } -
LíneaRutaLlegadaDistancia
No hay estimaciones disponibles
+ + {estimate.route}{`${estimate.minutes} min`} + {estimate.meters > -1 + ? formatDistance(estimate.meters) + : "No disponible"} +
No hay estimaciones disponibles
-} + ); +}; diff --git a/src/frontend/app/components/LineIcon.css b/src/frontend/app/components/LineIcon.css index e7e8949..4b39351 100644 --- a/src/frontend/app/components/LineIcon.css +++ b/src/frontend/app/components/LineIcon.css @@ -55,7 +55,8 @@ font-weight: 600; text-transform: uppercase; color: inherit; - /* Prevent color change on hover */ + background-color: white; + border-radius: 0.25rem 0.25rem 0 0; } .line-c1 { @@ -236,4 +237,4 @@ .line-u2 { border-color: var(--line-u2); -} \ No newline at end of file +} diff --git a/src/frontend/app/components/LineIcon.tsx b/src/frontend/app/components/LineIcon.tsx index 291b444..3d613e6 100644 --- a/src/frontend/app/components/LineIcon.tsx +++ b/src/frontend/app/components/LineIcon.tsx @@ -1,5 +1,5 @@ -import React from 'react'; -import './LineIcon.css'; +import React from "react"; +import "./LineIcon.css"; interface LineIconProps { line: string; diff --git a/src/frontend/app/components/NavBar.tsx b/src/frontend/app/components/NavBar.tsx index eba7196..6a06e63 100644 --- a/src/frontend/app/components/NavBar.tsx +++ b/src/frontend/app/components/NavBar.tsx @@ -9,14 +9,14 @@ function isWithinVigo(lngLat: LngLatLike): boolean { let lng: number, lat: number; if (Array.isArray(lngLat)) { [lng, lat] = lngLat; - } else if ('lng' in lngLat && 'lat' in lngLat) { + } else if ("lng" in lngLat && "lat" in lngLat) { lng = lngLat.lng; lat = lngLat.lat; } else { return false; } // Rough bounding box for Vigo - return lat >= 42.18 && lat <= 42.30 && lng >= -8.78 && lng <= -8.65; + return lat >= 42.18 && lat <= 42.3 && lng >= -8.78 && lng <= -8.65; } export default function NavBar() { @@ -25,20 +25,20 @@ export default function NavBar() { const navItems = [ { - name: t('navbar.stops', 'Paradas'), + name: t("navbar.stops", "Paradas"), icon: MapPin, - path: '/stops' + path: "/stops", }, { - name: t('navbar.map', 'Mapa'), + name: t("navbar.map", "Mapa"), icon: Map, - path: '/map', + path: "/map", callback: () => { - if (mapPositionMode !== 'gps') { + if (mapPositionMode !== "gps") { return; } - if (!('geolocation' in navigator)) { + if (!("geolocation" in navigator)) { return; } @@ -50,20 +50,20 @@ export default function NavBar() { updateMapState(coords, 16); } }, - () => { } + () => {}, ); - } + }, }, { - name: t('navbar.settings', 'Ajustes'), + name: t("navbar.settings", "Ajustes"), icon: Settings, - path: '/settings' - } + path: "/settings", + }, ]; return (