# 05 — Flights & Mission Planning

## 1. High-Level Overview

**Purpose**: One logical component covering everything mission/flight related — flight CRUD, waypoint editing, altitude profile, wind/battery calc, raw mission JSON I/O, and **GPS-Denied** operations (incl. an end-to-end **test mode** that simulates a real flight from a tlog + video pair). It is currently **physically split** across two codebases that the project intends to converge.

**Architectural Pattern**: Page composition (`FlightsPage`) wiring three sibling panels — `FlightListSidebar`, `FlightParamsPanel`, `FlightMap` — plus modals (`AltitudeDialog`, `JsonEditorDialog`) and a GPS-Denied sub-page.

**Implementation status — two trees, one component**:

| Tree | What it is | Deployed? |
|------|------------|-----------|
| `src/features/flights/` (15 modules, React 19 + Tailwind) | The **target** implementation. Mostly a mechanical port-in-progress of the tree below. | **Yes** — Dockerfile builds `src/` only. |
| `mission-planner/` (37 modules, React 18 + MUI 5) | The **port source / reference**. The richer, more battle-tested mission planner that the new SPA is being adapted from. | **No.** Disjoint dependency island. Deletion candidate after parity. |

The two trees are intentionally disjoint at the file level (no cross-imports — `00_discovery.md` §1) but they are **one component in the design**: same domain, same data model, same intent. Findings, port plan, and architecture decisions are tracked in this single component spec. Mission-planner files are listed in §"Module Inventory" below alongside the new SPA files; per-finding origin is preserved.

**Upstream dependencies** (target tree): `00_foundation`, `01_api-transport`, `03_shared-ui` (FlightContext, ConfirmDialog).

**Downstream consumers**: `10_app-shell` (routes `/flights` and the GPS-Denied sub-page).

## 2. Internal Interfaces

### Page entries (target tree, `src/features/flights/`)

