Add Phase 5 Popular Requests master plan

Documents the Angular feature analysis and sub-plan breakdown for
porting popular-requests to React with useSearchHistory hook.
This commit is contained in:
2026-04-15 09:58:14 +03:00
parent 5839692e52
commit 7d202b9436
@@ -0,0 +1,111 @@
# Phase 5 — Popular Requests MASTER Plan
> **This document is a plan INDEX, not an executable plan.** It lists the Phase 5 sub-plans, their dependency order, the contracts each sub-plan exports for downstream sub-plans to consume, and the shared files that cross sub-plan boundaries.
**Goal of Phase 5:** Port the Popular Requests feature from Angular to React, wire it into existing route pages, implement the `useSearchHistory` hook backed by `@/shared/storage`, and verify SEO + parity. Popular Requests is an embedded component (not a standalone routed page) — used inside the OnlineBoard and Schedule start pages. It also includes the language switcher (ported from Angular layout) and a per-language-namespaced search history hook.
**Phase 5 exit gate** (must pass before next phase starts):
- `PopularRequestsPanel` renders correctly with all 5 request modes: `FlightNumber`, `Route`, `Arrival`, `Departure`, `RouteWithBack`.
- `usePopularRequests` hook calls `GET /Requests/1/getpopular` and handles loading/error states.
- `useSearchHistory` hook persists search history via `@/shared/storage` with per-language namespacing (`afl_history_{lang}`).
- No direct `localStorage` access outside `src/shared/storage.ts` (enforced by ESLint `no-restricted-globals`).
- MF expose `PopularRequests.tsx` upgraded from stub to real component.
- Feature barrel `src/features/popular-requests/index.ts` exports all public surface.
- `pnpm typecheck && pnpm lint && pnpm test && pnpm build:standalone` all green.
**Angular source analyzed:**
- **API:** `GET /api/Requests/1/getpopular` returns `IPopularRequest[]` (union of 4 discriminated types keyed by `RequestMode`).
- **Types:** `RequestMode` enum: `FlightNumber | Route | RouteWithBack | Departure | Arrival`. `IPopularRequestType`: `'Schedule' | 'Onlineboard'`. `IPopularRequest` = union of `IPopularRouteRequest | IPopularArrivalRequest | IPopularDepartureRequest | IPopularFlightNumberRequest`.
- **Components:** `PopularRequestsComponent` (container, fetches data, handles clicks + navigation), `PopularRequestComponent` (switch by mode), `ArrivalRequestComponent`, `DepartureRequestComponent`, `FlightNumberRequestComponent`, `RouteRequestComponent`, `RequestInfoComponent` (styled clickable span).
- **Behavior:** On click, populates filter state in `OnlineBoardFiltersStateService` / `ScheduleFiltersStateService` and navigates to `onlineboard` or `schedule` route. Uses `cityName` pipe (maps IATA code to city name via dictionaries).
- **Search History:** `SearchHistoryService` — in-memory array of `ISearchHistoryItem` with dedup-by-URL and prepend logic. Displayed on start pages. React version should persist to localStorage via `@/shared/storage`.
- **No dedicated route** — embedded inside `OnlineBoardStartPage` and `ScheduleStartPage`.
---
## Sub-plan inventory
| ID | Sub-plan | Estimated size | Description |
|---|---|---|---|
| **5A** | Types + API + hooks | Small (8-12 tasks) | `PopularRequest` types, `getPopularRequests` API fn, `usePopularRequests` hook |
| **5B** | Route page + components | Medium (12-18 tasks) | `PopularRequestsPanel`, mode-specific sub-components, MF expose update |
| **5C** | SEO + parity + integration tests | Small (6-10 tasks) | Verify no SEO regression, integration tests for the panel |
| **5D** | Search history hook | Small (8-12 tasks) | `useSearchHistory` with per-language localStorage namespacing via `@/shared/storage` |
---
## Dependency graph
```
┌──────────────────────────────┐
│ 5A Types + API + hooks │
│ src/features/popular- │
│ requests/{types,api,hooks} │
└──────────────┬───────────────┘
┌──────────────▼───────────────┐
│ 5B Components + MF expose │◄── depends on 5A types/hooks
│ src/features/popular- │
│ requests/components/ │
│ src/mf/expose/ │
└──────────────┬───────────────┘
┌──────────────▼───────────────┐
│ 5C SEO + parity + tests │◄── depends on 5B components
└──────────────────────────────┘
┌──────────────────────────────┐
│ 5D useSearchHistory hook │ (independent of 5A-5C)
│ src/shared/hooks/ │
└──────────────────────────────┘
```
5D is independent and can be executed in parallel with 5A-5C.
---
## Contracts exported per sub-plan
### 5A — Types + API + hooks
**Files created:**
- `src/features/popular-requests/types.ts``RequestMode`, `PopularRequestType`, `PopularRequest` union, sub-types
- `src/features/popular-requests/api.ts``getPopularRequests(client: ApiClient): Promise<PopularRequest[]>`
- `src/features/popular-requests/api.test.ts` — API function tests
- `src/features/popular-requests/hooks/usePopularRequests.ts` — React hook wrapping the API call
- `src/features/popular-requests/index.ts` — barrel update
**Consumed by:** 5B (types + hook), 5C (tests)
### 5B — Components + MF expose
**Files created:**
- `src/features/popular-requests/components/PopularRequestsPanel.tsx` — container component
- `src/features/popular-requests/components/PopularRequestItem.tsx` — mode-switching renderer
- `src/features/popular-requests/components/RequestInfo.tsx` — styled clickable span
**Files modified:**
- `src/mf/expose/PopularRequests.tsx` — stub replaced with real component
- `src/features/popular-requests/index.ts` — barrel update
**Consumed by:** 5C (integration tests), route pages (OnlineBoardStartPage, ScheduleStartPage)
### 5C — SEO + parity + integration tests
**Files created:**
- `src/features/popular-requests/components/PopularRequestsPanel.test.tsx` — component tests
**Consumed by:** Exit gate verification
### 5D — Search history hook
**Files created:**
- `src/shared/hooks/useSearchHistory.ts` — hook with per-language localStorage namespacing
- `src/shared/hooks/useSearchHistory.test.ts` — hook tests
**Files modified:**
- `src/features/popular-requests/index.ts` — re-export if appropriate
**Consumed by:** Start pages (OnlineBoardStartPage, ScheduleStartPage) in future integration