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

## 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`); при `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`-флаги.

---
