Commit Graph

371 Commits

Author SHA1 Message Date
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 3f31ef591c Wire favicon via Modern.js html config
Angular's index.html referenced /assets/img/favicon.ico + a PNG icon +
an apple-touch-icon; the React port carried those assets through
config/public/ but never linked them in the HTML head, so the tab icon
was blank and /favicon.ico 404'd. Add html.favicon (copied to publicDir
root) plus html.tags for 16/32 PNG icons and apple-touch-icon.
2026-04-17 22:54:40 +03:00
gnezim 38b33aa349 Update board integration test assertions for dateTo = date + 1 day 2026-04-17 22:51:43 +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 6a7bbe2ce6 Drop unreferenced service-and-food-icons.zip from publicDir
A designer-source archive that got seeded from ClientApp/ alongside
the real SVG icons. No code under src/ or ClientApp/ references the
zip file (only the sibling .svg icons are imported). Removing so it
stops shipping in dist/standalone/public/ and the deploy image.
2026-04-17 18:31:30 +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 bf873bb6f6 Add project .mcp.json to enable Chromium sandbox for Playwright MCP
Playwright MCP v0.0.70 defaults to launching Chrome with --no-sandbox
because its sandbox-inference code in playwright-core mcp/config.js
only sets the default when browser.browserName is explicitly 'chromium';
the MCP leaves it undefined, so the default-to-true branch is skipped
and Playwright core pushes --no-sandbox into chromeArguments.

Chrome surfaces this as a yellow warning bar on every Playwright-driven
tab. Setting PLAYWRIGHT_MCP_SANDBOX=true via the MCP server env forces
chromiumSandbox=true, which skips the --no-sandbox push and removes the
warning. The override is scoped to this project; the plugin's own
.mcp.json is unchanged.
2026-04-17 17:30:02 +03:00
gnezim 2ae59d2074 Propagate Modern.js publicDir assets through sync + Docker build
The config/public/ directory (fonts, images, leaflet icons, favicons) is
Modern.js's publicDir convention — copied into dist/standalone/public/ at
build time. Two pre-existing gaps caused this to break on the deployed
SSR image and any fresh sync:

- scripts/sync-to-flights-front.sh did not copy config/ to the target
  repo, so the flights-front tree was missing /assets/** entirely.
- Dockerfile.react only copied src/, skipping config/; pnpm
  build:standalone ran without a publicDir source.

Result was that every /assets/** URL served the SSR HTML index with
Content-Type: text/html, producing OTS font-parse errors
(sfntVersion 1008821359 == '<!DT') and silently broken images.

Fix mirrors what was applied ad-hoc in Aeroflot.Flights.Front; this makes
future syncs and Docker builds carry the assets automatically.
2026-04-17 17:18:51 +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 aa7433b50b Add CityAutocomplete + regional picker implementation plan
Seven TDD tasks: buildCountryCityRows helper, CityPickerPopup with
regional tabs + grid, CityAutocomplete composite with popup trigger,
OnlineBoardFilter Route tab integration, FlightsMapFilter integration
with geolocate, time-selector compact view, final verification.
2026-04-17 14:58:05 +03:00
gnezim 0534b373f0 Add design spec for CityAutocomplete + regional picker parity
Cross-feature Angular-parity component: composite CityAutocomplete
(typeahead + clear + regional-picker trigger button) + CityPickerPopup
(regional tabs + country/city grid + optional GPS) + pure
buildCountryCityRows helper. Consumers: OnlineBoardFilter Route tab
and FlightsMapFilter. Time-selector switches to compact view in both.
2026-04-17 14:25:52 +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
gnezim 73a3d03469 Add useGeolocationDefault hook for flights-map departure pre-fill 2026-04-17 12:17:11 +03:00
gnezim ef04c19e13 Add calendarRange helpers for flights-map date picker window and snapping 2026-04-17 12:16:22 +03:00
gnezim 5a18e86bec Add Flights Map C.5 implementation plan
Six TDD tasks: calendarRange pure helpers, useGeolocationDefault hook,
FlightsMapFilter Calendar wiring (minDate/maxDate/disabledDates + snap
effect), Calendar prop capture tests, FlightsMapStartPage geolocation
hook call, final verification.
2026-04-17 12:13:50 +03:00
gnezim 9ee9c6b089 Add design spec for Flights Map C.5 (Calendar + Geolocation)
Final C-gap sub-feature: calendarRange pure helpers
(getMinDate/getMaxDate/buildDisabledDates/findNextEnabledDate), snap-to-
nearest-enabled effect in FlightsMapFilter, and useGeolocationDefault
hook that pre-fills departure from browser position on mount when no
dep/arr is already set.
2026-04-17 12:07:48 +03:00
gnezim f4b96b8248 Test FlightsMapStartPage filterRoutes + popups + auto-fallback wiring 2026-04-17 11:06:16 +03:00
gnezim 4e92e79a99 Wire filterRoutes, auto-fallback, and buy-ticket popups into Flights Map
routesToPolylines + intermediateCityIds now normalize airport codes to city
codes via the dictionaries so API responses resolve correctly. The page adds
effectiveConnections state + two effects for Angular-parity fallback
(retry connections=1 on empty direct-route result, then flip the UI toggle),
a filterRoutes memo feeding polylines and intermediateIds, and a popups memo
rendering departure + arrival buy-ticket popups in route mode only.
2026-04-17 11:00:40 +03:00
gnezim 77272423c1 Add buildBuyTicketUrl + escapeHtml helpers for popup content 2026-04-17 10:52:32 +03:00
gnezim 40f170f87a Add filterRoutes pure helper with airport-code normalization 2026-04-17 10:52:27 +03:00
gnezim 5225df0dd1 Add Flights Map C.4 implementation plan
Six TDD tasks covering filterRoutes, buildBuyTicketUrl + escapeHtml,
routesToPolylines airport-code normalization, FlightsMapStartPage wiring
(effectiveConnections + auto-fallback effects, filteredRoutes + popups
memos), integration tests, and final verification.
2026-04-17 10:48:02 +03:00
gnezim 299b0285b0 Add design spec for Flights Map C.4 (Popups + Filtering + Fallback)
Covers the final four Angular-parity gaps: filterRoutes pure helper,
buildBuyTicketUrl + escapeHtml, routesToPolylines/intermediateCityIds
airport→city normalization, and FlightsMapStartPage wiring for the
auto-fallback effect plus departure/arrival buy-ticket popups.
2026-04-17 10:43:24 +03:00
gnezim 76e9270f5e Test FlightsMapStartPage polyline + intermediateIds wiring 2026-04-17 10:14:06 +03:00
gnezim 4e103d8073 Drive polylines and intermediateIds from useFlightsMapSearch routes 2026-04-17 10:12:15 +03:00
gnezim a9ed92466f Draw routes as city-code polylines and force-open intermediate tooltips
- routesToPolylines + intermediateCityIds pure helpers with unit coverage.
- IMapPolyline reshaped from points to cityIds for Angular-parity drawing.
- MapCanvas resolves coords via markerIndex, filters invisible cities on
  every zoom/toggle change, and runs a second tooltip pass that keeps
  intermediate-city tooltips open regardless of zoom/highlight rules.
2026-04-17 10:08:44 +03:00
gnezim 514bae6051 Add Flights Map C.3 implementation plan
Six TDD tasks covering the routes-to-polylines pure helper, IMapPolyline
reshape to cityIds, MapCanvas polyline sync with visibility filtering,
intermediate-tooltip force-open pass, page wiring, and integration tests.
Tasks 1-3 share a commit due to coupling between type and consumer.
2026-04-17 10:00:48 +03:00
gnezim a23513045b Add design spec for Flights Map C.3 (Route + Spider Drawing)
Covers the polyline layer: routesToPolylines pure function, city-code-
based IMapPolyline shape, MapCanvas polyline sync via markerIndexRef
with visibility filtering, intermediate-city tooltip force-open pass.
2026-04-17 09:53:21 +03:00
gnezim a9b47036b5 Test FlightsMapStartPage marker construction from dictionaries 2026-04-17 08:46:43 +03:00
gnezim 1f24ee7159 Populate FlightsMapStartPage markers from dictionaries with zoom tiers 2026-04-17 08:44:40 +03:00