From 9ff034d19fe7db577edbc9d7c67fc0ff4cb088c5 Mon Sep 17 00:00:00 2001 From: gnezim Date: Mon, 20 Apr 2026 18:27:31 +0300 Subject: [PATCH] =?UTF-8?q?OnlineBoard=20search:=20render=20=D0=9A=D1=83?= =?UTF-8?q?=D0=BF=D0=B8=D1=82=D1=8C/=D0=9E=D0=BD=D0=BB=D0=B0=D0=B9=D0=BD?= =?UTF-8?q?=20=D1=80=D0=B5=D0=B3=D0=B8=D1=81=D1=82=D1=80=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8F=20in=20expanded=20row?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Angular's board search results expansion shows [Купить] [Онлайн регистрация] [Детали рейса]. React only rendered Details. Added a `renderActions` prop on FlightCard/FlightList so the feature layer can inject extra buttons without the ui layer importing from features. OnlineBoardSearchPage wires it to FlightActions with showShare=false (the row already has a dedicated share icon). Visibility rules fall through to canBuyTicket / canRegister (same as BoardDetailsHeader), so cancelled/past flights still hide the Buy button and carriers without a registrationUrl still hide the Online Registration button — matching Angular's per-flight gating. Integration test mocks useAppSettings to avoid requiring the real ApiClientProvider in flight-search.test.tsx. --- .../components/OnlineBoardSearchPage.tsx | 13 +++++++++++++ src/ui/flights/FlightCard.tsx | 14 ++++++++++++++ src/ui/flights/FlightList.tsx | 9 +++++++++ .../online-board/flight-search.test.tsx | 18 ++++++++++++++++++ 4 files changed, 54 insertions(+) diff --git a/src/features/online-board/components/OnlineBoardSearchPage.tsx b/src/features/online-board/components/OnlineBoardSearchPage.tsx index e69a6e85..a89117fd 100644 --- a/src/features/online-board/components/OnlineBoardSearchPage.tsx +++ b/src/features/online-board/components/OnlineBoardSearchPage.tsx @@ -18,6 +18,7 @@ import { useNavigate } from "@modern-js/runtime/router"; import { useLocale } from "@/i18n/useLocale.js"; import { useTranslation } from "@/i18n/provider.js"; import { FlightList } from "@/ui/flights/FlightList.js"; +import { FlightActions } from "./BoardDetailsHeader/FlightActions.js"; import { findClosestFlightId } from "../closestFlight.js"; import { PageLayout } from "@/ui/layout/PageLayout.js"; import { PageTabs } from "@/ui/layout/PageTabs.js"; @@ -514,6 +515,18 @@ export const OnlineBoardSearchPage: FC = ({ onFlightClick={handleFlightClick} initialCurrentFlightId={initialCurrentFlightId} direction={params.type} + renderActions={(flight) => ( + // Mirrors Angular's board search expansion: each row + // shows [Купить] [Онлайн регистрация] alongside the + // Details button. Visibility falls through to the + // same canBuyTicket / canRegister rules we use in the + // BoardDetailsHeader. + + )} /> )} diff --git a/src/ui/flights/FlightCard.tsx b/src/ui/flights/FlightCard.tsx index 510f8dbb..a62642be 100644 --- a/src/ui/flights/FlightCard.tsx +++ b/src/ui/flights/FlightCard.tsx @@ -46,6 +46,13 @@ export interface FlightCardProps { * pages to render the per-leg route diagram. */ renderExpandedBody?: (flight: ISimpleFlight) => ReactNode; + /** + * Extra action buttons rendered before the default share + details + * buttons in the expanded actions row. Used by the online-board + * search page to surface the same Купить / Онлайн регистрация + * buttons Angular shows on the search results expansion. + */ + renderActions?: (flight: ISimpleFlight) => ReactNode; } /** Extract the primary leg from a flight (first leg for multi-leg) */ @@ -116,6 +123,7 @@ export const FlightCard: FC = ({ onViewDetails, direction = "route", renderExpandedBody, + renderActions, }) => { const { t } = useTranslation(); const { language } = useLocale(); @@ -415,6 +423,12 @@ export const FlightCard: FC = ({ > + {/* Extra actions (Купить / Онлайн регистрация). Angular + renders Buy + Registration + Details here on every + expanded board row; the caller wires up FlightActions + via renderActions so the visibility rules live with + the feature instead of leaking into the ui layer. */} + {renderActions?.(flight)}