| Export | Notes |
|--------|-------|
| `FlightsPage()` | Top-level route component for `/flights`. Uses `useFlight()`, fetches flight detail by id, exposes save/delete/duplicate. Wires `react-leaflet`, `leaflet-draw`, and `chart.js`. |
| `GpsDeniedPage()` *(planned route, see §6)* | Sub-page for GPS-denied operations and test mode. Today a partial inline panel inside `FlightsPage` (finding #25) — slated to become its own route. |

### Internal modules — target tree (`src/features/flights/`)

| Module | Role |
|--------|------|
| `FlightsPage.tsx` | Orchestrator (route component) |
| `FlightMap.tsx` | Leaflet map + draw control + waypoint markers + minimap |
| `FlightListSidebar.tsx` | Left panel: flight list, search, new/duplicate/delete |
| `FlightParamsPanel.tsx` | Right panel: name, aircraft, takeoff/landing, altitude chart, waypoint list, wind effect |
| `WaypointList.tsx` | DnD-sortable waypoints (`@hello-pangea/dnd`) |
| `AltitudeChart.tsx` | `chart.js` altitude profile |
| `WindEffect.tsx` | Wind-vector visualisation |
| `MiniMap.tsx` | Inline overview map |
| `MapPoint.tsx` | Single waypoint marker |
| `DrawControl.tsx` | `leaflet-draw` integration |
| `AltitudeDialog.tsx` | Per-waypoint altitude/purpose modal |
| `JsonEditorDialog.tsx` | Raw mission-JSON editor |
| `flightPlanUtils.ts` | Distance / battery / weather computation helpers |
| `mapIcons.ts` | Leaflet icon factory |
| `types.ts` | Local feature types (waypoint shape, mission JSON shape) |

### Internal modules — port source (`mission-planner/`)

| Group | Modules | Role / what the target should learn |
|-------|---------|-------------------------------------|
| Entry | `main.tsx`, `App.tsx` (vestigial CRA stub) | Composition root + LanguageProvider. The CRA stub `App.tsx` is a deletion candidate post-port. |
| Page composition | `flightPlanning/flightPlan.tsx`, `LeftBoard.tsx` | Canonical page shape (sidebar + map). |
| Map | `flightPlanning/MapView.tsx` (cycle-paired with `MiniMap.tsx`), `MiniMap.tsx`, `DrawControl.tsx`, `MapPoint.tsx` | Reference Leaflet integration. **Cycle**: `MiniMap` imports the *named* helper `UpdateMapCenter` from `MapView`; `MapView` imports `MiniMap` as JSX child. Document the contract precisely if porting both at once. |
| Panels | `flightPlanning/PointsList.tsx`, `AltitudeChart.tsx`, `AltitudeDialog.tsx`, `WindEffect.tsx`, `TotalDistance.tsx`, `JsonEditorDialog.tsx`, `LanguageSwitcher.tsx`, `Aircraft.ts` | Reference panel shapes. Several have richer behaviour than the current SPA siblings. |
| Services | `services/calculateBatteryUsage.ts`, `AircraftService.ts`, `WeatherService.ts`, `calculateDistance.ts` | **Authoritative** battery / weather / distance logic. The target's `flightPlanUtils.ts` is still an inferior port on remaining axes (silent errors, sequential `await`). The hardcoded-API-key gap was closed by AZ-448 / AZ-449 (main SPA) and AZ-499 (mission-planner — env-resolved + fail-soft). |
| i18n | `flightPlanning/LanguageContext.tsx`, `constants/translations.ts`, `constants/languages.ts` | Local translation pattern. The port should converge to `00_foundation/i18n` instead. |
| Constants | `constants/{actionModes,maptypes,tileUrls,purposes}.ts` | Reference constant tables. |
| Icons | `icons/{MapIcons,PointIcons,SidebarIcons,PhoneIcon}.tsx` | Reference icon factory. |
| Utilities | `utils.ts`, `config.ts`, `types/index.ts` | Reference helpers + types. |
| Test (vestigial) | `test/jsonImport.test.ts`, `setupTests.ts` | One Jest test that **cannot run today** (Jest not in `package.json`). Out of scope for the live SPA test plan. |

## 3. External API Specification

Endpoints consumed (target tree + planned for GPS-Denied):

| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/flights` | List + page (`pageSize=1000` ceiling from `FlightContext`) |
| GET | `/api/flights/{id}` | Detail + waypoints |
| POST | `/api/flights` | Create |
| PUT | `/api/flights/{id}` | Update flight metadata |
| DELETE | `/api/flights/{id}` | Delete |
| POST/DELETE | `/api/flights/{id}/waypoints` | Add / remove waypoints |
| POST | `/api/gps-denied-desktop/...` | GPS-denied desktop service (mission setup, plan upload) — partial today |
| POST | `/api/gps-denied-onboard/...` | GPS-denied onboard service (frame + IMU consumer) — target of test-mode SITL output |
| GET | OpenWeatherMap (third-party, **direct from browser**) | Wind/temperature lookup. **Will be proxied via suite** as part of Step 4 (hardcoded key removal). |

Concrete GPS-Denied endpoint shapes are **not yet finalised** in the suite spec — flagged for confirmation in autodev Step 3 (Test Spec) and Step 6 (Problem Extraction).

## 5. Implementation Details

**State Management**: Page-local React state for the active flight; `FlightContext` (in `03_shared-ui`) for the list of flights.

**Save model — current shape, target tree** (finding #19): on Save, the UI deletes all existing waypoints and POSTs each one again. N delete + M POST round-trips. **Lossy** — concurrent edits race; if a POST fails halfway through, the saved flight is left with truncated waypoints. The `mission-planner/` reference does this differently (single mission-JSON PUT) and is the recommended target shape.

**Waypoint POST shape mismatch (PRIORITY, finding #20)**: target tree sends `{name, latitude, longitude, order}`; suite spec wants `{Geopoint:{Lat,Lon,MGRS}, Source, Objective, OrderNum, Height}`. Strict server returns 400. Owner of fix: this component + `00_foundation/types/index.ts::Waypoint`.

**Battery / weather calc** (target tree `flightPlanUtils.ts`):
- **Hardcoded OpenWeather API key in source** (`'335799082893fad97fa36118b131f919'`) — committed secret. Step 4 fix.
- Sequential `await` per segment — perf trap on long missions.
- Silent `try/catch` swallows weather errors.
- Mixes km / m altitudes; ambiguous battery-capacity unit (Wh vs Ws).
- Port source (`mission-planner/services/`) has a cleaner, more correct implementation — use it as the reference.

**Map / icons**:
- Target tree `mapIcons.ts::defaultIcon` CDN URL pinned to `leaflet@1.7.1` while `package.json` has 1.9.4.
- `MiniMap` map-tile licence attribution missing in some configurations.

**Modal a11y** (`AltitudeDialog`, `JsonEditorDialog`) — no `aria-modal` / `role="dialog"` / focus trap. Step 4. Same gap on the port-source counterparts.

**Tech-stack divergence between the two trees** (carry into the port plan):

| Concern | `mission-planner/` | `src/features/flights/` (target) |
|---------|-------------------|----------------------------------|
| React | 18 | 19 |
| UI library | MUI 5 | Tailwind 4 + custom `az-*` tokens |
| i18n | local `LanguageContext` | `react-i18next` (Foundation) |
| `react-leaflet` | 4.2 | 5 |
| `@hello-pangea/dnd` | 16 | 18 |

**Key Dependencies** (target tree): `leaflet` 1.9, `react-leaflet` 5, `leaflet-draw`, `leaflet-polylinedecorator`, `chart.js` 4, `@hello-pangea/dnd` 18.

## 6. GPS-Denied sub-feature

Today a **partial** UI panel exists inside `FlightsPage` (target tree finding #25). The component design promotes this to a first-class sub-page with two tabs.

### 6a. Operations tab — current intent (already present, partial)

- Upload / select a mission plan.
- Configure GPS-denied desktop parameters.
- Hand off to onboard.

The exact endpoints are partially wired today and tracked under "External API Specification" above.

### 6b. **Test mode** — new subitem (per `_docs/how_to_test.md`)

> **Source of truth**: `_docs/how_to_test.md`. Architecture Vision artifact (`/document` Step 4.5) MUST surface this verbatim.

**Goal**: enable end-to-end testing of the GPS-denied onboard system **without a real flight**, using a recorded telemetry log + video pair.

**User journey**:

1. **Upload tlog file** (MAVLink telemetry log).
2. **Upload video** synced with the tlog (the two are **usually not** time-aligned at the file level — the system aligns them).
3. The system:
   - Extracts **timestamps, IMU, and GPS** samples from the tlog.
   - **Auto-syncs** video to tlog by detecting the take-off moment in the IMU stream and matching it to the corresponding frame in the video. Most test sessions are quadcopter "ground-to-ground" flights; the start-on-ground signature in the IMU is the alignment anchor.
   - Spawns a **SITL** (Software-In-The-Loop) instance.
   - **Feeds IMU samples + video frames** into the GPS-denied **onboard** system in lockstep.
4. The user observes the onboard system's outputs (position estimate, drift, GPS-recovery moments) on the same map / chart components used for live flights — reusing `FlightMap`, `AltitudeChart`, and a results overlay.

**Scope notes for downstream skills**:

- This sub-feature is **not implemented today**. It is a planned addition that this component will own.
- The tlog parser, IMU/video sync, and SITL controller are **suite-side services**; this component contributes the upload UI, the run controller, and the result overlay.
- Test-mode I/O endpoints will live under `/api/gps-denied-desktop/test/...` (proposed; confirm in `autodev` Step 3 / Step 6).
- The existing `/document` Step 1 finding "GPS-Denied panel partial" remains valid for the Operations tab and is broadened to track the Test mode tab as well.

## 7. Caveats & Edge Cases

26 numbered findings consolidated in `src__features__flights.md`. Highest-priority Step 4 items:

1. **Hardcoded OpenWeather API key** — rotate + remove + proxy via suite.
2. **Waypoint POST shape mismatch + delete-then-recreate save model** — likely-broken on a strict suite server. Reference port-source's PUT-mission-JSON model.
3. **`flightPlanUtils.ts` units / silent errors / sequential awaits.** Replace with port-source services.
4. **Modal a11y** + **flight dropdown a11y** (overlap with Shared UI).
5. **Leaflet 1.7.1 CDN URL drift.**

Mission-planner-side caveats (port-only):

- `mission-planner/src/test/jsonImport.test.ts` cannot run (Jest absent). **Do not** add Jest just for this one test.
- `mission-planner/src/App.tsx` is a CRA stub. Delete only after parity.
- `MapView.tsx ↔ MiniMap.tsx` named-handle cycle. Document precisely.

GPS-Denied-side caveats:

- Operations tab is partial (finding #25 unchanged).
- **Test mode** is unimplemented — no risk of regression today, but the design must accommodate the eventual test-mode entry point in routing and in `FlightContext`.

## 8. Dependency Graph

**Must be implemented after**: `00_foundation`, `01_api-transport`, `03_shared-ui`.

**Can be implemented in parallel with**: every other feature page.

**Blocks**: `10_app-shell` (routes to `/flights` as default authenticated landing; will also host the GPS-Denied sub-route).

**Internal port order** (within this component): port `services/` (battery/weather/distance) → port `flightPlanning/` panels (`AltitudeChart`, `WindEffect`, `PointsList`) → port `MapView/MiniMap` cycle group → fold the `mission-planner/` tree out as the target reaches parity.

## Module Inventory

| Tree | Module Doc |
|------|------------|
| `src/features/flights/*` (15 modules) | `_docs/02_document/modules/src__features__flights.md` |
| `mission-planner/*` (37 modules) | `_docs/02_document/modules/mission-planner.md` |
