# Пользовательские истории: Результаты расписания и детали полётов

## US-35: Страница результатов расписания

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

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

1. **Переход из формы поиска** — после отправки формы пользователь попадает на `schedule-search-view` с параметрами маршрута и диапазоном недели.
2. **Загрузка данных** — пока идут запросы к API, показывается индикатор загрузки (`loading`).
3. **Отображение заголовка результатов** — `schedule-search-result-header` показывает маршрут и элементы сортировки.
4. **Вкладки недель** — под заголовком расположен `week-tabs` (понедельник–воскресенье) с активной текущей неделей.
5. **Список дней** — `schedule-days` рендерит аккордеон по дням недели; по умолчанию раскрыт день `state.selectedDate`, иначе сегодня, иначе первый непустой.
6. **Карточки рейсов** — внутри раскрытого дня `list-scheduled-flight` показывает рейсы, первый из которых развёрнут по умолчанию.
7. **Переключение направления** — при наличии обратного рейса пользователь может переключать `OUTBOUND`/`INBOUND` через контрол `schedule-direction-switch`. Кнопки-иконки самолётов сопровождаются `pTooltip`: `SHARED.DIRECT_FLIGHTS` для исходящего направления и `SHARED.RETURN_FLIGHTS` для обратного (позиция `top`, стиль `afl-tooltip`).

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

- ✅ URL содержит параметры маршрута и диапазона дат недели
- ✅ Заголовок, week-tabs, schedule-days и карточки рейсов отрисованы
- ✅ Первый день с рейсами раскрыт автоматически
- ✅ При пустом расписании показывается `page-empty-list`
- ✅ Переключение OUTBOUND/INBOUND меняет отображаемый набор рейсов; кнопки направления имеют `pTooltip` (`SHARED.DIRECT_FLIGHTS` / `SHARED.RETURN_FLIGHTS`)
- ✅ Макет адаптивен под мобильные экраны

---

## US-36: Переключение между днями в расписании

**Цель:** Пользователь выбирает конкретный день недели в аккордеоне `schedule-days` и видит рейсы только за этот день.

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

1. **Просмотр списка дней** — `schedule-days` отображает по одному `p-accordionTab` на каждый день текущей недели.
2. **Автораскрытие дня** — при загрузке метод `expandDefaultGroup` раскрывает день из `state.selectedDate`, иначе сегодня, иначе первый с рейсами.
3. **Клик по заголовку дня** — пользователь кликает на свёрнутый `schedule-search-result-day`, срабатывает `(onOpen)`.
4. **Загрузка содержимого вкладки** — `list-scheduled-flight` монтируется только при `item.expanded && item.flights.length`.
5. **Автоскролл к раскрытому дню** — используется `scrollTo` с задержкой `getScrollDelay` (500 мс на каждые 100 рейсов).
6. **Развёртывание первого рейса** — `expandDefaultFlight` автоматически раскрывает первый рейс в открытой группе.
7. **Пустой день** — если в дне нет рейсов, показывается `page-empty-list` вместо списка.

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

- ✅ Каждый день недели — отдельная вкладка аккордеона
- ✅ День по умолчанию раскрывается согласно приоритету: selectedDate → сегодня → первый непустой
- ✅ При открытии вкладки первый рейс раскрывается автоматически
- ✅ Срабатывает плавная прокрутка к раскрытому дню
- ✅ Пустые дни показывают компонент заглушки

---

## US-37: Навигация между неделями (week-tabs)

**Цель:** Пользователь переключает отображаемую неделю расписания через карусель вкладок `week-tabs`, соблюдая границы `scheduleMinDate`/`scheduleMaxDate`.

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

1. **Отображение карусели недель** — `week-tabs` наследуется от `DateSelectorBaseComponent` и генерирует вкладки по неделям (понедельник–воскресенье).
2. **Метки вкладок** — `getTabLabel` возвращает строку вида "дд.мм – дд.мм", `getTabMobileLabel` — полный формат для мобильных.
3. **Подсветка активной недели** — вкладка, содержащая `dateFrom` из `routeParams`, помечается как `active`.
4. **Ограничение диапазона** — вкладки вне `[minScheduleDate, maxScheduleDate]` получают `disabled: true` и не кликаются.
5. **Выбор недели** — пользователь кликает активную вкладку, эмитится `IWeekTabValue { dateFrom, dateTo }`.
6. **Обновление routeParams** — `handleDateChanged` вызывает `routeParamsChange` или `returnRouteParamsChange` в зависимости от направления.
7. **Перезагрузка дней** — `schedule-days` перерисовывается для новой недели, раскрывается день по умолчанию.

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

- ✅ Недели считаются от понедельника до воскресенья
- ✅ Активная неделя подсвечивается, недоступные — задизейблены
- ✅ Клик по вкладке обновляет `routeParams.dateFrom/dateTo`
- ✅ Границы ограничены `settings.scheduleMinDate` и `scheduleMaxDate`
- ✅ После смены недели обновляется список дней и рейсов

---

## US-38: Расширенный просмотр полёта в расписании

**Цель:** Пользователь раскрывает карточку конкретного рейса в `list-scheduled-flight`, чтобы увидеть детали маршрута без перехода на отдельную страницу.

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

