Commit Graph

72 Commits

Author SHA1 Message Date
gnezim 62d3d68c1b ScheduleFilter: wire time range slider to schedule URL + seed from URL 2026-04-20 12:21:04 +03:00
gnezim 0c1701086d Upgrade Schedule prefill codes to CitySuggestion objects once dictionaries load
ScheduleStartPage previously stored the raw IATA code from the prefill
in departureAirport / arrivalAirport state, so PrimeReact's AutoComplete
would render 'MOW' (or 'SVO' before the prior commit) literally in the
input. Now, once dictionaries resolve, the effect replaces each string
slot with a { code, name } object so the autocomplete shows 'Москва'.

Mirrors Angular CityAutocomplete.writeValue → getCityOrAirport, which
upgrades the bound string to a CityModel for display while keeping the
code as the outbound form value.
2026-04-20 11:44:38 +03:00
gnezim af473f9877 Resolve popular-request airport codes to city codes before prefill
Popular-requests API returns mixed airport (SVO) and city (MOW) IATA
codes. Clicking a "Шереметьево → Санкт-Петербург" entry used to paste
SVO into the departure field, leaving a specific airport pinned even
though the visible label already resolves to the owning city name.

Both start pages now route request.departure/arrival through
getCityCodeByAirportCode(dictionaries, code), so the filter form seeds
with MOW instead of SVO (and falls back to the raw code when
dictionaries aren't loaded yet). buildOnlineBoardPrefillState takes
an optional dictionaries arg for the same reason.

ScheduleStartPage.test mocks @/shared/dictionaries/index.js to preserve
the existing assertions (which expect unresolved codes).
2026-04-20 11:37:27 +03:00
gnezim 706b8f444b Clear the last 19 lint warnings — make check now passes clean
- BuyTicketButton / FlightsMiniListItem: narrow firstLeg/lastLeg with
  explicit null guards (throw / return '').
- FlightSchedule.tsx: `match?.[1] ?? iso` for the regex capture.
- OnlineBoardSearchPage + schedule/api: `split('T')[0] ?? iso` for the
  date-prefix extraction.
- ServicesPanel: icon lookup uses a third '' fallback instead of `!`.
- buildCountryCityRows: explicit `break` if cities[i] is undefined.
- useAppSettings: `match?.[1]` null-check before parseInt.
- datetime/index.ts: guard bare HH:MM capture groups together.
- ScheduleDetailsCatchAllRoute: drop unused `t` + useTranslation import.
- ScheduleDetailsPage.tsx: prefix unused `getLegs` with underscore.
- 4 seo/json-ld tests: drop now-redundant eslint-disable comments.
- calendarRange.test + api.test: prefix unused helper names with `_`.

Warning count: 19 → 0. make check (typecheck + lint + test) exits 0.
2026-04-20 09:30:34 +03:00
gnezim 298f007463 Drop 11 non-null assertions in api.ts, DayGroupedFlightList, FlightCard
Regex capture groups and array boundary accesses replaced with nullish
fallbacks / explicit guards. Warning count: 41 → 30.
2026-04-20 09:19:14 +03:00
gnezim 1fc96b603e Drop 14 non-null assertions in ScheduleFlightBody + CityPickerPopup
- ScheduleFlightBody.tsx: hms regex capture uses `?? "0"` fallback;
  inline IIFEs expose last-leg and transfer-to-next in narrowed scope.
- CityPickerPopup.tsx: row.city1/city2/city1Airports are lifted into
  locals so the narrowing survives into JSX event handler closures.

Warning count: 55 → 41.
2026-04-20 08:33:33 +03:00
gnezim a982d9a669 Fix lint: route sessionStorage through shared storage module, drop dead imports
- storage.ts: add sessionStore wrapper (getRaw/setRaw/delete/clear) so
  transientPrefill + ScheduleStartPage tests don't trip the
  no-restricted-globals rule.
- transientPrefill.ts + ScheduleStartPage.test.tsx: use sessionStore.
- closestFlight.ts: hoist bracket-index key so no newline-before-[ ASI.
- Test files: hoist typeof import(...) into named type alias with
  type-only namespace import.
- Drop unused imports: FlightCard (Link, languageToLocale),
  OnlineBoardDetailsPage (operatingCarrier),
  ScheduleSearchPage (FlightList, inline import() types),
  PageLayout (FeedbackButton).
- Drop react-hooks/exhaustive-deps disable comments for a rule not
  registered in eslint.config.js.
2026-04-20 08:15:21 +03:00
gnezim 68e7b3e9ec Normalize 4/6/8px radii to vars.$border-radius (3px) across 5 SCSS files 2026-04-20 07:10:12 +03:00
gnezim ef845f587f Final token sweep: pastel blues, blues, greys to design tokens (8 files) 2026-04-20 06:14:55 +03:00
gnezim 94e7180a2f Use colors.$white token across schedule components (6 files) 2026-04-20 05:16:56 +03:00
gnezim d960e469ed Use colors.$white token for ScheduleFlightBody backgrounds/button text 2026-04-20 04:53:23 +03:00
gnezim c8257baf26 Replace border-radius: 4px with vars.$border-radius (3px) to match Angular 2026-04-20 04:26:19 +03:00
gnezim 627f155f87 Replace #022040/#1a3a5c navy hex with colors.$blue-dark token 2026-04-20 04:21:47 +03:00
gnezim 2d7646d793 Replace custom brand hex with design tokens ($blue, $orange, $green, $red) across 13 SCSS files 2026-04-20 04:19:43 +03:00
gnezim fb82fc6ad1 Replace pastel-blue dividers/bg with $border and $blue-extra-light tokens (6 files) 2026-04-20 03:58:13 +03:00
gnezim 4aadab25e9 Normalize body-text #222 to colors.$text-color across 10 SCSS files 2026-04-20 03:50:54 +03:00
gnezim 1156dd6f90 Replace hardcoded #6b7280/#1c2330/#8a8a8a greys with design tokens ($light-gray/$text-color) 2026-04-20 03:02:10 +03:00
gnezim 353bd62296 Render branded 404 page on invalid URLs and malformed params
Replace the inline 'Invalid parameters' fallbacks and the framework's
default '404' text with the existing Aeroflot 404 screen. Unknown
locale, malformed flight/route/station params, and unmatched URLs
(including bad paths like onlineboard//route/...) now all land on the
same ErrorPage component.
2026-04-20 02:23:16 +03:00
gnezim 4095344b7b Revert "Convert schedule WeekTabs to day-of-week strip"
This reverts commit c097ab21fe.
2026-04-20 01:38:12 +03:00
gnezim c097ab21fe Convert schedule WeekTabs to day-of-week strip
Angular's schedule renders 7 day pills ("20 пн … 26 вс") spanning the
active week, not weekly date ranges. Match that: tabs now render the
seven days of selectedMonday's week with day-num + weekday-abbr stack.
Prev/next arrows shift by full weeks. Day clicks scroll to the
matching day group in DayGroupedFlightList for the schedule UX.
2026-04-20 01:29:53 +03:00
gnezim f1f0030b69 Auto-expand today's day group + dynamic dates in visual diff
Schedule list day accordion stays collapsed by default but auto-
opens the day matching today's date when it's in the visible week
window — mirrors Angular's p-accordion default-active behaviour
where today's flights are visible without a click. The user can
still collapse it; we never re-open after that for the same date.

Visual-diff URLs were hardcoded to a past date with the wrong React
URL format (Angular path-style /AAQ/16042026 instead of React's
single-segment /AAQ-20260420-00002400). Switch to dynamic
yyyyMMdd of today for onlineboard pages and Mon→Sun of the current
week for schedule. Schedule-route diff dropped from ~91% to ~28%
on desktop after these two fixes.
2026-04-20 01:17:58 +03:00
gnezim 9cdc8fd75b Default schedule day groups to collapsed
Match Angular's p-accordion default-collapsed state on the schedule
results page. State now tracks expanded days (default empty)
instead of collapsed days (default empty), so the initial render
shows day headers only and the user clicks to reveal flights.
2026-04-20 00:35:44 +03:00
gnezim e05ef1ca20 Render rich Schedule details page + fix broken SEO key
Schedule details page used to show only a one-line FlightCard and
stop. Reuse ScheduleFlightBody so each flight in the chain renders
the same per-leg layout the schedule results page uses (route
summary, leg cards, transit pill, share/Купить/Детали рейса
actions). Add a `Вернуться к Расписанию` back link to the header.

While here, fix the SEO title key — buildScheduleDetailsSeo was
calling SEO.SCHEDULE.DETAILS.TITLE with `flights={...}`, but the
i18n bundle only defines SEO.SCHEDULE.FLIGHT-DETAILS.TITLE with
`flightNumber={...}`. The unresolved key was leaking into the
document title as "SEO.SCHEDULE.DETAILS.TITLE".
2026-04-20 00:30:39 +03:00
gnezim b21ae2638b Add Туда/Обратно direction switch to round-trip schedule page
Round-trip schedules used to render outbound and inbound lists
stacked. Mirror Angular's schedule-direction-switch: keep both
fetches running, but render only the active direction's list and add
a button group to the sticky header that swaps which one is shown.
WeekTabs track the active direction's week independently, and tab
navigation updates whichever direction is currently active.
2026-04-20 00:21:20 +03:00
gnezim ddc8e9f6dc Wire Вы искали sidebar accordion to live search history
Each schedule + onlineboard search now records itself into the
existing useSearchHistory localStorage hook, with a structured
params payload (departure/arrival/dates/flightNumber). The
SearchHistory sidebar renders the rich Angular layout: clock or
plane icon, optional sub-title (e.g. "Расписание рейсов, в одну
сторону"), city pair, and date range, with inbound dates appended
for round-trip searches.
2026-04-20 00:12:58 +03:00
gnezim d6ef3c8433 Render Angular schedule expanded body in React
Schedule flight cards now expand into the rich Angular layout instead
of the online-board time/transition rows. Mirrors connecting-flight-
body / multi-flight-body: horizontal timeline summary, per-leg card
with section number + flight number + operator + aircraft + dep/arr
times + leg duration + stations, transfer-inline-extended pill
between legs (Пересадка, ground time, transit city), and the actions
row (share, Купить, Детали рейса).

Wired via a renderExpandedBody render prop on FlightCard/FlightList so
ui/flights doesn't need to know about schedule-specific bodies.
2026-04-20 00:01:24 +03:00
gnezim 8bf672f3fa Schedule-specific sidebar (ScheduleFilter)
Replace OnlineBoardFilter on schedule pages with a dedicated
ScheduleFilter that matches Angular's schedule-filter:
- Город вылета / Город прилета with swap arrows
- 'Показать расписание на' date range picker
- 'Время вылета' time slider
- 'Только прямые рейсы' checkbox (sets connections=0)
- 'Показать обратные рейсы' checkbox
- 'Показать расписание' submit button (blue, full-width)

The OnlineBoardFilter accordion (Номер рейса + Маршрут tabs) is no
longer rendered on schedule pages — Angular only ships flight-number
search on the online-board side.
2026-04-19 23:36:05 +03:00
gnezim d74061e03b Sortable schedule columns + collapsible day accordion
Add sort arrows on ВЫЛЕТ / ВРЕМЯ В ПУТИ / ПРИЛЕТ headers — clicking
toggles ascending/descending order; clicking again clears the sort.

Day groups (Понедельник 20 апреля, etc.) are now collapsible via the
header chevron — matches Angular's p-accordion structure where each
day is an accordionTab. Default state expanded.
2026-04-19 23:29:48 +03:00
gnezim 4c487ab1b2 Render Connecting flights + Angular grid for schedule rows
- Connecting (multi-leg via transit) flights are now folded into a
  synthetic MultiLeg shape with combined flight numbers (SU 6188,
  SU 6233) and per-leg airline logos, matching Angular's
  schedule-list-flight-header.

- Schedule grid now uses Angular's 8-column layout
  (80/120/100/240/100/100/240/16). The middle status icon is
  replaced by a duration column with the blue clock icon and
  '3ч. 48мин.' / '4h 19m' formatting.

- Multi-leg airline logos use the round badge variant (separate
  round.png assets) so two carriers fit side-by-side without overlap.

- Action buttons removed from collapsed rows — Angular only shows
  flight-actions in the expanded body. Added chevron column for
  every schedule card and made schedule cards expandable by default.

- Removed 'Туда: MOW → KUF' subhead from outbound section, matching
  Angular's bare flight list under the column header.
2026-04-19 23:24:06 +03:00
gnezim 27b1ab1329 Schedule start: empty date-range placeholder
ScheduleStartPage now starts dateFrom/dateTo as null so the input
shows the `ДД.ММ.ГГГГ - ДД.ММ.ГГГГ` placeholder Angular ships
instead of pre-filling with the current week. The submit handler
defaults to current-week range when the user submits without
picking dates, preserving the legacy "find this week" UX.

Same pattern as the onlineboard date fix.
2026-04-19 21:52:03 +03:00
gnezim 71d0eef3e2 Schedule heading + column header row
- ScheduleSearchPage: H1 now reads `Расписание по маршруту: …`
  using SCHEDULE.SCHEDULE-BY-ROUTE — the existing onlineboard
  variant was leaking through. Matches Angular's schedule-search
  title-bar verbatim.
- DayGroupedFlightList: render a non-sortable column header bar
  above the list with `Рейс / Авиакомпания, борт / Вылет / Время
  в пути / Прилет`. Mirrors Angular's schedule-list-flight-header
  column row. Sort arrows still TBD.
- New i18n keys: SCHEDULE.COL-FLIGHT, COL-AIRLINE, COL-DEPARTURE,
  COL-DURATION, COL-ARRIVAL (RU + EN both filled).

Schedule-route mismatch now 11.99% (was 12.47% pre-heading fix).
2026-04-19 21:44:19 +03:00
gnezim 69706e023d Schedule + flights-map structural parity
- flights-map: default departure to Москва (MOW) when geolocation
  doesn't yield a city. Mirrors Angular which seeds the orange
  marker on Moscow regardless of geo permission. Hook now has two
  effects — a synchronous MOW fallback that fires once dictionaries
  load, and the existing geo callback that may upgrade to a closer
  city when permission is granted.
- Schedule: introduce DayGroupedFlightList. Buckets the flat result
  list by scheduled-departure date and renders each group under a
  `Воскресенье 19 Апреля`-style header (Intl-driven, weekday +
  genitive month). Single-day result skips the grouping noise.
- Schedule: introduce WeekTabs. Replaces the daily DayTabs in the
  schedule sticky-content with Monday-anchored 7-day windows like
  `13 апр - 19 апр`, matching Angular's week-tabs component.
  handleWeekChange recomputes both dateFrom (Monday) and dateTo
  (Sunday) when the tab changes.
- Schedule: aircraft model now visible in the collapsed FlightCard
  row when `direction === "schedule"` (Sukhoi SuperJet 100 / Airbus
  A321 etc., per Angular's operator-logo-and-model column).
- FlightCard / FlightList: extend `direction` union with `"schedule"`.

Tests updated: useGeolocationDefault tests now assert the MOW
fallback fires when permission is denied / API missing / arrival
already set (was previously expected to no-op).
2026-04-19 20:52:41 +03:00
gnezim ce2ca4a689 i18n: BCP-47 URL locales + complete EN translations
- URL surface now matches Angular: `/ru-ru/`, `/en-us/`, `/zh-cn/`, …
  (BCP-47). Bare short codes still work — the [lang]/layout auto-
  promotes them with a replace navigation. Internally everything that
  needs the short language (i18n file lookup, API path segment,
  Accept-Language header, dictionary `title[lang]` key, Intl
  formatters) reads it through the new `useLocale()` hook, which
  returns both `locale` (BCP-47) and `language` (short).
- ApiClient.locale is now mutable and is updated from the [lang]
  layout whenever the URL locale changes — was hard-coded to "ru" in
  the root layout before, so backend responses for /en/... still came
  back in Russian. Cities / airports / flight statuses now arrive in
  the active language.
- All 21 empty EN translation keys filled in (AIRPLANE.*, BOARD.
  PREVIOUS-FLIGHT, SCHEDULE.FILE-NAME, SEO.SCHEDULE.*, SEO.FLIGHTS-
  MAP.*, SHARED.FLIGHT-TRANSFER-PLURAL-*, SHARED.WEEK_FORMAT-WRONG)
  so /en-us renders without falling back to raw keys.
- Added BOARD.LOAD-FAILED-TITLE / -MESSAGE keys (RU + EN) and removed
  the three hardcoded Russian error strings from the search-page
  error card.
- FlightStatus now reads `FLIGHT-STATUSES.{Status}` from i18n instead
  of hardcoding the Russian labels.
- FlightCard's OperatorLogo now picks the en/ru carrier-logo variant
  from `useLocale().language` instead of always passing "ru" — the
  Aeroflot/Rossiya logos display in the active language where
  variants exist.
- registerPrimeLocales(): all 9 supported languages get a PrimeReact
  `addLocale` entry at module load (RU + EN hand-curated, others built
  from Intl). Calendar/AutoComplete widgets switch with the URL.
- ErrorBoundary catches outside the i18n provider, so it now ships
  its own minimal localised string table keyed off the URL locale —
  no more "Something went wrong" leaking on the Russian site.
- Hreflang URLs now emit BCP-47 (`/en-us/...`) while `hreflang="en"`
  stays the short Google-friendly form.
- Datetime helpers accept either short or BCP-47 locale (`isRussianLocale`)
  so callers can pass through whatever the route hands them.
2026-04-19 17:36:24 +03:00
gnezim b8e595dc25 URL surface parity with Angular for /popular and start-page prefill
- Drop the React-only standalone /popular route (and its e2e
  smoketest). Angular returns 404 for /ru-ru/popular; popular
  requests are surfaced inline on onlineboard/schedule start pages
  via PopularRequestsPanel (which stays). Matching the URL surface
  is a contractual requirement for the MF remote.
- Replace ?tab/?departure/?arrival/?return query-string prefill on
  the onlineboard and schedule start pages with a sessionStorage
  transient slot. Mirrors Angular's OnlineBoardFiltersStateService /
  ScheduleFiltersStateService cross-page singletons: URLs stay
  clean of query strings, the start-page form still seeds itself
  from a popular-request click, and a fresh page reload (which
  bypasses the in-memory state in Angular) lands on a pristine form.
  Same-page popular clicks remount the filter via key bump so the
  useState initializers pick up the new prefill.
2026-04-19 16:51:31 +03:00
gnezim cb61cafbf1 Fix three parity issues from final audit
1. Route heading uses airport name when a code maps only to an airport
   (SVO → 'Шереметьево') but prefers the city when the code is a city
   too (LED → 'Санкт-Петербург', not 'Пулково'). Angular does the
   same. Apply the new lookup order in both the onlineboard and
   schedule search pages.

2. Append ', Сегодня' (or 'DD.MM.YYYY' for other dates) to the board
   search heading, matching Angular.

3. Render the '+1' day-change marker on FlightCard even when only
   scheduled times are known. Previously the fallback pulled the value
   from `actualBlockOff/On.dayChange`, which is undefined for
   scheduled-only flights — so overnight flights like SU 6805
   (23:30 → 00:55 +1) showed no indicator. Read
   `scheduledDeparture/Arrival.dayChange.value` when the actual block
   time is missing.

4. Localize the PrimeReact Calendar widget: register a Russian locale
   in [lang]/layout.tsx and set the active one on every locale change,
   so 'Choose Date' reads 'Выбрать дату' and month/day names localize.
2026-04-18 14:10:26 +03:00
gnezim 22025b3ab4 Drop duplicate leg row on direct schedule details
For a single-leg flight the FlightCard summary already shows
SVO → ALA with times; the extra 'SVO→ALA / 00:05 - 06:30' line
below was redundant noise. Render the per-leg row only when the
route has multiple legs (transfer case).
2026-04-18 13:58:17 +03:00
gnezim 13fb633ec4 Fix schedule details date format
Upstream /schedule/details returns 400 when dates are sent as
yyyy-MM-dd; it wants the full ISO datetime (yyyy-MM-ddT00:00:00), same
as Angular's ApiFormatterService.formatDate output. Update the date
helper in ScheduleDetailsPage to append T00:00:00.

Verified with curl: request now returns 200 with the full flight
payload for SU1942 on 2026-04-18.
2026-04-18 13:56:10 +03:00
gnezim abc387ac3c Omit blank departure/arrival in schedule details request
Upstream returned HTTP 400 for empty departure/arrival values. Angular
derives them from per-leg data when missing; easier fix here is to
simply not send them — the backend accepts the request without those
query params.
2026-04-18 13:47:19 +03:00
gnezim 34d74b44c5 Add filter sidebar to schedule search
Schedule results previously had nothing in the left rail. Angular fills
it with the route filter + search history. Reuse the OnlineBoardFilter
component pre-populated with current URL params plus SearchHistory
below it, so the layout matches the board pages.
2026-04-18 13:43:28 +03:00
gnezim 6f634092b2 Fix schedule search endpoint (GET not POST, dateTo + 1 day)
Angular's ScheduleApiService.getFlights uses GET with query params
and a half-open date interval (sends dateTo = requested end + 1 day).
React sent POST with a JSON body matching the params verbatim, which
returned HTTP 500 from the backend.

searchSchedule now:
- GETs flights/1/{lang}/schedule with ?departure=...&arrival=...
- extends dateTo by one day
- coerces connections to '0'/'1' and drops empty timeFrom/timeTo

Verified the call returns 200 with real flight data for
SVO→LED 2026-04-18..2026-04-25. Updated the api tests accordingly.
2026-04-18 13:38:15 +03:00
gnezim 08bddc0db6 Translate remaining raw status labels
- ScheduleDetailsPage flight status: rendered raw
  `flight.status` (English enum 'Scheduled'); now routes through
  FLIGHT-STATUSES.* keys for localized Russian labels.
- FlightsMiniListItem status icon aria-label: same fix, so screen
  readers say 'Запланирован' instead of 'Scheduled'.
2026-04-18 13:34:45 +03:00
gnezim 0c660671ea Close the remaining high-impact parity gaps
Batch of fixes identified by the comparison audit:

Schedule search page (ScheduleSearchPage):
- Resolve IATA codes to city/airport names, so the H1 reads
  'Маршрут: Шереметьево - Санкт-Петербург' instead of 'SVO - LED'.
- Breadcrumb trail now includes the human-friendly route as its
  last entry.

Details page (OnlineBoardDetailsPage):
- Hide the 'Перелет N' leg header for single-leg flights (Angular
  parity — that label is only meaningful for multi-leg routes).
- Translate the leg status through FLIGHT-STATUSES.* instead of
  emitting the raw enum ('Cancelled' → 'Отменен', etc.).
- Humanize leg and total flying time through formatDuration so the
  page reads '1ч 25м' rather than '01:25:00'.

Details meal panel (MealPanel):
- Use the same FOOD.* translation keys as Angular, so labels become
  'Эконом класс / Комфорт класс / Бизнес класс / Специальное
  питание'.
- Add the Special-meal icon + link (was stubbed out previously).

Accessibility:
- Route the English aria-labels through new SHARED.A11Y-* keys in
  DayTabs pagination, FlightListSkeleton, ScrollUpButton and
  PrintButton.

Breadcrumbs:
- Render the 'Главная' crumb as a link even when it's the only /
  last item (it was dropping to plain text on start pages). Angular
  always links it to aeroflot.ru.

Tests updated to assert the new translated labels and duration
formatting; 1258 tests passing.
2026-04-18 13:27:56 +03:00
gnezim 96235d5534 Wrap ScheduleDetailsPage in PageLayout + frame
The schedule-details view rendered bare content on the blue background
with no PageLayout, no PageTabs, no breadcrumbs, no white card — so
the flight-number heading, leg list and skeletons were invisible
against the page background. Wrap the component in PageLayout with
the schedule tab + breadcrumb + 'Информация о рейсе: SU 6805' title,
and put each branch inside a <section class='frame'> (error,
not-found, empty, success) so it sits on the same white card as the
board pages.

Also fix the same React key warning the board page had: the leg map
used leg.index as the key, which is undefined for Direct flights.
Fall back to the array index.
2026-04-18 13:11:28 +03:00
gnezim 77821c3893 Style schedule-start checkboxes to match Angular
The two filter toggles ('Только прямые рейсы' / 'Показать обратные рейсы')
were rendering as the browser default 16px native checkboxes, which look
inconsistent across browsers and thin against the rest of the page.
Add an appearance:none + custom box with a blue tick mark when checked,
matching Angular's filter card.
2026-04-18 13:00:07 +03:00
gnezim 96adf785aa Extract schedule \$.tsx body to a readable module
Modern.js routes catch-all paths via a file literally named \`\$.tsx\`.
The name is framework-mandated but cryptic at a glance. Move the real
component to src/features/schedule/ScheduleDetailsCatchAllRoute.tsx
and make \$.tsx a 12-line re-export, so every grep/import/stack-frame
shows the readable name and the \$.tsx file stays just a framework
shim.
2026-04-18 12:52:13 +03:00
gnezim de22fc3722 Rebuild schedule results page for Angular parity
Previously the /schedule/route results page rendered everything on the
dark-blue background and dumped the raw 382-char bitmask from the
/days endpoint straight into the DOM. Changes:

- Wrap the page in PageLayout with PageTabs, breadcrumb and an H1
  matching Angular ('Маршрут: SVO - LED').
- Swap the inline calendar loop for the shared <DayTabs> component
  (weekday + day + month labels, paging arrows).
- Replace the broken comma-split parser in getScheduleCalendarDays
  with the same bitmask-to-dates conversion the board endpoint uses,
  so the calendar now yields real yyyy-MM-dd strings.
- Frame the results in <section class='frame'> so they sit on a white
  card (matches the board pages).
- Translate the 'Invalid …' parameter errors on every route page to
  SHARED.INVALID-PARAMS ('Неверные параметры URL.') and wire t() into
  the two route files that still lacked useTranslation.
2026-04-18 00:35:37 +03:00
gnezim 692fb5e292 Translate schedule headers and connection badges
- Schedule round-trip page: 'Outbound' / 'Return' section headings now
  use SCHEDULE.OUTBOUND / SCHEDULE.RETURN keys ('Туда' / 'Обратно' in
  Russian).
- SignalR connection badge: 'Live' / 'Reconnecting…' / 'Offline' now
  route through SHARED.CONNECTION-* keys ('Онлайн' / 'Соединение…' /
  'Нет связи' in Russian). Applied on both board search and details
  pages.
- Bag-belt label on leg stations switched to the existing
  DETAILS.BAG_BELT key (SHARED variant doesn't exist).
- Integration tests updated to match the new badge keys under mocked t.
2026-04-18 00:27:09 +03:00
gnezim a444b71bcd Translate remaining English strings and color statuses
- FlightList empty-state, Operated-by label, details/schedule error
  and not-found messages now route through i18n instead of hardcoded
  English. Added BOARD.FLIGHT-NOT-FOUND, BOARD.LOAD-FAILED,
  BOARD.OPERATED-BY, SHARED.RETRY to all nine locales.
- FlightStatus label now picks up the same colour as the plane icon
  (red for Cancelled, green for Arrived/Landed, blue for In Flight,
  orange for Delayed) — matches Angular's flight-status text treatment
  so 'Отменен' reads at a glance.
- Tests updated to expect the translation keys under the mocked `t`.
2026-04-18 00:15:46 +03:00
gnezim 4a8ae20c47 Fix stale test URLs and skip tests for out-of-scope features 2026-04-16 22:47:16 +03:00
gnezim 9a278c3170 Fix route path: use onlineboard not online-board in navigation URLs 2026-04-16 18:37:18 +03:00