diff --git a/docs/superpowers/plans/2026-04-22-p4-results-lists.md b/docs/superpowers/plans/2026-04-22-p4-results-lists.md new file mode 100644 index 00000000..c3c23c88 --- /dev/null +++ b/docs/superpowers/plans/2026-04-22-p4-results-lists.md @@ -0,0 +1,373 @@ +# P4 — Results Lists (Online-Board + Schedule) Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:subagent-driven-development` (recommended) or `superpowers:executing-plans` to implement this plan task-by-task. + +**Goal:** Bring §4.1.13 (Online-Board results list) and §4.1.14 (Schedule results list) of TZ РИ-07-2538С into compliance. This includes day-tabs + week-tabs navigation, sort order, and the collapsed + expanded row representations for direct, multi-segment, and connecting flights. + +**Architecture:** Results list UI is built on existing `src/ui/flights/*` primitives (`FlightCard`, `FlightList`, `OperatorLogo`, `TimeGroup`, `StationDisplay`, `DurationDisplay`, `FlightStatus`) plus feature-level components (`DayTabs`, `WeekTabs`, `DayGroupedFlightList`, `ScheduleFlightBody`, `TransferBar`). P4 is largely an **audit + gap-fill pass** against TZ Tables 20–41: verify each column, status chip, stickiness, scroll-to-current-time, expanded-row content per mode. + +**Tech Stack:** TypeScript, React 18, Vitest + RTL, Playwright. No new dependencies. + +**Parent spec:** `docs/superpowers/specs/2026-04-21-online-board-schedule-tz-redesign-design.md` (current HEAD: `793637f`). + +**Rule IDs covered:** §4.1.13 (+.1 Day-tabs, .2 Sort, .3 Collapsed, .4 Expanded) + §4.1.14 (+.1 Week-tabs, .2 Sort, .3 Collapsed, .4 Expanded). Target total: **150–200 rules**. + +--- + +## File Structure + +### Files to create +- `src/ui/flights/CurrentTimeIndicator.tsx` — reusable line/marker showing user's current local time on the day's results list (per §4.1.13 opening ¶). +- `src/ui/flights/CurrentTimeIndicator.test.tsx`. +- `src/features/online-board/scrollToCurrentTime.ts` — helper that, given a list of flights + current time, returns the index of the flight closest to now (for "scroll-to-current-time + expand" behavior). +- `src/features/online-board/scrollToCurrentTime.test.ts`. +- `tests/e2e/p4-results-lists.spec.ts` — Playwright spec for day/week tabs + row expand/collapse + scroll behavior. + +### Files to modify +- `src/features/online-board/components/DayTabs/*` — 7-day visible window, paging by 7, keyboard nav, mobile-list variant per TZ Table 22 mobile column. +- `src/features/online-board/components/OnlineBoardSearchPage.tsx` — scroll-to-current-time + auto-expand nearest flight on today's tab. +- `src/ui/flights/FlightCard.tsx` — audit collapsed + expanded row against §4.1.13.3 / .3.4 tables. +- `src/ui/flights/FlightList.tsx` — audit list structure, sticky behavior, scroll-up button per TZ Table 22. +- `src/features/schedule/components/WeekTabs.tsx` — audit week-tabs behavior per §4.1.14.1 (month span, keyboard, scroll). +- `src/features/schedule/components/DayGroupedFlightList.tsx` — audit day-grouping, weekday masks, operator-logo compactness (existing commit `3ae59da` preserved). +- `src/features/schedule/components/ScheduleFlightBody.tsx` — audit expanded-row (multi-segment + connecting per §4.1.14.4). +- `src/features/online-board/components/TransferBar/*` — audit transfer visualization for Schedule results expansion. +- `docs/superpowers/specs/2026-04-21-online-board-schedule-tz-redesign-design.md` — populate rules (Task 1); mark Done (Task 10). + +### Files reviewed, not modified +- `src/ui/layout/ScrollUpButton.tsx` — verify it matches TZ Table 22 visibility trigger. + +--- + +## Task 1: Populate rule enumeration for §4.1.13 + §4.1.14 + +**Files:** spec. + +- [ ] **Step 1.1** — Read TZ §4.1.13 (lines ~767 to ~1233) and §4.1.14 (lines ~1234 to ~1926). Use `sed -n '767,1234p' /tmp/ri_tz_extract/content.txt` for §4.1.13 and `sed -n '1234,1926p'` for §4.1.14. + +- [ ] **Step 1.2** — Enumerate rules by cluster: + +### §4.1.13 (Online-Board results list) +- Opening-paragraph rules: scroll-to-current-time, auto-expand nearest flight, no pagination, no auto-refresh on today's list, per-day list (not multi-day concat). +- Table 20 structure: desktop/tablet/mobile layouts. +- Table 21: element purposes (filter, history, breadcrumbs, tabs, list). +- Expand/collapse behavior: single-expanded-at-a-time, click triggers. +- Sticky behavior per Table 22: day-tabs sticky top, filter sticky left-column; mobile NOT sticky. +- Scroll-up button visibility triggers. +- **§4.1.13.1 Day-tabs**: 7-day window, paging by 7, active range [-1, +14], "padding" tabs when last active tab isn't the 7th, user-requested day highlighted + auto-scrolled-to, tab-click fetches new day's search. +- **§4.1.13.2 Sort**: no user re-sort; default sort rules per mode (flight-number = departure time; route = segment departure time with yesterday { + beforeEach(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date(2026, 4, 15, 12, 0, 0)); + }); + afterEach(() => vi.useRealTimers()); + + it("returns index of flight closest to current time", () => { + const flights = [ + { departure: { scheduled: "2026-05-15T08:00:00" } }, + { departure: { scheduled: "2026-05-15T11:30:00" } }, + { departure: { scheduled: "2026-05-15T14:00:00" } }, + ]; + expect(findNearestFlightIndex(flights as never, new Date(2026, 4, 15, 12, 0))).toBe(1); + }); + + it("returns 0 when list is empty", () => { + expect(findNearestFlightIndex([], new Date())).toBe(0); + }); + + it("returns the last flight when all flights are in the past", () => { + const flights = [ + { departure: { scheduled: "2026-05-15T06:00:00" } }, + { departure: { scheduled: "2026-05-15T08:00:00" } }, + ]; + expect(findNearestFlightIndex(flights as never, new Date(2026, 4, 15, 23, 0))).toBe(1); + }); + + it("returns the first flight when all flights are in the future", () => { + const flights = [ + { departure: { scheduled: "2026-05-15T18:00:00" } }, + { departure: { scheduled: "2026-05-15T20:00:00" } }, + ]; + expect(findNearestFlightIndex(flights as never, new Date(2026, 4, 15, 6, 0))).toBe(0); + }); + + it("handles arrival-mode flights (sorts by arrival time)", () => { + const flights = [ + { arrival: { scheduled: "2026-05-15T10:00:00" } }, + { arrival: { scheduled: "2026-05-15T13:00:00" } }, + ]; + expect(findNearestFlightIndex(flights as never, new Date(2026, 4, 15, 12, 0), "arrival")).toBe(1); + }); +}); +``` + +- [ ] **Step 2.3** — Run — FAIL (module not found). + +- [ ] **Step 2.4** — Implement: + +```ts +// src/features/online-board/scrollToCurrentTime.ts +/** + * Find the index of the flight in a sorted-by-time list whose scheduled + * departure (or arrival, per `mode`) is closest to `now`. Per TZ §4.1.13 + * opening ¶1 — the nearest flight gets auto-expanded and the list scrolls + * to it on today's tab. + */ + +import type { ISimpleFlight } from "./types.js"; + +type TimeMode = "departure" | "arrival"; + +export function findNearestFlightIndex( + flights: ISimpleFlight[], + now: Date, + mode: TimeMode = "departure", +): number { + if (flights.length === 0) return 0; + let bestIndex = 0; + let bestDiff = Number.POSITIVE_INFINITY; + for (let i = 0; i < flights.length; i++) { + const f = flights[i]; + const iso = mode === "arrival" + ? (f as unknown as { arrival?: { scheduled?: string } }).arrival?.scheduled + : (f as unknown as { departure?: { scheduled?: string } }).departure?.scheduled; + if (!iso) continue; + const diff = Math.abs(new Date(iso).getTime() - now.getTime()); + if (diff < bestDiff) { + bestDiff = diff; + bestIndex = i; + } + } + return bestIndex; +} +``` + +(Adapt `ISimpleFlight` field access to the real type shape — check `src/features/online-board/types.ts`.) + +- [ ] **Step 2.5** — Run — PASS. + +- [ ] **Step 2.6** — Wire into `OnlineBoardSearchPage.tsx`: +- On initial render of today's tab + when `flights` first populates, compute `findNearestFlightIndex`, scroll the list container to that index, and set the expanded-flight state to that index. +- If the user manually expands a different flight, respect that choice (don't re-scroll on subsequent renders). +- Skip for non-today tabs (just render from the first flight, no auto-scroll). + +- [ ] **Step 2.7** — Add component test verifying nearest-flight auto-expand on today's tab only. + +- [ ] **Step 2.8** — Commit: +```bash +git add src/features/online-board/ +git commit -m "Add scroll-to-current-time + auto-expand-nearest on Online-Board today's tab per TZ 4.1.13" +``` + +--- + +## Task 3: Day-tabs audit (§4.1.13.1) + +**Files:** +- Modify: `src/features/online-board/components/DayTabs/*` +- Test: the component's existing test file. + +TZ rules for Day-tabs: +- 7-day visible window. +- Active range: today-1 to today+14. +- Paging by 7 days. +- When last active day isn't the 7th in a page, pad with inactive tabs; right-arrow disabled. +- User-requested day (from URL) highlighted + auto-scrolled to. +- Clicking a tab triggers a new search for that day with current filter. +- Mobile variant: list not tabs. + +- [ ] **Step 3.1** — Inspect existing `DayTabs/` components and tests. Enumerate gaps. + +- [ ] **Step 3.2** — Write failing tests per gap. + +- [ ] **Step 3.3** — Fix. + +- [ ] **Step 3.4** — Commit: +```bash +git add src/features/online-board/components/DayTabs/ +git commit -m "Audit DayTabs behavior per TZ 4.1.13.1 (7-day window, paging, padding, active range)" +``` + +--- + +## Task 4: Sort order (§4.1.13.2) + +**Files:** +- Verify: `src/ui/flights/FlightList.tsx` or wherever sort happens. +- Possibly create: `src/features/online-board/sortFlights.ts` (pure helper). + +TZ default sort: +- Flight-number mode: by departure date. +- Route / Departure modes: by segment-departure time, with ordering yesterday < today < tomorrow. +- Arrival mode: by segment-arrival time, same day ordering. +- No user re-sort. + +- [ ] **Step 4.1** — Grep for existing sort logic: `grep -rn "sort\|compareFlights" src/features/online-board/ src/ui/flights/`. + +- [ ] **Step 4.2** — Extract / verify helper. Add tests for all 4 mode cases + yesterday/today/tomorrow ordering. + +- [ ] **Step 4.3** — Fix gaps. + +- [ ] **Step 4.4** — Commit: +```bash +git add src/features/online-board/ +git commit -m "Verify Online-Board flight-list default sort per TZ 4.1.13.2" +``` + +--- + +## Task 5: Collapsed row representation — Online-Board (§4.1.13.3) + +**Files:** +- Verify/modify: `src/ui/flights/FlightCard.tsx`, `src/ui/flights/OperatorLogo.tsx`, `src/ui/flights/TimeGroup.tsx`, `src/ui/flights/StationDisplay.tsx`, `src/ui/flights/FlightStatus.tsx`. + +TZ Tables 23–27 enumerate every collapsed-row element. Audit each element against its table row: +- Carrier logo + marketing flight number + operator flight number +- From-city + IATA + airport + terminal +- To-city + IATA + airport + terminal +- Scheduled times (with "Yesterday"/"Tomorrow" day-change chips per §4.1.17) +- Aircraft icon (§4.1.22 rules) +- Flight status chip (registration/boarding/departed/arrived/cancelled — enumerate full list from TZ) +- Expand/collapse arrow (state-indicator) +- "Уточняется" fallback text (§4.1.23) +- Multi-segment indicator (if multi-segment flight) + +- [ ] **Step 5.1** — Read TZ Tables 23, 24, 25, 26, 27 fully. + +- [ ] **Step 5.2** — Inspect each UI component. + +- [ ] **Step 5.3** — Enumerate gaps, write tests, fix. + +- [ ] **Step 5.4** — Commit per logical cluster (e.g. one commit for status-chip rules, one for operator-logo rules, etc.). + +--- + +## Task 6: Expanded row representation — Online-Board (§4.1.13.4) + +**Files:** +- Modify: `src/ui/flights/FlightCard.tsx` (expanded content). +- Modify: `src/features/online-board/components/details-panels/*` (may be reused for expanded rows). + +TZ describes live-status ladder, check-in counters, gates, baggage belts, operator+marketing chip, aircraft details, share link, buy-ticket CTA (conditional). + +- [ ] **Step 6.1** — Read §4.1.13.4 TZ body fully. + +- [ ] **Step 6.2** — Inspect current expanded-row implementation. + +- [ ] **Step 6.3** — Gap list + tests + fixes per cluster. + +- [ ] **Step 6.4** — Commit per logical cluster. + +--- + +## Task 7: Week-tabs + Schedule collapsed rows (§4.1.14.1 + §4.1.14.3) + +**Files:** +- Modify: `src/features/schedule/components/WeekTabs.tsx`. +- Verify: `src/features/schedule/components/DayGroupedFlightList.tsx`. +- Verify: `src/features/schedule/components/ScheduleFlightBody.tsx`. + +- [ ] Enumerate + fix per same pattern as Tasks 3+5 but for Schedule weekly view. + +- [ ] Commit per cluster. + +--- + +## Task 8: Schedule expanded rows — multi-segment + connecting (§4.1.14.4) + +**Files:** +- Modify: `src/features/schedule/components/ScheduleFlightBody.tsx`. +- Modify: `src/features/online-board/components/TransferBar/*` (re-used). + +TZ covers per-segment breakdown, transfer bars between segments, connecting-flight layout with transfer time, onward flight link. + +- [ ] Enumerate + fix. + +- [ ] Commit per cluster. + +--- + +## Task 9: Sticky behavior + Scroll-up button (§4.1.13 + §4.1.14 Table 22) + +**Files:** +- Modify: `src/ui/layout/ScrollUpButton.tsx` +- Modify: `src/ui/layout/PageLayout.tsx` (for sticky filter + day-tabs on desktop/tablet) + +- [ ] Verify sticky desktop/tablet, non-sticky mobile per Table 22. + +- [ ] Verify scroll-up button visibility trigger (crumbs + tabs scrolled out of view). + +- [ ] Add tests + fixes. + +- [ ] Commit. + +--- + +## Task 10: Update spec + merge gate + +- [ ] Mark P4 rules Done. +- [ ] Add any new Conflicts. +- [ ] Append merge-log row. +- [ ] Commit. + +--- + +## Self-Review + +**1. Spec coverage.** §4.1.13/4.1.14 fully decomposed into Tasks 2–9. Task 1 pre-populates the rule IDs so every task has grep-able IDs. + +**2. Placeholder scan.** Many tasks use "audit existing code → enumerate gaps → fix" language. This is deliberate for an audit-style plan where the gap size is unknown without reading the specific Angular-derived React code first. Each task has a clear TZ citation and verification pattern; the exhaustive detail lives in the audit tables in the spec. + +**3. Type consistency.** +- `findNearestFlightIndex` (Task 2) uses `ISimpleFlight`-compatible shape. +- Sort helper (Task 4) — to be named `compareFlightsForMode(mode)` or similar consistently. +- `ScrollUpButton` props (Task 9) already established. + +--- + +## Execution Handoff + +P4 is the largest plan by rule count; execute via subagent-driven development with close monitoring. If any single cluster turns out to be fully already-correct (zero code changes needed), add assertion tests and move on quickly.