aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/app/routes
diff options
context:
space:
mode:
authorcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>2025-11-06 22:49:47 +0000
committerAriel Costas Guerrero <ariel@costas.dev>2025-11-07 10:46:51 +0100
commite51cdd89afc08274ca622e18b8127feca29e90a3 (patch)
tree1e016c9bb977f8db4e7c61ad5fe1b3be311b6fef /src/frontend/app/routes
parent43ea6cc94b6c1f2bfaf3f8787991d3283765da0b (diff)
Add gallery components and improve search functionality
Co-authored-by: arielcostas <94913521+arielcostas@users.noreply.github.com>
Diffstat (limited to 'src/frontend/app/routes')
-rw-r--r--src/frontend/app/routes/stoplist.tsx99
1 files changed, 53 insertions, 46 deletions
diff --git a/src/frontend/app/routes/stoplist.tsx b/src/frontend/app/routes/stoplist.tsx
index e77dfb8..80267ea 100644
--- a/src/frontend/app/routes/stoplist.tsx
+++ b/src/frontend/app/routes/stoplist.tsx
@@ -2,6 +2,8 @@ import { useEffect, useMemo, useRef, useState, useCallback } from "react";
import StopDataProvider, { type Stop } from "../data/StopDataProvider";
import StopItem from "../components/StopItem";
import StopItemSkeleton from "../components/StopItemSkeleton";
+import StopGallery from "../components/StopGallery";
+import ServiceAlerts from "../components/ServiceAlerts";
import Fuse from "fuse.js";
import "./stoplist.css";
import { useTranslation } from "react-i18next";
@@ -30,7 +32,10 @@ export default function StopList() {
);
const fuse = useMemo(
- () => new Fuse(data || [], { threshold: 0.3, keys: ["name.original"] }),
+ () => new Fuse(data || [], {
+ threshold: 0.3,
+ keys: ["name.original", "name.intersect", "stopId"]
+ }),
[data],
);
@@ -188,14 +193,14 @@ export default function StopList() {
}, [loadStops]);
const handleStopSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
- const stopName = event.target.value || "";
+ const searchQuery = event.target.value || "";
if (searchTimeout.current) {
clearTimeout(searchTimeout.current);
}
searchTimeout.current = setTimeout(() => {
- if (stopName.length === 0) {
+ if (searchQuery.length === 0) {
setSearchResults(null);
return;
}
@@ -205,8 +210,27 @@ export default function StopList() {
return;
}
- const results = fuse.search(stopName);
- const items = results.map((result) => result.item);
+ // 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);
+ if (exactMatch.length > 0) {
+ items = exactMatch;
+ } else {
+ // Fuzzy search if no exact match
+ const results = fuse.search(searchQuery);
+ items = results.map((result) => result.item);
+ }
+ } else {
+ // Text search using Fuse.js
+ const results = fuse.search(searchQuery);
+ items = results.map((result) => result.item);
+ }
+
setSearchResults(items);
}, 300);
};
@@ -243,60 +267,43 @@ export default function StopList() {
</div>
)}
- <div className="list-container">
- <h2 className="page-subtitle">{t("stoplist.favourites")}</h2>
-
- {favouriteIds.length === 0 && (
- <p className="message">
- {t(
- "stoplist.no_favourites",
- "Accede a una parada y márcala como favorita para verla aquí.",
- )}
- </p>
- )}
-
- <ul className="list">
- {loading && favouriteIds.length > 0 &&
- favouriteIds.map((id) => (
- <StopItemSkeleton key={id} showId={true} stopId={id} />
- ))
- }
- {!loading && favouriteStops
- .sort((a, b) => a.stopId - b.stopId)
- .map((stop) => <StopItem key={stop.stopId} stop={stop} />)}
- </ul>
- </div>
-
- {(recentIds.length > 0 || (!loading && recentStops.length > 0)) && (
- <div className="list-container">
- <h2 className="page-subtitle">{t("stoplist.recents")}</h2>
+ {!loading && (
+ <StopGallery
+ stops={recentStops.slice(0, 5)}
+ title={t("stoplist.recents")}
+ />
+ )}
- <ul className="list">
- {loading && recentIds.length > 0 &&
- recentIds.map((id) => (
- <StopItemSkeleton key={id} showId={true} stopId={id} />
- ))
- }
- {!loading && recentStops.map((stop) => (
- <StopItem key={stop.stopId} stop={stop} />
- ))}
- </ul>
- </div>
+ {!loading && (
+ <StopGallery
+ stops={favouriteStops.sort((a, b) => a.stopId - b.stopId)}
+ title={t("stoplist.favourites")}
+ emptyMessage={t("stoplist.no_favourites")}
+ />
)}
+ <ServiceAlerts />
+
<div className="list-container">
- <h2 className="page-subtitle">{t("stoplist.all_stops", "Paradas")}</h2>
+ <h2 className="page-subtitle">
+ {userLocation
+ ? t("stoplist.nearby_stops", "Nearby stops")
+ : t("stoplist.all_stops", "Paradas")
+ }
+ </h2>
<ul className="list">
{loading && (
<>
- {Array.from({ length: 8 }, (_, index) => (
+ {Array.from({ length: 6 }, (_, index) => (
<StopItemSkeleton key={`skeleton-${index}`} />
))}
</>
)}
{!loading && data
- ? 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>