From c9cfc5907c6c27f22814f5950a1c1eef7c8fdef0 Mon Sep 17 00:00:00 2001 From: gnezim Date: Thu, 16 Apr 2026 22:29:03 +0300 Subject: [PATCH] Add DeboardingPanel component for flight details accordion --- .../details-panels/DeboardingPanel.test.tsx | 71 +++++++++++++++++++ .../details-panels/DeboardingPanel.tsx | 63 ++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 src/features/online-board/components/details-panels/DeboardingPanel.test.tsx create mode 100644 src/features/online-board/components/details-panels/DeboardingPanel.tsx diff --git a/src/features/online-board/components/details-panels/DeboardingPanel.test.tsx b/src/features/online-board/components/details-panels/DeboardingPanel.test.tsx new file mode 100644 index 00000000..cb45988d --- /dev/null +++ b/src/features/online-board/components/details-panels/DeboardingPanel.test.tsx @@ -0,0 +1,71 @@ +// @vitest-environment jsdom +import { describe, it, expect, vi } from "vitest"; +import { render, screen } from "@testing-library/react"; +import { DeboardingPanel } from "./DeboardingPanel.js"; +import type { IFlightTransitionItem, IFlightLegArrivalStation } from "../../types.js"; + +vi.mock("@/i18n/provider.js", () => ({ + useTranslation: () => ({ t: (k: string) => k }), +})); + +const baseItem: IFlightTransitionItem = { + start: { + dayChange: { value: 0, title: "" }, + local: "13:00", + localTime: "13:00", + tzOffset: 3, + utc: "10:00", + }, + end: { + dayChange: { value: 0, title: "" }, + local: "13:30", + localTime: "13:30", + tzOffset: 3, + utc: "10:30", + }, + status: "InProgress", + isActual: true, +}; + +const arrival: IFlightLegArrivalStation = { + scheduled: { airport: "SVO", airportCode: "SVO", city: "Moscow", cityCode: "MOW", countryCode: "RU" }, + latest: { airport: "SVO", airportCode: "SVO", city: "Moscow", cityCode: "MOW", countryCode: "RU" }, + dispatch: "Terminal D", + gate: "B23", + terminal: "D", + bagBelt: "5", + times: { + scheduledArrival: { dayChange: { value: 0, title: "" }, local: "", localTime: "", tzOffset: 0, utc: "" }, + }, +}; + +describe("DeboardingPanel", () => { + it("renders transition times and status", () => { + render(); + expect(screen.getByText("13:00")).toBeTruthy(); + expect(screen.getByText("DETAILS.STATUS_IN_PROGRESS")).toBeTruthy(); + }); + + it("renders terminal, gate, baggage belt from arrival", () => { + render(); + expect(screen.getByText("D")).toBeTruthy(); + expect(screen.getByText("5")).toBeTruthy(); + }); + + it("omits terminal row when arrival has no terminal", () => { + const noTerminal = { ...arrival, terminal: undefined }; + render(); + expect(screen.queryByText("DETAILS.TERMINAL")).toBeNull(); + }); + + it("omits bag belt row when arrival has no bagBelt", () => { + const noBelt = { ...arrival, bagBelt: undefined }; + render(); + expect(screen.queryByText("DETAILS.BAG_BELT")).toBeNull(); + }); + + it("has data-testid", () => { + render(); + expect(screen.getByTestId("deboarding-panel")).toBeTruthy(); + }); +}); diff --git a/src/features/online-board/components/details-panels/DeboardingPanel.tsx b/src/features/online-board/components/details-panels/DeboardingPanel.tsx new file mode 100644 index 00000000..73e0725c --- /dev/null +++ b/src/features/online-board/components/details-panels/DeboardingPanel.tsx @@ -0,0 +1,63 @@ +import type { FC } from "react"; +import { useTranslation } from "@/i18n/provider.js"; +import type { + IFlightTransitionItem, + IFlightLegArrivalStation, + FlightTransitionStatus, +} from "../../types.js"; +import "./panels.scss"; + +const STATUS_KEYS: Record = { + Finished: "DETAILS.STATUS_FINISHED", + Expected: "DETAILS.STATUS_EXPECTED", + InProgress: "DETAILS.STATUS_IN_PROGRESS", + Specified: "DETAILS.STATUS_SPECIFIED", + Scheduled: "DETAILS.STATUS_SCHEDULED", +}; + +export interface DeboardingPanelProps { + item: IFlightTransitionItem; + arrival: IFlightLegArrivalStation; +} + +export const DeboardingPanel: FC = ({ item, arrival }) => { + const { t } = useTranslation(); + const hasEnd = Boolean(item.end?.local); + + return ( +
+
+ {t("DETAILS.STATUS")} + {t(STATUS_KEYS[item.status])} +
+
+ {t("DETAILS.SCHEDULED")} + {item.start.local} +
+ {hasEnd && ( +
+ {t("DETAILS.ACTUAL")} + {item.end.local} +
+ )} + {arrival.terminal && ( +
+ {t("DETAILS.TERMINAL")} + {arrival.terminal} +
+ )} + {arrival.gate && ( +
+ {t("DETAILS.GATE")} + {arrival.gate} +
+ )} + {arrival.bagBelt && ( +
+ {t("DETAILS.BAG_BELT")} + {arrival.bagBelt} +
+ )} +
+ ); +};