diff options
| author | Ariel Costas Guerrero <ariel@costas.dev> | 2025-09-07 19:22:28 +0200 |
|---|---|---|
| committer | Ariel Costas Guerrero <ariel@costas.dev> | 2025-09-07 19:22:28 +0200 |
| commit | 80bcf4a5f29ab926c2208d5efb4c19087c600323 (patch) | |
| tree | 1e5826b29d8a22e057616e16069232f95788f3ba /src/frontend/app/components/ErrorDisplay.tsx | |
| parent | 8182a08f60e88595984ba80b472f29ccf53c19bd (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.tsx | 84 |
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> + ); +}; |
