# Popular Requests Form Pre-fill 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. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Make popular request clicks pre-fill the search form on the start page via query params, matching Angular behavior.

**Architecture:** Each start page reads query params via `useSearchParams()` from `@modern-js/runtime/router`. The click handler builds a URL with query params and navigates to the same page. The form initializes state from those params.

**Tech Stack:** React, Modern.js router (`useSearchParams`, `useNavigate`), existing `PopularRequest` types.

---

## File Structure

### Modified files
- `src/features/online-board/components/OnlineBoardStartPage.tsx` — Add click handler + query param reading + pass initial props to filter
- `src/features/schedule/components/ScheduleStartPage.tsx` — Add click handler + query param reading + initialize form state from params

### No changes needed
- `src/features/online-board/components/OnlineBoardFilter.tsx` — Already accepts `initialDeparture`, `initialArrival`, `initialDate`, `initialTab`, `initialFlightNumber` props
- `src/features/popular-requests/` — No changes to any popular requests components

---

### Task 1: OnlineBoardStartPage — Pre-fill on Popular Request Click

**Files:**
- Modify: `src/features/online-board/components/OnlineBoardStartPage.tsx`

- [ ] **Step 1: Write the failing test**

Create `src/features/online-board/components/OnlineBoardStartPage.test.tsx`:

```tsx
import { describe, it, expect, vi } from "vitest";
import { buildPopularRequestQueryParams } from "./OnlineBoardStartPage.js";

describe("buildPopularRequestQueryParams", () => {
  it("returns tab=flight with carrier and flight for FlightNumber mode", () => {
    const params = buildPopularRequestQueryParams({
      mode: "FlightNumber",
      carrier: "SU",
      flightNumber: "0654",
      type: "Onlineboard",
    });
    expect(params.get("tab")).toBe("flight");
    expect(params.get("carrier")).toBe("SU");
    expect(params.get("flight")).toBe("0654");
  });

  it("returns tab=route with departure for Departure mode", () => {
    const params = buildPopularRequestQueryParams({
      mode: "Departure",
      departure: "LED",
      type: "Onlineboard",
    });
    expect(params.get("tab")).toBe("route");
    expect(params.get("departure")).toBe("LED");
    expect(params.has("arrival")).toBe(false);
  });

  it("returns tab=route with arrival for Arrival mode", () => {
    const params = buildPopularRequestQueryParams({
      mode: "Arrival",
      arrival: "VKO",
      type: "Onlineboard",
    });
    expect(params.get("tab")).toBe("route");
    expect(params.get("arrival")).toBe("VKO");
    expect(params.has("departure")).toBe(false);
  });

  it("returns tab=route with both cities for Route mode", () => {
    const params = buildPopularRequestQueryParams({
      mode: "Route",
      departure: "LED",
      arrival: "KRR",
      type: "Onlineboard",
    });
    expect(params.get("tab")).toBe("route");
    expect(params.get("departure")).toBe("LED");
    expect(params.get("arrival")).toBe("KRR");
  });
});
```

- [ ] **Step 2: Run test to verify it fails**

Run: `pnpm vitest run src/features/online-board/components/OnlineBoardStartPage.test.tsx`

Expected: FAIL — `buildPopularRequestQueryParams` is not exported.

- [ ] **Step 3: Implement the query param builder and wire up the component**

Edit `src/features/online-board/components/OnlineBoardStartPage.tsx`. Replace the entire file with:

