Commit Graph

268 Commits

Author SHA1 Message Date
gnezim ceeae1a7b1 Strip dashes from date when building flight-details URL
The API returns flight.flightId.date as 'yyyy-MM-dd' (dashed). Our URL
builder pasted it verbatim, producing /onlineboard/SU6162-2026-04-18
which the route parser (expecting yyyyMMdd) rejected. Normalise the
date to compact form inside buildFlightUrlParams so the URL always
matches the route pattern, regardless of whether the caller passes a
compact or dashed date.
2026-04-18 15:41:14 +03:00
gnezim 76f7acb0dd Implement inline expandable flight-card details
Clicking a row on the board search results page now toggles an inline
details panel instead of immediately navigating away. The layout
matches Angular's board-flight-header:

- Aircraft model ('Sukhoi SuperJet 100') appears below the flight
  number when expanded.
- 'Время' detail row: По расписанию / Фактическое times with UTC
  offsets for both the departure and the arrival sides.
- 'Посадка' detail row: boarding status (через the
  BOARDING-STATUSES.* keys), start and end times.
- 'Детали рейса' button (blue) in the bottom-right navigates to the
  full details page.
- Active rows get a blue left border + light-blue background.
- Chevron icon on the right rotates on expand.

Wire-up: FlightCard has two new props (expandable, onViewDetails).
FlightList automatically passes expandable=true when a click handler
is provided. Added SHARED.BOARDING-START / SHARED.BOARDING-END keys
across all nine locales for the time captions.
2026-04-18 15:36:14 +03:00
gnezim 916e594f06 Fix flights-map popup showing raw i18n key
The arrival-city popup called t('FLIGHTS-MAP.BUY-TICKET') but the key
in every locale file is FLIGHTS-MAP.BUY_TICKET_BTN (it was renamed
earlier). Map popups rendered the raw key 'FLIGHTS-MAP.BUY-TICKET'
instead of 'Купить билет'. Point the call at the existing key.
2026-04-18 15:21:25 +03:00
gnezim a0176cc336 Keep DayTabs enabled until calendar API resolves
With empty availableDates every day button rendered as [disabled],
leaving the user unable to navigate between days while the /days API
loads (or for routes where the calendar endpoint hasn't been wired
yet). Treat an empty availableDates array as 'unknown' — don't disable
anything, matching Angular's behaviour where tabs are tappable until
the upstream tells us a specific day has no flights.
2026-04-18 14:12:33 +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 4e91e9dca1 Display wall-clock times in TimeGroup instead of reprojecting
formatTime runs new Date(iso).getHours() which reprojects the
timestamp through the browser's local timezone. For a flight arriving
at 06:30 in Almaty (GMT+5) a viewer in Moscow saw '04:30'. Switch the
TimeGroup component to formatLocalTime which reads the wall-clock
directly out of the offset-aware ISO string, matching the rest of the
details/timetable views.
2026-04-18 14:00:03 +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 cd398fb8d9 Hide redundant 'Operated by' text on details page
The badge already conveys the operating carrier via the airline logo
(e.g. РОССИЯ for FV-operated Aeroflot flights), and code-share flights
get an additional 'KL 123, AF 456...' line below the flight number
from DetailsHeaderBadge. The separate 'Выполняет рейс: FV' line
duplicated that information.

Keep the div in the DOM with a visually-hidden class so tests that
target the data-testid still find it. Also add the .visually-hidden
utility class to layout.scss.

Test updated to assert the slot still exists but is hidden.
2026-04-18 13:53:13 +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 13708fce8e Raise ApiClient default timeout to 30s
The upstream /schedule endpoint regularly returns 7MB+ payloads and
takes 6-10s to complete. The 5s default was aborting those fetches
mid-body, cascading into a retry loop that showed ERR_ABORTED in
DevTools and 'Failed to load data' on the UI — even though the backend
eventually answered with HTTP 200. Matches Angular's default.
2026-04-18 13:41:40 +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 58631a3d73 Translate remaining aria-labels on shared widgets
Six more aria-labels that still read in English:
  - CityAutocomplete clear button ('clear')
  - CityAutocomplete picker trigger ('open regional picker')
  - Breadcrumbs nav landmark ('breadcrumb')
  - Timeline prev/next buttons ('Previous legs' / 'Next legs')

