Files
flights_web/docs/user-stories-5-flight-details.md
T
gnezim 71d0c983fd
CI / ci (push) Failing after 28s
Deploy / build-and-deploy (push) Failing after 5s
Fix API calls: bind fetch to globalThis, fix date format for calendar
Root cause of search not working: globalThis.fetch stored as a class
field loses its Window binding, causing 'Illegal invocation'. Fixed
with fetch.bind(globalThis).

Also fix calendar days endpoint date format from yyyyMMdd to
yyyy-MM-ddT00:00:00 matching Angular's ApiFormatterService.
2026-04-15 22:32:51 +03:00

35 KiB
Raw Blame History

Пользовательские истории: Детали полёта и расширенная информация

US-40: Услуги на борту

Цель: Пользователь видит набор услуг, предоставляемых на конкретном рейсе, чтобы оценить комфорт перед поездкой.

Путь клиента:

  1. Переход к странице деталей — пользователь открывает страницу деталей рейса из онлайн-табло или расписания.
  2. Прокрутка к секции услуг — в блоке деталей отображается секция flight-details-services с заголовком «Услуги» (SHARED.SERVICE) и иконкой раздела.
  3. Отображение списка услуг — каждая услуга из массива onBoardServices рендерится компонентом flight-details-icon с иконкой (getServiceIconSrc) и названием (service.title).
  4. Переход по ссылке услуги — у каждой услуги есть ссылка (getServiceUrl(service.id)), ведущая на описание услуги на сайте авиакомпании.
  5. Пустой список — если у рейса нет услуг в массиве, секция не отображает отдельные элементы, повторяя поведение Angular-шаблона с *ngFor.

Критерии приёмки:

  • Секция «Услуги» отображается с иконкой и заголовком из словаря SHARED.SERVICE.
  • Каждая услуга показывает иконку и название из модели рейса.
  • Клик по иконке услуги открывает ссылку с описанием.
  • Список строится из onBoardServices, без подстановки данных вне модели.
  • При отсутствии услуг секция корректно скрывает элементы списка.

US-41: Расписание полёта и время

Цель: Пользователь видит расписанное время вылета и прибытия, продолжительность полёта и дни выполнения, чтобы понимать регулярность рейса.

Путь клиента:

  1. Открытие блока «Расписание рейса» — компонент flight-schedule выводит аккордеон с заголовком SHARED.SCHEDULE-FLIGHT.
  2. Краткая информация в шапке — в свернутом виде показан заголовок; при раскрытии отображаются блоки времени (time-group-legacy) вылета и прибытия по расписанию с оффсетом пояса.
  3. Продолжительность полёта — отдельным свойством выводится SHARED.PATH-TIME с компонентом duration, привязанным к flight.duration.
  4. Дни выполнения — в теле аккордеона расположен блок SHARED.DAYS-EXECUTE-FLIGHT с семью днями недели; активные дни определяются методом isDayActive('1'..'7').
  5. Примечание о часовом поясе — под списком дней отображается note с текстом SHARED.NOTE-TIME-SCHEDULE, где подставлены реальные даты.

Критерии приёмки:

  • Секция «Расписание рейса» реализована в виде аккордеона с одним открытым элементом.
  • Отображаются запланированное время вылета и прибытия с учётом часового пояса.
  • Показывается длительность полёта через компонент duration.
  • Дни недели выводятся с классом inactive для неактивных дней.
  • Под днями показывается нота с замещёнными датами.

US-47: Страница деталей полёта

Цель: Пользователь попадает на страницу деталей конкретного рейса и видит цельный макет со всеми подсекциями информации о полёте.

Путь клиента:

  1. Переход с карточки рейса — клик по рейсу в расписании или онлайн-табло вызывает роутинг на flight-details-page (или schedule-flight-details-page).
  2. Загрузка данных — страница использует dataSource со свойствами flight, flightLegacy, flightsLegacy, loading; пока loading истинно, показывается индикатор загрузки.
  3. Мета-теги и SEO — рендерится компонент *-flight-details-meta-tags, формирующий JsonLD/OpenGraph для страницы.
  4. Заголовок и обёрткаdetails-view (online-board или schedule вариант) принимает проекцию title и выводит соответствующий flight-details-title.
  5. Секции деталей — внутри обёртки последовательно отображаются заголовок (header), статус маршрута, полное расписание маршрута (flight-details-full-route), расписание (flight-schedule), воздушное судно, услуги и другие секции.
  6. Индикатор смены дня — в компонентах времени (time-group, time-group-legacy) при пересечении полуночи рендерится day-change-square (или day-change-square-legacy) с pTooltip, показывающим дату смены через пайп dayChange (позиция top, стиль afl-tooltip).
  7. Обработка событий — страница подписана на open (навигация по сегментам) и dateChange (смена даты в онлайн-табло) через обработчики компонента.