1. **Список рейсов дня** — `list-scheduled-flight` получает `scheduleItem` и отсортированный `flights` по `sortBy`.
2. **Свёрнутые карточки** — все рейсы, кроме одного активного (`this.flight`), показаны в компактном виде.
3. **Клик по рейсу** — метод `toggle(index)` переключает `flight.expanded`.
4. **Сворачивание предыдущего** — если пользователь открывает другой рейс, предыдущий активный сворачивается автоматически (`this.flight.expanded = false`).
5. **Отрисовка тела по типу маршрута** — в зависимости от `RouteTypeLegacy` показывается прямое тело, `connecting-flight-body` или `multi-flight-body`.
6. **Ресортировка** — при изменении `sortBy` массив `flights` пересортировывается через `sortFlights`.
7. **Переход к деталям** — клик по ссылке/кнопке деталей эмитит `toDetails` с `IFlight`.

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

- ✅ В один момент раскрыта ровно одна карточка рейса
- ✅ Повторный клик по активной карточке сворачивает её
- ✅ Изменение sortBy перестраивает порядок карточек
- ✅ Тип тела выбирается по `RouteTypeLegacy` рейса
- ✅ Событие `toDetails` пробрасывается наверх с моделью рейса

---

## US-39: Сортировка результатов расписания

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

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

1. **Отображение контролов сортировки** — `schedule-search-result-header` показывает переключатели с текущим `sortBy` (по умолчанию `arrivalDown`).
2. **Выбор режима** — пользователь кликает по одному из шести режимов `ScheduleSortMode`: `departureUp`, `departureDown`, `timeUp`, `timeDown`, `arrivalUp`, `arrivalDown`.
3. **Эмиссия события** — `onSort(mode)` вызывает `sortByChange.emit(mode)`.
4. **Проброс в списки** — новое значение `sortBy` приходит в `list-scheduled-flight` каждого дня.
5. **Пересортировка** — `ngOnChanges` вызывает `sortFlights(this.flights, this.sortBy)` из `sort-flights-by.pipe`.
6. **Обновление UI** — карточки рейсов перерисовываются в новом порядке, активная карточка сохраняется по `hash`.

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

- ✅ Поддерживаются ровно 6 режимов из `ScheduleSortMode`
- ✅ По умолчанию используется `arrivalDown`
- ✅ Смена режима пересортировывает массив `flights` в каждом дне
- ✅ `trackBy` по `flight.hash` предотвращает полную перерисовку
- ✅ Контрол сортировки доступен на странице результатов

---

## US-42: Рейсы с пересадками и многосегментные

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

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

1. **Определение типа маршрута** — `list-scheduled-flight` выбирает тело по `RouteTypeLegacy`: `Direct`, `Connecting`, `MultiLeg`.
2. **Connecting flight** — `connecting-flight-body` в `ngOnChanges` вычисляет `departure = getFirstLeg(flight).departure`, `arrival = getLastLeg(flight).arrival`, и `legs = getLegs(flight)`.
3. **Стратегия сегментов** — `getFlightLegs` получает сегменты через `getStrategy(flight).getLegs(flight)`, что позволяет различать типы маршрутов.
4. **MultiLeg flight** — `multi-flight-body` принимает `MultiLegFlight` и отдаёт `flight.legs` напрямую через геттер `legs`.
5. **Отрисовка сегментов** — каждый `Leg` показывается с временем, аэропортом, номером рейса и длительностью пересадки между ними.
6. **Переход к деталям** — при клике любой `toDetails` эмитится наверх через `handleToDetailsEvent`.

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

- ✅ Рейсы с пересадкой используют `connecting-flight-body`
- ✅ Многосегментные рейсы используют `multi-flight-body`
- ✅ Отображаются все сегменты маршрута через `legs`
- ✅ Первая и последняя станция берутся из первого и последнего `Leg`
- ✅ Событие `toDetails` пробрасывается из вложенных тел наверх

---

## US-46: Кнопка "Назад" на странице деталей

**Цель:** На странице деталей полёта пользователь возвращается на предыдущую страницу результатов (расписание или онлайн-табло) по контекстной кнопке "Назад".

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

1. **Переход к деталям** — пользователь кликает рейс в `list-scheduled-flight` или на онлайн-табло, происходит навигация к странице деталей с переданным `viewType`.
2. **Отрисовка кнопки** — `details-back` получает `@Input() viewType: ViewType` и рендерит одну из двух кнопок.
3. **Ветка Schedule** — при `viewType === ViewType.Schedule` показывается кнопка с локализованной меткой `SHARED.BACK-SCHEDULE`.
4. **Ветка Onlineboard** — при `viewType === ViewType.Onlineboard` показывается кнопка с меткой `SHARED.BACK-BOARD`.
5. **Клик по кнопке** — вызывается `redirectSchedule()` или `redirectBoard()` соответственно.
6. **Навигация** — методы вызывают `RouterHandlerService.toSchedule()` / `toBoard()`, что возвращает пользователя на соответствующую страницу результатов.

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

- ✅ Кнопка показывает текст, соответствующий `viewType`
- ✅ Одновременно отрисовывается только одна кнопка (через `*ngIf`)
- ✅ Клик вызывает `RouterHandlerService.toSchedule()` или `toBoard()`
- ✅ Иконка `pi pi-arrow-left` отображается слева от метки
- ✅ Компонент использует `ChangeDetectionStrategy.OnPush`

---
