From 45a6cee9d826cd11a5bd3fd345c5b6008c9b6c6b Mon Sep 17 00:00:00 2001 From: gnezim Date: Thu, 16 Apr 2026 21:47:18 +0300 Subject: [PATCH] Add flight details accordion (B.1) design spec --- ...6-04-16-flight-details-accordion-design.md | 213 ++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-16-flight-details-accordion-design.md diff --git a/docs/superpowers/specs/2026-04-16-flight-details-accordion-design.md b/docs/superpowers/specs/2026-04-16-flight-details-accordion-design.md new file mode 100644 index 00000000..b9e038ee --- /dev/null +++ b/docs/superpowers/specs/2026-04-16-flight-details-accordion-design.md @@ -0,0 +1,213 @@ +# Flight Details Accordion (B.1) + +## Goal + +Add the per-leg details accordion to the React Online Board details page, matching the Angular app's six-panel structure: Registration, Boarding, Deboarding, Aircraft, Meal, On-board Services. + +## Scope + +This is sub-feature **B.1** of the Flight Details parity work. Other sub-features (mini flights list, day tabs, status header, back button, transfer timeline) are tracked separately. + +## Current State + +React `OnlineBoardDetailsPage` fetches flight details via `useFlightDetails` + live updates via `useLiveFlightDetails`, and renders per-leg station/time info. It does **not** render any of the expandable detail panels that Angular shows. + +## Approach + +Extend the React type system to match Angular's nested leg data (`transition.*`, `equipment.aircraft.*`, `equipment.meal[]`, `equipment.aircraft.actual.onBoardServices[]`), then build six focused panel components wrapped by an accordion container. Each panel is conditionally rendered based on data presence and view type. + +## Types Extension + +Extend `src/features/online-board/types.ts`: + +```typescript +export type FlightTransitionStatus = + | "Finished" | "Expected" | "InProgress" | "Specified" | "Scheduled"; + +export interface IFlightTransitionItem { + end: ITimesSet; + start: ITimesSet; + status: FlightTransitionStatus; + isActual: boolean; +} + +export interface IFlightTransitions { + registration?: IFlightTransitionItem; + boarding?: IFlightTransitionItem; + deboarding?: IFlightTransitionItem; +} + +export type MealType = "Economy" | "Comfort" | "Business" | "Special"; + +export interface IMealItem { + type: MealType; +} + +export interface IOnBoardService { + id: number; // 1-8 maps to specific service icons + title?: string; + url?: string; +} + +export interface IAircraftInfo { + title?: string; + onBoardServices?: IOnBoardService[]; +} + +export interface IEquipmentFull { + aircraft?: { + scheduled?: IAircraftInfo; + actual?: IAircraftInfo; + configuration?: string; + }; + meal?: IMealItem[]; +} +``` + +Extend `IFlightLeg`: + +```typescript +export interface IFlightLeg { + // ... existing fields ... + equipment: { name?: string; code?: string } & IEquipmentFull; + transition?: IFlightTransitions; +} +``` + +Existing `equipment: { name?, code? }` stays for current `FlightCard` usage. + +## Component Structure + +All new files in `src/features/online-board/components/details-panels/`: + +``` +details-panels/ +├── FlightDetailsAccordion.tsx # Container with PrimeReact Accordion +├── FlightDetailsAccordion.scss +├── RegistrationPanel.tsx # Check-in times/status +├── BoardingPanel.tsx # Boarding times/status +├── DeboardingPanel.tsx # Deboarding + gate/belt info +├── AircraftPanel.tsx # Aircraft type + configuration +├── MealPanel.tsx # Meal type icons (Econ/Comfort/Business) +├── ServicesPanel.tsx # On-board services icons +├── shared.ts # Shared helpers: formatTime, icon mappings +└── shared.scss # Shared panel styling +``` + +Container contract: + +```typescript +export interface FlightDetailsAccordionProps { + leg: IFlightLeg; + viewType: "Onlineboard" | "Schedule"; +} +``` + +The container computes visibility for each of the six panels, returns `null` when all are hidden, otherwise renders a `multiple`-mode PrimeReact Accordion with conditional tabs. + +Wiring in `OnlineBoardDetailsPage`: for each leg, render `` beneath the existing station/time block. + +## Visibility Rules + +**Transition panels** (Registration / Boarding / Deboarding): + +```typescript +function shouldShowTransition( + item: IFlightTransitionItem | undefined, + legStatus: FlightStatus, + viewType: "Onlineboard" | "Schedule" +): boolean { + if (viewType === "Schedule") return false; + if (legStatus === "Cancelled") return false; + if (!item) return false; + if (item.status === "Scheduled") return false; + return true; +} +``` + +**Aircraft panel**: show when `aircraft.actual?.title || aircraft.scheduled?.title || aircraft.configuration` exists. + +**Meal panel**: show when `equipment.meal?.length > 0`. + +**Services panel**: show when `equipment.aircraft?.actual?.onBoardServices?.length > 0`. + +## Panel Contents + +| Panel | Data source | Content | +|-------|-------------|---------| +| Registration | `leg.transition.registration` | Start time (local + UTC), End time, status badge | +| Boarding | `leg.transition.boarding` | Same structure as Registration | +| Deboarding | `leg.transition.deboarding` + `leg.arrival` | Times + status + arrival terminal/gate/bagBelt | +| Aircraft | `leg.equipment.aircraft` | `actual?.title ?? scheduled?.title`, configuration. If both differ, show both with labels. | +| Meal | `leg.equipment.meal[]` | Up to 3 icons (Economy, Comfort, Business) linking to aeroflot.ru info pages | +| Services | `leg.equipment.aircraft.actual.onBoardServices[]` | Service icons mapped by `id` (1-8) | + +**Aircraft edge case**: If `actual` exists but has empty title, fall back to `scheduled` (matches Angular's `flight-details-airplane` behavior). + +**Service icon mapping** (from Angular `flight-details-services.component.ts`): + +```typescript +const SERVICE_ICON_MAP: Record = { + 1: "shopping", + 2: "space", + 3: "taxi", + 4: "wifi", + 5: "gsm", + 6: "entertainment", + 7: "entertainment", + 8: "seat_reservation", +}; +``` + +Icons ported from `ClientApp/src/assets/img/service-and-food-icons/` to React's asset directory. + +**Meal links** (hardcoded aeroflot.ru URLs from Angular): +- Economy: `https://www.aeroflot.ru/ru-ru/information/onboard/dining?0000#meal-type_0` +- Comfort: `https://www.aeroflot.ru/ru-ru/information/onboard/dining?0000#meal-type_1` +- Business: `https://www.aeroflot.ru/ru-ru/information/onboard/dining?0000#meal-type_2` + +**Accordion behavior**: `` — multiple panels can be expanded simultaneously (matches Angular's ``). + +## Testing Plan + +**Unit tests** — one per component, co-located (`*.test.tsx`): + +- `FlightDetailsAccordion.test.tsx` — Visibility matrix (6 panels × relevant states), returns null when all hidden, multi-select accordion +- `RegistrationPanel.test.tsx` — Renders start/end times + status, handles missing `end` time gracefully +- `BoardingPanel.test.tsx` — Same pattern +- `DeboardingPanel.test.tsx` — Times + status + terminal/gate/bagBelt +- `AircraftPanel.test.tsx` — Actual vs scheduled fallback, configuration display, empty-title edge case +- `MealPanel.test.tsx` — Renders one icon per meal type, skips missing, correct hrefs +- `ServicesPanel.test.tsx` — Icon mapping for all IDs 1-8, link wrapping with `url`, unknown ID fallback + +**Integration** — extend `OnlineBoardDetailsPage.test.tsx`: + +- With a mock flight including `transition` + `equipment` fields, accordion renders +- Without those fields, accordion is not rendered +- `viewType="Schedule"` hides all three transition panels + +**No backend work**: panels consume existing `IFlightLeg` data from `useFlightDetails`. If real API omits `transition` / extended `equipment`, panels gracefully don't render. + +## i18n Keys + +Reuse Angular keys where possible. Required keys: + +- `DETAILS.REGISTRATION`, `DETAILS.BOARDING`, `DETAILS.DEBOARDING` +- `DETAILS.AIRCRAFT`, `DETAILS.MEAL`, `DETAILS.ON_BOARD_SERVICES` +- `DETAILS.SCHEDULED`, `DETAILS.ACTUAL` +- Meal type labels: `DETAILS.MEAL_ECONOMY`, `DETAILS.MEAL_COMFORT`, `DETAILS.MEAL_BUSINESS` + +Add to `ru` and `en` locale files at minimum (other languages can fall back to `en`). + +## Assets + +Copy SVG icons from `ClientApp/src/assets/img/service-and-food-icons/` to `src/assets/service-icons/` (or `public/assets/` depending on the project's existing static asset pattern). 8 service icons + potentially meal-type icons. + +## Out of Scope + +- Mini flights list sidebar (B.2) +- Day tabs (B.3) +- Status header with action buttons (B.4) +- Back button / flight schedule timeline (B.6) +- Transfer components / multi-leg route timeline (B.5) +- Changes to the REST API or SignalR integration