Критерии приёмки:

  • При переходе на URL рейса страница рендерится c нужным dataSource.
  • Пока данные не загружены, пользователю показан индикатор загрузки.
  • Рендерится компонент мета-тегов для SEO.
  • В макет проецируются заголовок и все секции деталей, предусмотренные Angular-шаблоном.
  • Индикатор смены дня (day-change-square) сопровождается pTooltip с датой пересечения полуночи.
  • События open и dateChange корректно пробрасываются в обработчики страницы.

US-48: Основная информация о полёте

Цель: Пользователь видит «шапку» деталей: бейдж рейса, быстрые действия, события рейса и время последнего обновления.

Путь клиента:

  1. Отображение бейджа — для онлайн-табло board-details-header выводит details-header-badge с флагами round: false, large: true, без статуса; для расписания schedule-details-header использует details-header-badges для нескольких сегментов.
  2. Блок действий с полётом — рядом с бейджем рендерится flight-actions с соответствующим набором кнопок (в расписании скрыта регистрация, в онлайн-табло показан статус).
  3. События рейса — в онлайн-табло отображается flight-events с направлением column-mobile, описанием, флагами changeRoute и reroute.
  4. Время последнего обновления — в правой части (или внизу) рендерится last-update для рейса c соответствующим viewType.
  5. Ветка connecting-рейсов — в schedule-details-header при isConnecting бейджи показываются с круглыми иконками и статусом, а кнопка статуса в flight-actions отключается.

Критерии приёмки:

  • Бейдж рейса отображается в соответствии с viewType (онлайн-табло или расписание).
  • Кнопки действий конфигурируются по правилам: в расписании без регистрации, в онлайн-табло без отдельной детализации.
  • В онлайн-табло показан компонент событий рейса.
  • Показан блок last-update с временем последнего обновления.
  • Для connecting-рейсов в расписании используется набор бейджей с флагом canShowStatus.

US-49: Статус и детали статуса

Цель: Пользователь видит текущий статус рейса на линии маршрута и связанную с ним информацию о времени в полёте.

Путь клиента:

  1. Линия маршрута — компонент route-status получает leg и при наличии рейса рисует полосу (status--line) с иконкой статуса (flight-status-icon).
  2. Статус в полёте — при inFlight ширина индикатора равна leg.flightPercent, а текст статуса берётся из словаря FLIGHT-STATUSES.* и позиционируется в соответствии с классами inFlightStatusClasses.
  3. Статус завершённого рейса — при finished текст статуса выравнивается по правому краю с классом status--text-right.
  4. Статус начинающегося рейса — при starting текст статуса выравнивается по левому краю с классом status--text-left.
  5. Время в полёте и оставшееся время — при inFlight под линией показаны SHARED.TRAVEL-TIME (с leg.duration | duration) и SHARED.TIME-LEFT (с remainingFlightDuration | duration), если значения доступны.
  6. Мультисегментность — для мультилег-рейсов иконка статуса берётся из текущего leg.status, иначе из flight.leg.status.

Критерии приёмки:

  • Компонент route-status рендерится только при наличии leg.
  • Статус локализуется через ключ FLIGHT-STATUSES.<status>.
  • Для летящего рейса ширина индикатора соответствует leg.flightPercent.
  • Тексты времени показываются только при доступных duration и remainingFlightDuration.
  • В мультилег-режиме используется статус текущего сегмента.

US-50: Информация о воздушном судне

Цель: Пользователь видит тип самолёта и его характеристики для онлайн-табло, а также ссылку на страницу модели.

Путь клиента:

  1. Блок «Самолёт» — компонент flight-details-airplane рендерится внутри flight-props с иконкой компании и свойством SHARED.PLANE.
  2. Ссылка на тип самолётаtitle отображается как ссылка getPlaneLink(), открывающаяся в новой вкладке.
  3. Название самолёта — в режиме ViewType.Onlineboard при наличии aircraft.name показывается свойство AIRPLANE.NAME.
  4. Конфигурация мест — при наличии seats отображаются свойства «Всего мест», «Эконом», «Комфорт» и «Бизнес» (AIRPLANE.SEATS-TOTAL/ECONOMY/COMFORT/BUSINESS) только для ненулевых значений.
  5. Предыдущий рейс борта — при наличии previous выводится BOARD.PREVIOUS-FLIGHT: либо ссылка на детали предыдущего рейса (если showPrevious), либо просто номер рейса через пайп flightNumber.

