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.
15 KiB
Пользовательские истории: Результаты расписания и детали полётов
US-35: Страница результатов расписания
Цель: Пользователь видит сгруппированные по дням рейсы на выбранную неделю с возможностью переключаться между неделями и направлениями без возврата на форму поиска.
Путь клиента:
- Переход из формы поиска — после отправки формы пользователь попадает на
schedule-search-viewс параметрами маршрута и диапазоном недели. - Загрузка данных — пока идут запросы к API, показывается индикатор загрузки (
loading). - Отображение заголовка результатов —
schedule-search-result-headerпоказывает маршрут и элементы сортировки. - Вкладки недель — под заголовком расположен
week-tabs(понедельник–воскресенье) с активной текущей неделей. - Список дней —
schedule-daysрендерит аккордеон по дням недели; по умолчанию раскрыт деньstate.selectedDate, иначе сегодня, иначе первый непустой. - Карточки рейсов — внутри раскрытого дня
list-scheduled-flightпоказывает рейсы, первый из которых развёрнут по умолчанию. - Переключение направления — при наличии обратного рейса пользователь может переключать
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 и видит рейсы только за этот день.
Путь клиента:
- Просмотр списка дней —
schedule-daysотображает по одномуp-accordionTabна каждый день текущей недели. - Автораскрытие дня — при загрузке метод
expandDefaultGroupраскрывает день изstate.selectedDate, иначе сегодня, иначе первый с рейсами. - Клик по заголовку дня — пользователь кликает на свёрнутый
schedule-search-result-day, срабатывает(onOpen). - Загрузка содержимого вкладки —
list-scheduled-flightмонтируется только приitem.expanded && item.flights.length. - Автоскролл к раскрытому дню — используется
scrollToс задержкойgetScrollDelay(500 мс на каждые 100 рейсов). - Развёртывание первого рейса —
expandDefaultFlightавтоматически раскрывает первый рейс в открытой группе. - Пустой день — если в дне нет рейсов, показывается
page-empty-listвместо списка.
Критерии приёмки:
- ✅ Каждый день недели — отдельная вкладка аккордеона
- ✅ День по умолчанию раскрывается согласно приоритету: selectedDate → сегодня → первый непустой
- ✅ При открытии вкладки первый рейс раскрывается автоматически
- ✅ Срабатывает плавная прокрутка к раскрытому дню
- ✅ Пустые дни показывают компонент заглушки
US-37: Навигация между неделями (week-tabs)
Цель: Пользователь переключает отображаемую неделю расписания через карусель вкладок week-tabs, соблюдая границы scheduleMinDate/scheduleMaxDate.
Путь клиента:
- Отображение карусели недель —
week-tabsнаследуется отDateSelectorBaseComponentи генерирует вкладки по неделям (понедельник–воскресенье). - Метки вкладок —
getTabLabelвозвращает строку вида "дд.мм – дд.мм",getTabMobileLabel— полный формат для мобильных. - Подсветка активной недели — вкладка, содержащая
dateFromизrouteParams, помечается какactive. - Ограничение диапазона — вкладки вне
[minScheduleDate, maxScheduleDate]получаютdisabled: trueи не кликаются. - Выбор недели — пользователь кликает активную вкладку, эмитится
IWeekTabValue { dateFrom, dateTo }. - Обновление routeParams —
handleDateChangedвызываетrouteParamsChangeилиreturnRouteParamsChangeв зависимости от направления. - Перезагрузка дней —
schedule-daysперерисовывается для новой недели, раскрывается день по умолчанию.
Критерии приёмки:
- ✅ Недели считаются от понедельника до воскресенья
- ✅ Активная неделя подсвечивается, недоступные — задизейблены
- ✅ Клик по вкладке обновляет
routeParams.dateFrom/dateTo - ✅ Границы ограничены
settings.scheduleMinDateиscheduleMaxDate - ✅ После смены недели обновляется список дней и рейсов
US-38: Расширенный просмотр полёта в расписании
Цель: Пользователь раскрывает карточку конкретного рейса в list-scheduled-flight, чтобы увидеть детали маршрута без перехода на отдельную страницу.
Путь клиента:
- Список рейсов дня —
list-scheduled-flightполучаетscheduleItemи отсортированныйflightsпоsortBy. - Свёрнутые карточки — все рейсы, кроме одного активного (
this.flight), показаны в компактном виде. - Клик по рейсу — метод
toggle(index)переключаетflight.expanded. - Сворачивание предыдущего — если пользователь открывает другой рейс, предыдущий активный сворачивается автоматически (
this.flight.expanded = false). - Отрисовка тела по типу маршрута — в зависимости от
RouteTypeLegacyпоказывается прямое тело,connecting-flight-bodyилиmulti-flight-body. - Ресортировка — при изменении
sortByмассивflightsпересортировывается черезsortFlights. - Переход к деталям — клик по ссылке/кнопке деталей эмитит
toDetailsсIFlight.
Критерии приёмки:
- ✅ В один момент раскрыта ровно одна карточка рейса
- ✅ Повторный клик по активной карточке сворачивает её
- ✅ Изменение sortBy перестраивает порядок карточек
- ✅ Тип тела выбирается по
RouteTypeLegacyрейса - ✅ Событие
toDetailsпробрасывается наверх с моделью рейса
US-39: Сортировка результатов расписания
Цель: Пользователь сортирует рейсы внутри дня по времени вылета, длительности или времени прибытия в обоих направлениях.
Путь клиента:
- Отображение контролов сортировки —
schedule-search-result-headerпоказывает переключатели с текущимsortBy(по умолчаниюarrivalDown). - Выбор режима — пользователь кликает по одному из шести режимов
ScheduleSortMode:departureUp,departureDown,timeUp,timeDown,arrivalUp,arrivalDown. - Эмиссия события —
onSort(mode)вызываетsortByChange.emit(mode). - Проброс в списки — новое значение
sortByприходит вlist-scheduled-flightкаждого дня. - Пересортировка —
ngOnChangesвызываетsortFlights(this.flights, this.sortBy)изsort-flights-by.pipe. - Обновление UI — карточки рейсов перерисовываются в новом порядке, активная карточка сохраняется по
hash.
Критерии приёмки:
- ✅ Поддерживаются ровно 6 режимов из
ScheduleSortMode - ✅ По умолчанию используется
arrivalDown - ✅ Смена режима пересортировывает массив
flightsв каждом дне - ✅
trackByпоflight.hashпредотвращает полную перерисовку - ✅ Контрол сортировки доступен на странице результатов
US-42: Рейсы с пересадками и многосегментные
Цель: Пользователь корректно видит рейсы с одной или несколькими пересадками, включая все сегменты маршрута.
Путь клиента:
- Определение типа маршрута —
list-scheduled-flightвыбирает тело поRouteTypeLegacy:Direct,Connecting,MultiLeg. - Connecting flight —
connecting-flight-bodyвngOnChangesвычисляетdeparture = getFirstLeg(flight).departure,arrival = getLastLeg(flight).arrival, иlegs = getLegs(flight). - Стратегия сегментов —
getFlightLegsполучает сегменты черезgetStrategy(flight).getLegs(flight), что позволяет различать типы маршрутов. - MultiLeg flight —
multi-flight-bodyпринимаетMultiLegFlightи отдаётflight.legsнапрямую через геттерlegs. - Отрисовка сегментов — каждый
Legпоказывается с временем, аэропортом, номером рейса и длительностью пересадки между ними. - Переход к деталям — при клике любой
toDetailsэмитится наверх черезhandleToDetailsEvent.
Критерии приёмки:
- ✅ Рейсы с пересадкой используют
connecting-flight-body - ✅ Многосегментные рейсы используют
multi-flight-body - ✅ Отображаются все сегменты маршрута через
legs - ✅ Первая и последняя станция берутся из первого и последнего
Leg - ✅ Событие
toDetailsпробрасывается из вложенных тел наверх
US-46: Кнопка "Назад" на странице деталей
Цель: На странице деталей полёта пользователь возвращается на предыдущую страницу результатов (расписание или онлайн-табло) по контекстной кнопке "Назад".
Путь клиента:
- Переход к деталям — пользователь кликает рейс в
list-scheduled-flightили на онлайн-табло, происходит навигация к странице деталей с переданнымviewType. - Отрисовка кнопки —
details-backполучает@Input() viewType: ViewTypeи рендерит одну из двух кнопок. - Ветка Schedule — при
viewType === ViewType.Scheduleпоказывается кнопка с локализованной меткойSHARED.BACK-SCHEDULE. - Ветка Onlineboard — при
viewType === ViewType.Onlineboardпоказывается кнопка с меткойSHARED.BACK-BOARD. - Клик по кнопке — вызывается
redirectSchedule()илиredirectBoard()соответственно. - Навигация — методы вызывают
RouterHandlerService.toSchedule()/toBoard(), что возвращает пользователя на соответствующую страницу результатов.
Критерии приёмки:
- ✅ Кнопка показывает текст, соответствующий
viewType - ✅ Одновременно отрисовывается только одна кнопка (через
*ngIf) - ✅ Клик вызывает
RouterHandlerService.toSchedule()илиtoBoard() - ✅ Иконка
pi pi-arrow-leftотображается слева от метки - ✅ Компонент использует
ChangeDetectionStrategy.OnPush