diff --git a/src/features/online-board/components/BoardDetailsHeader/FlightActions.test.tsx b/src/features/online-board/components/BoardDetailsHeader/FlightActions.test.tsx
new file mode 100644
index 00000000..e0fce849
--- /dev/null
+++ b/src/features/online-board/components/BoardDetailsHeader/FlightActions.test.tsx
@@ -0,0 +1,111 @@
+// @vitest-environment jsdom
+import { describe, it, expect, vi } from "vitest";
+import { render, screen } from "@testing-library/react";
+import { FlightActions } from "./FlightActions.js";
+import type { ISimpleFlight } from "../../types.js";
+
+vi.mock("@/i18n/provider.js", () => ({ useTranslation: () => ({ t: (k: string) => k }) }));
+vi.mock("@/shared/hooks/useAppSettings.js", () => ({
+ useAppSettings: () => ({
+ onlineboardSearchFrom: 2,
+ onlineboardSearchTo: 14,
+ scheduleSearchFrom: 30,
+ scheduleSearchTo: 30,
+ flightStatusAvailableFromHours: 24,
+ buyTicketMinHours: 2,
+ buyTicketMaxHours: 72,
+ loading: false,
+ error: null,
+ }),
+}));
+vi.mock("./visibility/buyTicketVisibility.js", () => ({ canBuyTicket: vi.fn(() => true) }));
+vi.mock("./visibility/registrationVisibility.js", () => ({ canRegister: vi.fn(() => true) }));
+vi.mock("./visibility/flightStatusVisibility.js", () => ({ canViewFlightStatus: vi.fn(() => true) }));
+
+function makeFlight(): ISimpleFlight {
+ return {
+ id: "X",
+ routeType: "Direct",
+ flyingTime: "1h",
+ status: "Scheduled",
+ flightId: { carrier: "SU", flightNumber: "0022", suffix: "", date: "20260417" },
+ operatingBy: { carrier: "SU", flightNumber: "0022" },
+ leg: {
+ arrival: {
+ scheduled: { airport: "", airportCode: "LED", city: "", cityCode: "", countryCode: "" },
+ latest: { airport: "", airportCode: "LED", city: "", cityCode: "", countryCode: "" },
+ dispatch: "",
+ gate: "",
+ terminal: "",
+ times: {
+ scheduledArrival: {
+ dayChange: { value: 0, title: "" },
+ local: "",
+ localTime: "",
+ tzOffset: 0,
+ utc: "",
+ },
+ },
+ },
+ dayChange: 0,
+ departure: {
+ scheduled: { airport: "", airportCode: "SVO", city: "", cityCode: "", countryCode: "" },
+ latest: { airport: "", airportCode: "SVO", city: "", cityCode: "", countryCode: "" },
+ dispatch: "",
+ gate: "",
+ terminal: "",
+ checkingStatus: "Scheduled",
+ parkingStand: "",
+ times: {
+ scheduledDeparture: {
+ dayChange: { value: 0, title: "" },
+ local: "",
+ localTime: "",
+ tzOffset: 0,
+ utc: "2026-04-20T10:00:00Z",
+ },
+ },
+ },
+ equipment: {},
+ flags: { checkinAvailable: false, returnToAirport: false, routeChanged: false },
+ flyingTime: "1h",
+ index: 0,
+ operatingBy: {},
+ status: "Scheduled",
+ updated: "",
+ },
+ } as ISimpleFlight;
+}
+
+describe("FlightActions", () => {
+ it("renders share, buy, register by default (status hidden)", () => {
+ render();
+ expect(screen.getByTestId("share-button")).toBeTruthy();
+ expect(screen.getByTestId("buy-ticket-button")).toBeTruthy();
+ expect(screen.getByTestId("registration-button")).toBeTruthy();
+ expect(screen.queryByTestId("flight-status-button")).toBeNull();
+ });
+
+ it("shows status button when showStatus=true", () => {
+ render();
+ expect(screen.getByTestId("flight-status-button")).toBeTruthy();
+ });
+
+ it("hides share button when showShare=false", () => {
+ render();
+ expect(screen.queryByTestId("share-button")).toBeNull();
+ });
+
+ it("hides buy ticket when canBuyTicket returns false", async () => {
+ const mod = await import("./visibility/buyTicketVisibility.js");
+ vi.mocked(mod.canBuyTicket).mockReturnValue(false);
+ render();
+ expect(screen.queryByTestId("buy-ticket-button")).toBeNull();
+ vi.mocked(mod.canBuyTicket).mockReturnValue(true);
+ });
+
+ it("has data-testid=flight-actions", () => {
+ render();
+ expect(screen.getByTestId("flight-actions")).toBeTruthy();
+ });
+});
diff --git a/src/features/online-board/components/BoardDetailsHeader/FlightActions.tsx b/src/features/online-board/components/BoardDetailsHeader/FlightActions.tsx
new file mode 100644
index 00000000..08cb7c63
--- /dev/null
+++ b/src/features/online-board/components/BoardDetailsHeader/FlightActions.tsx
@@ -0,0 +1,53 @@
+import type { FC } from "react";
+import { useAppSettings } from "@/shared/hooks/useAppSettings.js";
+import type { ISimpleFlight } from "../../types.js";
+import { AIRLINES, AIRLINES_WITH_STATUS } from "./airlines.js";
+import { canBuyTicket } from "./visibility/buyTicketVisibility.js";
+import { canRegister } from "./visibility/registrationVisibility.js";
+import { canViewFlightStatus } from "./visibility/flightStatusVisibility.js";
+import { BuyTicketButton } from "./BuyTicketButton.js";
+import { RegistrationButton } from "./RegistrationButton.js";
+import { FlightStatusButton } from "./FlightStatusButton.js";
+import { ShareButton } from "./ShareButton.js";
+import { PrintButton } from "./PrintButton.js";
+
+export interface FlightActionsProps {
+ flight: ISimpleFlight;
+ locale: string;
+ showStatus?: boolean;
+ showPrint?: boolean;
+ showShare?: boolean;
+ showRegister?: boolean;
+ showBuy?: boolean;
+}
+
+export const FlightActions: FC = ({
+ flight,
+ locale,
+ showStatus = false,
+ showPrint = false,
+ showShare = true,
+ showRegister = true,
+ showBuy = true,
+}) => {
+ const { flightStatusAvailableFromHours, buyTicketMinHours, buyTicketMaxHours } = useAppSettings();
+ const now = new Date();
+
+ const canBuy = showBuy && canBuyTicket(flight, now, buyTicketMinHours, buyTicketMaxHours);
+ const canReg = showRegister && canRegister(flight, AIRLINES);
+ const canStatus =
+ showStatus &&
+ canViewFlightStatus(flight, now, flightStatusAvailableFromHours, AIRLINES_WITH_STATUS);
+
+ const shareUrl = typeof window !== "undefined" ? window.location.href : "";
+
+ return (
+
+ {showPrint &&
}
+ {showShare &&
}
+ {canBuy &&
}
+ {canReg &&
}
+ {canStatus &&
}
+
+ );
+};