# Пользовательские истории: Детали полёта и расширенная информация ## 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.`. - ✅ Для летящего рейса ширина индикатора соответствует `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-спрайт/фоновую картинку, без ``-элемента. **Критерии приёмки:** - ✅ Логотип выводится только через 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('')` возвращает `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`); при `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` сопровождается `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`-флаги. ---