Критерии приёмки:

  • Блок самолёта всегда показывает заголовок SHARED.PLANE и ссылку на тип.
  • Остальные поля (имя, места, предыдущий рейс) показываются только в ViewType.Onlineboard и только при наличии данных.
  • Места выводятся по классам: total, economy, comfort, business.
  • Предыдущий рейс отображается как ссылка только при showPrevious.
  • Отсутствующие данные не создают пустых строк.

US-51: Информация об авиакомпании

Цель: Пользователь видит визуальный логотип оператора рейса и, при необходимости, подпись «Авиакомпания».

Путь клиента:

  1. Отображение логотипа — компонент operator-logo рендерит блок company-logo с классами, вычисляемыми из оператора рейса (свойство classes). При код-шеринге компонент отображает фактического оператора рейса (actualOperator), а не маркетингового перевозчика.
  2. Подпись блока — при входном флаге caption сверху показывается описание SHARED.AVIACOMPANY.
  3. Подсказка оператора — блок логотипа содержит атрибут pTooltip с названием operatingBy, что при наведении показывает человеко-читаемое название авиакомпании.
  4. Атрибут для тестирования — у элемента задан data-testid="flight-company-logo" для автотестов и селекторов.
  5. Встраивание в заголовокoperator-logo используется в заголовках деталей, карточках списков и бейджах, всегда через CSS-спрайт/фоновую картинку, без <img>-элемента.

Критерии приёмки:

  • Логотип выводится только через CSS-классы company-logo + оператор.
  • Подпись SHARED.AVIACOMPANY показывается только при активном флаге caption.
  • На наведении отображается тултип с именем оператора.
  • У элемента присутствует data-testid="flight-company-logo".
  • При код-шеринге показывается логотип фактического оператора, а не маркетингового перевозчика.
  • Компонент не создаёт лишнего контента внутри company-logo.

US-52: Информация об аэропортах

Цель: Пользователь видит город, терминал и, при изменении, старое название/терминал для станции вылета и прибытия.

Путь клиента:

  1. Отображение города — компонент station выводит название города через text с параметрами выравнивания, размера и жирности (cityStyles) и включённым ellipsis.
  2. Тултип города — при наведении на название показывается полный city через tooltip.
  3. Ссылка на терминал — под городом выводится terminal-link, связанный с station, что даёт пользователю информацию о номере терминала. Элемент имеет pTooltip с полным названием аэропорта и терминалом в формате скобок (station | airportTerminal: 'brackets', позиция top, стиль afl-tooltip).
  4. Отображение старого терминала — при наличии oldStation показывается второй terminal-link с флагом oldValue, визуально отмечающим предыдущий терминал.
  5. Отображение старого города — при shouldShowOldCity выводится text с oldCity красного цвета и размера 12, сигнализируя пользователю об изменении станции.
  6. Использование в заголовках и списках — компонент переиспользуется в бейджах деталей, списках рейсов и секциях маршрута, сохраняя одинаковое поведение.

Критерии приёмки:

  • Название города отображается с усечением и тултипом.
  • Терминал выводится через компонент terminal-link для текущей станции; элемент имеет pTooltip с аэропортом и терминалом в формате скобок.
  • При смене станции показывается второй terminal-link для старого значения.
  • При смене города показывается старое название красным цветом.
  • Компонент корректно переиспользуется во всех местах, где он встраивается в Angular-приложении.

US-53: Дни работы рейса

Цель: Пользователь видит, в какие дни недели выполняется рейс, по данным из модели.

Путь клиента:

  1. Блок «Дни выполнения» — внутри аккордеона flight-schedule рендерится секция с заголовком SHARED.DAYS-EXECUTE-FLIGHT.
  2. Семь дней недели — в контейнере days выводятся семь элементов с ключами DAYS.1..DAYS.7 (пн–вс согласно локализации).
  3. Активность дня — каждому элементу присваивается класс inactive, если isDayActive('<n>') возвращает false.
  4. Примечание о времени — под днями выводится компонент note с текстом SHARED.NOTE-TIME-SCHEDULE, где replaceDates подставляет реальные диапазоны дат.
  5. Отсутствие собственных иконок — секция выводит только текстовые ярлыки дней без дополнительных иконок или календаря.

Критерии приёмки:

  • Секция отображает ровно семь дней недели.
  • Неактивные дни получают CSS-класс inactive.
  • Активность определяется через метод isDayActive(n).
  • Под списком дней выводится примечание с подстановкой дат.
  • Секция присутствует только внутри flight-schedule и не дублируется в других блоках.