Routed through new SHARED.A11Y-* keys (translated into all nine
locales). This is screen-reader-only text but part of the parity
budget.
2026-04-18 13:32:35 +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 b01fc2f0c9 Populate filter sidebar when clicking a popular request
Two bugs prevented the popular-requests click from filling the filter:

1. OnlineBoardFilter seeded its fields from initial* props via
   useState(...), which only runs once. When a user clicked a popular
   request the parent pushed ?departure=SVO&arrival=LED into the URL
   and re-rendered with new initial* props, but the sidebar fields
   kept their previous empty values. Add an effect that diffs the
   initial* props against a ref and pushes the changes into local
   state, matching Angular's ngOnChanges behaviour.

2. CityAutocomplete's selectedCity only looked the value up in
   cityByCode. Airport codes like SVO aren't cities, so the header
   code label stayed blank. Fall back to airportByCode → city_code so
   the top-right code renders as 'MOW' when the input shows
   'Шереметьево'.

End-to-end behaviour now matches Angular: clicking
'Маршрут: Шереметьево - Санкт-Петербург' on the start page updates
the URL, populates 'Шереметьево' / 'Санкт-Петербург' in the inputs,
shows 'MOW' / 'LED' codes in the labels.
2026-04-18 12:43:33 +03:00
gnezim db163f5645 Resolve popular-request codes to city/airport names
useCityName was a 'phase 2 stub' that returned the IATA code
unchanged, so the popular-requests panel read 'Маршрут: SVO - LED'
instead of 'Маршрут: Шереметьево - Санкт-Петербург'. Rewire the hook
to read the current locale and look up cityByCode / airportByCode
from the loaded dictionaries, falling back to the code only while
dictionaries are loading or for unknown codes (matches Angular's
DictionariesService.getCityOrAirport).

Tests expanded to cover the city/airport resolutions and the
loading / unknown fallback paths (5 total, 1258 suite green).
2026-04-18 11:02:12 +03:00
gnezim 51f997e642 Match Angular layout in flights-map filter sidebar
- Add the 'Найдите свой маршрут' heading (uses the existing
  FLIGHTS-MAP.ROUTE key, previously unused).
- Reorder the fields to Angular's order: cities → info → toggles →
  date (date was previously stuck in the middle).
- Replace the three native checkboxes with styled pill toggles +
  sliding knobs — matches p-inputswitch used in the Angular filter.
- Add 'ДД.ММ.ГГГГ' placeholder to the date input so the empty state
  reads the same as Angular.
2026-04-18 10:26:24 +03:00
gnezim 25986dfca2 Route operatingBy.carrier lookups through operatingCarrier()
Several components still read `flight.operatingBy.carrier` directly.
The API actually returns `{scheduled, actual}` — I kept `.carrier` on
IOperatingBy as a deprecated alias during the refactor, so nothing
fails typechecking, but at runtime the lookup is always undefined,
which silently disabled the Registration, Flight-status and
Operated-by UIs (the Reg button did nothing on click, the status
visibility check always returned false).

Swap the affected sites — RegistrationButton, FlightStatusButton, the
canRegister / canViewFlightStatus visibility helpers, and the
'Operated by' block in OnlineBoardDetailsPage — to use the
operatingCarrier() helper that reads actual/scheduled/legacy in turn.
2026-04-18 10:04:22 +03:00
gnezim 0633e6de39 Redirect /\[lang\] to /\[lang\]/onlineboard
/ru rendered a blank page because no index route existed under
src/routes/\[lang\]/. Angular's app-routing.module.ts redirects any
empty top-level path to /onlineboard; mirror that in React by adding
a tiny <Navigate replace> index route.
2026-04-18 10:00:01 +03:00
gnezim cd4864c82e Translate remaining route-level English strings
- Flights-map empty/error states: 'Failed to load routes. Please try
  again.' and 'No directions found.' now use existing translation keys
  (BOARD.LOAD-FAILED + FLIGHTS-MAP.NO_DIRECTIONS_INFO).
- Flights-map feature-flag fallback '404 - Page Not Found / This
  feature is currently unavailable.' reduced to the translated
  PAGE404.HEADER string.
- Suspense fallbacks on /onlineboard, /schedule, /flights-map and
  /popular route pages now render the new SHARED.LOADING ('Загрузка…')
  instead of hardcoded 'Loading...'.