```tsx
/**
 * Online Board start page matching the Angular `online-board-start-page`
 * component DOM structure and CSS class names.
 *
 * Reads query params (set by popular request clicks) and passes them
 * as initial props to OnlineBoardFilter for form pre-fill.
 *
 * @module
 */

import { type FC, useCallback } from "react";
import { useNavigate, useParams, useSearchParams } from "@modern-js/runtime/router";
import { useTranslation } from "@/i18n/provider.js";
import { PageLayout } from "@/ui/layout/PageLayout.js";
import { PageTabs } from "@/ui/layout/PageTabs.js";
import { SearchHistory } from "@/ui/layout/SearchHistory.js";
import { OnlineBoardFilter } from "./OnlineBoardFilter.js";
import { PopularRequestsPanel } from "@/features/popular-requests/components/PopularRequestsPanel.js";
import type { PopularRequest } from "@/features/popular-requests/types.js";
import "./OnlineBoardStartPage.scss";

/** Build query params from a popular request for form pre-fill. */
export function buildPopularRequestQueryParams(request: PopularRequest): URLSearchParams {
  const params = new URLSearchParams();

  switch (request.mode) {
    case "FlightNumber":
      params.set("tab", "flight");
      params.set("carrier", request.carrier);
      params.set("flight", request.flightNumber);
      break;
    case "Departure":
      params.set("tab", "route");
      params.set("departure", request.departure);
      break;
    case "Arrival":
      params.set("tab", "route");
      params.set("arrival", request.arrival);
      break;
    case "Route":
    case "RouteWithBack":
      params.set("tab", "route");
      params.set("departure", request.departure);
      params.set("arrival", request.arrival);
      break;
  }

  return params;
}

export const OnlineBoardStartPage: FC = () => {
  const { t } = useTranslation();
  const navigate = useNavigate();
  const routeParams = useParams<{ lang: string }>();
  const lang = routeParams.lang ?? "ru";
  const [searchParams] = useSearchParams();

  const handlePopularRequestClick = useCallback((request: PopularRequest) => {
    // Schedule-type requests navigate to the schedule page
    if (request.type === "Schedule") {
      const params = new URLSearchParams();
      if (request.mode === "Route" || request.mode === "RouteWithBack") {
        params.set("departure", request.departure);
        params.set("arrival", request.arrival);
        if (request.mode === "RouteWithBack") {
          params.set("return", "true");
        }
      }
      void navigate(`/${lang}/schedule?${params.toString()}`);
      return;
    }

    const qp = buildPopularRequestQueryParams(request);
    void navigate(`/${lang}/onlineboard?${qp.toString()}`);
  }, [navigate, lang]);

  // Read query params for filter pre-fill
  const initialTab = searchParams.get("tab") === "flight" ? "flight" as const : searchParams.has("tab") ? "route" as const : undefined;
  const initialFlightNumber = searchParams.get("carrier") && searchParams.get("flight")
    ? `${searchParams.get("carrier")}${searchParams.get("flight")}`
    : undefined;
  const initialDeparture = searchParams.get("departure") ?? undefined;
  const initialArrival = searchParams.get("arrival") ?? undefined;

  return (
    <div className="online-board-start-page" data-testid="online-board-start">
      <PageLayout
        headerLeft={
          <PageTabs viewType="onlineboard" />
        }
        title={
          <h1 className="text--white page-title">
            {t("BOARD.TITLE")}
          </h1>
        }
        breadcrumbs={[]}
        contentLeft={
          <>
            <OnlineBoardFilter
              initialTab={initialTab}
              initialFlightNumber={initialFlightNumber}
              initialDeparture={initialDeparture}
              initialArrival={initialArrival}
            />
            <SearchHistory />
          </>
        }
      >
        <section className="frame">
          <h2>{t("BOARD.BOARD-START")}</h2>

          <div className="titles-container">
            <div className="title title1">
              <a>{t("BOARD.BOARD-START-TITLE1")}</a>
              <div>
                {t("BOARD.BOARD-START-TITLE1-DESCRIPTION")}
              </div>
            </div>

            <div className="title title2">
              <a>{t("BOARD.BOARD-START-TITLE2")}</a>
              <div>
                {t("BOARD.BOARD-START-TITLE2-DESCRIPTION")}
              </div>
            </div>

            <div className="title title3">
              <a>{t("BOARD.BOARD-START-TITLE3")}</a>
              <div>
                {t("BOARD.BOARD-START-TITLE3-DESCRIPTION")}
              </div>
            </div>

            <div className="title title4">
              <a>{t("BOARD.BOARD-START-TITLE4")}</a>
              <div>
                {t("BOARD.BOARD-START-TITLE4-DESCRIPTION")}
              </div>
            </div>
          </div>

          <PopularRequestsPanel onRequestClick={handlePopularRequestClick} />
        </section>
      </PageLayout>
    </div>
  );
};
```

- [ ] **Step 4: Run test to verify it passes**

Run: `pnpm vitest run src/features/online-board/components/OnlineBoardStartPage.test.tsx`

Expected: PASS — all 4 tests pass.

- [ ] **Step 5: Run existing tests to check for regressions**

Run: `pnpm test`

Expected: All existing tests pass. No regressions.

- [ ] **Step 6: Commit**

```bash
git add src/features/online-board/components/OnlineBoardStartPage.tsx src/features/online-board/components/OnlineBoardStartPage.test.tsx
git commit -m "Wire popular request clicks to pre-fill OnlineBoard filter via query params"
```

---

### Task 2: ScheduleStartPage — Pre-fill on Popular Request Click

**Files:**
- Modify: `src/features/schedule/components/ScheduleStartPage.tsx`

- [ ] **Step 1: Write the failing test**

Create `src/features/schedule/components/ScheduleStartPage.test.tsx`:

```tsx
import { describe, it, expect } from "vitest";
import { buildSchedulePopularRequestQueryParams } from "./ScheduleStartPage.js";

describe("buildSchedulePopularRequestQueryParams", () => {
  it("returns departure and arrival for Route mode", () => {
    const params = buildSchedulePopularRequestQueryParams({
      mode: "Route",
      departure: "LED",
      arrival: "KRR",
      type: "Schedule",
    });
    expect(params.get("departure")).toBe("LED");
    expect(params.get("arrival")).toBe("KRR");
    expect(params.has("return")).toBe(false);
  });

  it("returns departure, arrival, and return=true for RouteWithBack mode", () => {
    const params = buildSchedulePopularRequestQueryParams({
      mode: "RouteWithBack",
      departure: "VKO",
      arrival: "KUF",
      type: "Schedule",
    });
    expect(params.get("departure")).toBe("VKO");
    expect(params.get("arrival")).toBe("KUF");
    expect(params.get("return")).toBe("true");
  });

  it("returns empty params for non-route modes", () => {
    const params = buildSchedulePopularRequestQueryParams({
      mode: "Arrival",
      arrival: "VKO",
      type: "Onlineboard",
    });
    expect(params.toString()).toBe("");
  });
});
```