US-54: Действия с полётом

Цель: Пользователь может воспользоваться набором действий над рейсом (печать, поделиться, купить, регистрация, статус, подробнее), соответствующих контексту страницы.

Путь клиента:

  1. Рендер flight-actions — компонент принимает флаги print, share, buy, register, status, details, wide и рейс flight, а также viewType.
  2. Кнопка печати — при print отображается print-button с данными рейса для вывода страницы на печать. Кнопка имеет pTooltip с текстом SHARED.FLIGHT-INFO + номер рейса (позиция top, стиль afl-tooltip tooltip--one-line).
  3. Кнопка «Поделиться» — при share отображается share-button, открывающий share-panel со ссылкой getDetailsAbsoluteUrl(flight, viewType) (US-62).
  4. Кнопка покупки — при buy && canBuy отображается buy-ticket-button, использующий flight как direct для передачи параметров.
  5. Кнопка регистрации — при register && canRegister отображается registration-button с рейсом, доступный только для поддерживаемых рейсов.
  6. Кнопки статуса и деталей — при status && canViewStatus выводится flight-status-button с pTooltip SHARED.DETAILS-TOOLTIP (позиция top, стиль afl-tooltip); при detailsflight-details-button, эмитирующий событие toDetails через обработчик handleToDetailsEvent.

Критерии приёмки:

  • Кнопки buy, register, status рендерятся при истинности входного флага И соответствующего can*-условия (canBuy, canRegister, canViewStatus). Кнопки print, share, details зависят только от входного флага.
  • share-button открывает панель шаринга с абсолютной ссылкой на детали.
  • print-button сопровождается pTooltip с текстом SHARED.FLIGHT-INFO + номер рейса.
  • flight-status-button сопровождается pTooltip SHARED.DETAILS-TOOLTIP.
  • buy-ticket-button получает flight как direct.
  • flight-details-button эмитит событие toDetails, обработанное страницей.
  • При флаге wide добавляется разделитель k-space перед блоком покупки.

US-55: Схема маршрута / полная карточка маршрута

Цель: Пользователь видит полную визуализацию маршрута рейса с таймлайном сегментов.

Путь клиента:

  1. Обёртка секции — компонент flight-details-full-route рендерит элемент section.frame, группирующий содержимое блока маршрута.
  2. Таймлайн — внутри section.frame выводится компонент timeline с массивом legs, показывающий последовательность сегментов рейса. При смене станции в сегменте рендерится station-change с pTooltip, показывающим название города (from?.latest.city, позиция top, стиль afl-tooltip).
  3. Возможность изменения сегментов — флаг canChange у таймлайна равен viewType === ViewType.Onlineboard, включая интерактивность только на онлайн-табло.
  4. Отключённый flight-brief — в шаблоне присутствует закомментированный через *ngIf="false" компонент flight-brief (отключён по задаче 8072); он не отображается.
  5. Использование на обеих страницах — компонент применяется и в онлайн-табло, и в расписании с разницей только в viewType.

Критерии приёмки:

  • Секция flight-details-full-route всегда содержит один timeline c переданным legs.
  • Флаг canChange таймлайна зависит от viewType.
  • Компонент flight-brief остаётся отключённым (*ngIf="false").
  • Секция обёрнута в section.frame без дополнительных блоков.
  • На расписании таймлайн рендерится в неинтерактивном режиме.

US-56: Информация о пересадках

Цель: Пользователь видит информацию о пересадке или промежуточной посадке между сегментами маршрута.

Путь клиента:

  1. Условие показа — компонент transfer отображается только при наличии transfer.type (MultiLeg или Connecting).
  2. Иконка типа пересадки — по типу выбирается SVG: intermediate-landing для MultiLeg или flight-transfer для Connecting.
  3. Название типа — выводится через пайп transferType, передающий [transfer] и transfer.type.
  4. Длительность пересадки — рядом с иконкой времени (time-orange.svg) рендерится transfer-time, получающий arrival, departure и тип (actual для онлайн-табло, scheduled для расписания).
  5. Времена прибытия и вылета — блок transfer-times показывает два time-group-legacy (прилёт и вылет) через тире.
  6. Станция пересадки — компонент transfer-section рендерит детали станции пересадки, скрывая блок вылета при stationChange === 'noChange'.