2026-04-18 00:45:00 +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 4d73e2fd3c Clean up detail-page time formatting
Render ISO timestamps as "23:30 UTC+03:00 17.04.2026" instead of the
raw "2026-04-17T23:30:00+03:00" that the API returns. Applies to the
per-leg station blocks (new LegStation helper) plus the
Registration / Boarding / Deboarding panels.

formatLocalTime now preserves the wall-clock in the source string
(previously `new Date(...)` would shift it to the runtime's locale).
Added formatUtcOffset (UTC±HH:mm) and formatDayMonthYear (DD.MM.YYYY)
helpers next to it.

Also ship the Angular footer notes on the details page (estimated
times / local-time disclaimer) and added BOARD.LOCAL-TIME-NOTE,
BOARD.ESTIMATED-TIME-NOTE keys to all nine locales.
2026-04-17 23:58:22 +03:00
gnezim ad8367c203 Refine Angular parity: titles, airline header, labels
Search page:
- Title and breadcrumb now read the station dictionaries and render the
  human-friendly route heading (e.g. 'Маршрут: Шереметьево - Пулково')
  for route/departure/arrival/flight search URLs, mirroring Angular.

Details page:
- Main H1 becomes 'Информация о рейсе: SU 6805, Москва - Санкт-Петербург'
  (carrier + flight number + origin/destination cities), not a bare
  flight number.
- Add 'Детали рейса' section header above the accordion to match
  Angular's flight-details-wrapper layout.
- Promote the airline block in BoardDetailsHeader: drop the legacy
  OperatorLogo copy with broken asset paths and hand off to the shared
  <OperatorLogo> under src/ui/flights. Render it with the
  'авиакомпания' caption beside the enlarged flight number.
- Replace hardcoded English 'Leg' / 'Total flying time' / 'Aircraft:'
  with i18n keys, added to all nine locale files.

Test harness:
- Add vi.mock for useDictionaries in the three suites that render
  OnlineBoardSearchPage (the new heading helper calls the hook and
  crashed without ApiClientProvider). 1256 tests passing.
2026-04-17 23:48:06 +03:00
gnezim 330f9787a2 Bring flight row + details page closer to Angular
Search row now shows the full Angular header layout: flight number,
operator logo, scheduled/actual departure time, departure city +
terminal, plane icon with status label, mirrored arrival block. The
city input in the filter sidebar now shows the city name
('Шереметьево') instead of the IATA code.

Details page: expand the first accordion panel by default (Angular
parity), hide Print/Share on the board details view, and rewrite the
Aircraft panel as a property table with total/economy/comfort/business
seat counts and the previous-flight identifier — all pulled from the
real API shape, which is `{ seats: [{type, count}] }` rather than the
legacy string config.

Supporting work:
- New <OperatorLogo> component with the full carrier → asset mapping
  ported from ClientApp/src/styles (SU, FV, HZ, S7, …).
- Extend StationDisplay with a cityFirst variant for row usage.
- New FlightStatus icon-over-label layout, translated labels.
- Update IEquipmentFull types: configuration is an object with seats[],
  plus scheduled/actual/previousFlight; new IOperatingBy union.
- Tests + fixtures updated for the new shapes; 1262 passing.
2026-04-17 23:32:50 +03:00
gnezim 84e6d265fc Align board search + details with Angular visual parity
Both pages were rendering content directly on the dark-blue page
background, which made the flight list and details card effectively
invisible. Angular wraps the same content in a white card (section.frame)
with drop shadow.

Changes:
- Wrap FlightList in <section class='frame'> on the search page and wrap
  the details body the same way.
- Replace the inline numbered .calendar-day strip on the search page
  with the existing <DayTabs> component — the same one the details page
  already uses (weekday + day + month labels, ‹/› paging).
- Pass onFlightClick through FlightList into FlightCard so whole rows
  are keyboard-accessible buttons, matching Angular's row-level click.
  The off-screen data-testid='flight-link-*' buttons stay for e2e.
- Fix 'Leg NaN' header + the React key warning in FlightLegs when
  the API returns a Direct leg without an index field.
- Update the existing flight-search integration test to target the
  DayTabs testid instead of the old ad-hoc calendar-strip one.

A docs/parity-report-2026-04-17.md file captures the diffs I applied
and a punch list of the remaining parity gaps (operator logo on rows,
delay/day-change glyphs, Share button visibility on board details, the
aircraft panel as a table). Those need per-component work against the
Angular templates and will follow in a separate pass.
2026-04-17 23:14:59 +03:00
gnezim 40fa7c5f06 Group city autocomplete suggestions by city + airports
Match Angular's CityAutocompleteItemComponent: each suggestion is either
a city row (bold name, country in gray) or an indented airport row. Port
CitiesSearchService search (starts-with → includes → by-airport-name,
cap at 10 cities, then insert each city's other airports). Airport
selections resolve to the owning city code, matching Angular behavior
where typing 'SVO' or clicking the Sheremetyevo row sets city = MOW.
2026-04-17 23:00:07 +03:00
gnezim 79fcf2bdc1 Fix onlineboard empty results and flights-map polyline zoom hazard
Online board: the /board endpoint treats dateFrom/dateTo as a half-open
interval, so sending the same date for both yielded zero rows on routes
that obviously have flights (e.g. SVO-LED). Mirror Angular's
OnlineBoardApiService.getFlightsByRoute and use dateTo = date + 1 day.

Flights map: two stacked problems made arcs disappear on zoom.
 - syncPolylines gated endpoints on map.hasLayer(marker); when
   syncVisibility removed a zoom-tier layer, its arc went with it.
 - The zoomend and toggle effects both called syncPolylines, which
   captured a stale closure from the first render (polylines = []) and
   wiped the layer. Polyline coords are geographic — Leaflet rescales
   them on zoom — so the rebuild was never necessary.
Arcs now render once per polylines prop change and stay put through
zoom and filter toggles.
2026-04-17 22:48:18 +03:00
gnezim 18ab969e1c Keep flights-map arcs visible when zoom tier layers are removed
syncPolylines filtered endpoints by map.hasLayer(marker), which drops
polylines whose endpoint's zoom-tier layer was just removed by
syncVisibility. Result: in spider mode, arcs to small/distant cities
vanished when the user zoomed out.

Arc coordinates are fixed by the city's lat/lng; render them as long as
the markers exist in the index. User-level filtering (domestic /
international) already happens upstream in filterRoutes, so the
hasLayer gate was both incorrect and redundant.
2026-04-17 22:40:46 +03:00
gnezim 5551cb1821 Fix flights-map date format and dev proxy empty-body status parsing
Upstream /destinations and /days endpoints expect yyyy-MM-DD (dashed),
matching Angular's ApiFormatterService.formatDateOnly output. React was
sending the internal compact yyyyMMdd, triggering silent 400s.

Also fix dev-server.mjs status-code parsing: empty-body curl responses
start with the appended "\n%{http_code}" separator at index 0, so
`lastNewline > 0` mis-treated the status as body and defaulted to 200,
hiding real upstream 4xx/5xx responses. Changed to `>= 0`.
2026-04-17 22:25:48 +03:00
gnezim c8d0caa9cf Fix five console-level issues surfaced by live-deploy Playwright audit
1. FlightsMap tiles didn't render: MapCanvas inline height:100% resolved
   to 0 against min-height parents. Hand sizing to consumer CSS so
   .flights-map-start__map height:500px wins.

2. FlightsMap /map/api/tile/{z}/{x}/{y}.jpeg requests fell through to
   Modern.js SSR (HTML body). Dev proxy now forwards /map/* to the
   test env via curl with image headers and binary-safe piping.

3. PopularRequestsPanel duplicate React key (Route-SVO-LED appears
   twice in upstream). Suffix the key with the visible index.

4. OnlineBoardDetailsPage /onlineboard/details 400. Upstream expects
   an ISO datetime (yyyy-MM-DDTHH:mm:ss), matching Angular's
   ApiFormatterService.formatDate. Append T00:00:00.

5. Browser-level SignalR CORS errors on every details page: the
   default SIGNALR_HUB_URL pointed at an unreachable placeholder.
   Default to empty + skip the connection in useLiveFlights when
   blank. Also configureLogging(LogLevel.None) so SignalR stops
   writing its own negotiation failures to console. Live updates
   re-enable by setting SIGNALR_HUB_URL on a deployment.
2026-04-17 21:55:44 +03:00
gnezim b54746c74c Fix flights-map tile URL to Angular parity /map/api/tile/{z}/{x}/{y}.jpeg
The tile URL was built as `${env.API_BASE_URL}/tiles/{z}/{x}/{y}.png`,
which has three problems on a real deployment:

1. Wrong path segment: the backend serves tiles under /map/api/tile/
   (singular), not /tiles/.
2. Wrong extension: the backend emits .jpeg, not .png.
3. Wrong base: API_BASE_URL may be empty or point at the JSON API host.
   Tiles are served by a dedicated upstream behind the same reverse
   proxy that fronts the React SSR, so they must be same-origin and
   relative (matches Angular environment.mapApiUrl).

With this fix, the map renders its base tiles on the deployed site
instead of issuing doomed requests and showing a blank canvas behind
the markers. The Leaflet marker layer was already rendering; only the
tile layer was missing.
2026-04-17 18:40:52 +03:00
gnezim 7ec76486ec Clean up build warnings: MF DTS, autoprefixer color-adjust, dev CORS
Three non-fatal warnings surfaced by the Jenkins build log, fixed in
the config layer:

- module-federation.config.ts: dts: false. The @module-federation/
  dts-plugin fails under our strict tsconfig and logs TYPE-001 every
  build. Remote types are not needed at runtime; consumers generate
  their own via their dev toolchain.

- src/styles/_layout.scss: color-adjust → print-color-adjust. The
  unprefixed color-adjust shorthand is deprecated; the standard name
  is print-color-adjust. Matches the -webkit- prefixed sibling.

- modern.config.ts: tools.devServer.headers explicitly set. MF warns
  when devServer.headers is empty and auto-assigns '*'. Providing an
  explicit allowlist silences the banner in dev builds; production
  behavior is unaffected (real reverse proxy manages CORS).
2026-04-17 18:11:04 +03:00
gnezim 10dfc8609d Revert API_BASE_URL default: keep same-origin proxy for client-side CORS
Commit e20ef94 set the default to https://flights.test.aeroflot.ru/api,
which broke the browser client (no CORS headers on the test env;
scripts/dev-server.mjs is the only layer that can bypass it).

Keep PROD_ORIGIN pointing at the test env for SEO, but restore
API_BASE_URL default to http://localhost:8080/api with a comment
explaining the proxy chain: dev → Express+curl → flights.test.aeroflot.ru.
Production deployments continue to set API_BASE_URL explicitly.
2026-04-17 15:46:34 +03:00
gnezim e20ef940f8 Default API_BASE_URL and PROD_ORIGIN to the test environment
Previously API_BASE_URL defaulted to http://localhost:8080/api, which
only works inside the dev server proxy. For standalone/SSR runs without
the proxy, the default now points to https://flights.test.aeroflot.ru.

Dev continues to use the same-origin proxy because scripts/dev-server.mjs
explicitly injects API_BASE_URL=http://localhost:8080/api into the
Modern.js child process env, keeping browser fetches CORS/WAF safe.
2026-04-17 15:31:03 +03:00
gnezim 896e6bd83d Switch filter time-selector to Angular compact layout 2026-04-17 15:16:49 +03:00
gnezim 373f049e90 Use CityAutocomplete for FlightsMapFilter with geolocate on departure 2026-04-17 15:13:20 +03:00
gnezim b8d5de6ca7 Use CityAutocomplete for OnlineBoardFilter Route tab departure + arrival 2026-04-17 15:11:47 +03:00
gnezim ba302c6b03 Add CityAutocomplete composite with clear and regional-picker trigger 2026-04-17 15:06:46 +03:00
gnezim 419b4b8df1 Add CityPickerPopup with regional tabs and country/city grid 2026-04-17 15:03:39 +03:00
gnezim 6820a11e83 Add buildCountryCityRows helper for regional picker grid 2026-04-17 15:02:04 +03:00
gnezim c4ae1ef7aa Invoke useGeolocationDefault on FlightsMapStartPage mount 2026-04-17 12:22:29 +03:00
gnezim 0c65755553 Test FlightsMapFilter Calendar min/max/disabledDates + snap effect 2026-04-17 12:21:13 +03:00
gnezim 78b3e86418 Wire availableDays into FlightsMapFilter Calendar with snap-to-nearest 2026-04-17 12:20:37 +03:00