diff --git a/client/src/api/flightsSlice.ts b/client/src/api/flightsSlice.ts
index 84ab5cce..6329f7da 100644
--- a/client/src/api/flightsSlice.ts
+++ b/client/src/api/flightsSlice.ts
@@ -76,6 +76,8 @@ export const {
} = flightsSlice.actions;
export const selectFlights = (state: RootState) => state.flights;
+export const selectSelectedFlightId = (state: RootState) =>
+ state.flights.selected;
export const selectSelectedFlight = (state: RootState) => {
const id = state.flights.selected;
return id ? state.flights.flights[id] : null;
diff --git a/client/src/components/liberationmap/LiberationMap.tsx b/client/src/components/liberationmap/LiberationMap.tsx
index ba1bb932..a25f2a36 100644
--- a/client/src/components/liberationmap/LiberationMap.tsx
+++ b/client/src/components/liberationmap/LiberationMap.tsx
@@ -11,6 +11,7 @@ import SupplyRoutesLayer from "../supplyrouteslayer";
import TerrainZonesLayers from "../terrainzones/TerrainZonesLayers";
import TgosLayer from "../tgoslayer/TgosLayer";
import { CoalitionThreatZones } from "../threatzones";
+import { WaypointDebugZonesControls } from "../waypointdebugzones/WaypointDebugZonesControls";
import "./LiberationMap.css";
import { Map } from "leaflet";
import { useEffect, useRef } from "react";
@@ -96,6 +97,7 @@ export default function LiberationMap() {
+
);
diff --git a/client/src/components/waypointdebugzones/HoldZones.tsx b/client/src/components/waypointdebugzones/HoldZones.tsx
new file mode 100644
index 00000000..54bd6f19
--- /dev/null
+++ b/client/src/components/waypointdebugzones/HoldZones.tsx
@@ -0,0 +1,96 @@
+import { useGetDebugHoldZonesQuery } from "../../api/liberationApi";
+import { LayerGroup, Polygon, Polyline } from "react-leaflet";
+
+interface HoldZonesProps {
+ flightId: string;
+}
+
+function HoldZones(props: HoldZonesProps) {
+ const { data, error, isLoading } = useGetDebugHoldZonesQuery({
+ flightId: props.flightId,
+ });
+
+ if (isLoading) {
+ return <>>;
+ }
+
+ if (error) {
+ console.error("Error while loading waypoint IP zone info", error);
+ return <>>;
+ }
+
+ if (!data) {
+ console.log("Waypoint IP zone returned empty response");
+ return <>>;
+ }
+
+ return (
+ <>
+
+
+
+
+ {data.excludedZones.map((zone, idx) => {
+ return (
+
+ );
+ })}
+
+ {data.permissibleZones.map((zone, idx) => {
+ return (
+
+ );
+ })}
+
+ {data.preferredLines.map((zone, idx) => {
+ return (
+
+ );
+ })}
+ >
+ );
+}
+
+interface HoldZonesLayerProps {
+ flightId: string | null;
+}
+
+export function HoldZonesLayer(props: HoldZonesLayerProps) {
+ return (
+
+ {props.flightId ? : <>>}
+
+ );
+}
diff --git a/client/src/components/waypointdebugzones/IpZones.tsx b/client/src/components/waypointdebugzones/IpZones.tsx
new file mode 100644
index 00000000..39beef27
--- /dev/null
+++ b/client/src/components/waypointdebugzones/IpZones.tsx
@@ -0,0 +1,73 @@
+import { useGetDebugIpZonesQuery } from "../../api/liberationApi";
+import { LayerGroup, Polygon } from "react-leaflet";
+
+interface IpZonesProps {
+ flightId: string;
+}
+
+function IpZones(props: IpZonesProps) {
+ const { data, error, isLoading } = useGetDebugIpZonesQuery({
+ flightId: props.flightId,
+ });
+
+ if (isLoading) {
+ return <>>;
+ }
+
+ if (error) {
+ console.error("Error while loading waypoint IP zone info", error);
+ return <>>;
+ }
+
+ if (!data) {
+ console.log("Waypoint IP zone returned empty response");
+ return <>>;
+ }
+
+ return (
+ <>
+
+
+
+
+ {data.safeZones.map((zone, idx) => {
+ return (
+
+ );
+ })}
+ >
+ );
+}
+
+interface IpZonesLayerProps {
+ flightId: string | null;
+}
+
+export function IpZonesLayer(props: IpZonesLayerProps) {
+ return (
+
+ {props.flightId ? : <>>}
+
+ );
+}
diff --git a/client/src/components/waypointdebugzones/JoinZones.tsx b/client/src/components/waypointdebugzones/JoinZones.tsx
new file mode 100644
index 00000000..7a67222d
--- /dev/null
+++ b/client/src/components/waypointdebugzones/JoinZones.tsx
@@ -0,0 +1,96 @@
+import { useGetDebugJoinZonesQuery } from "../../api/liberationApi";
+import { LayerGroup, Polygon, Polyline } from "react-leaflet";
+
+interface JoinZonesProps {
+ flightId: string;
+}
+
+function JoinZones(props: JoinZonesProps) {
+ const { data, error, isLoading } = useGetDebugJoinZonesQuery({
+ flightId: props.flightId,
+ });
+
+ if (isLoading) {
+ return <>>;
+ }
+
+ if (error) {
+ console.error("Error while loading waypoint join zone info", error);
+ return <>>;
+ }
+
+ if (!data) {
+ console.log("Waypoint join zone returned empty response");
+ return <>>;
+ }
+
+ return (
+ <>
+
+
+
+
+ {data.excludedZones.map((zone, idx) => {
+ return (
+
+ );
+ })}
+
+ {data.permissibleZones.map((zone, idx) => {
+ return (
+
+ );
+ })}
+
+ {data.preferredLines.map((zone, idx) => {
+ return (
+
+ );
+ })}
+ >
+ );
+}
+
+interface JoinZonesLayerProps {
+ flightId: string | null;
+}
+
+export function JoinZonesLayer(props: JoinZonesLayerProps) {
+ return (
+
+ {props.flightId ? : <>>}
+
+ );
+}
diff --git a/client/src/components/waypointdebugzones/WaypointDebugZonesControls.tsx b/client/src/components/waypointdebugzones/WaypointDebugZonesControls.tsx
new file mode 100644
index 00000000..7982fda4
--- /dev/null
+++ b/client/src/components/waypointdebugzones/WaypointDebugZonesControls.tsx
@@ -0,0 +1,30 @@
+import { selectSelectedFlightId } from "../../api/flightsSlice";
+import { useAppSelector } from "../../app/hooks";
+import { HoldZonesLayer } from "./HoldZones";
+import { IpZonesLayer } from "./IpZones";
+import { JoinZonesLayer } from "./JoinZones";
+import { LayersControl } from "react-leaflet";
+
+const ENABLE_EXPENSIVE_DEBUG_TOOLS = false;
+
+export function WaypointDebugZonesControls() {
+ const selectedFlightId = useAppSelector(selectSelectedFlightId);
+
+ if (!ENABLE_EXPENSIVE_DEBUG_TOOLS) {
+ return <>>;
+ }
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+ >
+ );
+}