From 80f6263516e307bcc6d887f6f91757bc73ae63f2 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Nov 2025 17:34:28 +0100 Subject: Add loading states, error handling, and refresh feedback to stops-$id page (#89) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: arielcostas <94913521+arielcostas@users.noreply.github.com> --- .../Stops/ConsolidatedCirculationListSkeleton.tsx | 45 ++++++++++++++++++++++ src/frontend/app/routes/estimates-$id.css | 25 ++++++++++++ src/frontend/app/routes/stops-$id.tsx | 36 ++++++++++++----- src/frontend/package-lock.json | 22 +---------- 4 files changed, 98 insertions(+), 30 deletions(-) create mode 100644 src/frontend/app/components/Stops/ConsolidatedCirculationListSkeleton.tsx (limited to 'src') diff --git a/src/frontend/app/components/Stops/ConsolidatedCirculationListSkeleton.tsx b/src/frontend/app/components/Stops/ConsolidatedCirculationListSkeleton.tsx new file mode 100644 index 0000000..43f02ca --- /dev/null +++ b/src/frontend/app/components/Stops/ConsolidatedCirculationListSkeleton.tsx @@ -0,0 +1,45 @@ +import React from "react"; +import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; +import "react-loading-skeleton/dist/skeleton.css"; +import "./ConsolidatedCirculationList.css"; + +export const ConsolidatedCirculationListSkeleton: React.FC = () => { + return ( + +
+
+ +
+ +
+ {[1, 2, 3, 4, 5].map((i) => ( +
+
+
+ +
+ +
+ +
+ +
+ + +
+
+ +
+ + +
+
+ ))} +
+
+
+ ); +}; diff --git a/src/frontend/app/routes/estimates-$id.css b/src/frontend/app/routes/estimates-$id.css index 0658156..81fba1d 100644 --- a/src/frontend/app/routes/estimates-$id.css +++ b/src/frontend/app/routes/estimates-$id.css @@ -268,3 +268,28 @@ [data-theme="dark"] .experimental-notice strong { color: #ffd966; } + +.refresh-status { + display: flex; + align-items: center; + gap: 0.5rem; + justify-content: center; + padding: 0.75rem 1rem; + margin-bottom: 1rem; + background-color: rgba(0, 123, 255, 0.1); + border: 1px solid rgba(0, 123, 255, 0.2); + border-radius: 8px; + color: var(--primary-color); + font-size: 0.9rem; + font-weight: 500; +} + +.refresh-status .refresh-icon { + width: 1rem; + height: 1rem; +} + +[data-theme="dark"] .refresh-status { + background-color: rgba(0, 123, 255, 0.15); + border-color: rgba(0, 123, 255, 0.3); +} diff --git a/src/frontend/app/routes/stops-$id.tsx b/src/frontend/app/routes/stops-$id.tsx index 7d533a5..86e74d9 100644 --- a/src/frontend/app/routes/stops-$id.tsx +++ b/src/frontend/app/routes/stops-$id.tsx @@ -11,6 +11,8 @@ import { type RegionId, getRegionConfig } from "~/data/RegionConfig"; import { StopAlert } from "~/components/StopAlert"; import LineIcon from "~/components/LineIcon"; import { ConsolidatedCirculationList } from "~/components/Stops/ConsolidatedCirculationList"; +import { ConsolidatedCirculationListSkeleton } from "~/components/Stops/ConsolidatedCirculationListSkeleton"; +import { ErrorDisplay } from "~/components/ErrorDisplay"; export interface ConsolidatedCirculation { line: string; @@ -197,7 +199,7 @@ export default function Estimates() {
- {/* TODO: Implement skeleton */} +
@@ -255,17 +257,33 @@ export default function Estimates() {

+ {(isManualRefreshing || dataLoading) && ( +
+ + {t("estimates.refreshing", "Actualizando datos...")} +
+ )} + {stopData && }
- {data ? ( - <> - - + {dataLoading ? ( + + ) : dataError ? ( + + ) : data ? ( + ) : null}
diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 947918b..880fd67 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -11,7 +11,6 @@ "@fontsource-variable/roboto": "^5.2.8", "@react-router/node": "^7.9.4", "@react-router/serve": "^7.9.4", - "@rollup/rollup-linux-x64-gnu": "^4.53.0", "framer-motion": "^12.23.24", "fuse.js": "^7.1.0", "i18next-browser-languagedetector": "^8.2.0", @@ -85,7 +84,6 @@ "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", @@ -1603,7 +1601,6 @@ "integrity": "sha512-sww8oDNqz8SgaXEQ3maqTuMlibCMpmWvLE0s5zyEyOQb1G99clYMcXceQ2HNU2jtXJkp+P5XI1CngpGpngyTnw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@mjackson/node-fetch-server": "^0.2.0", "@react-router/express": "7.9.5", @@ -1985,7 +1982,6 @@ "integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -1996,7 +1992,6 @@ "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -2067,7 +2062,6 @@ "integrity": "sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.3", "@typescript-eslint/types": "8.46.3", @@ -2374,7 +2368,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2615,7 +2608,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", @@ -3180,7 +3172,6 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -3388,7 +3379,6 @@ "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -4283,7 +4273,6 @@ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -4393,8 +4382,7 @@ "version": "1.9.4", "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", - "license": "BSD-2-Clause", - "peer": true + "license": "BSD-2-Clause" }, "node_modules/leaflet.markercluster": { "version": "1.5.3", @@ -4474,7 +4462,6 @@ "integrity": "sha512-XY2KHMsrVwSxGmudI94BXvP6v++9KxFs6/MEm9yPaF83H1YYDE8MPe2kc+2yAuPV7c1iA6Y5tmBsPcNj4J/ywg==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "@mapbox/geojson-rewind": "^0.5.2", "@mapbox/jsonlint-lines-primitives": "^2.0.2", @@ -5099,7 +5086,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -5316,7 +5302,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -5326,7 +5311,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -5458,7 +5442,6 @@ "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.5.tgz", "integrity": "sha512-JmxqrnBZ6E9hWmf02jzNn9Jm3UqyeimyiwzD69NjxGySG6lIz/1LVPsoTCwN7NBX2XjCEa1LIX5EMz1j2b6u6A==", "license": "MIT", - "peer": true, "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" @@ -6313,7 +6296,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6508,7 +6490,6 @@ "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -6784,7 +6765,6 @@ "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } -- cgit v1.3