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.
35 KiB
Пользовательские истории: Детали полёта и расширенная информация
US-40: Услуги на борту
Цель: Пользователь видит набор услуг, предоставляемых на конкретном рейсе, чтобы оценить комфорт перед поездкой.
Путь клиента:
- Переход к странице деталей — пользователь открывает страницу деталей рейса из онлайн-табло или расписания.
- Прокрутка к секции услуг — в блоке деталей отображается секция
flight-details-servicesс заголовком «Услуги» (SHARED.SERVICE) и иконкой раздела. - Отображение списка услуг — каждая услуга из массива
onBoardServicesрендерится компонентомflight-details-iconс иконкой (getServiceIconSrc) и названием (service.title). - Переход по ссылке услуги — у каждой услуги есть ссылка (
getServiceUrl(service.id)), ведущая на описание услуги на сайте авиакомпании. - Пустой список — если у рейса нет услуг в массиве, секция не отображает отдельные элементы, повторяя поведение Angular-шаблона с
*ngFor.
Критерии приёмки:
- ✅ Секция «Услуги» отображается с иконкой и заголовком из словаря
SHARED.SERVICE. - ✅ Каждая услуга показывает иконку и название из модели рейса.
- ✅ Клик по иконке услуги открывает ссылку с описанием.
- ✅ Список строится из
onBoardServices, без подстановки данных вне модели. - ✅ При отсутствии услуг секция корректно скрывает элементы списка.
US-41: Расписание полёта и время
Цель: Пользователь видит расписанное время вылета и прибытия, продолжительность полёта и дни выполнения, чтобы понимать регулярность рейса.
Путь клиента:
- Открытие блока «Расписание рейса» — компонент
flight-scheduleвыводит аккордеон с заголовкомSHARED.SCHEDULE-FLIGHT. - Краткая информация в шапке — в свернутом виде показан заголовок; при раскрытии отображаются блоки времени (
time-group-legacy) вылета и прибытия по расписанию с оффсетом пояса. - Продолжительность полёта — отдельным свойством выводится
SHARED.PATH-TIMEс компонентомduration, привязанным кflight.duration. - Дни выполнения — в теле аккордеона расположен блок
SHARED.DAYS-EXECUTE-FLIGHTс семью днями недели; активные дни определяются методомisDayActive('1'..'7'). - Примечание о часовом поясе — под списком дней отображается
noteс текстомSHARED.NOTE-TIME-SCHEDULE, где подставлены реальные даты.
Критерии приёмки:
- ✅ Секция «Расписание рейса» реализована в виде аккордеона с одним открытым элементом.
- ✅ Отображаются запланированное время вылета и прибытия с учётом часового пояса.
- ✅ Показывается длительность полёта через компонент
duration. - ✅ Дни недели выводятся с классом
inactiveдля неактивных дней. - ✅ Под днями показывается нота с замещёнными датами.
US-47: Страница деталей полёта
Цель: Пользователь попадает на страницу деталей конкретного рейса и видит цельный макет со всеми подсекциями информации о полёте.
Путь клиента:
- Переход с карточки рейса — клик по рейсу в расписании или онлайн-табло вызывает роутинг на
flight-details-page(илиschedule-flight-details-page). - Загрузка данных — страница использует
dataSourceсо свойствамиflight,flightLegacy,flightsLegacy,loading; покаloadingистинно, показывается индикатор загрузки. - Мета-теги и SEO — рендерится компонент
*-flight-details-meta-tags, формирующий JsonLD/OpenGraph для страницы. - Заголовок и обёртка —
details-view(online-board или schedule вариант) принимает проекциюtitleи выводит соответствующийflight-details-title. - Секции деталей — внутри обёртки последовательно отображаются заголовок (header), статус маршрута, полное расписание маршрута (
flight-details-full-route), расписание (flight-schedule), воздушное судно, услуги и другие секции. - Индикатор смены дня — в компонентах времени (
time-group,time-group-legacy) при пересечении полуночи рендеритсяday-change-square(илиday-change-square-legacy) сpTooltip, показывающим дату смены через пайпdayChange(позицияtop, стильafl-tooltip). - Обработка событий — страница подписана на
open(навигация по сегментам) иdateChange(смена даты в онлайн-табло) через обработчики компонента.
Критерии приёмки:
- ✅ При переходе на URL рейса страница рендерится c нужным
dataSource. - ✅ Пока данные не загружены, пользователю показан индикатор загрузки.
- ✅ Рендерится компонент мета-тегов для SEO.
- ✅ В макет проецируются заголовок и все секции деталей, предусмотренные Angular-шаблоном.
- ✅ Индикатор смены дня (
day-change-square) сопровождаетсяpTooltipс датой пересечения полуночи. - ✅ События
openиdateChangeкорректно пробрасываются в обработчики страницы.
US-48: Основная информация о полёте
Цель: Пользователь видит «шапку» деталей: бейдж рейса, быстрые действия, события рейса и время последнего обновления.
Путь клиента:
- Отображение бейджа — для онлайн-табло
board-details-headerвыводитdetails-header-badgeс флагамиround: false,large: true, без статуса; для расписанияschedule-details-headerиспользуетdetails-header-badgesдля нескольких сегментов. - Блок действий с полётом — рядом с бейджем рендерится
flight-actionsс соответствующим набором кнопок (в расписании скрыта регистрация, в онлайн-табло показан статус). - События рейса — в онлайн-табло отображается
flight-eventsс направлениемcolumn-mobile, описанием, флагамиchangeRouteиreroute. - Время последнего обновления — в правой части (или внизу) рендерится
last-updateдля рейса c соответствующимviewType. - Ветка connecting-рейсов — в
schedule-details-headerприisConnectingбейджи показываются с круглыми иконками и статусом, а кнопка статуса вflight-actionsотключается.
Критерии приёмки:
- ✅ Бейдж рейса отображается в соответствии с
viewType(онлайн-табло или расписание). - ✅ Кнопки действий конфигурируются по правилам: в расписании без регистрации, в онлайн-табло без отдельной детализации.
- ✅ В онлайн-табло показан компонент событий рейса.
- ✅ Показан блок
last-updateс временем последнего обновления. - ✅ Для connecting-рейсов в расписании используется набор бейджей с флагом
canShowStatus.
US-49: Статус и детали статуса
Цель: Пользователь видит текущий статус рейса на линии маршрута и связанную с ним информацию о времени в полёте.
Путь клиента:
- Линия маршрута — компонент
route-statusполучаетlegи при наличии рейса рисует полосу (status--line) с иконкой статуса (flight-status-icon). - Статус в полёте — при
inFlightширина индикатора равнаleg.flightPercent, а текст статуса берётся из словаряFLIGHT-STATUSES.*и позиционируется в соответствии с классамиinFlightStatusClasses. - Статус завершённого рейса — при
finishedтекст статуса выравнивается по правому краю с классомstatus--text-right. - Статус начинающегося рейса — при
startingтекст статуса выравнивается по левому краю с классомstatus--text-left. - Время в полёте и оставшееся время — при
inFlightпод линией показаныSHARED.TRAVEL-TIME(сleg.duration | duration) иSHARED.TIME-LEFT(сremainingFlightDuration | duration), если значения доступны. - Мультисегментность — для мультилег-рейсов иконка статуса берётся из текущего
leg.status, иначе изflight.leg.status.
Критерии приёмки:
- ✅ Компонент
route-statusрендерится только при наличииleg. - ✅ Статус локализуется через ключ
FLIGHT-STATUSES.<status>. - ✅ Для летящего рейса ширина индикатора соответствует
leg.flightPercent. - ✅ Тексты времени показываются только при доступных
durationиremainingFlightDuration. - ✅ В мультилег-режиме используется статус текущего сегмента.
US-50: Информация о воздушном судне
Цель: Пользователь видит тип самолёта и его характеристики для онлайн-табло, а также ссылку на страницу модели.
Путь клиента:
- Блок «Самолёт» — компонент
flight-details-airplaneрендерится внутриflight-propsс иконкой компании и свойствомSHARED.PLANE. - Ссылка на тип самолёта —
titleотображается как ссылкаgetPlaneLink(), открывающаяся в новой вкладке. - Название самолёта — в режиме
ViewType.Onlineboardпри наличииaircraft.nameпоказывается свойствоAIRPLANE.NAME. - Конфигурация мест — при наличии
seatsотображаются свойства «Всего мест», «Эконом», «Комфорт» и «Бизнес» (AIRPLANE.SEATS-TOTAL/ECONOMY/COMFORT/BUSINESS) только для ненулевых значений. - Предыдущий рейс борта — при наличии
previousвыводитсяBOARD.PREVIOUS-FLIGHT: либо ссылка на детали предыдущего рейса (еслиshowPrevious), либо просто номер рейса через пайпflightNumber.
Критерии приёмки:
- ✅ Блок самолёта всегда показывает заголовок
SHARED.PLANEи ссылку на тип. - ✅ Остальные поля (имя, места, предыдущий рейс) показываются только в
ViewType.Onlineboardи только при наличии данных. - ✅ Места выводятся по классам: total, economy, comfort, business.
- ✅ Предыдущий рейс отображается как ссылка только при
showPrevious. - ✅ Отсутствующие данные не создают пустых строк.
US-51: Информация об авиакомпании
Цель: Пользователь видит визуальный логотип оператора рейса и, при необходимости, подпись «Авиакомпания».
Путь клиента:
- Отображение логотипа — компонент
operator-logoрендерит блокcompany-logoс классами, вычисляемыми из оператора рейса (свойствоclasses). При код-шеринге компонент отображает фактического оператора рейса (actualOperator), а не маркетингового перевозчика. - Подпись блока — при входном флаге
captionсверху показывается описаниеSHARED.AVIACOMPANY. - Подсказка оператора — блок логотипа содержит атрибут
pTooltipс названиемoperatingBy, что при наведении показывает человеко-читаемое название авиакомпании. - Атрибут для тестирования — у элемента задан
data-testid="flight-company-logo"для автотестов и селекторов. - Встраивание в заголовок —
operator-logoиспользуется в заголовках деталей, карточках списков и бейджах, всегда через CSS-спрайт/фоновую картинку, без<img>-элемента.
Критерии приёмки:
- ✅ Логотип выводится только через CSS-классы
company-logo+ оператор. - ✅ Подпись
SHARED.AVIACOMPANYпоказывается только при активном флагеcaption. - ✅ На наведении отображается тултип с именем оператора.
- ✅ У элемента присутствует
data-testid="flight-company-logo". - ✅ При код-шеринге показывается логотип фактического оператора, а не маркетингового перевозчика.
- ✅ Компонент не создаёт лишнего контента внутри
company-logo.
US-52: Информация об аэропортах
Цель: Пользователь видит город, терминал и, при изменении, старое название/терминал для станции вылета и прибытия.
Путь клиента:
- Отображение города — компонент
stationвыводит название города черезtextс параметрами выравнивания, размера и жирности (cityStyles) и включённымellipsis. - Тултип города — при наведении на название показывается полный
cityчерезtooltip. - Ссылка на терминал — под городом выводится
terminal-link, связанный сstation, что даёт пользователю информацию о номере терминала. Элемент имеетpTooltipс полным названием аэропорта и терминалом в формате скобок (station | airportTerminal: 'brackets', позицияtop, стильafl-tooltip). - Отображение старого терминала — при наличии
oldStationпоказывается второйterminal-linkс флагомoldValue, визуально отмечающим предыдущий терминал. - Отображение старого города — при
shouldShowOldCityвыводитсяtextсoldCityкрасного цвета и размера 12, сигнализируя пользователю об изменении станции. - Использование в заголовках и списках — компонент переиспользуется в бейджах деталей, списках рейсов и секциях маршрута, сохраняя одинаковое поведение.
Критерии приёмки:
- ✅ Название города отображается с усечением и тултипом.
- ✅ Терминал выводится через компонент
terminal-linkдля текущей станции; элемент имеетpTooltipс аэропортом и терминалом в формате скобок. - ✅ При смене станции показывается второй
terminal-linkдля старого значения. - ✅ При смене города показывается старое название красным цветом.
- ✅ Компонент корректно переиспользуется во всех местах, где он встраивается в Angular-приложении.
US-53: Дни работы рейса
Цель: Пользователь видит, в какие дни недели выполняется рейс, по данным из модели.
Путь клиента:
- Блок «Дни выполнения» — внутри аккордеона
flight-scheduleрендерится секция с заголовкомSHARED.DAYS-EXECUTE-FLIGHT. - Семь дней недели — в контейнере
daysвыводятся семь элементов с ключамиDAYS.1..DAYS.7(пн–вс согласно локализации). - Активность дня — каждому элементу присваивается класс
inactive, еслиisDayActive('<n>')возвращаетfalse. - Примечание о времени — под днями выводится компонент
noteс текстомSHARED.NOTE-TIME-SCHEDULE, гдеreplaceDatesподставляет реальные диапазоны дат. - Отсутствие собственных иконок — секция выводит только текстовые ярлыки дней без дополнительных иконок или календаря.
Критерии приёмки:
- ✅ Секция отображает ровно семь дней недели.
- ✅ Неактивные дни получают CSS-класс
inactive. - ✅ Активность определяется через метод
isDayActive(n). - ✅ Под списком дней выводится примечание с подстановкой дат.
- ✅ Секция присутствует только внутри
flight-scheduleи не дублируется в других блоках.
US-54: Действия с полётом
Цель: Пользователь может воспользоваться набором действий над рейсом (печать, поделиться, купить, регистрация, статус, подробнее), соответствующих контексту страницы.
Путь клиента:
- Рендер
flight-actions— компонент принимает флагиprint,share,buy,register,status,details,wideи рейсflight, а такжеviewType. - Кнопка печати — при
printотображаетсяprint-buttonс данными рейса для вывода страницы на печать. Кнопка имеетpTooltipс текстомSHARED.FLIGHT-INFO+ номер рейса (позицияtop, стильafl-tooltip tooltip--one-line). - Кнопка «Поделиться» — при
shareотображаетсяshare-button, открывающийshare-panelсо ссылкойgetDetailsAbsoluteUrl(flight, viewType)(US-62). - Кнопка покупки — при
buy && canBuyотображаетсяbuy-ticket-button, использующийflightкакdirectдля передачи параметров. - Кнопка регистрации — при
register && canRegisterотображаетсяregistration-buttonс рейсом, доступный только для поддерживаемых рейсов. - Кнопки статуса и деталей — при
status && canViewStatusвыводитсяflight-status-buttonсpTooltipSHARED.DETAILS-TOOLTIP(позицияtop, стильafl-tooltip); приdetails—flight-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сопровождаетсяpTooltipSHARED.DETAILS-TOOLTIP. - ✅
buy-ticket-buttonполучаетflightкакdirect. - ✅
flight-details-buttonэмитит событиеtoDetails, обработанное страницей. - ✅ При флаге
wideдобавляется разделительk-spaceперед блоком покупки.
US-55: Схема маршрута / полная карточка маршрута
Цель: Пользователь видит полную визуализацию маршрута рейса с таймлайном сегментов.
Путь клиента:
- Обёртка секции — компонент
flight-details-full-routeрендерит элементsection.frame, группирующий содержимое блока маршрута. - Таймлайн — внутри
section.frameвыводится компонентtimelineс массивомlegs, показывающий последовательность сегментов рейса. При смене станции в сегменте рендеритсяstation-changeсpTooltip, показывающим название города (from?.latest.city, позицияtop, стильafl-tooltip). - Возможность изменения сегментов — флаг
canChangeу таймлайна равенviewType === ViewType.Onlineboard, включая интерактивность только на онлайн-табло. - Отключённый
flight-brief— в шаблоне присутствует закомментированный через*ngIf="false"компонентflight-brief(отключён по задаче 8072); он не отображается. - Использование на обеих страницах — компонент применяется и в онлайн-табло, и в расписании с разницей только в
viewType.
Критерии приёмки:
- ✅ Секция
flight-details-full-routeвсегда содержит одинtimelinec переданнымlegs. - ✅ Флаг
canChangeтаймлайна зависит отviewType. - ✅ Компонент
flight-briefостаётся отключённым (*ngIf="false"). - ✅ Секция обёрнута в
section.frameбез дополнительных блоков. - ✅ На расписании таймлайн рендерится в неинтерактивном режиме.
US-56: Информация о пересадках
Цель: Пользователь видит информацию о пересадке или промежуточной посадке между сегментами маршрута.
Путь клиента:
- Условие показа — компонент
transferотображается только при наличииtransfer.type(MultiLegилиConnecting). - Иконка типа пересадки — по типу выбирается SVG:
intermediate-landingдляMultiLegилиflight-transferдляConnecting. - Название типа — выводится через пайп
transferType, передающий[transfer]иtransfer.type. - Длительность пересадки — рядом с иконкой времени (
time-orange.svg) рендеритсяtransfer-time, получающийarrival,departureи тип (actualдля онлайн-табло,scheduledдля расписания). - Времена прибытия и вылета — блок
transfer-timesпоказывает дваtime-group-legacy(прилёт и вылет) через тире. - Станция пересадки — компонент
transfer-sectionрендерит детали станции пересадки, скрывая блок вылета приstationChange === 'noChange'.
Критерии приёмки:
- ✅ Компонент не рендерит содержимое, если
transfer.typeотсутствует. - ✅ Для каждого типа пересадки используется соответствующая SVG-иконка.
- ✅ Длительность пересадки вычисляется компонентом
transfer-timeс правильным режимом поviewType. - ✅ Отображаются два времени (прилёт и вылет) через разделитель « — ».
- ✅ При
stationChange === 'noChange'блок вылета вtransfer-sectionскрывается.
US-62: Поделиться
Цель: Пользователь может поделиться ссылкой на конкретный рейс, отправив её через панель шаринга.
Путь клиента:
- Кнопка «Поделиться» — компонент
share-buttonотображает круглую прозрачную кнопку с иконкойsvg--shareи локализованной меткойBOARD.SHARE. - Тултип — при наведении над кнопкой показывается подсказка
BOARD.SHAREсверху. - Открытие панели — клик вызывает метод
share($event), который переключаетshare-panel(togglePanel) передавая ссылку рейса. - Формирование ссылки —
getFlightLinkвозвращаетnavigate.getDetailsAbsoluteUrl(flight, viewType)при наличии рейса, иначе — текущийwindow.location.href. - Использование во
flight-actions— кнопка встраивается только если задан флагshare, всегда получаетflightиviewType.
Критерии приёмки:
- ✅ Кнопка рендерится с тултипом и меткой
BOARD.SHARE. - ✅ Клик открывает
share-panelс актуальной ссылкой рейса. - ✅ Ссылка формируется через
RouterHandlerService.getDetailsAbsoluteUrl. - ✅ Если рейс не передан, используется текущий URL окна.
- ✅ Компонент встраивается через
flight-actionsпо флагуshare.
US-63: Отсутствие ошибок на странице деталей
Цель: Страница деталей стабильно загружается и рендерится без ошибок во всех режимах (online-board, schedule, connecting).
Путь клиента:
- Инициализация страницы —
flight-details-page(илиschedule-flight-details-page) подписывается наdataSourceи корректно обрабатывает начальное состояниеloading: true. - Отсутствующие поля — компоненты
flight-details-airplane,operator-logo,transferпроверяют наличие данных (*ngIf) перед рендером, что исключает ошибки при отсутствии значений. - Условные ветки статуса —
route-statusрендерится только при наличииlegи выбирает ветку (inFlight/finished/starting) без падений. - Действия — кнопки
buy,register,statusвоflight-actionsактивируются при соответствующихcan*-флагах (canBuy,canRegister,canViewStatus), предотвращая вызов недоступных действий. - События страницы — обработчики
handleOpenEvent,handleDateChange,handleToDetailsEventполучают события без выбрасывания ошибок. - Верификация через автотесты — существующие
*.spec.tsдляroute-status,flight-actions,share-button,print-button,operator-logoподтверждают стабильность компонентов.
Критерии приёмки:
- ✅ При загрузке страницы деталей не возникает ошибок в консоли.
- ✅ Все условные блоки корректно скрываются при отсутствии данных.
- ✅ Обработчики событий страницы отрабатывают без исключений.
- ✅ Действия
buy,register,statusвыполняются только при активныхcanBuy,canRegister,canViewStatus-флагах. - ✅ Unit-тесты ключевых компонентов проходят.
US-64: Целостность данных
Цель: Данные, отображаемые на странице деталей, соответствуют модели рейса, без выдумывания значений и без рассинхронизации между секциями.
Путь клиента:
- Единый источник данных — страница получает
flight,flightLegacy,flightsLegacyизdataSourceи передаёт их в дочерние компоненты без трансформаций, несущих потерю данных. - Сегменты маршрута —
flight-details-full-routeиспользуетlegsнапрямую;route-statusработает с конкретнымleg, сохраняя согласованность статуса и процента прогресса. - Расписание и дни —
flight-scheduleотображаетflight.duration,departureLeg.departure.times.scheduled,arrivalLeg.arrival.times.scheduledи массив активных дней черезisDayActive. - Самолёт и услуги —
flight-details-airplaneиспользует поляaircraft.name,seats,previous;flight-details-servicesперебираетonBoardServices— без фолбэков на «типовые» значения. - Пересадки —
transferчитаетtransfer.type,arrival,departure,stationChangeи показывает ровно ту станцию и время, которые присутствуют в данных. - Кнопки действий —
flight-actionsиспользуетflightи предикатыcanBuy,canRegister,canViewStatus, чтобы не показывать действия, недоступные для данной записи.
Критерии приёмки:
- ✅ Все отображаемые поля читаются непосредственно из модели рейса.
- ✅ Ни одна секция не подставляет значения «по умолчанию» при отсутствии данных.
- ✅ Секции маршрута, расписания и статуса согласованы по сегментам
legs. - ✅ Услуги, места и предыдущий рейс отображаются только при наличии соответствующих полей.
- ✅ Кнопки действий
buy,register,statusотражают реальные возможности рейса черезcanBuy,canRegister,canViewStatus-флаги.