diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index a4d53e7..2abc22e 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -4,8 +4,9 @@ import { API_BASE } from "@/lib/api"; import { useEffect, useState, useRef, useCallback } from "react"; import dynamic from 'next/dynamic'; import { motion } from "framer-motion"; +import { ChevronLeft, ChevronRight } from "lucide-react"; import WorldviewLeftPanel from "@/components/WorldviewLeftPanel"; -import WorldviewRightPanel from "@/components/WorldviewRightPanel"; + import NewsFeed from "@/components/NewsFeed"; import MarketsPanel from "@/components/MarketsPanel"; import FilterPanel from "@/components/FilterPanel"; @@ -123,6 +124,8 @@ export default function Dashboard() { // Stable reference for child components — only changes when dataVersion increments const data = dataRef.current; const [uiVisible, setUiVisible] = useState(true); + const [leftOpen, setLeftOpen] = useState(true); + const [rightOpen, setRightOpen] = useState(true); const [settingsOpen, setSettingsOpen] = useState(false); const [legendOpen, setLegendOpen] = useState(false); const [mapView, setMapView] = useState({ zoom: 2, latitude: 20 }); @@ -435,21 +438,54 @@ export default function Dashboard() {
VSR
- {/* LEFT HUD CONTAINER */} -
+ {/* LEFT HUD CONTAINER — slides off left edge when hidden */} + {/* LEFT PANEL - DATA LAYERS */} setSettingsOpen(true)} onLegendClick={() => setLegendOpen(true)} gibsDate={gibsDate} setGibsDate={setGibsDate} gibsOpacity={gibsOpacity} setGibsOpacity={setGibsOpacity} onEntityClick={setSelectedEntity} onFlyTo={(lat, lng) => setFlyToLocation({ lat, lng, ts: Date.now() })} /> + - {/* LEFT BOTTOM - DISPLAY CONFIG */} - - - -
+ {/* LEFT SIDEBAR TOGGLE TAB */} + + + - {/* RIGHT HUD CONTAINER */} -
+ {/* RIGHT SIDEBAR TOGGLE TAB */} + + + + + {/* RIGHT HUD CONTAINER — slides off right edge when hidden */} + {/* FIND / LOCATE */} @@ -505,7 +541,7 @@ export default function Dashboard() {
- + {/* BOTTOM CENTER COORDINATE / LOCATION BAR — hidden when Sentinel-2 imagery overlay is open */} {!(selectedEntity?.type === 'region_dossier' && regionDossier?.sentinel2) && { + const interpFlight = useCallback((f: any): [number, number] => { if (!f.speed_knots || f.speed_knots <= 0 || dtSeconds <= 0) return [f.lng, f.lat]; if (f.alt != null && f.alt <= 100) return [f.lng, f.lat]; if (dtSeconds < 1) return [f.lng, f.lat]; const heading = f.true_track || f.heading || 0; const [newLat, newLng] = interpolatePosition(f.lat, f.lng, heading, f.speed_knots, dtSeconds); return [newLng, newLat]; - }; + }, [dtSeconds]); // Helper: interpolate a ship's position using SOG + heading - const interpShip = (s: any): [number, number] => { + const interpShip = useCallback((s: any): [number, number] => { if (typeof s.sog !== 'number' || !s.sog || s.sog <= 0 || dtSeconds <= 0) return [s.lng, s.lat]; const heading = (typeof s.cog === 'number' ? s.cog : 0) || s.heading || 0; const [newLat, newLng] = interpolatePosition(s.lat, s.lng, heading, s.sog, dtSeconds); return [newLng, newLat]; - }; + }, [dtSeconds]); // Helper: interpolate a satellite's position between API updates - const interpSat = (s: any): [number, number] => { + const interpSat = useCallback((s: any): [number, number] => { if (!s.speed_knots || s.speed_knots <= 0 || dtSeconds < 1) return [s.lng, s.lat]; const [newLat, newLng] = interpolatePosition(s.lat, s.lng, s.heading || 0, s.speed_knots, dtSeconds, 0, 65); return [newLng, newLat]; - }; + }, [dtSeconds]); // Satellite GeoJSON with interpolated positions const satellitesGeoJSON = useMemo(() => {