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/home.tsx106
-rw-r--r--src/frontend/app/routes/lines.tsx37
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>
+ );
+}