- [ ] **Step 2: Run test to verify it fails**

Run: `pnpm vitest run src/features/schedule/components/ScheduleStartPage.test.tsx`

Expected: FAIL — `buildSchedulePopularRequestQueryParams` is not exported.

- [ ] **Step 3: Implement the query param builder and wire up the component**

Edit `src/features/schedule/components/ScheduleStartPage.tsx`. Make these changes:

**Add imports** — add `useSearchParams` to the router import:

```tsx
import { useNavigate, useParams, useSearchParams } from "@modern-js/runtime/router";
```

**Add the exported builder function** — after the `addDays` helper, before the component:

```tsx
/** Build query params from a popular request for schedule form pre-fill. */
export function buildSchedulePopularRequestQueryParams(request: PopularRequest): URLSearchParams {
  const params = new URLSearchParams();
  if (request.mode === "Route" || request.mode === "RouteWithBack") {
    params.set("departure", request.departure);
    params.set("arrival", request.arrival);
    if (request.mode === "RouteWithBack") {
      params.set("return", "true");
    }
  }
  return params;
}
```

**Read search params in the component** — after the `lang` line, add:

```tsx
const [searchParams] = useSearchParams();
```

**Initialize form state from query params** — change the `useState` calls:

```tsx
const [departureAirport, setDepartureAirport] = useState<CitySuggestion | string>(searchParams.get("departure") ?? "");
const [arrivalAirport, setArrivalAirport] = useState<CitySuggestion | string>(searchParams.get("arrival") ?? "");
const [dateFrom, setDateFrom] = useState<Date | null>(today);
const [dateTo, setDateTo] = useState<Date | null>(addDays(today, 7));
const [timeRange, setTimeRange] = useState<[number, number]>([0, 1440]);
const [directOnly, setDirectOnly] = useState(false);
const [isRoundTrip, setIsRoundTrip] = useState(searchParams.get("return") === "true");
const [returnDateFrom, setReturnDateFrom] = useState<Date | null>(addDays(today, 7));
const [returnDateTo, setReturnDateTo] = useState<Date | null>(addDays(today, 14));
const [returnTimeRange, setReturnTimeRange] = useState<[number, number]>([0, 1440]);
```

**Implement the click handler** — replace the empty callback:

```tsx
const handlePopularRequestClick = useCallback((request: PopularRequest) => {
  // Onlineboard-type requests navigate to the onlineboard page
  if (request.type === "Onlineboard") {
    const params = new URLSearchParams();
    switch (request.mode) {
      case "FlightNumber":
        params.set("tab", "flight");
        params.set("carrier", request.carrier);
        params.set("flight", request.flightNumber);
        break;
      case "Departure":
        params.set("tab", "route");
        params.set("departure", request.departure);
        break;
      case "Arrival":
        params.set("tab", "route");
        params.set("arrival", request.arrival);
        break;
      case "Route":
        params.set("tab", "route");
        params.set("departure", request.departure);
        params.set("arrival", request.arrival);
        break;
    }
    void navigate(`/${lang}/onlineboard?${params.toString()}`);
    return;
  }

  const qp = buildSchedulePopularRequestQueryParams(request);
  void navigate(`/${lang}/schedule?${qp.toString()}`);
}, [navigate, lang]);
```

- [ ] **Step 4: Run test to verify it passes**

Run: `pnpm vitest run src/features/schedule/components/ScheduleStartPage.test.tsx`

Expected: PASS — all 3 tests pass.

- [ ] **Step 5: Run all tests**

Run: `pnpm test`

Expected: All tests pass, no regressions.

- [ ] **Step 6: Commit**

```bash
git add src/features/schedule/components/ScheduleStartPage.tsx src/features/schedule/components/ScheduleStartPage.test.tsx
git commit -m "Wire popular request clicks to pre-fill Schedule form via query params"
```

---

### Task 3: Manual Verification

- [ ] **Step 1: Start the React dev server**

Run: `pnpm dev`

- [ ] **Step 2: Navigate to Online Board start page and test popular requests**

Open `http://localhost:8080/ru/onlineboard` in browser.

Verify:
- Click each popular request item
- URL updates with query params (e.g., `?tab=flight&carrier=SU&flight=0654`)
- Filter form is pre-filled with the correct values
- Clicking "Search" from the pre-filled form navigates to the correct search results page

- [ ] **Step 3: Navigate to Schedule start page and test popular requests**

Open `http://localhost:8080/ru/schedule` in browser.

Verify:
- Click a Route-type popular request
- URL updates with query params (e.g., `?departure=LED&arrival=KRR`)
- Departure and arrival fields are pre-filled
- For RouteWithBack: round-trip checkbox is checked
- Clicking "Search" navigates to schedule search results

- [ ] **Step 4: Run comparison pipeline to verify improvement**

Run: `pnpm compare:behavior`

Expected: Popular requests tests (spec 19) that previously failed should now pass for the React project.
