diff options
| author | Ariel Costas Guerrero <ariel@costas.dev> | 2025-12-01 00:10:58 +0100 |
|---|---|---|
| committer | Ariel Costas Guerrero <ariel@costas.dev> | 2025-12-01 00:10:58 +0100 |
| commit | 89d83f305db69f5bf86a4290341785b2673c35d0 (patch) | |
| tree | 0e3596b7cac91804b8e6f40a14ede120eccba1fb /src/frontend/app/routes | |
| parent | a477dda9dc4291ab25fffe2525acf44177154c86 (diff) | |
Add line list with link to official schedules
Diffstat (limited to 'src/frontend/app/routes')
| -rw-r--r-- | src/frontend/app/routes/home.tsx | 106 | ||||
| -rw-r--r-- | src/frontend/app/routes/lines.tsx | 37 |
2 files changed, 94 insertions, 49 deletions
diff --git a/src/frontend/app/routes/home.tsx b/src/frontend/app/routes/home.tsx index cb640c3..5d56b48 100644 --- a/src/frontend/app/routes/home.tsx +++ b/src/frontend/app/routes/home.tsx @@ -259,7 +259,7 @@ export default function StopList() { </div> {/* Search Results */} - {searchResults && searchResults.length > 0 && ( + {searchResults && searchResults.length > 0 ? ( <div className="w-full px-4 flex flex-col gap-2"> <h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100"> {t("stoplist.search_results", "Resultados de la búsqueda")} @@ -270,58 +270,66 @@ export default function StopList() { ))} </ul> </div> - )} - - {/* Favourites Gallery */} - {!loading && ( - <StopGallery - stops={favouriteStops.sort((a, b) => a.stopId - b.stopId)} - title={t("stoplist.favourites")} - emptyMessage={t("stoplist.no_favourites")} - /> - )} + ) : searchResults !== null ? ( + <div className="w-full px-4 flex flex-col gap-2"> + <p className="text-center text-gray-600 dark:text-gray-400 py-8"> + {t("stoplist.no_results", "No se encontraron resultados")} + </p> + </div> + ) : ( + <> + {/* Favourites Gallery */} + {!loading && ( + <StopGallery + stops={favouriteStops.sort((a, b) => a.stopId - b.stopId)} + title={t("stoplist.favourites")} + emptyMessage={t("stoplist.no_favourites")} + /> + )} - {/* Recent Stops Gallery - only show if no favourites */} - {!loading && favouriteStops.length === 0 && ( - <StopGallery - stops={recentStops.slice(0, 5)} - title={t("stoplist.recents")} - /> - )} + {/* Recent Stops Gallery - only show if no favourites */} + {!loading && favouriteStops.length === 0 && ( + <StopGallery + stops={recentStops.slice(0, 5)} + title={t("stoplist.recents")} + /> + )} - {/*<ServiceAlerts />*/} + {/*<ServiceAlerts />*/} - {/* All Stops / Nearby Stops */} - <div className="w-full px-4 flex flex-col gap-2"> - <div className="flex items-center gap-2"> - {userLocation && ( - <svg className="w-5 h-5 text-blue-600 dark:text-blue-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"> - <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" /> - <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" /> - </svg> - )} - <h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100"> - {userLocation - ? t("stoplist.nearby_stops", "Nearby stops") - : t("stoplist.all_stops", "Paradas")} - </h2> - </div> + {/* All Stops / Nearby Stops */} + <div className="w-full px-4 flex flex-col gap-2"> + <div className="flex items-center gap-2"> + {userLocation && ( + <svg className="w-5 h-5 text-blue-600 dark:text-blue-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"> + <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" /> + <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" /> + </svg> + )} + <h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100"> + {userLocation + ? t("stoplist.nearby_stops", "Nearby stops") + : t("stoplist.all_stops", "Paradas")} + </h2> + </div> - <ul className="list-none p-0 m-0 flex flex-col gap-2 md:grid md:grid-cols-[repeat(auto-fill,minmax(300px,1fr))] lg:grid-cols-[repeat(auto-fill,minmax(320px,1fr))]"> - {loading && ( - <> - {Array.from({ length: 6 }, (_, index) => ( - <StopItemSkeleton key={`skeleton-${index}`} /> - ))} - </> - )} - {!loading && data - ? (userLocation ? sortedAllStops.slice(0, 6) : sortedAllStops).map( - (stop) => <StopItem key={stop.stopId} stop={stop} /> - ) - : null} - </ul> - </div> + <ul className="list-none p-0 m-0 flex flex-col gap-2 md:grid md:grid-cols-[repeat(auto-fill,minmax(300px,1fr))] lg:grid-cols-[repeat(auto-fill,minmax(320px,1fr))]"> + {loading && ( + <> + {Array.from({ length: 6 }, (_, index) => ( + <StopItemSkeleton key={`skeleton-${index}`} /> + ))} + </> + )} + {!loading && data + ? (userLocation ? sortedAllStops.slice(0, 6) : sortedAllStops).map( + (stop) => <StopItem key={stop.stopId} stop={stop} /> + ) + : null} + </ul> + </div> + </> + )} </div> ); } diff --git a/src/frontend/app/routes/lines.tsx b/src/frontend/app/routes/lines.tsx new file mode 100644 index 0000000..658716f --- /dev/null +++ b/src/frontend/app/routes/lines.tsx @@ -0,0 +1,37 @@ +import { useTranslation } from "react-i18next"; +import LineIcon from "~/components/LineIcon"; +import { usePageTitle } from "~/contexts/PageTitleContext"; +import { VIGO_LINES } from "~/data/LinesData"; +import '../tailwind-full.css'; + +export default function LinesPage() { + const { t } = useTranslation(); + usePageTitle(t("navbar.lines", "Líneas")); + + return ( + <div className="container mx-auto px-4 py-6"> + <p className="mb-6 text-gray-700 dark:text-gray-300"> + {t("lines.description", "A continuación se muestra una lista de las líneas de autobús urbano de Vigo con sus respectivas rutas y enlaces a los horarios oficiales.")} + </p> + + <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> + {VIGO_LINES.map((line) => ( + <a + key={line.lineNumber} + href={line.scheduleUrl} + target="_blank" + rel="noopener noreferrer" + className="flex items-center gap-3 p-4 bg-white dark:bg-gray-800 rounded-lg shadow hover:shadow-lg transition-shadow border border-gray-200 dark:border-gray-700" + > + <LineIcon line={line.lineNumber} mode="rounded" /> + <div className="flex-1 min-w-0"> + <p className="text-sm md:text-md font-semibold text-gray-900 dark:text-gray-100"> + {line.routeName} + </p> + </div> + </a> + ))} + </div> + </div> + ); +} |
