diff options
Diffstat (limited to 'src/pages')
| -rw-r--r-- | src/pages/Home.tsx | 75 | ||||
| -rw-r--r-- | src/pages/Stop.tsx | 84 |
2 files changed, 87 insertions, 72 deletions
diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 5070e5f..1f5c319 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -1,46 +1,16 @@ +import { useEffect, useMemo, useState } from "react"; import { Link, useNavigate } from "react-router-dom"; -import useSWR from "swr"; +import { Stop, StopDataProvider } from "../data/stopDataProvider"; -interface Stop { - stopId: number - name: string; - latitude?: number; - longitude?: number; - lines: string[]; -} - -interface CachedStopList { - timestamp: number; - data: Stop[]; -} +const sdp = new StopDataProvider(); export function Home() { - const navigate = useNavigate() - const { data, error, isLoading } = useSWR<Stop[]>('home', async () => { - const rawCachedData = localStorage.getItem('cachedStopList'); - if (rawCachedData) { - const parsedData: CachedStopList = JSON.parse(rawCachedData) + const [data, setData] = useState<Stop[] | null>(null) + const navigate = useNavigate(); - // Cache for 12 hours - if (Date.now() - parsedData.timestamp < 1000 * 60 * 60 * 12) { - return parsedData.data - } else { - localStorage.removeItem('cachedStopList') - } - } - - const response = await fetch('/api/ListStops') - const body = await response.json(); - - const cachedData: CachedStopList = { - timestamp: Date.now(), - data: body - } - - localStorage.setItem('cachedStopList', JSON.stringify(cachedData)); - - return body; - }); + useEffect(() => { + sdp.getStops().then((stops: Stop[]) => setData(stops)) + }, []); const handleStopSearch = async (event: React.FormEvent) => { event.preventDefault() @@ -54,12 +24,15 @@ export function Home() { } } - if (isLoading) return <h1>Loading...</h1> - if (error) return <h1>Error</h1> + const favouritedStops = useMemo(() => { + return data?.filter(stop => stop.favourite) ?? [] + }, [data]) + + if (data === null) return <h1>Loading...</h1> return ( <> - <h1>Home</h1> + <h1>UrbanoVigo Web</h1> <form action="none" onSubmit={handleStopSearch}> <div> @@ -72,6 +45,26 @@ export function Home() { <button type="submit">Buscar</button> </form> + <h2>Paradas favoritas</h2> + + {favouritedStops?.length == 1 && ( + <p> + Accede a una parada y márcala como favorita para verla aquí. + </p> + )} + + <ul> + {favouritedStops?.sort((a, b) => a.stopId - b.stopId).map((stop: Stop) => ( + <li key={stop.stopId}> + <Link to={`/${stop.stopId}`}> + ({stop.stopId}) {stop.name} - {stop.lines?.join(', ')} + </Link> + </li> + ))} + </ul> + + <h2>Paradas</h2> + <ul> {data?.sort((a, b) => a.stopId - b.stopId).map((stop: Stop) => ( <li key={stop.stopId}> diff --git a/src/pages/Stop.tsx b/src/pages/Stop.tsx index a5effc1..4fa68cf 100644 --- a/src/pages/Stop.tsx +++ b/src/pages/Stop.tsx @@ -1,5 +1,6 @@ +import { useEffect, useState } from "react"; import { Link, useParams } from "react-router-dom"; -import useSWR from "swr"; +import { StopDataProvider } from "../data/stopDataProvider"; interface StopDetails { stop: { @@ -8,28 +9,33 @@ interface StopDetails { latitude: number; longitude: number; } - estimates: [{ + estimates: { line: string; route: string; minutes: number; meters: number; - }] + }[] } export function Stop(): JSX.Element { + const sdp = new StopDataProvider(); + const [data, setData] = useState<StopDetails | null>(null); + const [favourited, setFavourited] = useState(false); const params = useParams(); - const { data, error, isLoading } = useSWR<StopDetails>(`stop-${params.stopId}`, async () => { - let response; + const loadData = () => { + fetch(`/api/GetStopEstimates?id=${params.stopId}`) + .then(r => r.json()) + .then((body: StopDetails) => setData(body)); + }; - try { - response = await fetch(`/api/GetStopEstimates?id=${params.stopId}`) - return response.json() - } catch (error) { - console.error(error) - throw new Error(`Failed to fetch data, status ${response!.status}, body: ${await response!.text()}`) - } - }); + useEffect(() => { + loadData(); + + setFavourited( + sdp.isFavourite(parseInt(params.stopId ?? "")) + ); + }) const absoluteArrivalTime = (minutes: number) => { const now = new Date() @@ -40,9 +46,7 @@ export function Stop(): JSX.Element { }).format(arrival) } - if (isLoading) return <h1>Loading...</h1> - if (error) return <h1>Error: {JSON.stringify(error)}</h1> - if (data === undefined) return <h1>No data</h1> + if (data === null) return <h1>Cargando datos en tiempo real...</h1> return ( <> @@ -50,21 +54,31 @@ export function Stop(): JSX.Element { <h1>{data?.stop.name} ({data?.stop.id})</h1> </div> - <Link to="/"> - <svg - xmlns="http://www.w3.org/2000/svg" - viewBox="0 0 24 24" - fill="none" - stroke="currentColor" - strokeWidth="2" - strokeLinecap="round" - strokeLinejoin="round" - style={{marginInlineEnd: '0.5em', width: '1em', height: '1em'}} - > - <path d="M19 12H5M12 19l-7-7 7-7" /> - </svg> - Volver al listado de paradas - </Link> + <div style={{display: 'flex', gap: '1rem'}}> + <Link to="/" className="button"> + 🔙 Volver al listado de paradas + </Link> + + {!favourited && ( + <button type="button" onClick={() => { + sdp.addFavourite(parseInt(params.stopId ?? "")); + setFavourited(true); + }}> + ⭐ Añadir a favoritos + </button> + )} + + {favourited && ( + <button type="button" onClick={() => { + sdp.removeFavourite(parseInt(params.stopId ?? "")); + setFavourited(false); + }}> + ⭐Quitar de favoritos + </button> + )} + + <button onClick={loadData}>⬇️ Recargar</button> + </div> <table> <caption>Estimaciones de llegadas</caption> @@ -97,6 +111,14 @@ export function Stop(): JSX.Element { </tr> ))} </tbody> + + {data?.estimates.length === 0 && ( + <tfoot> + <tr> + <td colSpan={4}>No hay estimaciones disponibles</td> + </tr> + </tfoot> + )} </table> <p> |
