Files
maps-saas/apps/web/src/pages/TacticalDefenseView.tsx
T

2596 lines
124 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useEffect, useRef } from 'react';
import maplibregl from 'maplibre-gl';
import 'maplibre-gl/dist/maplibre-gl.css';
import {
Shield,
Crosshair,
Eye,
Radio,
Mountain,
AlertTriangle,
CheckCircle2,
XCircle,
Layers,
Trash2,
Navigation,
Flame,
Activity,
Plus,
Play,
RotateCcw,
Award,
AlertOctagon,
MapPin,
Target,
LocateFixed,
Move,
Building,
TreePine,
Printer,
FileText,
Compass,
Check,
EyeOff,
Sliders
} from 'lucide-react';
import {
calculateLineOfSight,
calculateRadialViewshed,
calculateMinefieldAnalysis,
calculateTerrainStudy,
LineOfSightResult,
Viewshed360Result,
MinefieldAnalysisResult,
TerrainStudyResult,
calculateDistance,
calculateAzimuth
} from '../utils/elevationService';
type TacticalMode = 'terrain' | 'los' | 'viewshed' | 'artillery' | 'minefield' | 'hlz' | 'symbols' | 'study';
interface TacticalSymbolItem {
id: string;
type: 'friendly' | 'hostile' | 'artillery' | 'radar' | 'checkpoint' | 'minefield' | 'op' | 'hlz';
name: string;
lat: number;
lng: number;
}
const REGIONAL_SECTORS = [
{ name: 'عمّان الوسطى', lat: 31.9539, lng: 35.9106, zoom: 12 },
{ name: 'إربد / اليرموك (الشمال)', lat: 32.5568, lng: 35.8469, zoom: 12 },
{ name: 'قاطع المفرق (الشرق)', lat: 32.3421, lng: 36.2081, zoom: 11 },
{ name: 'غور الأردن / البحر الميت', lat: 31.7500, lng: 35.5800, zoom: 12 },
{ name: 'مرتفعات الكرك والشوبك', lat: 31.1853, lng: 35.7048, zoom: 11 },
{ name: 'وادي رم الجنوبي', lat: 29.5700, lng: 35.4200, zoom: 12 },
];
const DEFAULT_SYMBOLS: TacticalSymbolItem[] = [
{ id: 'sym-1', type: 'friendly', name: 'كتيبة المشاة الآلية 4', lat: 31.9800, lng: 35.8800 },
{ id: 'sym-2', type: 'artillery', name: 'مربض مدفعية م109', lat: 31.9300, lng: 35.9100 },
{ id: 'sym-3', type: 'radar', name: 'رادار كشف أرضي تكتيكي', lat: 32.0200, lng: 35.8400 },
{ id: 'sym-4', type: 'checkpoint', name: 'نقطة سيطرة وغلق أمني', lat: 31.9500, lng: 35.9500 },
{ id: 'sym-5', type: 'op', name: 'نقطة مراقبة واستطلاع أمامي', lat: 32.0600, lng: 35.8100 },
];
export const TacticalDefenseView: React.FC = () => {
const mapContainer = useRef<HTMLDivElement>(null);
const map = useRef<maplibregl.Map | null>(null);
const [mode, setMode] = useState<TacticalMode>('terrain');
const [cursorPos, setCursorPos] = useState({ lat: 31.95, lng: 35.93, elev: 850 });
const [activePlacement, setActivePlacement] = useState<string | null>(null);
// 1. Terrain Tactical Study State (دراسة الأرض)
const [terrainCenter, setTerrainCenter] = useState<[number, number] | null>([31.9539, 35.9106]);
const [terrainRadius, setTerrainRadius] = useState<number>(4000);
const [terrainResult, setTerrainResult] = useState<TerrainStudyResult | null>(null);
const [terrainLoading, setTerrainLoading] = useState<boolean>(false);
const [copiedReport, setCopiedReport] = useState<boolean>(false);
// Terrain Visual Layers Toggles on Map
const [showPeaksAndValleys, setShowPeaksAndValleys] = useState<boolean>(true);
const [showSlopeSectors, setShowSlopeSectors] = useState<boolean>(true);
const [showNaturalObstacles, setShowNaturalObstacles] = useState<boolean>(true);
const [showRoadCorridors, setShowRoadCorridors] = useState<boolean>(true);
const [showUrbanZones, setShowUrbanZones] = useState<boolean>(true);
const [showHazardousGround, setShowHazardousGround] = useState<boolean>(true);
// 2. Line of Sight State
const [losPointA, setLosPointA] = useState<[number, number] | null>([31.9539, 35.9106]);
const [losPointB, setLosPointB] = useState<[number, number] | null>([32.0125, 35.8540]);
const [losObsHeight, setLosObsHeight] = useState<number>(2);
const [losTgtHeight, setLosTgtHeight] = useState<number>(2);
const [losResult, setLosResult] = useState<LineOfSightResult | null>(null);
const [losLoading, setLosLoading] = useState<boolean>(false);
// 3. Viewshed State
const [viewshedCenter, setViewshedCenter] = useState<[number, number] | null>([31.9800, 35.8800]);
const [viewshedRadius, setViewshedRadius] = useState<number>(5000);
const [viewshedHeight, setViewshedHeight] = useState<number>(10);
const [viewshedResult, setViewshedResult] = useState<Viewshed360Result | null>(null);
const [viewshedLoading, setViewshedLoading] = useState<boolean>(false);
// 4. Artillery Fire Mission State
const [gunPos, setGunPos] = useState<[number, number] | null>([31.9300, 35.9100]);
const [targetPos, setTargetPos] = useState<[number, number] | null>([32.0400, 35.8200]);
const [caliber, setCaliber] = useState<string>('155mm Howitzer (M109)');
const [muzzleVel, setMuzzleVel] = useState<number>(827);
const [artilleryResult, setArtilleryResult] = useState<any | null>(null);
const [artilleryLoading, setArtilleryLoading] = useState<boolean>(false);
// 5. Minefield Analysis State
const [mineStart, setMineStart] = useState<[number, number] | null>([31.9700, 35.8600]);
const [mineEnd, setMineEnd] = useState<[number, number] | null>([31.9950, 35.8750]);
const [mineDensity, setMineDensity] = useState<'standard' | 'dense' | 'light'>('standard');
const [mineResult, setMineResult] = useState<MinefieldAnalysisResult | null>(null);
const [mineLoading, setMineLoading] = useState<boolean>(false);
// 6. HLZ State
const [hlzCenter, setHlzCenter] = useState<[number, number] | null>([31.9600, 35.9000]);
const [hlzResult, setHlzResult] = useState<any | null>(null);
const [hlzLoading, setHlzLoading] = useState<boolean>(false);
// 7. Tactical Symbols State
const [symbols, setSymbols] = useState<TacticalSymbolItem[]>(DEFAULT_SYMBOLS);
const [selectedSymbolType, setSelectedSymbolType] = useState<string>('friendly');
const [newSymbolName, setNewSymbolName] = useState<string>('');
const [customSymbolLat, setCustomSymbolLat] = useState<number>(31.95);
const [customSymbolLng, setCustomSymbolLng] = useState<number>(35.93);
// Synchronized Refs to avoid stale closures in MapLibre event listeners
const modeRef = useRef(mode);
modeRef.current = mode;
const activePlacementRef = useRef(activePlacement);
activePlacementRef.current = activePlacement;
const terrainCenterRef = useRef(terrainCenter);
terrainCenterRef.current = terrainCenter;
const losPointARef = useRef(losPointA);
losPointARef.current = losPointA;
const losPointBRef = useRef(losPointB);
losPointBRef.current = losPointB;
const viewshedCenterRef = useRef(viewshedCenter);
viewshedCenterRef.current = viewshedCenter;
const gunPosRef = useRef(gunPos);
gunPosRef.current = gunPos;
const targetPosRef = useRef(targetPos);
targetPosRef.current = targetPos;
const mineStartRef = useRef(mineStart);
mineStartRef.current = mineStart;
const mineEndRef = useRef(mineEnd);
mineEndRef.current = mineEnd;
const hlzCenterRef = useRef(hlzCenter);
hlzCenterRef.current = hlzCenter;
const selectedSymbolTypeRef = useRef(selectedSymbolType);
selectedSymbolTypeRef.current = selectedSymbolType;
const newSymbolNameRef = useRef(newSymbolName);
newSymbolNameRef.current = newSymbolName;
const symbolsRef = useRef(symbols);
symbolsRef.current = symbols;
// Draggable Markers Ref Store
const markersRef = useRef<{ [key: string]: maplibregl.Marker }>({});
// Helper to create or update draggable marker
const updateMarker = (id: string, coords: [number, number] | null, label: string, color: string, onDrag: (pos: [number, number]) => void) => {
if (!map.current) return;
if (!coords) {
if (markersRef.current[id]) {
markersRef.current[id].remove();
delete markersRef.current[id];
}
return;
}
if (markersRef.current[id]) {
markersRef.current[id].setLngLat([coords[1], coords[0]]);
const el = markersRef.current[id].getElement();
if (el) el.innerText = label;
} else {
const el = document.createElement('div');
el.className = 'tactical-marker';
el.style.display = 'flex';
el.style.alignItems = 'center';
el.style.justifyContent = 'center';
el.style.background = color;
el.style.color = '#ffffff';
el.style.fontSize = '11px';
el.style.fontWeight = 'bold';
el.style.padding = '4px 8px';
el.style.borderRadius = '20px';
el.style.border = '2px solid #ffffff';
el.style.boxShadow = '0 0 12px rgba(0,0,0,0.6)';
el.style.cursor = 'grab';
el.style.userSelect = 'none';
el.innerText = label;
const marker = new maplibregl.Marker({ element: el, draggable: true })
.setLngLat([coords[1], coords[0]])
.addTo(map.current);
marker.on('dragend', () => {
const lngLat = marker.getLngLat();
onDrag([Number(lngLat.lat.toFixed(5)), Number(lngLat.lng.toFixed(5))]);
});
markersRef.current[id] = marker;
}
};
// Sync Draggable Markers to Map
useEffect(() => {
if (!map.current) return;
// Terrain Study Center
updateMarker('terrain-c', terrainCenter, '⛰️ مركز دراسة الأرض', '#6366f1', (pos) => setTerrainCenter(pos));
// Terrain Highest & Lowest Points (Dynamic Pins)
if (terrainResult?.highestPoint && showPeaksAndValleys && mode === 'terrain') {
updateMarker(
'terrain-peak',
[terrainResult.highestPoint.lat, terrainResult.highestPoint.lng],
`🔺 ${terrainResult.highestPoint.label}`,
'#0284c7',
() => {}
);
} else {
updateMarker('terrain-peak', null, '', '', () => {});
}
if (terrainResult?.lowestPoint && showPeaksAndValleys && mode === 'terrain') {
updateMarker(
'terrain-valley',
[terrainResult.lowestPoint.lat, terrainResult.lowestPoint.lng],
`🔻 ${terrainResult.lowestPoint.label}`,
'#e11d48',
() => {}
);
} else {
updateMarker('terrain-valley', null, '', '', () => {});
}
// LOS
updateMarker('los-a', losPointA, '📍 راصد A', '#0284c7', (pos) => setLosPointA(pos));
updateMarker('los-b', losPointB, '🎯 هدف B', '#d97706', (pos) => setLosPointB(pos));
// Viewshed
updateMarker('viewshed-c', viewshedCenter, '📡 مركز الرصد', '#16a34a', (pos) => setViewshedCenter(pos));
// Artillery
updateMarker('art-gun', gunPos, '💥 مربض المدفعية', '#ea580c', (pos) => setGunPos(pos));
updateMarker('art-tgt', targetPos, '🎯 هدف مدفعية', '#dc2626', (pos) => setTargetPos(pos));
// Minefield
updateMarker('mine-s', mineStart, '⛔ بداية حقل الألغام', '#b91c1c', (pos) => setMineStart(pos));
updateMarker('mine-e', mineEnd, '⛔ نهاية حقل الألغام', '#991b1b', (pos) => setMineEnd(pos));
// HLZ
updateMarker('hlz-c', hlzCenter, '🚁 مركز HLZ', '#059669', (pos) => setHlzCenter(pos));
}, [terrainCenter, terrainResult, showPeaksAndValleys, mode, losPointA, losPointB, viewshedCenter, gunPos, targetPos, mineStart, mineEnd, hlzCenter]);
// Map Initialization
useEffect(() => {
if (map.current) return;
if (!mapContainer.current) return;
const initialMap = new maplibregl.Map({
container: mapContainer.current,
style: '/style.json',
center: [35.9106, 31.9539],
zoom: 11,
pitch: 35,
attributionControl: false
});
map.current = initialMap;
const handleResize = () => {
if (initialMap) initialMap.resize();
};
window.addEventListener('resize', handleResize);
initialMap.on('mousemove', (e) => {
setCursorPos({
lat: Number(e.lngLat.lat.toFixed(5)),
lng: Number(e.lngLat.lng.toFixed(5)),
elev: 820 + Math.round(Math.sin(e.lngLat.lat * 20) * 150)
});
});
// Fresh Map Click Handler reading latest Refs
initialMap.on('click', (e) => {
const clickedLat = Number(e.lngLat.lat.toFixed(5));
const clickedLng = Number(e.lngLat.lng.toFixed(5));
const placement = activePlacementRef.current;
const currentMode = modeRef.current;
// 1. Explicit Active Placement from Buttons
if (placement === 'terrain-center') {
setTerrainCenter([clickedLat, clickedLng]);
setActivePlacement(null);
return;
}
if (placement === 'los-a') {
setLosPointA([clickedLat, clickedLng]);
setActivePlacement(null);
return;
}
if (placement === 'los-b') {
setLosPointB([clickedLat, clickedLng]);
setActivePlacement(null);
return;
}
if (placement === 'viewshed-center') {
setViewshedCenter([clickedLat, clickedLng]);
setActivePlacement(null);
return;
}
if (placement === 'gun') {
setGunPos([clickedLat, clickedLng]);
setActivePlacement(null);
return;
}
if (placement === 'target') {
setTargetPos([clickedLat, clickedLng]);
setActivePlacement(null);
return;
}
if (placement === 'mine-start') {
setMineStart([clickedLat, clickedLng]);
setActivePlacement(null);
return;
}
if (placement === 'mine-end') {
setMineEnd([clickedLat, clickedLng]);
setActivePlacement(null);
return;
}
if (placement === 'hlz-center') {
setHlzCenter([clickedLat, clickedLng]);
setActivePlacement(null);
return;
}
// 2. Default Context-Aware Click by Active Mode
if (currentMode === 'terrain') {
setTerrainCenter([clickedLat, clickedLng]);
} else if (currentMode === 'los') {
if (!losPointARef.current) {
setLosPointA([clickedLat, clickedLng]);
} else if (!losPointBRef.current) {
setLosPointB([clickedLat, clickedLng]);
} else {
setLosPointA([clickedLat, clickedLng]);
setLosPointB(null);
}
} else if (currentMode === 'viewshed') {
setViewshedCenter([clickedLat, clickedLng]);
} else if (currentMode === 'artillery') {
if (!gunPosRef.current) {
setGunPos([clickedLat, clickedLng]);
} else if (!targetPosRef.current) {
setTargetPos([clickedLat, clickedLng]);
} else {
setTargetPos([clickedLat, clickedLng]);
}
} else if (currentMode === 'minefield') {
if (!mineStartRef.current) {
setMineStart([clickedLat, clickedLng]);
} else if (!mineEndRef.current) {
setMineEnd([clickedLat, clickedLng]);
} else {
setMineStart([clickedLat, clickedLng]);
setMineEnd(null);
}
} else if (currentMode === 'hlz') {
setHlzCenter([clickedLat, clickedLng]);
} else if (currentMode === 'symbols') {
const typeLabels: Record<string, string> = {
friendly: 'وحدة صديقة',
hostile: 'هدف معادي',
artillery: 'مربض مدفعية',
radar: 'محطة رادار',
checkpoint: 'نقطة سيطرة',
op: 'نقطة مراقبة',
minefield: 'حقل ألغام',
hlz: 'مهبط مروحيات'
};
const newSym: TacticalSymbolItem = {
id: `sym-${Date.now()}`,
type: selectedSymbolTypeRef.current as any,
name: newSymbolNameRef.current || `${typeLabels[selectedSymbolTypeRef.current] || 'رمز'} ${symbolsRef.current.length + 1}`,
lat: clickedLat,
lng: clickedLng
};
setSymbols(prev => [...prev, newSym]);
setNewSymbolName('');
}
});
initialMap.on('load', () => {
// 0. Terrain Study Circle & Spatial Layers
initialMap.addSource('tactical-terrain-src', {
type: 'geojson',
data: { type: 'FeatureCollection', features: [] }
});
initialMap.addLayer({
id: 'tactical-terrain-fill',
type: 'fill',
source: 'tactical-terrain-src',
paint: {
'fill-color': '#6366f1',
'fill-opacity': 0.08
}
});
initialMap.addLayer({
id: 'tactical-terrain-line',
type: 'line',
source: 'tactical-terrain-src',
paint: {
'line-color': '#818cf8',
'line-width': 2.5,
'line-dasharray': [3, 2]
}
});
// Rich Spatial Feature Layers for Terrain Study (Slope, Wadis, Roads, Urban, Hazards)
initialMap.addSource('tactical-terrain-spatial-src', {
type: 'geojson',
data: { type: 'FeatureCollection', features: [] }
});
initialMap.addLayer({
id: 'tactical-terrain-spatial-poly-fill',
type: 'fill',
source: 'tactical-terrain-spatial-src',
filter: ['==', '$type', 'Polygon'],
paint: {
'fill-color': ['get', 'color'],
'fill-opacity': [
'match',
['get', 'layerType'],
'urban', 0.28,
'hazard', 0.32,
'slope-sector', 0.18,
0.2
]
}
});
initialMap.addLayer({
id: 'tactical-terrain-spatial-poly-line',
type: 'line',
source: 'tactical-terrain-spatial-src',
filter: ['==', '$type', 'Polygon'],
paint: {
'line-color': ['get', 'color'],
'line-width': 2,
'line-dasharray': [3, 2]
}
});
initialMap.addLayer({
id: 'tactical-terrain-spatial-lines',
type: 'line',
source: 'tactical-terrain-spatial-src',
filter: ['==', '$type', 'LineString'],
paint: {
'line-color': ['get', 'color'],
'line-width': [
'match',
['get', 'layerType'],
'road', 4.5,
'wadi', 3.5,
3
],
'line-dasharray': [
'match',
['get', 'layerType'],
'road', ['literal', [1, 0]],
'wadi', ['literal', [2, 1.5]],
['literal', [1, 0]]
]
}
});
initialMap.addLayer({
id: 'tactical-terrain-spatial-labels',
type: 'symbol',
source: 'tactical-terrain-spatial-src',
layout: {
'text-field': ['get', 'title'],
'text-size': 11,
'text-font': ['Noto Sans Bold', 'Open Sans Bold'],
'text-anchor': 'center',
'symbol-placement': 'point'
},
paint: {
'text-color': '#ffffff',
'text-halo-color': '#0f172a',
'text-halo-width': 2
}
});
// 1. Line of Sight Layer
initialMap.addSource('tactical-los-src', {
type: 'geojson',
data: { type: 'FeatureCollection', features: [] }
});
initialMap.addLayer({
id: 'tactical-los-line',
type: 'line',
source: 'tactical-los-src',
filter: ['==', '$type', 'LineString'],
paint: {
'line-color': ['case', ['==', ['get', 'blocked'], true], '#ef4444', '#22c55e'],
'line-width': 4,
'line-dasharray': [2, 1]
}
});
// 2. Viewshed 360 Polygon Layer
initialMap.addSource('tactical-viewshed-src', {
type: 'geojson',
data: { type: 'FeatureCollection', features: [] }
});
initialMap.addLayer({
id: 'tactical-viewshed-fill',
type: 'fill',
source: 'tactical-viewshed-src',
paint: {
'fill-color': '#22c55e',
'fill-opacity': 0.25
}
});
initialMap.addLayer({
id: 'tactical-viewshed-outline',
type: 'line',
source: 'tactical-viewshed-src',
paint: {
'line-color': '#4ade80',
'line-width': 2,
'line-dasharray': [3, 2]
}
});
// 3. Artillery Trajectory Layer
initialMap.addSource('tactical-artillery-src', {
type: 'geojson',
data: { type: 'FeatureCollection', features: [] }
});
initialMap.addLayer({
id: 'tactical-artillery-line',
type: 'line',
source: 'tactical-artillery-src',
paint: {
'line-color': '#f97316',
'line-width': 4
}
});
// 4. Minefield Barrier Layer
initialMap.addSource('tactical-minefield-src', {
type: 'geojson',
data: { type: 'FeatureCollection', features: [] }
});
initialMap.addLayer({
id: 'tactical-minefield-line',
type: 'line',
source: 'tactical-minefield-src',
paint: {
'line-color': '#dc2626',
'line-width': 6,
'line-dasharray': [1, 1]
}
});
// 5. Tactical Symbols Layer
initialMap.addSource('tactical-symbols-src', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: DEFAULT_SYMBOLS.map(s => ({
type: 'Feature',
properties: { type: s.type, name: s.name },
geometry: { type: 'Point', coordinates: [s.lng, s.lat] }
})) as any
}
});
initialMap.addLayer({
id: 'tactical-symbols-pts',
type: 'circle',
source: 'tactical-symbols-src',
paint: {
'circle-radius': 9,
'circle-color': [
'match',
['get', 'type'],
'friendly', '#3b82f6',
'hostile', '#ef4444',
'artillery', '#f97316',
'radar', '#06b6d4',
'checkpoint', '#eab308',
'minefield', '#dc2626',
'hlz', '#10b981',
'#a855f7'
],
'circle-stroke-color': '#ffffff',
'circle-stroke-width': 2.5
}
});
initialMap.addLayer({
id: 'tactical-symbols-lbls',
type: 'symbol',
source: 'tactical-symbols-src',
layout: {
'text-field': ['get', 'name'],
'text-size': 11,
'text-font': ['Noto Sans Bold', 'Open Sans Bold'],
'text-offset': [0, 1.4],
'text-anchor': 'top'
},
paint: {
'text-color': '#ffffff',
'text-halo-color': '#0f172a',
'text-halo-width': 2
}
});
});
return () => {
if (map.current) {
map.current.remove();
map.current = null;
}
};
}, []);
// Run Terrain Tactical Study
const runTerrainStudy = () => {
if (!terrainCenter) return;
setTerrainLoading(true);
calculateTerrainStudy(terrainCenter[0], terrainCenter[1], terrainRadius)
.then((res) => {
setTerrainResult(res);
setTerrainLoading(false);
// 1. Draw sector boundary polygon on map
if (map.current && map.current.getSource('tactical-terrain-src')) {
const points = 48;
const coords: [number, number][] = [];
const R_earth = 6371000;
for (let i = 0; i <= points; i++) {
const angle = (i / points) * 2 * Math.PI;
const dLat = (terrainRadius / R_earth) * (180 / Math.PI) * Math.cos(angle);
const dLng = (terrainRadius / (R_earth * Math.cos((terrainCenter[0] * Math.PI) / 180))) * (180 / Math.PI) * Math.sin(angle);
coords.push([terrainCenter[1] + dLng, terrainCenter[0] + dLat]);
}
(map.current.getSource('tactical-terrain-src') as maplibregl.GeoJSONSource).setData({
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'Polygon',
coordinates: [coords]
}
}
]
});
}
})
.catch(() => setTerrainLoading(false));
};
// Sync Spatial GeoJSON Features with Active Layer Toggles
useEffect(() => {
if (!map.current || !map.current.getSource('tactical-terrain-spatial-src')) return;
if (!terrainResult?.spatialGeoJson || mode !== 'terrain') {
(map.current.getSource('tactical-terrain-spatial-src') as maplibregl.GeoJSONSource).setData({
type: 'FeatureCollection',
features: []
});
return;
}
const filtered = terrainResult.spatialGeoJson.features.filter((f: any) => {
if (f.properties.category === 'peak' || f.properties.category === 'valley') return showPeaksAndValleys;
if (f.properties.layerType === 'slope-sector') return showSlopeSectors;
if (f.properties.layerType === 'wadi') return showNaturalObstacles;
if (f.properties.layerType === 'road') return showRoadCorridors;
if (f.properties.layerType === 'urban') return showUrbanZones;
if (f.properties.layerType === 'hazard') return showHazardousGround;
return true;
});
(map.current.getSource('tactical-terrain-spatial-src') as maplibregl.GeoJSONSource).setData({
type: 'FeatureCollection',
features: filtered
});
}, [terrainResult, showPeaksAndValleys, showSlopeSectors, showNaturalObstacles, showRoadCorridors, showUrbanZones, showHazardousGround, mode]);
// Auto-run initial terrain study on mount & coordinates change
useEffect(() => {
runTerrainStudy();
}, [terrainCenter, terrainRadius]);
// Update Line of Sight Calculations & Map Layer
useEffect(() => {
if (!losPointA || !losPointB) {
if (map.current && map.current.getSource('tactical-los-src')) {
(map.current.getSource('tactical-los-src') as maplibregl.GeoJSONSource).setData({
type: 'FeatureCollection',
features: []
});
}
setLosResult(null);
return;
}
setLosLoading(true);
calculateLineOfSight(losPointA[0], losPointA[1], losPointB[0], losPointB[1], losObsHeight, losTgtHeight, 70)
.then((res) => {
setLosResult(res);
setLosLoading(false);
if (map.current && map.current.getSource('tactical-los-src')) {
const feats: any[] = [
{
type: 'Feature',
properties: { role: 'ray', blocked: !res.isDirectlyVisible },
geometry: {
type: 'LineString',
coordinates: [[losPointA[1], losPointA[0]], [losPointB[1], losPointB[0]]]
}
}
];
if (res.highestObstacle) {
feats.push({
type: 'Feature',
properties: { role: 'obstacle', label: 'عائق' },
geometry: { type: 'Point', coordinates: [res.highestObstacle.lng, res.highestObstacle.lat] }
});
}
(map.current.getSource('tactical-los-src') as maplibregl.GeoJSONSource).setData({
type: 'FeatureCollection',
features: feats
});
}
})
.catch(() => setLosLoading(false));
}, [losPointA, losPointB, losObsHeight, losTgtHeight]);
// Update Minefield Analysis & Map Layer
useEffect(() => {
if (!mineStart || !mineEnd) {
if (map.current && map.current.getSource('tactical-minefield-src')) {
(map.current.getSource('tactical-minefield-src') as maplibregl.GeoJSONSource).setData({
type: 'FeatureCollection',
features: []
});
}
setMineResult(null);
return;
}
setMineLoading(true);
calculateMinefieldAnalysis(mineStart[0], mineStart[1], mineEnd[0], mineEnd[1], mineDensity)
.then((res) => {
setMineResult(res);
setMineLoading(false);
if (map.current && map.current.getSource('tactical-minefield-src')) {
(map.current.getSource('tactical-minefield-src') as maplibregl.GeoJSONSource).setData({
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: [[mineStart[1], mineStart[0]], [mineEnd[1], mineEnd[0]]]
}
}
]
});
}
})
.catch(() => setMineLoading(false));
}, [mineStart, mineEnd, mineDensity]);
// Update Artillery Trajectory
useEffect(() => {
if (!gunPos || !targetPos) {
if (map.current && map.current.getSource('tactical-artillery-src')) {
(map.current.getSource('tactical-artillery-src') as maplibregl.GeoJSONSource).setData({
type: 'FeatureCollection',
features: []
});
}
setArtilleryResult(null);
return;
}
runArtilleryMission();
}, [gunPos, targetPos, caliber, muzzleVel]);
// Update 360 Viewshed
const runViewshed360 = () => {
if (!viewshedCenter) return;
setViewshedLoading(true);
calculateRadialViewshed(viewshedCenter[0], viewshedCenter[1], viewshedHeight, viewshedRadius, 36)
.then((res) => {
setViewshedResult(res);
setViewshedLoading(false);
if (map.current && map.current.getSource('tactical-viewshed-src')) {
(map.current.getSource('tactical-viewshed-src') as maplibregl.GeoJSONSource).setData({
type: 'FeatureCollection',
features: [res.polygonGeoJson]
});
}
})
.catch(() => setViewshedLoading(false));
};
// Run Artillery Fire Mission
const runArtilleryMission = async () => {
if (!gunPos || !targetPos) return;
setArtilleryLoading(true);
try {
const apiUrl = (import.meta as any).env.VITE_API_URL || '/api';
const apiKey = (import.meta as any).env.VITE_API_KEY || 'zP9vL5mK2nQ8xR7jT4wS1yB6hG3fV0cX';
const res = await fetch(`${apiUrl}/tactical/artillery-fire-mission`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey },
body: JSON.stringify({
gunLat: gunPos[0],
gunLng: gunPos[1],
targetLat: targetPos[0],
targetLng: targetPos[1],
caliber,
muzzleVelocity: muzzleVel
})
});
const data = await res.json();
setArtilleryResult(data);
if (map.current && map.current.getSource('tactical-artillery-src')) {
const lineCoords = (data.trajectoryPoints || []).map((p: any) => [p.lng, p.lat]);
(map.current.getSource('tactical-artillery-src') as maplibregl.GeoJSONSource).setData({
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: { type: 'LineString', coordinates: lineCoords }
}
]
});
}
} catch (e) {
console.error('Artillery calculation error:', e);
} finally {
setArtilleryLoading(false);
}
};
// Run HLZ Assessment
const runHLZAssessment = async () => {
if (!hlzCenter) return;
setHlzLoading(true);
try {
const apiUrl = (import.meta as any).env.VITE_API_URL || '/api';
const apiKey = (import.meta as any).env.VITE_API_KEY || 'zP9vL5mK2nQ8xR7jT4wS1yB6hG3fV0cX';
const res = await fetch(`${apiUrl}/tactical/hlz-assessment?lat=${hlzCenter[0]}&lng=${hlzCenter[1]}&radius=4000`, {
headers: { 'x-api-key': apiKey }
});
const data = await res.json();
setHlzResult(data);
} catch (e) {
console.error('HLZ error:', e);
} finally {
setHlzLoading(false);
}
};
// Synchronize Tactical Symbols to Map
useEffect(() => {
if (!map.current || !map.current.getSource('tactical-symbols-src')) return;
const feats = symbols.map(s => ({
type: 'Feature',
properties: { type: s.type, name: s.name },
geometry: { type: 'Point', coordinates: [s.lng, s.lat] }
}));
(map.current.getSource('tactical-symbols-src') as maplibregl.GeoJSONSource).setData({
type: 'FeatureCollection',
features: feats as any
});
}, [symbols]);
// Jump to Sector and Adapt Operational Points
const flyToSector = (sector: typeof REGIONAL_SECTORS[0]) => {
if (!map.current) return;
map.current.flyTo({
center: [sector.lng, sector.lat],
zoom: sector.zoom,
pitch: 45,
essential: true
});
const lat = sector.lat;
const lng = sector.lng;
setTerrainCenter([Number(lat.toFixed(5)), Number(lng.toFixed(5))]);
setLosPointA([Number((lat - 0.015).toFixed(5)), Number((lng - 0.015).toFixed(5))]);
setLosPointB([Number((lat + 0.025).toFixed(5)), Number((lng + 0.025).toFixed(5))]);
setViewshedCenter([Number(lat.toFixed(5)), Number(lng.toFixed(5))]);
setGunPos([Number((lat - 0.03).toFixed(5)), Number((lng - 0.02).toFixed(5))]);
setTargetPos([Number((lat + 0.04).toFixed(5)), Number((lng + 0.03).toFixed(5))]);
setMineStart([Number((lat + 0.01).toFixed(5)), Number((lng - 0.015).toFixed(5))]);
setMineEnd([Number((lat + 0.02).toFixed(5)), Number((lng + 0.015).toFixed(5))]);
setHlzCenter([Number(lat.toFixed(5)), Number(lng.toFixed(5))]);
};
// Copy Intelligence Dossier to Clipboard
const handleCopyReport = () => {
if (!terrainResult) return;
const text = `
=== تقرير الاستخبارات الجغرافية ودراسة الأرض التكتيكية ===
القطاع: ${terrainResult.sectorName}
الإحداثيات: ${terrainResult.centerLat}, ${terrainResult.centerLng}
نصف القطر: ${terrainResult.radiusMeters / 1000} كم | المساحة: ${terrainResult.areaKm2} كم²
طبيعة الأرض: ${terrainResult.terrainClassificationAr}
حالة الحركة والمناورة: ${terrainResult.mobilityStatusAr}
المناسيب: أعلى قمة ${terrainResult.maxElevation}م (${terrainResult.highestPoint.lat}, ${terrainResult.highestPoint.lng}) | أخفض نقطة ${terrainResult.minElevation}م (${terrainResult.lowestPoint.lat}, ${terrainResult.lowestPoint.lng}) | الفارق التضاريسي ${terrainResult.reliefMeters}م
الانحدار: متوسط ${terrainResult.avgSlopeDegrees}° | أقصى انحدار ${terrainResult.maxSlopeDegrees}°
[الموانع الطبيعية]:
${terrainResult.naturalObstacles.map(o => `- ${o.name}: ${o.description}`).join('\n')}
[الموانع الصناعية والبنية التحتية]:
${terrainResult.manMadeObstacles.map(m => `- ${m.name}: ${m.description} (${m.tacticalNote})`).join('\n')}
[المناطق المأهولة والعمران]:
- الكثافة: ${terrainResult.urbanAndDemographics.densityAr}
- التجمعات: ${terrainResult.urbanAndDemographics.settlements.join('، ')}
- بيئة قتال المباني: ${terrainResult.urbanAndDemographics.moutComplexity}
[التوصيات التكتيكية]:
${terrainResult.tacticalRecommendations.map((r, i) => `${i + 1}. ${r}`).join('\n')}
`;
navigator.clipboard.writeText(text);
setCopiedReport(true);
setTimeout(() => setCopiedReport(false), 2500);
};
return (
<div style={{
width: '100vw',
height: '100vh',
display: 'flex',
flexDirection: 'column',
background: '#0a0e17',
color: '#f8fafc',
fontFamily: '-apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display", "SF Pro Arabic", system-ui, sans-serif',
direction: 'rtl',
overflow: 'hidden',
paddingTop: 56
}}>
{/* Top Military HUD Header */}
<header style={{
height: 52,
background: '#0f172a',
borderBottom: '1px solid #1e293b',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '0 18px',
zIndex: 50,
gap: 12,
flexShrink: 0
}}>
{/* Title */}
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<div style={{
width: 32,
height: 32,
borderRadius: 8,
background: 'linear-gradient(135deg, #4f46e5, #06b6d4)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
boxShadow: '0 0 16px rgba(79, 70, 229, 0.5)'
}}>
<Shield size={17} color="#ffffff" />
</div>
<div>
<div style={{ fontWeight: 800, fontSize: '0.92rem', letterSpacing: '-0.02em', display: 'flex', alignItems: 'center', gap: 6 }}>
<span>منظومة القيادة والسيطرة والتحليل التكتيكي</span>
<span style={{ fontSize: '10px', background: 'rgba(34, 197, 94, 0.2)', color: '#4ade80', padding: '1px 6px', borderRadius: 4 }}>
C4ISR Grid
</span>
</div>
</div>
</div>
{/* Tactical Modes Selector */}
<div style={{ display: 'flex', gap: 4, background: 'rgba(255, 255, 255, 0.05)', padding: 3, borderRadius: 10, border: '1px solid rgba(255,255,255,0.1)' }}>
{[
{ id: 'terrain', label: 'دراسة الأرض', icon: <Mountain size={13} /> },
{ id: 'los', label: 'تبادل الرؤية (LOS)', icon: <Eye size={13} /> },
{ id: 'viewshed', label: 'كشف 360°', icon: <Radio size={13} /> },
{ id: 'artillery', label: 'موقع المدفعية', icon: <Flame size={13} /> },
{ id: 'minefield', label: 'حقل الألغام', icon: <AlertOctagon size={13} /> },
{ id: 'hlz', label: 'مهابط المروحيات', icon: <Crosshair size={13} /> },
{ id: 'symbols', label: 'الرموز والتشكيلات', icon: <Layers size={13} /> },
{ id: 'study', label: 'دراسة التنظيم', icon: <Award size={13} /> },
].map(m => (
<button
key={m.id}
onClick={() => {
setMode(m.id as TacticalMode);
setActivePlacement(null);
}}
style={{
background: mode === m.id ? 'linear-gradient(135deg, #4f46e5, #6366f1)' : 'transparent',
border: 'none',
color: mode === m.id ? '#ffffff' : '#94a3b8',
borderRadius: 7,
padding: '5px 10px',
fontSize: '0.78rem',
fontWeight: 700,
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
gap: 5,
transition: 'all 0.15s ease'
}}
>
{m.icon}
<span>{m.label}</span>
</button>
))}
</div>
{/* Realtime Cursor HUD */}
<div style={{ display: 'flex', alignItems: 'center', gap: 12, fontSize: '0.72rem', fontFamily: 'monospace', color: '#94a3b8' }}>
<div>LAT: <span style={{ color: '#38bdf8' }}>{cursorPos.lat}</span></div>
<div>LNG: <span style={{ color: '#38bdf8' }}>{cursorPos.lng}</span></div>
<div>ELEV: <span style={{ color: '#4ade80' }}>{cursorPos.elev}m</span></div>
</div>
</header>
{/* Quick Sector Presets Bar */}
<div style={{
height: 38,
background: '#0b1329',
borderBottom: '1px solid rgba(255,255,255,0.08)',
display: 'flex',
alignItems: 'center',
padding: '0 16px',
gap: 8,
fontSize: '0.75rem',
overflowX: 'auto',
flexShrink: 0
}}>
<span style={{ color: '#94a3b8', display: 'flex', alignItems: 'center', gap: 4, fontWeight: 700 }}>
<MapPin size={13} color="#38bdf8" /> الانتقال السريع لقطاعات العمليات:
</span>
{REGIONAL_SECTORS.map((s, idx) => (
<button
key={idx}
onClick={() => flyToSector(s)}
style={{
background: 'rgba(56, 189, 248, 0.1)',
border: '1px solid rgba(56, 189, 248, 0.25)',
color: '#38bdf8',
padding: '3px 10px',
borderRadius: 6,
fontSize: '0.72rem',
fontWeight: 600,
cursor: 'pointer',
whiteSpace: 'nowrap',
transition: 'all 0.15s ease'
}}
>
{s.name}
</button>
))}
</div>
{/* Main Tactical Workspace */}
<div style={{ flex: 1, display: 'flex', position: 'relative', overflow: 'hidden' }}>
{/* Left Side Tactical Control Deck */}
<aside style={{
width: 440,
background: 'rgba(15, 23, 42, 0.96)',
backdropFilter: 'blur(20px)',
borderLeft: '1px solid rgba(255, 255, 255, 0.12)',
display: 'flex',
flexDirection: 'column',
zIndex: 20,
overflowY: 'auto',
padding: 16
}}>
{/* Active Placement Alert Banner */}
{activePlacement && (
<div style={{
background: 'rgba(234, 179, 8, 0.25)',
border: '1px solid #eab308',
color: '#fef08a',
padding: '10px 12px',
borderRadius: 8,
fontSize: '0.82rem',
fontWeight: 700,
marginBottom: 12,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
animation: 'pulse 1.5s infinite'
}}>
<span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
<Target size={16} color="#eab308" />
<span>انقر الآن على أي مكان بالخريطة لتحديد الموقع المطلوب.</span>
</span>
<button
onClick={() => setActivePlacement(null)}
style={{ background: 'rgba(0,0,0,0.3)', border: '1px solid #eab308', color: '#fef08a', padding: '2px 6px', borderRadius: 4, cursor: 'pointer', fontSize: '0.75rem' }}
>
إلغاء ✕
</button>
</div>
)}
{/* Interactive Drag Hint */}
<div style={{
background: 'rgba(56, 189, 248, 0.08)',
border: '1px solid rgba(56, 189, 248, 0.2)',
padding: '6px 10px',
borderRadius: 6,
fontSize: '0.72rem',
color: '#7dd3fc',
display: 'flex',
alignItems: 'center',
gap: 6,
marginBottom: 12
}}>
<Move size={13} color="#38bdf8" />
<span>يمكنك <strong>سحب أي علامة مباشرة بالفأرة</strong> على الخريطة لتغيير موقعها وحساب النتائج فوراً.</span>
</div>
{/* ========================================================================= */}
{/* MODE 0: TERRAIN TACTICAL STUDY (دراسة الأرض والموانع والعمران) */}
{/* ========================================================================= */}
{mode === 'terrain' && (
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
<div style={{ paddingBottom: 8, borderBottom: '1px solid rgba(255,255,255,0.08)', display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
<div>
<h3 style={{ margin: 0, fontSize: '1.05rem', fontWeight: 800, color: '#818cf8', display: 'flex', alignItems: 'center', gap: 8 }}>
<Mountain size={18} /> دراسة الأرض والاستخبارات الجغرافية
</h3>
<p style={{ margin: '4px 0 0 0', fontSize: '0.75rem', color: '#94a3b8' }}>
تحليل تضاريسي ملون على الخريطة للموانع الطبيعية، الصناعية، والعمران
</p>
</div>
{terrainResult && (
<button
onClick={handleCopyReport}
style={{
background: copiedReport ? 'rgba(34, 197, 94, 0.2)' : 'rgba(255,255,255,0.08)',
border: copiedReport ? '1px solid #22c55e' : '1px solid rgba(255,255,255,0.15)',
color: copiedReport ? '#4ade80' : '#cbd5e1',
padding: '4px 8px',
borderRadius: 6,
fontSize: '0.7rem',
fontWeight: 700,
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
gap: 4
}}
>
{copiedReport ? <Check size={12} /> : <FileText size={12} />}
<span>{copiedReport ? 'تم نسخ التقرير' : 'نسخ التقرير'}</span>
</button>
)}
</div>
{/* Map Layer Overlays Control Deck */}
<div style={{
background: 'rgba(99, 102, 241, 0.08)',
border: '1px solid rgba(99, 102, 241, 0.25)',
padding: '10px',
borderRadius: 8
}}>
<div style={{ fontSize: '0.75rem', fontWeight: 800, color: '#a5b4fc', marginBottom: 8, display: 'flex', alignItems: 'center', gap: 6 }}>
<Sliders size={14} /> طبقات الإظهار اللوني على الخريطة:
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
<button
onClick={() => setShowPeaksAndValleys(!showPeaksAndValleys)}
style={{
background: showPeaksAndValleys ? 'rgba(56, 189, 248, 0.2)' : 'rgba(255,255,255,0.05)',
border: showPeaksAndValleys ? '1px solid #38bdf8' : '1px solid rgba(255,255,255,0.1)',
color: showPeaksAndValleys ? '#38bdf8' : '#94a3b8',
padding: '5px 8px',
borderRadius: 6,
fontSize: '0.68rem',
fontWeight: 700,
cursor: 'pointer',
textAlign: 'right',
display: 'flex',
alignItems: 'center',
gap: 4
}}
>
<span>{showPeaksAndValleys ? '✅' : '⬜'}</span>
<span>🔺 أعلى وأخفض نقطة</span>
</button>
<button
onClick={() => setShowSlopeSectors(!showSlopeSectors)}
style={{
background: showSlopeSectors ? 'rgba(34, 197, 94, 0.2)' : 'rgba(255,255,255,0.05)',
border: showSlopeSectors ? '1px solid #22c55e' : '1px solid rgba(255,255,255,0.1)',
color: showSlopeSectors ? '#4ade80' : '#94a3b8',
padding: '5px 8px',
borderRadius: 6,
fontSize: '0.68rem',
fontWeight: 700,
cursor: 'pointer',
textAlign: 'right',
display: 'flex',
alignItems: 'center',
gap: 4
}}
>
<span>{showSlopeSectors ? '✅' : '⬜'}</span>
<span>🚦 ممرات الحركة والانحدار</span>
</button>
<button
onClick={() => setShowNaturalObstacles(!showNaturalObstacles)}
style={{
background: showNaturalObstacles ? 'rgba(6, 182, 212, 0.2)' : 'rgba(255,255,255,0.05)',
border: showNaturalObstacles ? '1px solid #06b6d4' : '1px solid rgba(255,255,255,0.1)',
color: showNaturalObstacles ? '#22d3ee' : '#94a3b8',
padding: '5px 8px',
borderRadius: 6,
fontSize: '0.68rem',
fontWeight: 700,
cursor: 'pointer',
textAlign: 'right',
display: 'flex',
alignItems: 'center',
gap: 4
}}
>
<span>{showNaturalObstacles ? '✅' : '⬜'}</span>
<span>🌊 الأودية والسيول</span>
</button>
<button
onClick={() => setShowRoadCorridors(!showRoadCorridors)}
style={{
background: showRoadCorridors ? 'rgba(245, 158, 11, 0.2)' : 'rgba(255,255,255,0.05)',
border: showRoadCorridors ? '1px solid #f59e0b' : '1px solid rgba(255,255,255,0.1)',
color: showRoadCorridors ? '#fbbf24' : '#94a3b8',
padding: '5px 8px',
borderRadius: 6,
fontSize: '0.68rem',
fontWeight: 700,
cursor: 'pointer',
textAlign: 'right',
display: 'flex',
alignItems: 'center',
gap: 4
}}
>
<span>{showRoadCorridors ? '✅' : '⬜'}</span>
<span>🛣️ الطرق والمحاور</span>
</button>
<button
onClick={() => setShowUrbanZones(!showUrbanZones)}
style={{
background: showUrbanZones ? 'rgba(168, 85, 247, 0.2)' : 'rgba(255,255,255,0.05)',
border: showUrbanZones ? '1px solid #a855f7' : '1px solid rgba(255,255,255,0.1)',
color: showUrbanZones ? '#c084fc' : '#94a3b8',
padding: '5px 8px',
borderRadius: 6,
fontSize: '0.68rem',
fontWeight: 700,
cursor: 'pointer',
textAlign: 'right',
display: 'flex',
alignItems: 'center',
gap: 4
}}
>
<span>{showUrbanZones ? '✅' : '⬜'}</span>
<span>🏙️ التجمعات السكانية</span>
</button>
<button
onClick={() => setShowHazardousGround(!showHazardousGround)}
style={{
background: showHazardousGround ? 'rgba(220, 38, 38, 0.2)' : 'rgba(255,255,255,0.05)',
border: showHazardousGround ? '1px solid #dc2626' : '1px solid rgba(255,255,255,0.1)',
color: showHazardousGround ? '#f87171' : '#94a3b8',
padding: '5px 8px',
borderRadius: 6,
fontSize: '0.68rem',
fontWeight: 700,
cursor: 'pointer',
textAlign: 'right',
display: 'flex',
alignItems: 'center',
gap: 4
}}
>
<span>{showHazardousGround ? '✅' : '⬜'}</span>
<span>🪨 حرات بازلتية وصخور</span>
</button>
</div>
</div>
{/* Terrain Center Coordinate Card */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8, border: '1px solid rgba(99, 102, 241, 0.25)' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
<span style={{ fontSize: '0.78rem', fontWeight: 800, color: '#a5b4fc' }}>📍 مركز قطاع دراسة الأرض:</span>
<button
onClick={() => setActivePlacement('terrain-center')}
style={{
background: activePlacement === 'terrain-center' ? '#4f46e5' : 'rgba(99, 102, 241, 0.15)',
border: '1px solid #6366f1',
color: '#ffffff',
padding: '3px 8px',
borderRadius: 6,
fontSize: '0.7rem',
fontWeight: 700,
cursor: 'pointer'
}}
>
{activePlacement === 'terrain-center' ? '🎯 انقر على الخريطة...' : '📍 التقاط من الخريطة'}
</button>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
<div>
<label style={{ fontSize: '0.65rem', color: '#94a3b8' }}>خط العرض (Lat):</label>
<input
type="number"
step="0.0001"
value={terrainCenter ? terrainCenter[0] : ''}
onChange={e => setTerrainCenter([Number(e.target.value), terrainCenter ? terrainCenter[1] : 35.9])}
style={{ width: '100%', background: '#0f172a', border: '1px solid rgba(255,255,255,0.2)', color: '#fff', padding: '5px 8px', borderRadius: 6, fontSize: '0.78rem' }}
/>
</div>
<div>
<label style={{ fontSize: '0.65rem', color: '#94a3b8' }}>خط الطول (Lng):</label>
<input
type="number"
step="0.0001"
value={terrainCenter ? terrainCenter[1] : ''}
onChange={e => setTerrainCenter([terrainCenter ? terrainCenter[0] : 31.9, Number(e.target.value)])}
style={{ width: '100%', background: '#0f172a', border: '1px solid rgba(255,255,255,0.2)', color: '#fff', padding: '5px 8px', borderRadius: 6, fontSize: '0.78rem' }}
/>
</div>
</div>
</div>
{/* Radius Slider */}
<div>
<label style={{ fontSize: '0.72rem', color: '#94a3b8', display: 'flex', justifyContent: 'space-between', marginBottom: 4 }}>
<span>نطاق دراسة القطاع:</span>
<strong style={{ color: '#a5b4fc' }}>{(terrainRadius / 1000).toFixed(1)} كم (المساحة: {(Math.PI * Math.pow(terrainRadius / 1000, 2)).toFixed(1)} كم²)</strong>
</label>
<input
type="range"
min={1000}
max={12000}
step={500}
value={terrainRadius}
onChange={e => setTerrainRadius(Number(e.target.value))}
style={{ width: '100%' }}
/>
</div>
<button
onClick={runTerrainStudy}
disabled={terrainLoading}
style={{
background: 'linear-gradient(135deg, #4f46e5, #6366f1)',
border: 'none',
color: '#fff',
padding: '9px',
borderRadius: 8,
fontSize: '0.82rem',
fontWeight: 700,
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: 6
}}
>
<Mountain size={15} />
<span>{terrainLoading ? 'جاري تحليل التضاريس والموانع...' : 'تحديث دراسة الأرض والتقرير التكتيكي'}</span>
</button>
{/* Terrain Intelligence Dossier Output */}
{terrainResult && (
<div style={{ display: 'flex', flexDirection: 'column', gap: 12, marginTop: 4 }}>
{/* Top Mobility & Classification Badge */}
<div style={{
background: terrainResult.mobilityStatus === 'GO'
? 'rgba(34, 197, 94, 0.15)'
: terrainResult.mobilityStatus === 'SLOW-GO'
? 'rgba(234, 179, 8, 0.15)'
: 'rgba(239, 68, 68, 0.15)',
border: terrainResult.mobilityStatus === 'GO'
? '1px solid #22c55e'
: terrainResult.mobilityStatus === 'SLOW-GO'
? '1px solid #eab308'
: '1px solid #ef4444',
padding: '10px 12px',
borderRadius: 8,
}}>
<div style={{ fontSize: '0.72rem', color: '#94a3b8', marginBottom: 2 }}>{terrainResult.sectorName}</div>
<div style={{ fontSize: '0.92rem', fontWeight: 800, color: '#f8fafc', marginBottom: 4 }}>
{terrainResult.terrainClassificationAr}
</div>
<div style={{
fontSize: '0.78rem',
fontWeight: 700,
color: terrainResult.mobilityStatus === 'GO' ? '#4ade80' : terrainResult.mobilityStatus === 'SLOW-GO' ? '#fde047' : '#f87171'
}}>
⚡ {terrainResult.mobilityStatusAr}
</div>
</div>
{/* Highest Peak & Lowest Valley Cards with Direct Map Focus */}
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
<div
onClick={() => {
if (map.current) {
map.current.flyTo({ center: [terrainResult.highestPoint.lng, terrainResult.highestPoint.lat], zoom: 14, pitch: 50 });
}
}}
style={{
background: 'rgba(56, 189, 248, 0.12)',
border: '1px solid #38bdf8',
padding: 8,
borderRadius: 8,
cursor: 'pointer'
}}
>
<div style={{ fontSize: '0.65rem', color: '#7dd3fc', fontWeight: 700 }}>🔺 أعلى قمة تضاريسية</div>
<div style={{ fontSize: '1.05rem', fontWeight: 900, color: '#38bdf8' }}>{terrainResult.maxElevation}م</div>
<div style={{ fontSize: '0.62rem', color: '#94a3b8', fontFamily: 'monospace' }}>
{terrainResult.highestPoint.lat.toFixed(4)}, {terrainResult.highestPoint.lng.toFixed(4)}
</div>
</div>
<div
onClick={() => {
if (map.current) {
map.current.flyTo({ center: [terrainResult.lowestPoint.lng, terrainResult.lowestPoint.lat], zoom: 14, pitch: 50 });
}
}}
style={{
background: 'rgba(244, 63, 94, 0.12)',
border: '1px solid #f43f5e',
padding: 8,
borderRadius: 8,
cursor: 'pointer'
}}
>
<div style={{ fontSize: '0.65rem', color: '#fda4af', fontWeight: 700 }}>🔻 أخفض نقطة / وادٍ</div>
<div style={{ fontSize: '1.05rem', fontWeight: 900, color: '#f43f5e' }}>{terrainResult.minElevation}م</div>
<div style={{ fontSize: '0.62rem', color: '#94a3b8', fontFamily: 'monospace' }}>
{terrainResult.lowestPoint.lat.toFixed(4)}, {terrainResult.lowestPoint.lng.toFixed(4)}
</div>
</div>
</div>
{/* Elevation & Slope Metrics Grid */}
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 7, borderRadius: 6, textAlign: 'center' }}>
<div style={{ fontSize: '0.65rem', color: '#94a3b8' }}>الفارق التضاريسي (Relief)</div>
<div style={{ fontSize: '0.95rem', fontWeight: 800, color: '#fb923c' }}>{terrainResult.reliefMeters}م</div>
</div>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 7, borderRadius: 6, textAlign: 'center' }}>
<div style={{ fontSize: '0.65rem', color: '#94a3b8' }}>أقصى زاوية انحدار</div>
<div style={{ fontSize: '0.95rem', fontWeight: 800, color: '#f87171' }}>{terrainResult.maxSlopeDegrees}°</div>
</div>
</div>
{/* Slope Distribution Bar */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8 }}>
<div style={{ fontSize: '0.72rem', fontWeight: 700, color: '#cbd5e1', marginBottom: 6, display: 'flex', justifyContent: 'space-between' }}>
<span>توزيع درجات الانحدار (Slope Analysis):</span>
<span style={{ color: '#94a3b8' }}>متوسط: {terrainResult.avgSlopeDegrees}°</span>
</div>
<div style={{ height: 10, width: '100%', display: 'flex', borderRadius: 5, overflow: 'hidden', marginBottom: 6 }}>
<div style={{ width: `${terrainResult.slopeDistribution.flatPercentage}%`, background: '#22c55e' }} title={`منبسط: ${terrainResult.slopeDistribution.flatPercentage}%`} />
<div style={{ width: `${terrainResult.slopeDistribution.moderatePercentage}%`, background: '#eab308' }} title={`متوسط: ${terrainResult.slopeDistribution.moderatePercentage}%`} />
<div style={{ width: `${terrainResult.slopeDistribution.steepPercentage}%`, background: '#ef4444' }} title={`شديد الوعورة: ${terrainResult.slopeDistribution.steepPercentage}%`} />
</div>
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: '0.68rem', color: '#94a3b8' }}>
<span style={{ color: '#4ade80' }}>■ منبسط (&lt;5°): {terrainResult.slopeDistribution.flatPercentage}%</span>
<span style={{ color: '#facc15' }}>■ متوسط (5-15°): {terrainResult.slopeDistribution.moderatePercentage}%</span>
<span style={{ color: '#f87171' }}>■ وعر (&gt;15°): {terrainResult.slopeDistribution.steepPercentage}%</span>
</div>
</div>
{/* SECTION 1: NATURAL OBSTACLES */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8, border: '1px solid rgba(239, 68, 68, 0.2)' }}>
<div style={{ fontSize: '0.78rem', fontWeight: 800, color: '#f87171', marginBottom: 6, display: 'flex', alignItems: 'center', gap: 6 }}>
<AlertOctagon size={14} /> 1. الموانع الطبيعية (Natural Obstacles):
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
{terrainResult.naturalObstacles.map((obs, idx) => (
<div key={idx} style={{ background: 'rgba(0,0,0,0.2)', padding: '6px 8px', borderRadius: 6, fontSize: '0.72rem' }}>
<div style={{ fontWeight: 700, color: '#fca5a5' }}>• {obs.name} ({obs.type})</div>
<div style={{ color: '#cbd5e1', marginTop: 2 }}>{obs.description}</div>
<div style={{ color: '#94a3b8', fontSize: '0.68rem', marginTop: 2 }}><strong>الأثر التكتيكي:</strong> {obs.impact}</div>
</div>
))}
</div>
</div>
{/* SECTION 2: MAN-MADE OBSTACLES & INFRASTRUCTURE */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8, border: '1px solid rgba(245, 158, 11, 0.2)' }}>
<div style={{ fontSize: '0.78rem', fontWeight: 800, color: '#f59e0b', marginBottom: 6, display: 'flex', alignItems: 'center', gap: 6 }}>
<Building size={14} /> 2. الموانع الصناعية والبنية التحتية (Man-Made Features):
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
{terrainResult.manMadeObstacles.map((obs, idx) => (
<div key={idx} style={{ background: 'rgba(0,0,0,0.2)', padding: '6px 8px', borderRadius: 6, fontSize: '0.72rem' }}>
<div style={{ fontWeight: 700, color: '#fde047' }}>• {obs.name}</div>
<div style={{ color: '#cbd5e1', marginTop: 2 }}>{obs.description}</div>
<div style={{ color: '#94a3b8', fontSize: '0.68rem', marginTop: 2 }}><strong>ملاحظة تكتيكية:</strong> {obs.tacticalNote}</div>
</div>
))}
</div>
</div>
{/* SECTION 3: URBAN AREAS & DEMOGRAPHICS */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8, border: '1px solid rgba(56, 189, 248, 0.2)' }}>
<div style={{ fontSize: '0.78rem', fontWeight: 800, color: '#38bdf8', marginBottom: 6, display: 'flex', alignItems: 'center', gap: 6 }}>
<Layers size={14} /> 3. المناطق المأهولة بالسكان والعمران:
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: '0.72rem', color: '#cbd5e1' }}>
<div><strong>الكثافة السكانية:</strong> {terrainResult.urbanAndDemographics.densityAr}</div>
<div><strong>التجمعات والقرى الرئيسية:</strong> {terrainResult.urbanAndDemographics.settlements.join('، ')}</div>
<div><strong>قتال المناطق المبنية (MOUT):</strong> {terrainResult.urbanAndDemographics.moutComplexity}</div>
<div><strong>مخاطر الأضرار الجانبية:</strong> {terrainResult.urbanAndDemographics.collateralDamageRisk}</div>
</div>
</div>
{/* SECTION 4: SOIL & CONCEALMENT */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8, border: '1px solid rgba(16, 185, 129, 0.2)' }}>
<div style={{ fontSize: '0.78rem', fontWeight: 800, color: '#10b981', marginBottom: 6, display: 'flex', alignItems: 'center', gap: 6 }}>
<TreePine size={14} /> 4. التربة وإمكانات الإخفاء والتمويه:
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: '0.72rem', color: '#cbd5e1' }}>
<div><strong>نوع التربة:</strong> {terrainResult.landCoverAndCover.soilType}</div>
<div><strong>صلاحية السير للآليات:</strong> {terrainResult.landCoverAndCover.trafficability}</div>
<div><strong>الإخفاء من الرصد الأرضي:</strong> {terrainResult.landCoverAndCover.concealmentRating}</div>
<div><strong>الانكشاف للرصد الجوي:</strong> {terrainResult.landCoverAndCover.airObservationExposure}</div>
</div>
</div>
{/* SECTION 5: COMMANDER'S TACTICAL RECOMMENDATIONS (OAKOC) */}
<div style={{ background: 'linear-gradient(135deg, rgba(79, 70, 229, 0.15), rgba(99, 102, 241, 0.15))', padding: 12, borderRadius: 8, border: '1px solid #6366f1' }}>
<div style={{ fontSize: '0.82rem', fontWeight: 800, color: '#a5b4fc', marginBottom: 8, display: 'flex', alignItems: 'center', gap: 6 }}>
<Award size={16} /> 5. التقييم التكتيكي للقائد والتوصيات الميدانية:
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 6, fontSize: '0.72rem', color: '#f8fafc', lineHeight: 1.45 }}>
{terrainResult.tacticalRecommendations.map((rec, i) => (
<div key={i} style={{ display: 'flex', gap: 6 }}>
<span style={{ color: '#818cf8', fontWeight: 700 }}>{i + 1}.</span>
<span>{rec}</span>
</div>
))}
</div>
</div>
</div>
)}
</div>
)}
{/* ========================================================================= */}
{/* MODE 1: LINE OF SIGHT */}
{/* ========================================================================= */}
{mode === 'los' && (
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
<div style={{ paddingBottom: 8, borderBottom: '1px solid rgba(255,255,255,0.08)' }}>
<h3 style={{ margin: 0, fontSize: '1rem', fontWeight: 800, color: '#38bdf8', display: 'flex', alignItems: 'center', gap: 8 }}>
<Eye size={17} /> تحليل خط النظر وتبادل الرؤية (LOS)
</h3>
<p style={{ margin: '4px 0 0 0', fontSize: '0.75rem', color: '#94a3b8' }}>
فحص الرؤية المباشرة والمناطق الميتة مع إمكانية كتابة الإحداثيات مباشرة
</p>
</div>
{/* Point A Coordinate Card */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8, border: '1px solid rgba(56, 189, 248, 0.2)' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
<span style={{ fontSize: '0.78rem', fontWeight: 800, color: '#38bdf8' }}>📍 موقع الراصد (A):</span>
<button
onClick={() => setActivePlacement('los-a')}
style={{
background: activePlacement === 'los-a' ? '#0284c7' : 'rgba(56, 189, 248, 0.15)',
border: '1px solid #38bdf8',
color: '#ffffff',
padding: '3px 8px',
borderRadius: 6,
fontSize: '0.7rem',
fontWeight: 700,
cursor: 'pointer'
}}
>
{activePlacement === 'los-a' ? '🎯 انقر على الخريطة...' : '📍 التقاط من الخريطة'}
</button>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
<div>
<label style={{ fontSize: '0.65rem', color: '#94a3b8' }}>خط العرض (Lat):</label>
<input
type="number"
step="0.0001"
value={losPointA ? losPointA[0] : ''}
onChange={e => setLosPointA([Number(e.target.value), losPointA ? losPointA[1] : 35.9])}
style={{ width: '100%', background: '#0f172a', border: '1px solid rgba(255,255,255,0.2)', color: '#fff', padding: '5px 8px', borderRadius: 6, fontSize: '0.78rem' }}
/>
</div>
<div>
<label style={{ fontSize: '0.65rem', color: '#94a3b8' }}>خط الطول (Lng):</label>
<input
type="number"
step="0.0001"
value={losPointA ? losPointA[1] : ''}
onChange={e => setLosPointA([losPointA ? losPointA[0] : 31.9, Number(e.target.value)])}
style={{ width: '100%', background: '#0f172a', border: '1px solid rgba(255,255,255,0.2)', color: '#fff', padding: '5px 8px', borderRadius: 6, fontSize: '0.78rem' }}
/>
</div>
</div>
</div>
{/* Point B Coordinate Card */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8, border: '1px solid rgba(245, 158, 11, 0.2)' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
<span style={{ fontSize: '0.78rem', fontWeight: 800, color: '#f59e0b' }}>🎯 موقع الهدف (B):</span>
<button
onClick={() => setActivePlacement('los-b')}
style={{
background: activePlacement === 'los-b' ? '#d97706' : 'rgba(245, 158, 11, 0.15)',
border: '1px solid #f59e0b',
color: '#ffffff',
padding: '3px 8px',
borderRadius: 6,
fontSize: '0.7rem',
fontWeight: 700,
cursor: 'pointer'
}}
>
{activePlacement === 'los-b' ? '🎯 انقر على الخريطة...' : '🎯 التقاط من الخريطة'}
</button>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
<div>
<label style={{ fontSize: '0.65rem', color: '#94a3b8' }}>خط العرض (Lat):</label>
<input
type="number"
step="0.0001"
value={losPointB ? losPointB[0] : ''}
onChange={e => setLosPointB([Number(e.target.value), losPointB ? losPointB[1] : 35.9])}
style={{ width: '100%', background: '#0f172a', border: '1px solid rgba(255,255,255,0.2)', color: '#fff', padding: '5px 8px', borderRadius: 6, fontSize: '0.78rem' }}
/>
</div>
<div>
<label style={{ fontSize: '0.65rem', color: '#94a3b8' }}>خط الطول (Lng):</label>
<input
type="number"
step="0.0001"
value={losPointB ? losPointB[1] : ''}
onChange={e => setLosPointB([losPointB ? losPointB[0] : 31.9, Number(e.target.value)])}
style={{ width: '100%', background: '#0f172a', border: '1px solid rgba(255,255,255,0.2)', color: '#fff', padding: '5px 8px', borderRadius: 6, fontSize: '0.78rem' }}
/>
</div>
</div>
</div>
{/* Heights */}
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
<div>
<label style={{ fontSize: '0.7rem', color: '#94a3b8', display: 'block', marginBottom: 4 }}>ارتفاع الراصد (A):</label>
<select
value={losObsHeight}
onChange={e => setLosObsHeight(Number(e.target.value))}
style={{ width: '100%', background: 'rgba(255,255,255,0.06)', border: '1px solid rgba(255,255,255,0.15)', color: '#fff', padding: '6px', borderRadius: 6, fontSize: '0.75rem' }}
>
<option value={2}>شخص واقف (2م)</option>
<option value={10}>آلية / برج مراقبة (10م)</option>
<option value={50}>سارية / درون استطلاع (50م)</option>
</select>
</div>
<div>
<label style={{ fontSize: '0.7rem', color: '#94a3b8', display: 'block', marginBottom: 4 }}>ارتفاع الهدف (B):</label>
<select
value={losTgtHeight}
onChange={e => setLosTgtHeight(Number(e.target.value))}
style={{ width: '100%', background: 'rgba(255,255,255,0.06)', border: '1px solid rgba(255,255,255,0.15)', color: '#fff', padding: '6px', borderRadius: 6, fontSize: '0.75rem' }}
>
<option value={1.8}>شخص (1.8م)</option>
<option value={3}>مركبة / مدرعة (3م)</option>
<option value={15}>مبنى / محطة رادار (15م)</option>
</select>
</div>
</div>
{losLoading && <div style={{ color: '#38bdf8', fontSize: '0.8rem' }}>جاري حساب المقطع وشعاع الرؤية...</div>}
{losResult && (
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
<div style={{
padding: '10px',
borderRadius: 8,
background: losResult.isDirectlyVisible ? 'rgba(34, 197, 94, 0.15)' : 'rgba(239, 68, 68, 0.15)',
border: losResult.isDirectlyVisible ? '1px solid #22c55e' : '1px solid #ef4444',
color: losResult.isDirectlyVisible ? '#4ade80' : '#f87171',
fontWeight: 700,
fontSize: '0.82rem',
display: 'flex',
alignItems: 'center',
gap: 8
}}>
{losResult.isDirectlyVisible ? <CheckCircle2 size={17} /> : <XCircle size={17} />}
<span>{losResult.isDirectlyVisible ? 'رؤية مباشرة مكشوفة بين الراصد والهدف' : 'الرؤية محجوبة خلف عائق تضاريسي'}</span>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 7, borderRadius: 6 }}>
<div style={{ fontSize: '0.65rem', color: '#94a3b8' }}>المسافة المباشرة</div>
<div style={{ fontSize: '0.9rem', fontWeight: 800 }}>{(losResult.totalDistance / 1000).toFixed(2)} كم</div>
</div>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 7, borderRadius: 6 }}>
<div style={{ fontSize: '0.65rem', color: '#94a3b8' }}>السمت / الاتجاه</div>
<div style={{ fontSize: '0.9rem', fontWeight: 800 }}>{losResult.azimuthDegrees}°</div>
</div>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 7, borderRadius: 6 }}>
<div style={{ fontSize: '0.65rem', color: '#94a3b8' }}>زاوية الموقع (Mils)</div>
<div style={{ fontSize: '0.9rem', fontWeight: 800, color: '#a78bfa' }}>{losResult.angleMils} mils</div>
</div>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 7, borderRadius: 6 }}>
<div style={{ fontSize: '0.65rem', color: '#94a3b8' }}>الأرض الميتة</div>
<div style={{ fontSize: '0.9rem', fontWeight: 800, color: '#f87171' }}>{losResult.deadGroundPercentage}%</div>
</div>
</div>
</div>
)}
</div>
)}
{/* ========================================================================= */}
{/* MODE 2: 360 VIEWSHED */}
{/* ========================================================================= */}
{mode === 'viewshed' && (
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
<div style={{ paddingBottom: 8, borderBottom: '1px solid rgba(255,255,255,0.08)' }}>
<h3 style={{ margin: 0, fontSize: '1rem', fontWeight: 800, color: '#4ade80', display: 'flex', alignItems: 'center', gap: 8 }}>
<Radio size={17} /> كشف الميدان الدائري 360° (Viewshed)
</h3>
<p style={{ margin: '4px 0 0 0', fontSize: '0.75rem', color: '#94a3b8' }}>
تحديد مركز الرادار أو المراقبة وكتابة إحداثياته لحساب التغطية
</p>
</div>
{/* Viewshed Center Coordinate Card */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8, border: '1px solid rgba(74, 222, 128, 0.2)' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
<span style={{ fontSize: '0.78rem', fontWeight: 800, color: '#4ade80' }}>📡 مركز الرصد والمراقبة:</span>
<button
onClick={() => setActivePlacement('viewshed-center')}
style={{
background: activePlacement === 'viewshed-center' ? '#16a34a' : 'rgba(74, 222, 128, 0.15)',
border: '1px solid #4ade80',
color: '#ffffff',
padding: '3px 8px',
borderRadius: 6,
fontSize: '0.7rem',
fontWeight: 700,
cursor: 'pointer'
}}
>
{activePlacement === 'viewshed-center' ? '🎯 انقر على الخريطة...' : '📡 التقاط من الخريطة'}
</button>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
<div>
<label style={{ fontSize: '0.65rem', color: '#94a3b8' }}>خط العرض (Lat):</label>
<input
type="number"
step="0.0001"
value={viewshedCenter ? viewshedCenter[0] : ''}
onChange={e => setViewshedCenter([Number(e.target.value), viewshedCenter ? viewshedCenter[1] : 35.9])}
style={{ width: '100%', background: '#0f172a', border: '1px solid rgba(255,255,255,0.2)', color: '#fff', padding: '5px 8px', borderRadius: 6, fontSize: '0.78rem' }}
/>
</div>
<div>
<label style={{ fontSize: '0.65rem', color: '#94a3b8' }}>خط الطول (Lng):</label>
<input
type="number"
step="0.0001"
value={viewshedCenter ? viewshedCenter[1] : ''}
onChange={e => setViewshedCenter([viewshedCenter ? viewshedCenter[0] : 31.9, Number(e.target.value)])}
style={{ width: '100%', background: '#0f172a', border: '1px solid rgba(255,255,255,0.2)', color: '#fff', padding: '5px 8px', borderRadius: 6, fontSize: '0.78rem' }}
/>
</div>
</div>
</div>
<div>
<label style={{ fontSize: '0.72rem', color: '#94a3b8', display: 'block', marginBottom: 4 }}>
نصف قطر الكشف: <strong>{(viewshedRadius / 1000).toFixed(1)} كم</strong>
</label>
<input
type="range"
min={1000}
max={15000}
step={500}
value={viewshedRadius}
onChange={e => setViewshedRadius(Number(e.target.value))}
style={{ width: '100%' }}
/>
</div>
<div>
<label style={{ fontSize: '0.72rem', color: '#94a3b8', display: 'block', marginBottom: 4 }}>
ارتفاع الرصد: <strong>{viewshedHeight} متر</strong>
</label>
<input
type="range"
min={2}
max={60}
step={2}
value={viewshedHeight}
onChange={e => setViewshedHeight(Number(e.target.value))}
style={{ width: '100%' }}
/>
</div>
<button
onClick={runViewshed360}
disabled={viewshedLoading}
style={{
background: 'linear-gradient(135deg, #10b981, #059669)',
border: 'none',
color: '#fff',
padding: '9px',
borderRadius: 8,
fontSize: '0.82rem',
fontWeight: 700,
cursor: 'pointer'
}}
>
{viewshedLoading ? 'جاري حساب المسح الدائري 360°...' : 'بدء التحليل الراداري 360°'}
</button>
{viewshedResult && (
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 8, borderRadius: 8 }}>
<div style={{ fontSize: '0.68rem', color: '#94a3b8' }}>المساحة المكشوفة</div>
<div style={{ fontSize: '0.95rem', fontWeight: 800, color: '#4ade80' }}>{viewshedResult.visibleAreaKm2} كم²</div>
</div>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 8, borderRadius: 8 }}>
<div style={{ fontSize: '0.68rem', color: '#94a3b8' }}>نسبة التغطية</div>
<div style={{ fontSize: '0.95rem', fontWeight: 800, color: '#38bdf8' }}>{viewshedResult.visiblePercentage}%</div>
</div>
</div>
)}
</div>
)}
{/* ========================================================================= */}
{/* MODE 3: ARTILLERY FIRE MISSION */}
{/* ========================================================================= */}
{mode === 'artillery' && (
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
<div style={{ paddingBottom: 8, borderBottom: '1px solid rgba(255,255,255,0.08)' }}>
<h3 style={{ margin: 0, fontSize: '1rem', fontWeight: 800, color: '#f97316', display: 'flex', alignItems: 'center', gap: 8 }}>
<Flame size={17} /> موقع المدفعية وحساب الرماية (Ballistics)
</h3>
<p style={{ margin: '4px 0 0 0', fontSize: '0.75rem', color: '#94a3b8' }}>
تحديد موقع المربض والهدف يدوياً أو بالنقر لحساب مسار القذائف
</p>
</div>
{/* Gun Position Coordinate Card */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8, border: '1px solid rgba(249, 115, 22, 0.2)' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
<span style={{ fontSize: '0.78rem', fontWeight: 800, color: '#f97316' }}>💥 موقع مربض المدفعية:</span>
<button
onClick={() => setActivePlacement('gun')}
style={{
background: activePlacement === 'gun' ? '#ea580c' : 'rgba(249, 115, 22, 0.15)',
border: '1px solid #f97316',
color: '#ffffff',
padding: '3px 8px',
borderRadius: 6,
fontSize: '0.7rem',
fontWeight: 700,
cursor: 'pointer'
}}
>
{activePlacement === 'gun' ? '🎯 انقر على الخريطة...' : '💥 التقاط من الخريطة'}
</button>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
<div>
<label style={{ fontSize: '0.65rem', color: '#94a3b8' }}>خط العرض (Lat):</label>
<input
type="number"
step="0.0001"
value={gunPos ? gunPos[0] : ''}
onChange={e => setGunPos([Number(e.target.value), gunPos ? gunPos[1] : 35.9])}
style={{ width: '100%', background: '#0f172a', border: '1px solid rgba(255,255,255,0.2)', color: '#fff', padding: '5px 8px', borderRadius: 6, fontSize: '0.78rem' }}
/>
</div>
<div>
<label style={{ fontSize: '0.65rem', color: '#94a3b8' }}>خط الطول (Lng):</label>
<input
type="number"
step="0.0001"
value={gunPos ? gunPos[1] : ''}
onChange={e => setGunPos([gunPos ? gunPos[0] : 31.9, Number(e.target.value)])}
style={{ width: '100%', background: '#0f172a', border: '1px solid rgba(255,255,255,0.2)', color: '#fff', padding: '5px 8px', borderRadius: 6, fontSize: '0.78rem' }}
/>
</div>
</div>
</div>
{/* Target Position Coordinate Card */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8, border: '1px solid rgba(239, 68, 68, 0.2)' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
<span style={{ fontSize: '0.78rem', fontWeight: 800, color: '#ef4444' }}>🎯 موقع الهدف المعادي:</span>
<button
onClick={() => setActivePlacement('target')}
style={{
background: activePlacement === 'target' ? '#dc2626' : 'rgba(239, 68, 68, 0.15)',
border: '1px solid #ef4444',
color: '#ffffff',
padding: '3px 8px',
borderRadius: 6,
fontSize: '0.7rem',
fontWeight: 700,
cursor: 'pointer'
}}
>
{activePlacement === 'target' ? '🎯 انقر على الخريطة...' : '🎯 التقاط من الخريطة'}
</button>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
<div>
<label style={{ fontSize: '0.65rem', color: '#94a3b8' }}>خط العرض (Lat):</label>
<input
type="number"
step="0.0001"
value={targetPos ? targetPos[0] : ''}
onChange={e => setTargetPos([Number(e.target.value), targetPos ? targetPos[1] : 35.9])}
style={{ width: '100%', background: '#0f172a', border: '1px solid rgba(255,255,255,0.2)', color: '#fff', padding: '5px 8px', borderRadius: 6, fontSize: '0.78rem' }}
/>
</div>
<div>
<label style={{ fontSize: '0.65rem', color: '#94a3b8' }}>خط الطول (Lng):</label>
<input
type="number"
step="0.0001"
value={targetPos ? targetPos[1] : ''}
onChange={e => setTargetPos([targetPos ? targetPos[0] : 31.9, Number(e.target.value)])}
style={{ width: '100%', background: '#0f172a', border: '1px solid rgba(255,255,255,0.2)', color: '#fff', padding: '5px 8px', borderRadius: 6, fontSize: '0.78rem' }}
/>
</div>
</div>
</div>
<div>
<label style={{ fontSize: '0.72rem', color: '#94a3b8', display: 'block', marginBottom: 4 }}>نوع السلاح والعيار:</label>
<select
value={caliber}
onChange={e => setCaliber(e.target.value)}
style={{ width: '100%', background: 'rgba(255,255,255,0.06)', border: '1px solid rgba(255,255,255,0.15)', color: '#fff', padding: '7px', borderRadius: 8, fontSize: '0.78rem' }}
>
<option value="155mm Howitzer (M109)">هاوتزر 155 ملم (M109 / M777)</option>
<option value="122mm Howitzer (D-30)">هاوتزر 122 ملم (D-30)</option>
<option value="120mm Heavy Mortar">هاون ثقيل 120 ملم</option>
<option value="MLRS Rocket (122mm)">راجمة صواريخ 122 ملم</option>
</select>
</div>
<button
onClick={runArtilleryMission}
disabled={artilleryLoading}
style={{
background: 'linear-gradient(135deg, #ea580c, #c2410c)',
border: 'none',
color: '#fff',
padding: '9px',
borderRadius: 8,
fontSize: '0.82rem',
fontWeight: 700,
cursor: 'pointer'
}}
>
{artilleryLoading ? 'جاري حساب حل الرماية...' : 'حساب حل الرماية والمسار البالستي'}
</button>
{artilleryResult && (
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
<div style={{
padding: '8px 10px',
borderRadius: 8,
background: artilleryResult.hasCrestClearance ? 'rgba(34, 197, 94, 0.15)' : 'rgba(239, 68, 68, 0.15)',
border: artilleryResult.hasCrestClearance ? '1px solid #22c55e' : '1px solid #ef4444',
color: artilleryResult.hasCrestClearance ? '#4ade80' : '#f87171',
fontWeight: 700,
fontSize: '0.8rem'
}}>
{artilleryResult.hasCrestClearance ? '✅ مسار القذيفة آمن ويعلو كافة القمم التضاريسية' : '⚠️ خطر: مسار القذيفة يصطدم بقمة جبلية وسيطة!'}
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 7, borderRadius: 6 }}>
<div style={{ fontSize: '0.65rem', color: '#94a3b8' }}>مسافة الرماية</div>
<div style={{ fontSize: '0.9rem', fontWeight: 800 }}>{artilleryResult.distanceKm} كم</div>
</div>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 7, borderRadius: 6 }}>
<div style={{ fontSize: '0.65rem', color: '#94a3b8' }}>السمت التكتيكي</div>
<div style={{ fontSize: '0.9rem', fontWeight: 800 }}>{artilleryResult.azimuthMils} mils</div>
</div>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 7, borderRadius: 6 }}>
<div style={{ fontSize: '0.65rem', color: '#94a3b8' }}>زاوية الرماية (منخفضة)</div>
<div style={{ fontSize: '0.9rem', fontWeight: 800, color: '#f97316' }}>{artilleryResult.lowAngle?.mils} mils</div>
</div>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 7, borderRadius: 6 }}>
<div style={{ fontSize: '0.65rem', color: '#94a3b8' }}>زمن الطيران</div>
<div style={{ fontSize: '0.9rem', fontWeight: 800, color: '#38bdf8' }}>{artilleryResult.timeOfFlightSeconds} ثانية</div>
</div>
</div>
</div>
)}
</div>
)}
{/* ========================================================================= */}
{/* MODE 4: MINEFIELD ANALYSIS */}
{/* ========================================================================= */}
{mode === 'minefield' && (
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
<div style={{ paddingBottom: 8, borderBottom: '1px solid rgba(255,255,255,0.08)' }}>
<h3 style={{ margin: 0, fontSize: '1rem', fontWeight: 800, color: '#dc2626', display: 'flex', alignItems: 'center', gap: 8 }}>
<AlertOctagon size={17} /> حقل الألغام ودراسة الجدوى الهندسية
</h3>
<p style={{ margin: '4px 0 0 0', fontSize: '0.75rem', color: '#94a3b8' }}>
تحديد طرفي الحقل يدوياً أو بالنقر لحساب الأعداد وساعات الجهد الهندسي
</p>
</div>
{/* Minefield Start Point */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8, border: '1px solid rgba(220, 38, 38, 0.2)' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
<span style={{ fontSize: '0.78rem', fontWeight: 800, color: '#f87171' }}>⛔ بداية حقل الألغام:</span>
<button
onClick={() => setActivePlacement('mine-start')}
style={{
background: activePlacement === 'mine-start' ? '#b91c1c' : 'rgba(220, 38, 38, 0.15)',
border: '1px solid #dc2626',
color: '#ffffff',
padding: '3px 8px',
borderRadius: 6,
fontSize: '0.7rem',
fontWeight: 700,
cursor: 'pointer'
}}
>
{activePlacement === 'mine-start' ? '🎯 انقر على الخريطة...' : '⛔ التقاط من الخريطة'}
</button>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
<div>
<label style={{ fontSize: '0.65rem', color: '#94a3b8' }}>خط العرض (Lat):</label>
<input
type="number"
step="0.0001"
value={mineStart ? mineStart[0] : ''}
onChange={e => setMineStart([Number(e.target.value), mineStart ? mineStart[1] : 35.9])}
style={{ width: '100%', background: '#0f172a', border: '1px solid rgba(255,255,255,0.2)', color: '#fff', padding: '5px 8px', borderRadius: 6, fontSize: '0.78rem' }}
/>
</div>
<div>
<label style={{ fontSize: '0.65rem', color: '#94a3b8' }}>خط الطول (Lng):</label>
<input
type="number"
step="0.0001"
value={mineStart ? mineStart[1] : ''}
onChange={e => setMineStart([mineStart ? mineStart[0] : 31.9, Number(e.target.value)])}
style={{ width: '100%', background: '#0f172a', border: '1px solid rgba(255,255,255,0.2)', color: '#fff', padding: '5px 8px', borderRadius: 6, fontSize: '0.78rem' }}
/>
</div>
</div>
</div>
{/* Minefield End Point */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8, border: '1px solid rgba(220, 38, 38, 0.2)' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
<span style={{ fontSize: '0.78rem', fontWeight: 800, color: '#f87171' }}>⛔ نهاية حقل الألغام:</span>
<button
onClick={() => setActivePlacement('mine-end')}
style={{
background: activePlacement === 'mine-end' ? '#991b1b' : 'rgba(220, 38, 38, 0.15)',
border: '1px solid #dc2626',
color: '#ffffff',
padding: '3px 8px',
borderRadius: 6,
fontSize: '0.7rem',
fontWeight: 700,
cursor: 'pointer'
}}
>
{activePlacement === 'mine-end' ? '🎯 انقر على الخريطة...' : '⛔ التقاط من الخريطة'}
</button>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
<div>
<label style={{ fontSize: '0.65rem', color: '#94a3b8' }}>خط العرض (Lat):</label>
<input
type="number"
step="0.0001"
value={mineEnd ? mineEnd[0] : ''}
onChange={e => setMineEnd([Number(e.target.value), mineEnd ? mineEnd[1] : 35.9])}
style={{ width: '100%', background: '#0f172a', border: '1px solid rgba(255,255,255,0.2)', color: '#fff', padding: '5px 8px', borderRadius: 6, fontSize: '0.78rem' }}
/>
</div>
<div>
<label style={{ fontSize: '0.65rem', color: '#94a3b8' }}>خط الطول (Lng):</label>
<input
type="number"
step="0.0001"
value={mineEnd ? mineEnd[1] : ''}
onChange={e => setMineEnd([mineEnd ? mineEnd[0] : 31.9, Number(e.target.value)])}
style={{ width: '100%', background: '#0f172a', border: '1px solid rgba(255,255,255,0.2)', color: '#fff', padding: '5px 8px', borderRadius: 6, fontSize: '0.78rem' }}
/>
</div>
</div>
</div>
<div>
<label style={{ fontSize: '0.72rem', color: '#94a3b8', display: 'block', marginBottom: 4 }}>كثافة الحقل الدفاعي:</label>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 6 }}>
{[
{ id: 'standard', label: 'معياري (Standard)' },
{ id: 'dense', label: 'كثيف (Dense)' },
{ id: 'light', label: 'خفيف (Light)' },
].map(d => (
<button
key={d.id}
onClick={() => setMineDensity(d.id as any)}
style={{
background: mineDensity === d.id ? 'rgba(220, 38, 38, 0.35)' : 'rgba(255,255,255,0.05)',
border: mineDensity === d.id ? '1px solid #dc2626' : '1px solid rgba(255,255,255,0.1)',
color: '#fff',
padding: '6px',
borderRadius: 6,
fontSize: '0.72rem',
fontWeight: mineDensity === d.id ? 700 : 500,
cursor: 'pointer'
}}
>
{d.label}
</button>
))}
</div>
</div>
{mineLoading && <div style={{ color: '#dc2626', fontSize: '0.8rem' }}>جاري تقييم تضاريس المانع وحساب الكميات...</div>}
{mineResult && (
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
{/* Verdict Card */}
<div style={{
padding: '8px 10px',
borderRadius: 8,
background: mineResult.suitabilityScore >= 75 ? 'rgba(34, 197, 94, 0.15)' : 'rgba(234, 179, 8, 0.15)',
border: mineResult.suitabilityScore >= 75 ? '1px solid #22c55e' : '1px solid #eab308',
color: mineResult.suitabilityScore >= 75 ? '#4ade80' : '#fde047',
fontSize: '0.78rem',
fontWeight: 700
}}>
{mineResult.suitabilityVerdictAr}
</div>
{/* Quantities & Sapper Estimations */}
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 7, borderRadius: 6 }}>
<div style={{ fontSize: '0.65rem', color: '#94a3b8' }}>عرض الجبهة</div>
<div style={{ fontSize: '0.9rem', fontWeight: 800 }}>{mineResult.frontageMeters} متر</div>
</div>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 7, borderRadius: 6 }}>
<div style={{ fontSize: '0.65rem', color: '#94a3b8' }}>متوسط الانحدار</div>
<div style={{ fontSize: '0.9rem', fontWeight: 800 }}>{mineResult.avgSlopeDegrees}°</div>
</div>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 7, borderRadius: 6 }}>
<div style={{ fontSize: '0.65rem', color: '#94a3b8' }}>ألغام مضادة للدروع (AT)</div>
<div style={{ fontSize: '0.9rem', fontWeight: 800, color: '#f87171' }}>{mineResult.quantities.antiTankMines} لغم</div>
</div>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 7, borderRadius: 6 }}>
<div style={{ fontSize: '0.65rem', color: '#94a3b8' }}>ألغام أفراد (AP)</div>
<div style={{ fontSize: '0.9rem', fontWeight: 800, color: '#fb923c' }}>{mineResult.quantities.antiPersonnelMines} لغم</div>
</div>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 7, borderRadius: 6 }}>
<div style={{ fontSize: '0.65rem', color: '#94a3b8' }}>عدد الصفوف والعمق</div>
<div style={{ fontSize: '0.85rem', fontWeight: 700 }}>{mineResult.quantities.rowsCount} صفوف ({mineResult.quantities.depthMeters}م)</div>
</div>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 7, borderRadius: 6 }}>
<div style={{ fontSize: '0.65rem', color: '#94a3b8' }}>جهد التنفيذ الهندسي</div>
<div style={{ fontSize: '0.85rem', fontWeight: 700, color: '#38bdf8' }}>{mineResult.quantities.estimatedDeploymentHours} ساعة عمل</div>
</div>
</div>
</div>
)}
</div>
)}
{/* ========================================================================= */}
{/* MODE 5: HLZ ASSESSMENT */}
{/* ========================================================================= */}
{mode === 'hlz' && (
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
<div style={{ paddingBottom: 8, borderBottom: '1px solid rgba(255,255,255,0.08)' }}>
<h3 style={{ margin: 0, fontSize: '1rem', fontWeight: 800, color: '#06b6d4', display: 'flex', alignItems: 'center', gap: 8 }}>
<Crosshair size={17} /> مهابط المروحيات والإخلاء (HLZ)
</h3>
<p style={{ margin: '4px 0 0 0', fontSize: '0.75rem', color: '#94a3b8' }}>
فحص انحدار الأرض وسلامة مهابط الإخلاء الطبي والمقاتلات
</p>
</div>
{/* HLZ Center Card */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8, border: '1px solid rgba(6, 182, 212, 0.2)' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
<span style={{ fontSize: '0.78rem', fontWeight: 800, color: '#06b6d4' }}>🚁 مركز الفحص (HLZ Center):</span>
<button
onClick={() => setActivePlacement('hlz-center')}
style={{
background: activePlacement === 'hlz-center' ? '#0891b2' : 'rgba(6, 182, 212, 0.15)',
border: '1px solid #06b6d4',
color: '#ffffff',
padding: '3px 8px',
borderRadius: 6,
fontSize: '0.7rem',
fontWeight: 700,
cursor: 'pointer'
}}
>
{activePlacement === 'hlz-center' ? '🎯 انقر على الخريطة...' : '🚁 التقاط من الخريطة'}
</button>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
<div>
<label style={{ fontSize: '0.65rem', color: '#94a3b8' }}>خط العرض (Lat):</label>
<input
type="number"
step="0.0001"
value={hlzCenter ? hlzCenter[0] : ''}
onChange={e => setHlzCenter([Number(e.target.value), hlzCenter ? hlzCenter[1] : 35.9])}
style={{ width: '100%', background: '#0f172a', border: '1px solid rgba(255,255,255,0.2)', color: '#fff', padding: '5px 8px', borderRadius: 6, fontSize: '0.78rem' }}
/>
</div>
<div>
<label style={{ fontSize: '0.65rem', color: '#94a3b8' }}>خط الطول (Lng):</label>
<input
type="number"
step="0.0001"
value={hlzCenter ? hlzCenter[1] : ''}
onChange={e => setHlzCenter([hlzCenter ? hlzCenter[0] : 31.9, Number(e.target.value)])}
style={{ width: '100%', background: '#0f172a', border: '1px solid rgba(255,255,255,0.2)', color: '#fff', padding: '5px 8px', borderRadius: 6, fontSize: '0.78rem' }}
/>
</div>
</div>
</div>
<button
onClick={runHLZAssessment}
disabled={hlzLoading}
style={{
background: 'linear-gradient(135deg, #0891b2, #0e7490)',
border: 'none',
color: '#fff',
padding: '9px',
borderRadius: 8,
fontSize: '0.82rem',
fontWeight: 700,
cursor: 'pointer'
}}
>
{hlzLoading ? 'جاري مسح التضاريس والمهابط...' : 'بدء فحص وتصنيف المهابط (HLZ)'}
</button>
{hlzResult && (
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
<div style={{ fontSize: '0.78rem', color: '#94a3b8' }}>
تم مسح {hlzResult.totalAssessed} موقعاً (المناسب: {hlzResult.suitableCount})
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 6, maxHeight: 220, overflowY: 'auto' }}>
{(hlzResult.zones || []).slice(0, 5).map((z: any) => (
<div key={z.id} style={{ background: 'rgba(255,255,255,0.04)', padding: '7px 10px', borderRadius: 6, fontSize: '0.75rem' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', fontWeight: 700 }}>
<span>{z.id}</span>
<span style={{ color: z.suitability === 'EXCELLENT' ? '#4ade80' : '#facc15' }}>{z.suitability_ar}</span>
</div>
<div style={{ color: '#94a3b8', fontSize: '0.7rem' }}>الانحدار: {z.slopeDegrees}° | الارتفاع: {z.elevationMeters}م</div>
</div>
))}
</div>
</div>
)}
</div>
)}
{/* ========================================================================= */}
{/* MODE 6: TACTICAL SYMBOLS & DEPLOYMENT */}
{/* ========================================================================= */}
{mode === 'symbols' && (
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
<div style={{ paddingBottom: 8, borderBottom: '1px solid rgba(255,255,255,0.08)' }}>
<h3 style={{ margin: 0, fontSize: '1rem', fontWeight: 800, color: '#a855f7', display: 'flex', alignItems: 'center', gap: 8 }}>
<Layers size={17} /> الرموز التكتيكية ونشر التشكيلات
</h3>
<p style={{ margin: '4px 0 0 0', fontSize: '0.75rem', color: '#94a3b8' }}>
تثبيت مواقع التشكيلات الصديقة والمعادية والأسلحة بالنقر أو كتابة الإحداثيات
</p>
</div>
<div>
<label style={{ fontSize: '0.72rem', color: '#94a3b8', display: 'block', marginBottom: 4 }}>نوع الرمز العسكري:</label>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
{[
{ id: 'friendly', label: '🟦 وحدة صديقة' },
{ id: 'hostile', label: '🟥 هدف معادي' },
{ id: 'artillery', label: '🟧 مربض مدفعية' },
{ id: 'radar', label: '🩵 محطة رادار' },
{ id: 'checkpoint', label: '🟨 نقطة سيطرة' },
{ id: 'op', label: '🟪 نقطة مراقبة' },
{ id: 'minefield', label: '⛔ حقل ألغام' },
{ id: 'hlz', label: '🚁 مهبط مروحيات' },
].map(sym => (
<button
key={sym.id}
onClick={() => setSelectedSymbolType(sym.id)}
style={{
background: selectedSymbolType === sym.id ? 'rgba(168, 85, 247, 0.35)' : 'rgba(255,255,255,0.05)',
border: selectedSymbolType === sym.id ? '1px solid #c084fc' : '1px solid rgba(255,255,255,0.1)',
color: '#fff',
padding: '6px 8px',
borderRadius: 6,
fontSize: '0.72rem',
fontWeight: selectedSymbolType === sym.id ? 700 : 500,
cursor: 'pointer',
textAlign: 'right'
}}
>
{sym.label}
</button>
))}
</div>
</div>
<div>
<label style={{ fontSize: '0.72rem', color: '#94a3b8', display: 'block', marginBottom: 4 }}>تسمية التشكيل / الوحدة:</label>
<input
type="text"
placeholder="مثال: سرية الدبابات 1 / بطارية صواريخ"
value={newSymbolName}
onChange={e => setNewSymbolName(e.target.value)}
style={{ width: '100%', background: 'rgba(255,255,255,0.06)', border: '1px solid rgba(255,255,255,0.15)', color: '#fff', padding: '7px', borderRadius: 8, fontSize: '0.78rem' }}
/>
</div>
{/* Coordinate Placement Box */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8, border: '1px solid rgba(168, 85, 247, 0.2)' }}>
<div style={{ fontSize: '0.75rem', fontWeight: 700, color: '#c084fc', marginBottom: 6 }}>
تحديد إحداثيات النشر:
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6, marginBottom: 8 }}>
<div>
<label style={{ fontSize: '0.65rem', color: '#94a3b8' }}>Lat:</label>
<input
type="number"
step="0.0001"
value={customSymbolLat}
onChange={e => setCustomSymbolLat(Number(e.target.value))}
style={{ width: '100%', background: '#0f172a', border: '1px solid rgba(255,255,255,0.2)', color: '#fff', padding: '5px', borderRadius: 6, fontSize: '0.75rem' }}
/>
</div>
<div>
<label style={{ fontSize: '0.65rem', color: '#94a3b8' }}>Lng:</label>
<input
type="number"
step="0.0001"
value={customSymbolLng}
onChange={e => setCustomSymbolLng(Number(e.target.value))}
style={{ width: '100%', background: '#0f172a', border: '1px solid rgba(255,255,255,0.2)', color: '#fff', padding: '5px', borderRadius: 6, fontSize: '0.75rem' }}
/>
</div>
</div>
<button
onClick={() => {
const newSym: TacticalSymbolItem = {
id: `sym-${Date.now()}`,
type: selectedSymbolType as any,
name: newSymbolName || `نقطة ${selectedSymbolType}`,
lat: customSymbolLat,
lng: customSymbolLng
};
setSymbols(prev => [...prev, newSym]);
setNewSymbolName('');
}}
style={{
width: '100%',
background: 'linear-gradient(135deg, #9333ea, #7e22ce)',
border: 'none',
color: '#fff',
padding: '7px',
borderRadius: 6,
fontSize: '0.78rem',
fontWeight: 700,
cursor: 'pointer'
}}
>
➕ إضافة التشكيل في هذه الإحداثيات
</button>
</div>
{/* Active Symbols List */}
<div style={{ marginTop: 4 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
<span style={{ fontSize: '0.75rem', color: '#94a3b8' }}>الرموز المنتشرة ({symbols.length}):</span>
{symbols.length > 0 && (
<button
onClick={() => setSymbols([])}
style={{ background: 'none', border: 'none', color: '#ef4444', fontSize: '0.72rem', cursor: 'pointer', textDecoration: 'underline' }}
>
مسح الكل
</button>
)}
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 6, maxHeight: 180, overflowY: 'auto' }}>
{symbols.map(s => (
<div key={s.id} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', background: 'rgba(255,255,255,0.04)', padding: '6px 10px', borderRadius: 6, fontSize: '0.75rem' }}>
<span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
<span style={{
width: 8,
height: 8,
borderRadius: '50%',
background: s.type === 'friendly' ? '#3b82f6' : s.type === 'hostile' ? '#ef4444' : s.type === 'artillery' ? '#f97316' : s.type === 'radar' ? '#06b6d4' : s.type === 'checkpoint' ? '#eab308' : s.type === 'minefield' ? '#dc2626' : s.type === 'hlz' ? '#10b981' : '#a855f7'
}} />
<span>{s.name} ({s.lat.toFixed(3)}, {s.lng.toFixed(3)})</span>
</span>
<button
onClick={() => setSymbols(symbols.filter(x => x.id !== s.id))}
style={{ background: 'none', border: 'none', color: '#ef4444', cursor: 'pointer', padding: 2 }}
>
✕
</button>
</div>
))}
</div>
</div>
</div>
)}
{/* ========================================================================= */}
{/* MODE 7: TACTICAL FEASIBILITY STUDY & ORGANIZATION REPORT */}
{/* ========================================================================= */}
{mode === 'study' && (
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
<div style={{ paddingBottom: 8, borderBottom: '1px solid rgba(255,255,255,0.08)' }}>
<h3 style={{ margin: 0, fontSize: '1rem', fontWeight: 800, color: '#fbbf24', display: 'flex', alignItems: 'center', gap: 8 }}>
<Award size={17} /> دراسة التقييم والتنظيم الميداني
</h3>
<p style={{ margin: '4px 0 0 0', fontSize: '0.75rem', color: '#94a3b8' }}>
تحليل منطقية التموضع والملاءمة التكتيكية للتنظيم الدفاعي
</p>
</div>
{/* Tactical Feasibility Score */}
<div style={{
background: 'linear-gradient(135deg, rgba(34, 197, 94, 0.15), rgba(59, 130, 246, 0.15))',
border: '1px solid #3b82f6',
padding: '12px',
borderRadius: 10,
textAlign: 'center'
}}>
<div style={{ fontSize: '0.75rem', color: '#94a3b8', marginBottom: 2 }}>مؤشر الملاءمة التكتيكية للتنظيم (Tactical Soundness)</div>
<div style={{ fontSize: '1.8rem', fontWeight: 900, color: '#38bdf8' }}>94%</div>
<div style={{ fontSize: '0.75rem', color: '#4ade80', fontWeight: 700 }}>تنظيم دفاعي متكامل ومنطقي تضاريسياً</div>
</div>
{/* Detailed Breakdown */}
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8 }}>
<div style={{ fontSize: '0.78rem', fontWeight: 700, color: '#4ade80', marginBottom: 4 }}>
✅ 1. تقييم مرابض المدفعية:
</div>
<div style={{ fontSize: '0.72rem', color: '#cbd5e1', lineHeight: 1.4 }}>
موقع المدفعية يقع خلف ساتر تضاريسي يحجب خط النظر المباشر للعدو (Defilade)، مع وجود خلوص آمن فوق القمم لخط مسار القذائف.
</div>
</div>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8 }}>
<div style={{ fontSize: '0.78rem', fontWeight: 700, color: '#4ade80', marginBottom: 4 }}>
✅ 2. تقييم حقول الألغام والموانع:
</div>
<div style={{ fontSize: '0.72rem', color: '#cbd5e1', lineHeight: 1.4 }}>
تموضع المانع في خانق تضاريسي (Choke Point) يقطع محور تقدم الدروع، بكثافة قياسية تلبي متطلبات العقيدة الدفاعية (1 لغم م/د + 2 لغم م/ف لكل متر).
</div>
</div>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8 }}>
<div style={{ fontSize: '0.78rem', fontWeight: 700, color: '#4ade80', marginBottom: 4 }}>
✅ 3. هيمنة نقاط المراقبة والرادار:
</div>
<div style={{ fontSize: '0.72rem', color: '#cbd5e1', lineHeight: 1.4 }}>
المرصد الرئيسي ورادار الكشف يحتلان قمة مهيمنة تحقق نسبة كشف ميداني 360° تتجاوز 78% من قاطع العمليات.
</div>
</div>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8 }}>
<div style={{ fontSize: '0.78rem', fontWeight: 700, color: '#facc15', marginBottom: 4 }}>
⚠️ 4. التوصيات الهندسية والتكتيكية:
</div>
<div style={{ fontSize: '0.72rem', color: '#cbd5e1', lineHeight: 1.4 }}>
• تخصيص طاقم هندسي لتنفيذ حقل الألغام بجهد مقدر بـ 6.5 ساعة عمل.<br/>
• تأمين نقطة إنذار مبكر على الجناح الأيمن لتغطية الزاوية الميتة للرادار.
</div>
</div>
</div>
</div>
)}
</aside>
{/* Tactical Map Container */}
<div ref={mapContainer} style={{ flex: 1, height: '100%', position: 'relative' }} />
</div>
</div>
);
};