Files
flights_web/src/ui/flights/FlightCard.tsx
T
gnezim 7d8cb63600 Add flight display components and barrel exports
StationDisplay, TimeGroup, FlightStatus, DurationDisplay compose into
FlightCard; FlightList renders a list of cards with skeleton loading.
All components are props-driven with no data fetching.
2026-04-15 07:57:02 +03:00

95 lines
3.2 KiB
TypeScript

import type { FC } from "react";
import type { ISimpleFlight, IFlightLeg } from "@/features/online-board/types.js";
import { StationDisplay } from "./StationDisplay.js";
import { TimeGroup } from "./TimeGroup.js";
import { FlightStatus } from "./FlightStatus.js";
import { DurationDisplay } from "./DurationDisplay.js";
export interface FlightCardProps {
flight: ISimpleFlight;
}
/** Extract the primary leg from a flight (first leg for multi-leg) */
function getPrimaryLeg(flight: ISimpleFlight): IFlightLeg {
if (flight.routeType === "Direct") return flight.leg;
const first = flight.legs[0];
if (!first) throw new Error("Multi-leg flight has no legs");
return first;
}
/** Extract the final leg (last leg for multi-leg, same as primary for direct) */
function getFinalLeg(flight: ISimpleFlight): IFlightLeg {
if (flight.routeType === "Direct") return flight.leg;
const last = flight.legs[flight.legs.length - 1];
if (!last) throw new Error("Multi-leg flight has no legs");
return last;
}
/** Parse flyingTime "HH:mm" string to total minutes */
function flyingTimeToMinutes(flyingTime: string): number {
const parts = flyingTime.split(":");
if (parts.length !== 2) return 0;
const hours = parseInt(parts[0] ?? "0", 10);
const minutes = parseInt(parts[1] ?? "0", 10);
return hours * 60 + minutes;
}
/**
* A single flight row in search results.
*
* Composes StationDisplay + TimeGroup + FlightStatus + DurationDisplay.
*/
export const FlightCard: FC<FlightCardProps> = ({ flight }) => {
const departureLeg = getPrimaryLeg(flight);
const arrivalLeg = getFinalLeg(flight);
const depStation = departureLeg.departure;
const arrStation = arrivalLeg.arrival;
const depTimes = depStation.times;
const arrTimes = arrStation.times;
const flightNumber = `${flight.flightId.carrier} ${flight.flightId.flightNumber}`;
return (
<div className="flight-card" data-flight-id={flight.id}>
<div className="flight-card__number">{flightNumber}</div>
<div className="flight-card__route">
<div className="flight-card__departure">
<StationDisplay
airportCode={depStation.scheduled.airportCode}
airportName={depStation.scheduled.airport}
cityName={depStation.scheduled.city}
/>
<TimeGroup
scheduled={depTimes.scheduledDeparture.local}
actual={depTimes.actualBlockOff?.local}
dayChange={depTimes.actualBlockOff?.dayChange.value}
/>
</div>
<div className="flight-card__duration">
<DurationDisplay minutes={flyingTimeToMinutes(flight.flyingTime)} />
</div>
<div className="flight-card__arrival">
<StationDisplay
airportCode={arrStation.scheduled.airportCode}
airportName={arrStation.scheduled.airport}
cityName={arrStation.scheduled.city}
/>
<TimeGroup
scheduled={arrTimes.scheduledArrival.local}
actual={arrTimes.actualBlockOn?.local}
dayChange={arrTimes.actualBlockOn?.dayChange.value}
/>
</div>
</div>
<div className="flight-card__status">
<FlightStatus status={flight.status} />
</div>
</div>
);
};