diff --git a/src/features/online-board/components/BoardDetailsHeader/LastUpdate.test.tsx b/src/features/online-board/components/BoardDetailsHeader/LastUpdate.test.tsx new file mode 100644 index 00000000..d6527618 --- /dev/null +++ b/src/features/online-board/components/BoardDetailsHeader/LastUpdate.test.tsx @@ -0,0 +1,90 @@ +// @vitest-environment jsdom +import { describe, it, expect, vi } from "vitest"; +import { render, screen } from "@testing-library/react"; +import { LastUpdate } from "./LastUpdate.js"; +import type { ISimpleFlight, IFlightLeg } from "../../types.js"; + +vi.mock("@/i18n/provider.js", () => ({ useTranslation: () => ({ t: (k: string) => k }) })); + +function makeLeg(updated: string): IFlightLeg { + return { + 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 IFlightLeg; +} + +function makeFlight(updated: string): ISimpleFlight { + return { + id: "X", + routeType: "Direct", + flyingTime: "1h", + status: "Scheduled", + flightId: { carrier: "SU", flightNumber: "0022", suffix: "", date: "20260417" }, + operatingBy: { carrier: "SU", flightNumber: "0022" }, + leg: makeLeg(updated), + } as ISimpleFlight; +} + +describe("LastUpdate", () => { + it("renders LAST-UPDATE label", () => { + render(); + expect(screen.getByText(/SHARED\.LAST-UPDATE/)).toBeTruthy(); + }); + + it("renders timestamp in HH:mm dd.MM.yyyy format", () => { + render(); + const ts = screen.getByTestId("last-update-timestamp"); + expect(ts.textContent).toMatch(/\d{2}:\d{2}\s+\d{2}\.\d{2}\.\d{4}/); + }); + + it("renders empty timestamp when leg.updated is empty", () => { + render(); + const ts = screen.getByTestId("last-update-timestamp"); + expect(ts.textContent?.trim()).toBe(""); + }); + + it("renders share button", () => { + render(); + expect(screen.getByTestId("share-button")).toBeTruthy(); + }); +}); diff --git a/src/features/online-board/components/BoardDetailsHeader/LastUpdate.tsx b/src/features/online-board/components/BoardDetailsHeader/LastUpdate.tsx new file mode 100644 index 00000000..e08b58ec --- /dev/null +++ b/src/features/online-board/components/BoardDetailsHeader/LastUpdate.tsx @@ -0,0 +1,40 @@ +import type { FC } from "react"; +import { parseISO, format, isValid } from "date-fns"; +import { useTranslation } from "@/i18n/provider.js"; +import type { ISimpleFlight } from "../../types.js"; +import { ShareButton } from "./ShareButton.js"; + +export interface LastUpdateProps { + flight: ISimpleFlight; + locale: string; +} + +function getUpdated(flight: ISimpleFlight): string | undefined { + const leg = flight.routeType === "Direct" ? flight.leg : flight.legs[0]; + return leg?.updated; +} + +function formatUpdated(updated: string | undefined): string { + if (!updated) return ""; + const d = parseISO(updated); + if (!isValid(d)) return ""; + return format(d, "HH:mm dd.MM.yyyy"); +} + +export const LastUpdate: FC = ({ flight, locale }) => { + const { t } = useTranslation(); + const timestamp = formatUpdated(getUpdated(flight)); + const shareUrl = typeof window !== "undefined" ? window.location.href : ""; + + return ( +
+ + + {t("SHARED.LAST-UPDATE")}: + +  {timestamp} + + +
+ ); +};