From 11026cd244679c06ddbd17c0dfcb8c319da4842b Mon Sep 17 00:00:00 2001 From: gnezim Date: Wed, 15 Apr 2026 20:58:07 +0300 Subject: [PATCH] Add graceful API error state with retry on search pages When the API fetch fails (backend unavailable), show a styled white error card with a Russian-language message and retry button instead of barely-visible text on a dark background. --- .../components/OnlineBoardSearchPage.scss | 32 ++++- .../components/OnlineBoardSearchPage.tsx | 132 +++++++++++------- 2 files changed, 111 insertions(+), 53 deletions(-) diff --git a/src/features/online-board/components/OnlineBoardSearchPage.scss b/src/features/online-board/components/OnlineBoardSearchPage.scss index bd2ca8fb..a17a028c 100644 --- a/src/features/online-board/components/OnlineBoardSearchPage.scss +++ b/src/features/online-board/components/OnlineBoardSearchPage.scss @@ -19,11 +19,41 @@ } } - &__error { + &__error-card { + background: colors.$white; + border-radius: vars.$border-radius; padding: vars.$space-xl; + text-align: center; + } + + &__error-title { + margin: 0 0 vars.$space-m; + font-size: 18px; color: colors.$red; } + &__error-message { + margin: 0 0 vars.$space-xl; + color: #666; + font-size: 14px; + } + + &__retry-btn { + display: inline-block; + padding: vars.$space-s2 vars.$space-xl; + background-color: colors.$blue; + color: colors.$white; + border: none; + border-radius: vars.$border-radius; + cursor: pointer; + font-size: 14px; + font-weight: 500; + + &:hover { + opacity: 0.9; + } + } + &__actions { display: none; } diff --git a/src/features/online-board/components/OnlineBoardSearchPage.tsx b/src/features/online-board/components/OnlineBoardSearchPage.tsx index 801f4d59..dab598f2 100644 --- a/src/features/online-board/components/OnlineBoardSearchPage.tsx +++ b/src/features/online-board/components/OnlineBoardSearchPage.tsx @@ -15,7 +15,11 @@ import type { FC } from "react"; import { useCallback } from "react"; import { useNavigate, useParams } from "@modern-js/runtime/router"; +import { useTranslation } from "@/i18n/provider.js"; import { FlightList } from "@/ui/flights/FlightList.js"; +import { PageLayout } from "@/ui/layout/PageLayout.js"; +import { PageTabs } from "@/ui/layout/PageTabs.js"; +import { OnlineBoardFilter } from "./OnlineBoardFilter.js"; import "./OnlineBoardSearchPage.scss"; import { JsonLdRenderer } from "@/shared/seo/json-ld.js"; import { useOnlineBoard } from "../hooks/useOnlineBoard.js"; @@ -133,6 +137,7 @@ export const OnlineBoardSearchPage: FC = ({ params, }) => { const navigate = useNavigate(); + const { t } = useTranslation(); const routeParams = useParams<{ lang: string }>(); const lang = routeParams.lang ?? "ru"; @@ -196,62 +201,85 @@ export const OnlineBoardSearchPage: FC = ({ return (
{jsonLd && } - {/* Connection status indicator */} -
- {connectionStatus === "live" && ( - Live - )} - {connectionStatus === "reconnecting" && ( - Reconnecting... - )} - {connectionStatus === "offline" && ( - Offline - )} -
- - {/* Calendar strip (simple date list for now) */} - {calendarDays.length > 0 && ( -
- {calendarDays.map((day) => ( - - ))} + } + title={ +

+ {t("BOARD.TITLE")} +

+ } + contentLeft={} + stickyContent={ + calendarDays.length > 0 ? ( +
+ {calendarDays.map((day) => ( + + ))} +
+ ) : undefined + } + > + {/* Connection status indicator */} +
+ {connectionStatus === "live" && ( + Live + )} + {connectionStatus === "reconnecting" && ( + Reconnecting... + )} + {connectionStatus === "offline" && ( + Offline + )}
- )} - {/* Error state */} - {error && ( -
-

Failed to load flights. Please try again.

- -
- )} + {/* Error state */} + {error && ( +
+
+

+ Не удалось загрузить данные +

+

+ API сервер недоступен. Проверьте подключение и попробуйте снова. +

+ +
+
+ )} - {/* Flight list */} - + {/* Flight list */} + {!error && } - {/* Flight click overlay — we make the list clickable */} - {!loading && displayFlights.length > 0 && ( -
- {displayFlights.map((flight) => ( - - ))} -
- )} + {/* Flight click overlay — we make the list clickable */} + {!loading && displayFlights.length > 0 && ( +
+ {displayFlights.map((flight) => ( + + ))} +
+ )} +
); };