aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/app/components/ErrorDisplay.tsx
diff options
context:
space:
mode:
authorAriel Costas Guerrero <ariel@costas.dev>2025-09-07 19:22:28 +0200
committerAriel Costas Guerrero <ariel@costas.dev>2025-09-07 19:22:28 +0200
commit80bcf4a5f29ab926c2208d5efb4c19087c600323 (patch)
tree1e5826b29d8a22e057616e16069232f95788f3ba /src/frontend/app/components/ErrorDisplay.tsx
parent8182a08f60e88595984ba80b472f29ccf53c19bd (diff)
feat: Enhance StopSheet component with error handling and loading states
- Added skeleton loading state to StopSheet for better UX during data fetch. - Implemented error handling with descriptive messages for network and server errors. - Introduced manual refresh functionality to reload stop estimates. - Updated styles for loading and error states. - Created StopSheetSkeleton and TimetableSkeleton components for consistent loading indicators. feat: Improve StopList component with loading indicators and network data fetching - Integrated loading state for StopList while fetching stops from the network. - Added skeleton loading indicators for favourite and recent stops. - Refactored data fetching logic to include favourite and recent stops with full data. - Enhanced user experience with better loading and error handling. feat: Update Timetable component with loading and error handling - Added loading skeletons to Timetable for improved user experience. - Implemented error handling for timetable data fetching. - Refactored data loading logic to handle errors gracefully and provide retry options. chore: Update package dependencies - Upgraded react-router, lucide-react, and other dependencies to their latest versions. - Updated types for TypeScript compatibility.
Diffstat (limited to 'src/frontend/app/components/ErrorDisplay.tsx')
-rw-r--r--src/frontend/app/components/ErrorDisplay.tsx84
1 files changed, 84 insertions, 0 deletions
diff --git a/src/frontend/app/components/ErrorDisplay.tsx b/src/frontend/app/components/ErrorDisplay.tsx
new file mode 100644
index 0000000..3c91db6
--- /dev/null
+++ b/src/frontend/app/components/ErrorDisplay.tsx
@@ -0,0 +1,84 @@
+import React from "react";
+import { AlertTriangle, RefreshCw, Wifi, WifiOff } from "lucide-react";
+import { useTranslation } from "react-i18next";
+import "./ErrorDisplay.css";
+
+interface ErrorDisplayProps {
+ error: {
+ type: 'network' | 'server' | 'unknown';
+ status?: number;
+ message?: string;
+ };
+ onRetry?: () => void;
+ title?: string;
+ className?: string;
+}
+
+export const ErrorDisplay: React.FC<ErrorDisplayProps> = ({
+ error,
+ onRetry,
+ title,
+ className = ""
+}) => {
+ const { t } = useTranslation();
+
+ const getErrorIcon = () => {
+ switch (error.type) {
+ case 'network':
+ return <WifiOff className="error-icon" />;
+ case 'server':
+ return <AlertTriangle className="error-icon" />;
+ default:
+ return <AlertTriangle className="error-icon" />;
+ }
+ };
+
+ const getErrorMessage = () => {
+ switch (error.type) {
+ case 'network':
+ return t("errors.network", "No hay conexión a internet. Comprueba tu conexión y vuelve a intentarlo.");
+ case 'server':
+ if (error.status === 404) {
+ return t("errors.not_found", "No se encontraron datos para esta parada.");
+ }
+ if (error.status === 500) {
+ return t("errors.server_error", "Error del servidor. Inténtalo de nuevo más tarde.");
+ }
+ if (error.status && error.status >= 400) {
+ return t("errors.client_error", "Error en la solicitud. Verifica que la parada existe.");
+ }
+ return t("errors.server_generic", "Error del servidor ({{status}})", { status: error.status || 'desconocido' });
+ default:
+ return error.message || t("errors.unknown", "Ha ocurrido un error inesperado.");
+ }
+ };
+
+ const getErrorTitle = () => {
+ if (title) return title;
+
+ switch (error.type) {
+ case 'network':
+ return t("errors.network_title", "Sin conexión");
+ case 'server':
+ return t("errors.server_title", "Error del servidor");
+ default:
+ return t("errors.unknown_title", "Error");
+ }
+ };
+
+ return (
+ <div className={`error-display ${className}`}>
+ <div className="error-content">
+ {getErrorIcon()}
+ <h3 className="error-title">{getErrorTitle()}</h3>
+ <p className="error-message">{getErrorMessage()}</p>
+ {onRetry && (
+ <button className="error-retry-button" onClick={onRetry}>
+ <RefreshCw className="retry-icon" />
+ {t("errors.retry", "Reintentar")}
+ </button>
+ )}
+ </div>
+ </div>
+ );
+};