Критерии приёмки:

  • Компонент не рендерит содержимое, если transfer.type отсутствует.
  • Для каждого типа пересадки используется соответствующая SVG-иконка.
  • Длительность пересадки вычисляется компонентом transfer-time с правильным режимом по viewType.
  • Отображаются два времени (прилёт и вылет) через разделитель « — ».
  • При stationChange === 'noChange' блок вылета в transfer-section скрывается.

US-62: Поделиться

Цель: Пользователь может поделиться ссылкой на конкретный рейс, отправив её через панель шаринга.

Путь клиента:

  1. Кнопка «Поделиться» — компонент share-button отображает круглую прозрачную кнопку с иконкой svg--share и локализованной меткой BOARD.SHARE.
  2. Тултип — при наведении над кнопкой показывается подсказка BOARD.SHARE сверху.
  3. Открытие панели — клик вызывает метод share($event), который переключает share-panel (togglePanel) передавая ссылку рейса.
  4. Формирование ссылкиgetFlightLink возвращает navigate.getDetailsAbsoluteUrl(flight, viewType) при наличии рейса, иначе — текущий window.location.href.
  5. Использование во flight-actions — кнопка встраивается только если задан флаг share, всегда получает flight и viewType.

Критерии приёмки:

  • Кнопка рендерится с тултипом и меткой BOARD.SHARE.
  • Клик открывает share-panel с актуальной ссылкой рейса.
  • Ссылка формируется через RouterHandlerService.getDetailsAbsoluteUrl.
  • Если рейс не передан, используется текущий URL окна.
  • Компонент встраивается через flight-actions по флагу share.

US-63: Отсутствие ошибок на странице деталей

Цель: Страница деталей стабильно загружается и рендерится без ошибок во всех режимах (online-board, schedule, connecting).

Путь клиента:

  1. Инициализация страницыflight-details-page (или schedule-flight-details-page) подписывается на dataSource и корректно обрабатывает начальное состояние loading: true.
  2. Отсутствующие поля — компоненты flight-details-airplane, operator-logo, transfer проверяют наличие данных (*ngIf) перед рендером, что исключает ошибки при отсутствии значений.
  3. Условные ветки статусаroute-status рендерится только при наличии leg и выбирает ветку (inFlight/finished/starting) без падений.
  4. Действия — кнопки buy, register, status во flight-actions активируются при соответствующих can*-флагах (canBuy, canRegister, canViewStatus), предотвращая вызов недоступных действий.
  5. События страницы — обработчики handleOpenEvent, handleDateChange, handleToDetailsEvent получают события без выбрасывания ошибок.
  6. Верификация через автотесты — существующие *.spec.ts для route-status, flight-actions, share-button, print-button, operator-logo подтверждают стабильность компонентов.

Критерии приёмки:

  • При загрузке страницы деталей не возникает ошибок в консоли.
  • Все условные блоки корректно скрываются при отсутствии данных.
  • Обработчики событий страницы отрабатывают без исключений.
  • Действия buy, register, status выполняются только при активных canBuy, canRegister, canViewStatus-флагах.
  • Unit-тесты ключевых компонентов проходят.

US-64: Целостность данных

Цель: Данные, отображаемые на странице деталей, соответствуют модели рейса, без выдумывания значений и без рассинхронизации между секциями.

Путь клиента:

  1. Единый источник данных — страница получает flight, flightLegacy, flightsLegacy из dataSource и передаёт их в дочерние компоненты без трансформаций, несущих потерю данных.
  2. Сегменты маршрутаflight-details-full-route использует legs напрямую; route-status работает с конкретным leg, сохраняя согласованность статуса и процента прогресса.
  3. Расписание и дниflight-schedule отображает flight.duration, departureLeg.departure.times.scheduled, arrivalLeg.arrival.times.scheduled и массив активных дней через isDayActive.
  4. Самолёт и услугиflight-details-airplane использует поля aircraft.name, seats, previous; flight-details-services перебирает onBoardServices — без фолбэков на «типовые» значения.
  5. Пересадкиtransfer читает transfer.type, arrival, departure, stationChange и показывает ровно ту станцию и время, которые присутствуют в данных.
  6. Кнопки действийflight-actions использует flight и предикаты canBuy, canRegister, canViewStatus, чтобы не показывать действия, недоступные для данной записи.

Критерии приёмки:

  • Все отображаемые поля читаются непосредственно из модели рейса.
  • Ни одна секция не подставляет значения «по умолчанию» при отсутствии данных.
  • Секции маршрута, расписания и статуса согласованы по сегментам legs.
  • Услуги, места и предыдущий рейс отображаются только при наличии соответствующих полей.
  • Кнопки действий buy, register, status отражают реальные возможности рейса через canBuy, canRegister, canViewStatus-флаги.