# 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 currently an inferior port (silent errors, sequential `await`, hardcoded API key). | | 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` |