Files
flights_web/docs/user-stories-4-schedule-results.md
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

15 KiB
Raw Permalink Blame History

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

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. Обновление routeParamshandleDateChanged вызывает 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 flightconnecting-flight-body в ngOnChanges вычисляет departure = getFirstLeg(flight).departure, arrival = getLastLeg(flight).arrival, и legs = getLegs(flight).
  3. Стратегия сегментовgetFlightLegs получает сегменты через getStrategy(flight).getLegs(flight), что позволяет различать типы маршрутов.
  4. MultiLeg flightmulti-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