Files
flights_web/docs/parity-report/REPORT.md
T
gnezim 8005356db5 docs: parity report markdown + auto-memory plan
Companion markdown to the comparison-report/visual/report.html with
the same coverage matrix and per-page findings. Useful for git-based
review without serving the HTML.

Also adds AGENTS.md (subagent role definitions for future sessions)
and the modernjs-v3-upgrade plan stub from the earlier scoping.
2026-04-19 22:06:05 +03:00

297 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Visual Parity Report — Angular vs React (full coverage)
**Date:** 2026-04-19
**Tool:** Playwright MCP (`@playwright/mcp`)
**Angular reference:** https://flights.test.aeroflot.ru/{locale}/...
**React under test:** http://localhost:8080/{locale}/... (`pnpm dev:full` proxy)
**Locales tested:** `ru-ru` (primary), `en-en` (sample)
**Viewports:** desktop 1440×900, mobile 375×812
**Test scenarios:**
- Route SVO ↔ LED, 19 Apr 2026 (today)
- Flight SU 6497, 19 Apr 2026
- Departure ex-SVO / Arrival in LED
- Schedule one-way SVO→LED, week of 1926 Apr 2026
- Flights map (no filter)
Screenshots in `screenshots-v2/`. Legend: 🟢 visually equivalent · 🟡 minor diffs (spacing, dev chrome) · 🟠 functional/content gap · 🔴 major regression.
---
## At a glance
| # | Page | URL pattern | Desktop | Mobile | EN |
|---|---|---|---|---|---|
| 1 | Onlineboard start | `/{locale}/onlineboard` | 🟢 | 🟢 | 🟢 |
| 2 | Onlineboard route results | `/{locale}/onlineboard/route/{dep}-{arr}-{date}` | 🟢 | 🟢 | 🟡 |
| 3 | Onlineboard departure-only | `/{locale}/onlineboard/departure/{dep}-{date}` | 🟢 | — | — |
| 4 | Onlineboard arrival-only | `/{locale}/onlineboard/arrival/{arr}-{date}` | 🟢 | — | — |
| 5 | Onlineboard flight-number search | `/{locale}/onlineboard/flight/{carrier}{num}-{date}` | 🟢 | — | — |
| 6 | Flight details (single flight) | `/{locale}/onlineboard/{carrier}{num}-{date}` | 🟠 | 🟠 | 🟠 |
| 7 | Schedule start | `/{locale}/schedule` | 🟢 | — | — |
| 8 | Schedule route results (one-way) | `/{locale}/schedule/route/{dep}-{arr}-{from}-{to}` | 🔴 | — | — |
| 9 | Schedule route results (round-trip) | `/{locale}/schedule/route/.../...` | 🔴 | — | — |
| 10 | Schedule flight details | `/{locale}/schedule/{flightId}` | not tested | — | — |
| 11 | Flights map | `/{locale}/flights-map` | 🟡 | 🟡 | — |
| 12 | Popular requests panel (inline) | embedded on 1, 7 | 🟢 | 🟢 | 🟢 |
| 13 | Error pages (404/500) | `/error/{code}` | not tested | — | — |
| 14 | Smoke / health | `/{locale}/smoke` | not user-facing | — | — |
---
## URL surface
Both apps now use the same BCP-47-like URL contract:
`/{xx-xx}/...` where both halves are the language code (`/ru-ru/`, `/en-en/`, `/zh-zh/`, `/ja-ja/`, `/de-de/`, …).
Bare short codes (`/ru/onlineboard`) auto-redirect to canonical (`/ru-ru/onlineboard`) on the React side; Angular config redirects the same way. ✅ URL parity.
---
## 1. Onlineboard — start page
| Angular | React |
|---|---|
| ![](screenshots-v2/angular-onlineboard-start.png) | ![](screenshots-v2/react-onlineboard-start.png) |
**Diffs:**
- Angular shows test-env chrome React hides (orange "Тестовая версия", "rc/2026-04-06", dev counter "383", chat-widget bubble). Out of scope.
- Date input placeholder: Angular `ДД.ММ.ГГГГ` vs React `Сегодня` (filled when date is today). React's behaviour matches Angular's loaded state once a date is picked.
- Form-row vertical spacing slightly tighter in React (~6 px less between inputs).
- 4 info tiles, popular-requests block, breadcrumb — pixel-equivalent.
🟢 Equivalent.
## 2. Onlineboard — route results (`/onlineboard/route/SVO-LED-20260419`)
| Angular | React |
|---|---|
| ![](screenshots-v2/angular-onlineboard-route.png) | ![](screenshots-v2/react-onlineboard-route.png) |
**Diffs:**
- Both auto-expand the same closest-to-now flight (SU 6579) with the `Время` + `Посадка` accordion. Layout, columns, and label order match.
- Angular shows the actual arrival time (20:22) as primary with the scheduled time (20:25) struck-through; React shows 20:25 primary with 20:22 in the expanded panel. Both convey the same data, slightly different emphasis.
- Date-tab strip (`18 апр 24 апр`) and current-day highlight identical.
- React keeps the filter "Дата рейса" / "Время рейса" rows visible after submit; Angular hides them. Minor.
🟢 Equivalent on layout; 🟡 minor on time-emphasis treatment.
## 3. Onlineboard — departure-only (`/onlineboard/departure/SVO-20260419`)
| Angular | React |
|---|---|
| ![](screenshots-v2/angular-onlineboard-departure.png) | ![](screenshots-v2/react-onlineboard-departure.png) |
**Diffs:**
- Angular's `Город прилета` field defaults to `Все направления`; React leaves it empty. Functionally equivalent (both query "all destinations from SVO"); cosmetic.
- Both list flights to varied destinations (Сыктывкар, Махачкала, Ереван, Санкт-Петербург, …) with the same time/airline/airport columns.
- Same card-expansion behaviour.
🟢 Equivalent.
## 4. Onlineboard — arrival-only (`/onlineboard/arrival/LED-20260419`)
| Angular | React |
|---|---|
| ![](screenshots-v2/angular-onlineboard-arrival.png) | ![](screenshots-v2/react-onlineboard-arrival.png) |
**Diffs:**
- Same `Все направления` placeholder difference as #3.
- Boarding section in Angular labelled `Высадка` (deboarding); React labels it `Посадка` (boarding) but with the same fields. Consider switching React to `Высадка` for arrival-direction flights to match Angular wording.
- Status pill colours match: green check-mark (`Прибыл`), blue plane (`Вылетел`).
🟢 Equivalent on layout; 🟡 boarding/deboarding label.
## 5. Onlineboard — flight-number search (`/onlineboard/flight/SU6497-20260419`)
Equivalent to the route-results page in chrome (same `OnlineBoardSearchPage` shell). Both render a single-card list filtered to the flight number, with the same expanded card. H1 heading reads `Рейс: SU 6497, Сегодня` on both. 🟢 Equivalent.
## 6. Flight details — full page (`/onlineboard/SU6497-20260419`)
| Angular | React |
|---|---|
| ![](screenshots-v2/angular-flight-details.png) | ![](screenshots-v2/react-flight-details.png) |
**Diffs:**
- **H1 wording matches**: `Информация о рейсе: SU 6497, Москва - Санкт-Петербург`.
- **Carrier logo (RU)**: Angular renders the RU variant `РОССИЯ` (Cyrillic); React serves the EN variant `ROSSIYA` (Latin) even on the Russian site. **🔴 Bug** — `BoardDetailsHeader`'s `OperatorLogo` is still passing locale="en" or the asset fallback is wrong. The fix that landed for `FlightCard` didn't reach the details-page header.
- **Sidebar mini-list**: Angular shows arrows pointing right between origin/destination; React's mini-list item is plainer (just times + cities, no arrows). Angular uses the localised `Прибыл` time; React shows blank status row in the mini-list.
- **`Регистрация` row times**: Angular shows `16:00 ⁻¹` (with a small superscript day-change marker for the day-before start) plus `15:15` end time; React shows the same. ✅
- **`Посадка` (boarding) — in flight-details accordion**: matching `Закончена` status, same start/end times.
- **`Высадка` (deboarding)** row: matching status + times.
- **`Последнее обновление` stamp**: both show fresh client-side load timestamp. ✅
- **Footer note** `* Время прилета и расстояния являются расчётными...`: present on both.
🟠 — H1 + accordion match Angular; the carrier-logo locale slip is the only visible regression.
## 7. Schedule — start page
| Angular | React |
|---|---|
| ![](screenshots-v2/angular-schedule-start.png) | ![](screenshots-v2/react-schedule-start.png) |
**Diffs:**
- Date-range field: Angular shows placeholder `ДД.ММ.ГГГГ - ДД.ММ.ГГГГ`; React fills with the default current-week range `19.04.2026 - 26.04.2026` (calendar shows actual values). React's behaviour matches Angular's after the calendar mounts.
- 4 info tiles, popular-requests block, "Расписание рейсов Аэрофлота" SEO body — equivalent.
🟢 Equivalent.
## 8. Schedule — route results, one-way (`/schedule/route/SVO-LED-20260419-20260426`)
| Angular | React |
|---|---|
| ![](screenshots-v2/angular-schedule-route.png) | ![](screenshots-v2/react-schedule-route.png) |
**Diffs (major):**
- **Day-grouping headers**: Angular groups flights by day (`Воскресенье 19 Апреля`); React shows a flat list with no day grouping.
- **Code-share + multi-leg expansion**: Angular shows multi-carrier flights bundled (`SU 1942, SU 6532` with two airline logos), then a route diagram (4ч.25мин. + 2ч.20мин. transfer + 5ч.10мин.) and the legs broken out below. React renders each leg as a separate row with no diagram, no transfer pill, and no parent-flight grouping.
- **Column headers**: Angular has a sortable header row (`РЕЙС` / `АВИАКОМПАНИЯ, БОРТ` / `ВЫЛЕТ` / `ВРЕМЯ В ПУТИ` / `ПРИЛЕТ`) with sort arrows on `ВЫЛЕТ` and `ПРИЛЕТ`; React shows no headers.
- **Date-range tab strip**: Angular shows weekly windows (`13 апр - 19 апр`, `20 апр - 26 апр`, …); React shows daily tabs (`17 апр.`, `18 апр.`, `19 апреля`, …).
- **Aircraft model**: Angular shows `Airbus A321` / `Airbus A319` per leg; React doesn't show aircraft model in the row.
- **Action buttons**: Angular has both `Статус рейса` and `Детали рейса` per flight; React has only `Детали рейса`.
- **`Вы искали` (search history)** sidebar accordion: Angular has one; React has it elsewhere or not at all on this page.
🔴 **Major regression** — schedule results page is the largest gap in the entire app. The React page is essentially the onlineboard list reskinned, missing all of: day grouping, multi-leg/code-share grouping, transfer diagram, sortable headers, weekly date tabs, aircraft column, and the secondary action button.
## 9. Schedule — route results, round-trip
Not separately captured in this run. Same structural gaps as #8 are expected (Angular shows two side-by-side panels for outbound + return, each with full grouping). 🔴
## 10. Schedule — flight details
Not captured. Angular has a dedicated detail page reachable via `Детали рейса`/`Статус рейса` buttons; React's catch-all route handles this URL but renders the onlineboard details page. Worth a dedicated capture pass.
## 11. Flights map
| Angular | React |
|---|---|
| ![](screenshots-v2/angular-flights-map.png) | ![](screenshots-v2/react-flights-map.png) |
**Diffs:**
- Origin marker: Angular shows red dot on `Москва` (geolocation result). React shows no marker — `useGeolocationDefault()` either silently failed or the headless Playwright didn't grant permission. In a real browser with geo enabled both should show the dot.
- Default zoom/extent: Angular includes Санкт-Петербург at top of viewport; React's viewport is shifted south, cutting off SPb. Both use Leaflet center `[53,45]` zoom `5`; the diff is viewport-width and tile-loading order, not code.
- Swap arrows: vertical blue `↑↓` on both. ✅
- City labels: text-only with text-shadow halo on both. ✅
- Form panel: no `Найдите свой маршрут` header on either. ✅
- Date input: React still has the filled-blue calendar tile, Angular has an outline icon. Minor.
🟡 Minor — geo marker + slight zoom drift.
## 12. Popular requests panel
Embedded inline on Onlineboard start (#1) and Schedule start (#7). Renders 4 items in 2 columns; each is `Маршрут: City - City`, `Номер рейса: SU XXXX`, `Вылет: City`, or `Прилет: City`. Both apps use a service singleton (Angular `*FiltersStateService`) / sessionStorage prefill (React) to hand off click data without polluting the URL — confirmed in earlier patches. 🟢
## 13. Error pages
The React app has `/error/{code}/page.tsx` covering 404/500/etc. with a localised header + search box. Angular has `/error-pages/error-page` with the same shape. Not captured this round (need a deliberately broken URL); structurally aligned per the code.
## 14. Smoke / health
`/{locale}/smoke` is React-only — used by health-check probes. No Angular equivalent expected.
---
## English (`/en-en/`) — locale parity sample
| Angular | React |
|---|---|
| ![](screenshots-v2/angular-en-onlineboard-route.png) | ![](screenshots-v2/react-en-onlineboard-route.png) |
**Diffs:**
- All form labels (`Flight number`, `Route`, `City of departure`, `City of destination`, `Flight date`, `Search`) — match.
- Date tabs (`18 Apr`, `April 19`, `20 Apr`, …): Angular uses `apr` lowercase weekday-relative form; React capitalised `Apr`. Minor.
- Status pills (`Arrived`, `In flight`, `Scheduled`) — match.
- Carrier logos — both render the EN variants `ROSSIYA` / `AEROFLOT`. ✅
- Boarding row labels (`Status`, `Start time`, `End time`/`Close time`): Angular uses `Close Time`, React uses `End time`. Cosmetic.
🟢 Equivalent. EN translation now complete (was 21 keys empty before — all filled).
---
## Mobile (375×812)
### Onlineboard start
| Angular | React |
|---|---|
| ![](screenshots-v2/angular-mobile-onlineboard-start.png) | ![](screenshots-v2/react-mobile-onlineboard-start.png) |
**Diffs:**
- Both surface the 3-day quick-pick row (`19 Вс. Апреля / 20 Пн. Апреля / 21 Вт. Апреля`) above the manual `ДД.ММ.ГГГГ` input. ✅ React's selected-day chip uses solid blue fill; Angular leaves all three white. Minor.
- Tabs (`Онлайн-Табло` / `Расписание` / `Карта полетов`) — match.
- Form fields, swap arrow, accordion behaviour — match.
🟢 Equivalent.
### Flight details
| Angular | React |
|---|---|
| ![](screenshots-v2/angular-mobile-flight-details.png) | ![](screenshots-v2/react-mobile-flight-details.png) |
**Diffs:**
- **Carrier logo**: Angular `РОССИЯ` (RU); React `ROSSIYA` (EN). Same bug as desktop #6. **🔴**
- Both show the date-picker (`19 Апреля 2026` / `Сегодня`) and the back-to-board button.
- Both show the same flight-progress timeline (16:00 → 17:23 with `Прибыл`, strikethrough scheduled `17:25`).
- Both show the same `По расписанию` / `Фактическое` time blocks.
- Last-update stamp (`Последнее обновление: 19:09 19.04.2026`) — both fresh client-load time. ✅
- React shows an extra wrapped header with "SU 6497 / 16:00 / 17:23 / Москва / Санкт-Петербург" above the main accordion — duplicates info that's already in the main card. Angular doesn't have this duplicate.
🟠 — Carrier logo locale + duplicate sidebar-card on mobile.
---
## Cross-cutting findings
### What's already at parity ✅
- URL contract (BCP-47 `/xx-xx/`, both halves matching).
- 9-language i18n coverage (RU complete, EN complete after this round, others have file-level coverage from the prior project).
- API client locale (now mutable, follows `[lang]/layout.tsx`) — cities/airports/statuses respond in the active language.
- Onlineboard start, route, departure, arrival, flight-search pages — visually equivalent.
- Schedule start page — equivalent.
- Popular requests panel + cross-page prefill via sessionStorage.
- Mobile day-quick-pick (3-day chip row).
- Flights map: swap arrows, city labels, form panel header, leaflet tooltip styling.
- Last-update stamp using client-side load time (matches Angular's `flight.lastUpdate = new Date()`).
- Share menu (translated labels + brand-icon-on-top layout).
- Footer note `* Время в системе - МЕСТНОЕ.` on search results.
- Boarding-status leading dot (grey for `Уточняется`).
### Open gaps 🟠/🔴
| Severity | Page | Gap |
|---|---|---|
| 🔴 | Schedule results (#8, #9) | No day grouping, no code-share/multi-leg bundling, no transfer-diagram, no sortable headers, no weekly date tabs, no aircraft column, missing `Статус рейса` button. Schedule is a markedly different feature in Angular than what React currently ships. |
| 🔴 | Flight-details carrier logo (#6, mobile) | `BoardDetailsHeader` still serves the EN logo variant on Russian pages. `FlightCard` was fixed in the i18n migration, but the details-page header was missed. One-line fix in `BoardDetailsHeader.tsx` to pass `useLocale().language`. |
| 🟠 | Flight-details mobile (#6) | React renders an extra summary-card above the main details card that Angular doesn't have. |
| 🟠 | Onlineboard arrival (#4) | Boarding-row label says `Посадка` for arrival-direction flights; Angular says `Высадка`. |
| 🟡 | Onlineboard departure/arrival (#3, #4) | Empty `Город прилета` / `Город вылета` field instead of Angular's `Все направления` placeholder. |
| 🟡 | Flights map | Default origin red dot (geo-driven, not code). |
| 🟡 | Flights map calendar icon | Filled blue tile vs Angular outline icon (PrimeReact theme). |
| 🟡 | Onlineboard route filter | Date / time inputs stay visible after submit; Angular hides them. |
| 🟡 | EN tabs label casing | `Apr` vs `apr` weekday header. |
### Items intentionally divergent
- React `[lang]/smoke` route — health probe, not present in Angular.
- React `[lang]/popular` route was deleted to match Angular's 404 (popular requests are inline only).
- React in-page test/dev chrome (`Тестовая версия` badge, env counter, chat widget) — Angular reflects the test-env deployment; React's `dev:full` is dev-only.
---
## Recommended priorities
**P0 — content correctness on the Russian site:**
1. Fix `BoardDetailsHeader` to pass `useLocale().language` to `OperatorLogo` so RU pages get `РОССИЯ` not `ROSSIYA` (desktop + mobile #6).
**P1 — feature gaps on schedule:**
2. Schedule results page (#8) — port Angular's day-grouped, multi-leg / code-share bundled, weekly-date-tab layout. This is the single biggest UX gap remaining in the React app.
3. Schedule round-trip results — same approach, two-pane.
**P2 — small parity wins:**
4. Mobile flight-details: drop the duplicate summary card above the main details card (#6 mobile).
5. Onlineboard arrival page: rename boarding row to `Высадка` when direction = arrival (#4).
6. Departure / arrival pages: prefill the opposite-direction field with `Все направления` placeholder (#3, #4).
**P3 — polish:**
7. Onlineboard route filter: hide `Дата рейса` / `Время рейса` rows post-submit.
8. Flights map: outline calendar icon instead of filled blue tile.
9. EN onlineboard date tabs: lowercase short month name to match Angular (`apr` not `Apr`).