Add P4 implementation plan: results lists (Online-Board + Schedule)

This commit is contained in:
2026-04-21 22:25:06 +03:00
parent 793637ffc3
commit 890d575e88
@@ -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 2041: 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: **150200 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<today<tomorrow; departure = same; arrival = segment arrival time with same day ordering).
- **§4.1.13.3 Collapsed representation** (Tables 2327): columns per flight type — carrier+logo+marketing/operating, flight number, from-city+airport+terminal, to-city+airport+terminal, scheduled times, aircraft icon, flight status chip (booking/boarding/departed/arrived/cancelled/etc.), expand/collapse arrow.
- **§4.1.13.4 Expanded representation**: live status ladder (registration/boarding/deboarding blocks with times), check-in counters (if published), gate, baggage-belt, operator+marketing, aircraft details, share link, buy ticket (conditional).
### §4.1.14 (Schedule results list)
- Opening-paragraph rules.
- Table structure similar to §4.1.13 but with weekly grouping.
- **§4.1.14.1 Week-tabs**: month-span navigation, multi-week active range per Schedule date window, keyboard nav.
- **§4.1.14.2 Sort**: route mode default sort by earliest departure time in week.
- **§4.1.14.3 Collapsed** (Tables 3034): day-grouping with weekday mask, operator logos (compact in multi-leg — preserved by commit `3ae59da`), times, duration.
- **§4.1.14.4 Expanded**: per-segment breakdown, transfer bars, connecting flight layout with transfer time, onward flight link.
Target rule count: §4.1.13 ≥70, §4.1.14 ≥80.
- [ ] **Step 1.3** — Append to spec, preserve column structure, Status=TBD, Plan=P4, Viewport=all (with mobile/desktop differentiated where TZ differs).
- [ ] **Step 1.4** — Update Coverage summary.
- [ ] **Step 1.5** — Commit:
```bash
git add docs/superpowers/specs/2026-04-21-online-board-schedule-tz-redesign-design.md
git commit -m "Populate rule rows for P4 subsections 4.1.13/14 in TZ audit spec"
```
---
## Task 2: Scroll-to-current-time + auto-expand nearest flight (today's tab)
**Files:**
- Create: `src/features/online-board/scrollToCurrentTime.ts` + test.
- Modify: `src/features/online-board/components/OnlineBoardSearchPage.tsx`.
Per §4.1.13 opening ¶1: on the today's tab, the list scrolls to the current time and the flight nearest to now is auto-expanded.
- [ ] **Step 2.1** — Read `OnlineBoardSearchPage.tsx` to find the list-render + scroll-container pattern.
- [ ] **Step 2.2** — Write pure helper test:
```ts
// src/features/online-board/scrollToCurrentTime.test.ts
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
import { findNearestFlightIndex } from "./scrollToCurrentTime.js";
describe("findNearestFlightIndex", () => {
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 2327 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 29. 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.