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

3550 lines
167 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 { getSharedDemSource } from '../utils/demSource';
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,
Lock,
Unlock,
Key,
ShieldAlert,
ShieldCheck,
LogOut,
Clock,
Zap,
Share2
} 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' | 'isochrone';
export interface EmergencyHubPreset {
id: string;
name: string;
lat: number;
lng: number;
type: 'hospital' | 'civil_defense' | 'security';
badge: string;
description: string;
}
export const EMERGENCY_HUBS: EmergencyHubPreset[] = [
{ id: 'hub-1', name: 'المدينة الطبية الملكية (عمان)', lat: 31.9865, lng: 35.8340, type: 'hospital', badge: 'مستشفى رئيسي', description: 'مجمع طبي عسكري متقدم - غرب عمان' },
{ id: 'hub-2', name: 'مستشفى الجامعة الأردنية', lat: 32.0080, lng: 35.8720, type: 'hospital', badge: 'مستشفى جامعي', description: 'مركز طوارئ وإسعاف رئيسي شمال عمان' },
{ id: 'hub-3', name: 'مستشفى البشير الحكومي (عمان)', lat: 31.9425, lng: 35.9450, type: 'hospital', badge: 'طوارئ عامة', description: 'أكبر مجمع إسعاف وطوارئ في شرق ووسط عمان' },
{ id: 'hub-4', name: 'مديرية دفاع مدني العبدلي / وسط البلد', lat: 31.9680, lng: 35.9120, type: 'civil_defense', badge: 'مركز إطفاء وإنقاذ', description: 'مركز إطفاء وإنقاذ رئيسي لوسط العاصمة' },
{ id: 'hub-5', name: 'مركز دفاع مدني غرب عمان (خلدا/صويلح)', lat: 31.9960, lng: 35.8450, type: 'civil_defense', badge: 'إسعاف فوري', description: 'تغطية سريعة لمحاور شمال وغرب عمان' },
{ id: 'hub-6', name: 'مستشفى الملك المؤسس عبدالله الجامعي (إربد)', lat: 32.4950, lng: 35.9920, type: 'hospital', badge: 'إقليم الشمال', description: 'صرح طبي متقدم يخدم محافظات الشمال' },
{ id: 'hub-7', name: 'مديرية دفاع مدني إربد المركزية', lat: 32.5590, lng: 35.8450, type: 'civil_defense', badge: 'مركز رئيسي', description: 'مركز قيادة عمليات الإنقاذ والإسعاف في الشمال' },
{ id: 'hub-8', name: 'مستشفى الزرقاء الحكومي الجديد', lat: 32.0620, lng: 36.1280, type: 'hospital', badge: 'طوارئ الزرقاء', description: 'تغطية الطوارئ والحوادث لمحافظة الزرقاء' },
{ id: 'hub-9', name: 'مستشفى الأمير هاشم العسكري (العقبة)', lat: 29.5350, lng: 35.0080, type: 'hospital', badge: 'إقليم الجنوب', description: 'مستشفى عسكري وإسناد طارئ لخليج العقبة' },
];
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);
// Tactical License & Access Clearance State
const [authKey, setAuthKey] = useState<string>(() => {
return new URLSearchParams(window.location.search).get('key') ||
new URLSearchParams(window.location.search).get('api_key') ||
sessionStorage.getItem('tactical_api_key') || '';
});
const [authStatus, setAuthStatus] = useState<'validating' | 'authorized' | 'unauthorized'>('validating');
const [authInfo, setAuthInfo] = useState<{ tenantName?: string; plan?: string; keyName?: string } | null>(null);
const [authError, setAuthError] = useState<string>('');
const [inputKey, setInputKey] = useState<string>('');
const [isVerifying, setIsVerifying] = useState<boolean>(false);
const verifyTacticalLicense = async (keyCandidate: string) => {
if (!keyCandidate || !keyCandidate.trim()) {
setAuthStatus('unauthorized');
return;
}
setIsVerifying(true);
setAuthError('');
try {
const apiUrl = (import.meta as any).env.VITE_API_URL || '/api';
const res = await fetch(`${apiUrl}/tactical/verify-license`, {
headers: { 'x-api-key': keyCandidate.trim() }
});
if (res.ok) {
const data = await res.json();
setAuthInfo(data);
setAuthKey(keyCandidate.trim());
sessionStorage.setItem('tactical_api_key', keyCandidate.trim());
setAuthStatus('authorized');
} else {
sessionStorage.removeItem('tactical_api_key');
setAuthStatus('unauthorized');
setAuthError('مفتاح التصريح التكتيكي غير صالح، أو منتهي الصلاحية، أو تم إلغاؤه من قبل الإدارة.');
}
} catch (e) {
sessionStorage.removeItem('tactical_api_key');
setAuthStatus('unauthorized');
setAuthError('تعذر التحقق من خادم التخويل التكتيكي. يرجى التأكد من الاتصال.');
} finally {
setIsVerifying(false);
}
};
useEffect(() => {
if (authKey) {
verifyTacticalLicense(authKey);
} else {
setAuthStatus('unauthorized');
}
}, []);
const handleRevokeOrLock = () => {
sessionStorage.removeItem('tactical_api_key');
setAuthKey('');
setAuthStatus('unauthorized');
setAuthInfo(null);
setAuthError('');
};
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 [showContours, setShowContours] = useState<boolean>(true);
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);
// 8. Isochrone / Emergency Reachability State (خارطة زمن الوصول والاستجابة)
const [isochroneCenter, setIsochroneCenter] = useState<[number, number]>([31.9865, 35.8340]); // King Hussein Medical City
const [isochroneProfile, setIsochroneProfile] = useState<string>('emergency');
const [isochroneTimes, setIsochroneTimes] = useState<number[]>([300, 600, 900]);
const [isochroneData, setIsochroneData] = useState<any>(null);
const [isochroneLoading, setIsochroneLoading] = useState<boolean>(false);
const [selectedHubName, setSelectedHubName] = useState<string>('المدينة الطبية الملكية (عمان)');
const [copiedIsochroneGeoJson, setCopiedIsochroneGeoJson] = useState<boolean>(false);
// 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 isochroneCenterRef = useRef(isochroneCenter);
isochroneCenterRef.current = isochroneCenter;
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));
// 8. Isochrone / Response Reachability Hub
if (mode === 'isochrone' && isochroneCenter) {
updateMarker(
'isochrone-c',
isochroneCenter,
`🚑 ${selectedHubName.split(' ')[0]} (نقطة الانطلاق)`,
'#10b981',
(pos) => {
setIsochroneCenter(pos);
setSelectedHubName(`نقطة مخصصة (${pos[0].toFixed(4)}, ${pos[1].toFixed(4)})`);
}
);
} else {
updateMarker('isochrone-c', null, '', '', () => {});
}
}, [terrainCenter, terrainResult, showPeaksAndValleys, mode, losPointA, losPointB, viewshedCenter, gunPos, targetPos, mineStart, mineEnd, hlzCenter, isochroneCenter, selectedHubName]);
// 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;
}
if (placement === 'isochrone-center') {
setIsochroneCenter([clickedLat, clickedLng]);
setSelectedHubName(`نقطة مخصصة (${clickedLat.toFixed(4)}, ${clickedLng.toFixed(4)})`);
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 === 'isochrone') {
setIsochroneCenter([clickedLat, clickedLng]);
setSelectedHubName(`نقطة مخصصة (${clickedLat.toFixed(4)}, ${clickedLng.toFixed(4)})`);
} 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]
}
});
// Dynamic On-the-Fly Contours from Elevation DEM
const demSource = getSharedDemSource();
const contourUrl = demSource.contourProtocolUrl({
thresholds: {
10: [100, 500],
11: [50, 250],
12: [25, 100],
13: [20, 100],
14: [10, 50],
15: [10, 50],
16: [10, 50],
17: [10, 50],
18: [10, 50],
},
elevationKey: 'ele',
levelKey: 'level',
contourLayer: 'contours',
});
if (!initialMap.getSource('contour-source')) {
initialMap.addSource('contour-source', {
type: 'vector',
tiles: [contourUrl],
maxzoom: 18,
});
initialMap.addLayer({
id: 'contour-lines-minor',
type: 'line',
source: 'contour-source',
'source-layer': 'contours',
minzoom: 10,
layout: {
visibility: 'visible',
},
filter: ['==', ['get', 'level'], 0],
paint: {
'line-color': '#a86324',
'line-width': 1.1,
'line-opacity': 0.85,
},
});
initialMap.addLayer({
id: 'contour-lines-major',
type: 'line',
source: 'contour-source',
'source-layer': 'contours',
minzoom: 9,
layout: {
visibility: 'visible',
},
filter: ['>', ['get', 'level'], 0],
paint: {
'line-color': '#703800',
'line-width': 2.0,
'line-opacity': 0.95,
},
});
initialMap.addLayer({
id: 'contour-labels',
type: 'symbol',
source: 'contour-source',
'source-layer': 'contours',
minzoom: 12,
layout: {
visibility: 'visible',
'symbol-placement': 'line',
'text-field': ['concat', ['to-string', ['get', 'ele']], ' m'],
'text-size': 10,
'text-font': ['Noto Sans Bold', 'Open Sans Bold'],
},
paint: {
'text-color': '#5c2d00',
'text-halo-color': 'rgba(255, 255, 255, 0.95)',
'text-halo-width': 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'],
'cliff', 0.38,
'urban', 0.20,
'hazard', 0.24,
'slope-sector', 0.14,
0.15
]
}
});
initialMap.addLayer({
id: 'tactical-terrain-spatial-poly-line',
type: 'line',
source: 'tactical-terrain-spatial-src',
filter: ['==', '$type', 'Polygon'],
paint: {
'line-color': [
'match',
['get', 'layerType'],
'cliff', '#ef4444',
'rgba(255, 255, 255, 0.10)'
],
'line-width': [
'match',
['get', 'layerType'],
'cliff', 1.8,
0.4
]
}
});
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'],
'cliff', 3.5,
'road', 4.0,
'wadi', 3.5,
'ridge', 2.5,
2.5
],
'line-dasharray': [
'match',
['get', 'layerType'],
'cliff', ['literal', [2, 1]],
'ridge', ['literal', [3, 1.5]],
'road', ['literal', [1, 0]],
'wadi', ['literal', [1, 0]],
['literal', [1, 0]]
]
}
});
initialMap.addLayer({
id: 'tactical-terrain-spatial-points',
type: 'circle',
source: 'tactical-terrain-spatial-src',
filter: ['==', '$type', 'Point'],
paint: {
'circle-radius': 5,
'circle-color': ['coalesce', ['get', 'color'], '#38bdf8'],
'circle-stroke-width': 2,
'circle-stroke-color': '#ffffff'
}
});
initialMap.addLayer({
id: 'tactical-terrain-spatial-labels',
type: 'symbol',
source: 'tactical-terrain-spatial-src',
filter: ['all', ['==', '$type', 'Point'], ['has', 'title']],
layout: {
'text-field': ['get', 'title'],
'text-size': 12,
'text-font': ['Noto Sans Bold', 'Open Sans Bold'],
'text-anchor': 'bottom',
'text-offset': [0, -0.6],
'symbol-placement': 'point',
'text-allow-overlap': false
},
paint: {
'text-color': '#ffffff',
'text-halo-color': '#0f172a',
'text-halo-width': 2.5
}
});
// 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
}
});
// 6. Isochrone Reachability Polygons Layer (خارطة زمن الاستجابة والوصول)
initialMap.addSource('tactical-isochrone-src', {
type: 'geojson',
data: { type: 'FeatureCollection', features: [] }
});
initialMap.addLayer({
id: 'tactical-isochrone-fill',
type: 'fill',
source: 'tactical-isochrone-src',
paint: {
'fill-color': ['coalesce', ['get', 'color'], '#22c55e'],
'fill-opacity': 0.32
}
});
initialMap.addLayer({
id: 'tactical-isochrone-line',
type: 'line',
source: 'tactical-isochrone-src',
paint: {
'line-color': ['coalesce', ['get', 'color'], '#22c55e'],
'line-width': 2.5,
'line-opacity': 0.95
}
});
});
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));
};
// Toggle Contour Layers Visibility
useEffect(() => {
if (!map.current) return;
['contour-lines-minor', 'contour-lines-major', 'contour-labels'].forEach(layerId => {
if (map.current!.getLayer(layerId)) {
map.current!.setLayoutProperty(layerId, 'visibility', showContours ? 'visible' : 'none');
}
});
}, [showContours]);
// 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' || f.properties.layerType === 'ridge') 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' || f.properties.layerType === 'cliff') 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 keyToSend = authKey || (import.meta as any).env.VITE_API_KEY || '';
const res = await fetch(`${apiUrl}/tactical/artillery-fire-mission`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'x-api-key': keyToSend },
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 keyToSend = authKey || (import.meta as any).env.VITE_API_KEY || '';
const res = await fetch(`${apiUrl}/tactical/hlz-assessment?lat=${hlzCenter[0]}&lng=${hlzCenter[1]}&radius=4000`, {
headers: { 'x-api-key': keyToSend }
});
const data = await res.json();
setHlzResult(data);
} catch (e) {
console.error('HLZ error:', e);
} finally {
setHlzLoading(false);
}
};
// Helper for High-Precision Tactical Isochrone Polygons (نطاقات زمن الوصول والاستجابة)
const generateLocalIsochrone = (lat: number, lng: number, timeBuckets: number[], profile: string) => {
const avgSpeedKmh = profile === 'emergency' ? 60 : profile === 'patrol' ? 50 : 38;
const colors = ['#22c55e', '#eab308', '#ef4444', '#8b5cf6'];
const tiers = timeBuckets.map((seconds, idx) => {
const minutes = Math.round(seconds / 60);
const effectiveRadiusM = (avgSpeedKmh * 1000 / 3600) * seconds * 0.70;
const numPoints = 36;
const ringCoords: [number, number][] = [];
for (let i = 0; i < numPoints; i++) {
const angle = (i * 2 * Math.PI) / numPoints;
const roadCorridor = 1 + 0.14 * Math.sin(angle * 4) + 0.08 * Math.cos(angle * 2);
const r = effectiveRadiusM * roadCorridor;
const dLat = (r / 6371000) * (180 / Math.PI) * Math.cos(angle);
const dLng = (r / (6371000 * Math.cos((lat * Math.PI) / 180))) * (180 / Math.PI) * Math.sin(angle);
ringCoords.push([Number((lng + dLng).toFixed(6)), Number((lat + dLat).toFixed(6))]);
}
ringCoords.push(ringCoords[0]);
const tierColor = colors[idx % colors.length];
const areaKm2 = Math.round(Math.PI * Math.pow(effectiveRadiusM / 1000, 2) * 10) / 10;
return {
timeSeconds: seconds,
timeMinutes: minutes,
label: `${minutes} دقائق استجابة`,
color: tierColor,
estimatedAreaKm2: areaKm2,
polygon: {
type: 'Feature',
properties: {
timeMinutes: minutes,
timeSeconds: seconds,
color: tierColor,
label: `${minutes} دقائق`
},
geometry: {
type: 'Polygon',
coordinates: [ringCoords]
}
}
};
});
return {
center: { lat, lng },
vehicleProfile: profile,
tiers,
featureCollection: {
type: 'FeatureCollection',
features: tiers.map(t => t.polygon)
}
};
};
// Synchronize Isochrone Data to Map
useEffect(() => {
if (!map.current || !map.current.getSource('tactical-isochrone-src')) return;
if (mode === 'isochrone' && isochroneData?.featureCollection) {
(map.current.getSource('tactical-isochrone-src') as maplibregl.GeoJSONSource).setData(isochroneData.featureCollection);
} else {
(map.current.getSource('tactical-isochrone-src') as maplibregl.GeoJSONSource).setData({
type: 'FeatureCollection',
features: []
});
}
}, [mode, isochroneData]);
// Run Isochrone Calculation
const runIsochroneCalculation = async (center = isochroneCenter, profile = isochroneProfile, times = isochroneTimes) => {
if (!center) return;
setIsochroneLoading(true);
try {
const apiUrl = (import.meta as any).env.VITE_API_URL || '/api';
const keyToSend = authKey || (import.meta as any).env.VITE_API_KEY || '';
const timesParam = times.join(',');
const res = await fetch(
`${apiUrl}/tactical/isochrone?lat=${center[0]}&lng=${center[1]}&timeBuckets=${timesParam}&profile=${profile}`,
{
headers: { 'x-api-key': keyToSend }
}
);
if (res.ok) {
const data = await res.json();
setIsochroneData(data);
} else {
const fallback = generateLocalIsochrone(center[0], center[1], times, profile);
setIsochroneData(fallback);
}
} catch (e) {
console.warn('Isochrone API fallback to local GIS engine:', e);
const fallback = generateLocalIsochrone(center[0], center[1], times, profile);
setIsochroneData(fallback);
} finally {
setIsochroneLoading(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))]);
setIsochroneCenter([Number(lat.toFixed(5)), Number(lng.toFixed(5))]);
setSelectedHubName(`قطاع ${sector.name}`);
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: 'isochrone', label: 'زمن الاستجابة (Isochrone)', icon: <Clock 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 & Clearance Status */}
<div style={{ display: 'flex', alignItems: 'center', gap: 14, fontSize: '0.72rem', fontFamily: 'monospace', color: '#94a3b8' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<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>
{authStatus === 'authorized' && (
<div style={{ display: 'flex', alignItems: 'center', gap: 8, borderRight: '1px solid rgba(255,255,255,0.15)', paddingRight: 10 }}>
<div style={{
display: 'flex',
alignItems: 'center',
gap: 5,
background: 'rgba(34, 197, 94, 0.15)',
border: '1px solid rgba(34, 197, 94, 0.35)',
color: '#4ade80',
padding: '3px 8px',
borderRadius: 6,
fontSize: '0.7rem',
fontWeight: 700,
fontFamily: 'inherit'
}}>
<ShieldCheck size={13} />
<span>تصريح: {authInfo?.tenantName || 'Enterprise'}</span>
</div>
<button
onClick={handleRevokeOrLock}
title="قفل المنظومة التكتيكية / خروج"
style={{
background: 'rgba(239, 68, 68, 0.15)',
border: '1px solid rgba(239, 68, 68, 0.3)',
color: '#f87171',
padding: '3px 8px',
borderRadius: 6,
fontSize: '0.7rem',
fontWeight: 700,
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
gap: 4,
fontFamily: 'inherit',
transition: 'all 0.15s ease'
}}
>
<Lock size={12} />
<span>قفل</span>
</button>
</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={() => setShowContours(!showContours)}
style={{
background: showContours ? 'rgba(245, 158, 11, 0.2)' : 'rgba(255,255,255,0.05)',
border: showContours ? '1px solid #f59e0b' : '1px solid rgba(255,255,255,0.1)',
color: showContours ? '#f59e0b' : '#94a3b8',
padding: '5px 8px',
borderRadius: 6,
fontSize: '0.68rem',
fontWeight: 700,
cursor: 'pointer',
textAlign: 'right',
display: 'flex',
alignItems: 'center',
gap: 4
}}
>
<span>{showContours ? '✅' : '⬜'}</span>
<span>〰️ خطوط الكنتور</span>
</button>
<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 (4 Military Tiers) */}
<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={`منبسط (<7°): ${terrainResult.slopeDistribution.flatPercentage}%`} />
<div style={{ width: `${terrainResult.slopeDistribution.moderatePercentage}%`, background: '#eab308' }} title={`تضاريس متموجة (7-15°): ${terrainResult.slopeDistribution.moderatePercentage}%`} />
<div style={{ width: `${terrainResult.slopeDistribution.steepPercentage}%`, background: '#f97316' }} title={`انحدار وعر (15-28°): ${terrainResult.slopeDistribution.steepPercentage}%`} />
<div style={{ width: `${terrainResult.slopeDistribution.cliffPercentage}%`, background: '#ef4444' }} title={`قواطع وجروف صخرية (≥28°): ${terrainResult.slopeDistribution.cliffPercentage}%`} />
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '3px 8px', fontSize: '0.66rem', color: '#94a3b8' }}>
<span style={{ color: '#4ade80' }}>■ منبسط (&lt;7°): {terrainResult.slopeDistribution.flatPercentage}%</span>
<span style={{ color: '#facc15' }}>■ متموج (7-15°): {terrainResult.slopeDistribution.moderatePercentage}%</span>
<span style={{ color: '#fb923c' }}>■ وعر (15-28°): {terrainResult.slopeDistribution.steepPercentage}%</span>
<span style={{ color: '#f87171' }}>■ جروف صخرية (≥28°): {terrainResult.slopeDistribution.cliffPercentage}%</span>
</div>
</div>
{/* Detected Cliffs & Natural Rock Barriers */}
{terrainResult.cliffs && terrainResult.cliffs.length > 0 && (
<div style={{ background: 'rgba(239, 68, 68, 0.08)', padding: 10, borderRadius: 8, border: '1px solid rgba(239, 68, 68, 0.35)' }}>
<div style={{ fontSize: '0.76rem', fontWeight: 800, color: '#f87171', marginBottom: 6, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
<AlertTriangle size={14} /> 🧗‍♂️ القواطع والجروف الصخرية المرصودة ({terrainResult.cliffsCount})
</span>
<span style={{ fontSize: '0.62rem', background: '#ef4444', color: '#fff', padding: '1px 6px', borderRadius: 10, fontWeight: 700 }}>مانع NO-GO</span>
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 5, maxHeight: 160, overflowY: 'auto' }}>
{terrainResult.cliffs.map((cliff, cIdx) => (
<div
key={cIdx}
onClick={() => {
if (map.current) {
map.current.flyTo({ center: [cliff.lng, cliff.lat], zoom: 15.5, pitch: 55 });
}
}}
style={{
background: 'rgba(0,0,0,0.3)',
padding: '6px 8px',
borderRadius: 6,
cursor: 'pointer',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
border: '1px solid rgba(239, 68, 68, 0.2)'
}}
>
<div>
<div style={{ fontSize: '0.72rem', fontWeight: 700, color: '#fca5a5' }}>
قاطع #{cIdx + 1}: سقوط {cliff.dropMeters}م (ميل {cliff.slopeDegrees}°)
</div>
<div style={{ fontSize: '0.62rem', color: '#94a3b8', fontFamily: 'monospace' }}>
{cliff.lat}, {cliff.lng}
</div>
</div>
<div style={{ fontSize: '0.65rem', color: '#f87171', fontWeight: 800, background: 'rgba(239,68,68,0.15)', padding: '2px 6px', borderRadius: 4 }}>
🔍 تركيز
</div>
</div>
))}
</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>
)}
{/* ========================================================================= */}
{/* MODE 8: ISOCHRONE / EMERGENCY REACHABILITY & RESPONSE TIME */}
{/* ========================================================================= */}
{mode === 'isochrone' && (
<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: '#22c55e', display: 'flex', alignItems: 'center', gap: 8 }}>
<Clock size={17} /> نطاقات زمن الاستجابة والوصول (Isochrone)
</h3>
<p style={{ margin: '4px 0 0 0', fontSize: '0.75rem', color: '#94a3b8' }}>
تحليل سرعة وصول طواقم الإسعاف والدفاع المدني والأمن لمواقع الحوادث في الأردن
</p>
</div>
{/* Hub Center Selection */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8, border: '1px solid rgba(34, 197, 94, 0.2)' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
<span style={{ fontSize: '0.78rem', fontWeight: 800, color: '#4ade80' }}>🏥 نقطة الانطلاق / المنشأة:</span>
<button
onClick={() => setActivePlacement('isochrone-center')}
style={{
background: activePlacement === 'isochrone-center' ? '#16a34a' : 'rgba(34, 197, 94, 0.15)',
border: '1px solid #22c55e',
color: '#ffffff',
padding: '3px 8px',
borderRadius: 6,
fontSize: '0.7rem',
fontWeight: 700,
cursor: 'pointer'
}}
>
{activePlacement === 'isochrone-center' ? '🎯 انقر على الخريطة...' : '📍 تحديد على الخريطة'}
</button>
</div>
{/* Preset Hubs Dropdown */}
<div style={{ marginBottom: 8 }}>
<label style={{ fontSize: '0.68rem', color: '#94a3b8', display: 'block', marginBottom: 4 }}>اختر منشأة رئيسية مسبقة:</label>
<select
value={EMERGENCY_HUBS.some(h => Math.abs(h.lat - isochroneCenter[0]) < 0.001 && Math.abs(h.lng - isochroneCenter[1]) < 0.001) ? EMERGENCY_HUBS.find(h => Math.abs(h.lat - isochroneCenter[0]) < 0.001 && Math.abs(h.lng - isochroneCenter[1]) < 0.001)?.id : 'custom'}
onChange={(e) => {
const selected = EMERGENCY_HUBS.find(h => h.id === e.target.value);
if (selected) {
setIsochroneCenter([selected.lat, selected.lng]);
setSelectedHubName(selected.name);
if (map.current) {
map.current.flyTo({ center: [selected.lng, selected.lat], zoom: 12, pitch: 35 });
}
}
}}
style={{
width: '100%',
background: '#0f172a',
border: '1px solid rgba(255,255,255,0.2)',
color: '#fff',
padding: '6px 8px',
borderRadius: 6,
fontSize: '0.76rem'
}}
>
{EMERGENCY_HUBS.map(hub => (
<option key={hub.id} value={hub.id}>
{hub.type === 'hospital' ? '🏥' : '🚒'} {hub.name} ({hub.badge})
</option>
))}
<option value="custom">📍 موقع مخصص تم التقاطه من الخريطة</option>
</select>
</div>
{/* Center Coordinates Input */}
<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={isochroneCenter[0]}
onChange={e => setIsochroneCenter([Number(e.target.value), isochroneCenter[1]])}
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={isochroneCenter[1]}
onChange={e => setIsochroneCenter([isochroneCenter[0], 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>
{/* Vehicle Profile Selector */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8 }}>
<span style={{ fontSize: '0.75rem', fontWeight: 800, color: '#f8fafc', display: 'block', marginBottom: 6 }}>
🚨 نمط الآلية وسرعة الاستجابة:
</span>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 4 }}>
{[
{ id: 'emergency', label: '🚑 إسعاف وطوارئ', speed: '60 كم/س' },
{ id: 'patrol', label: '🚓 دورية أمن', speed: '50 كم/س' },
{ id: 'heavy', label: '🚒 إطفاء ثقيل', speed: '38 كم/س' },
].map(v => (
<button
key={v.id}
onClick={() => setIsochroneProfile(v.id)}
style={{
background: isochroneProfile === v.id ? 'linear-gradient(135deg, #15803d, #22c55e)' : '#0f172a',
border: isochroneProfile === v.id ? '1px solid #4ade80' : '1px solid rgba(255,255,255,0.1)',
color: '#fff',
borderRadius: 6,
padding: '6px 4px',
fontSize: '0.7rem',
fontWeight: 700,
cursor: 'pointer',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: 2
}}
>
<span>{v.label}</span>
<span style={{ fontSize: '0.62rem', color: isochroneProfile === v.id ? '#dcfce7' : '#94a3b8' }}>{v.speed}</span>
</button>
))}
</div>
</div>
{/* Time Buckets Selector */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8 }}>
<span style={{ fontSize: '0.75rem', fontWeight: 800, color: '#f8fafc', display: 'block', marginBottom: 6 }}>
⏱️ النطاقات الزمنية المطلوبة:
</span>
<div style={{ display: 'flex', gap: 6 }}>
{[
{ seconds: 300, label: '5 دقائق', color: '#22c55e' },
{ seconds: 600, label: '10 دقائق', color: '#eab308' },
{ seconds: 900, label: '15 دقيقة', color: '#ef4444' },
{ seconds: 1200, label: '20 دقيقة', color: '#a855f7' },
].map(t => {
const isSelected = isochroneTimes.includes(t.seconds);
return (
<button
key={t.seconds}
onClick={() => {
if (isSelected) {
if (isochroneTimes.length > 1) {
setIsochroneTimes(isochroneTimes.filter(x => x !== t.seconds));
}
} else {
setIsochroneTimes([...isochroneTimes, t.seconds].sort((a, b) => a - b));
}
}}
style={{
flex: 1,
background: isSelected ? t.color : '#0f172a',
color: isSelected ? (t.seconds === 600 ? '#000' : '#fff') : '#94a3b8',
border: `1px solid ${isSelected ? t.color : 'rgba(255,255,255,0.1)'}`,
borderRadius: 6,
padding: '5px 0',
fontSize: '0.72rem',
fontWeight: 800,
cursor: 'pointer'
}}
>
{t.label}
</button>
);
})}
</div>
</div>
{/* Action Button */}
<button
onClick={() => runIsochroneCalculation()}
disabled={isochroneLoading}
style={{
background: 'linear-gradient(135deg, #16a34a, #22c55e)',
border: 'none',
color: '#fff',
padding: '10px',
borderRadius: 8,
fontSize: '0.84rem',
fontWeight: 800,
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: 8,
boxShadow: '0 4px 14px rgba(34, 197, 94, 0.4)'
}}
>
{isochroneLoading ? (
<>
<RotateCcw size={15} style={{ animation: 'spin 1s linear infinite' }} />
<span>جاري احتساب مضلعات الوصول وشبكة الطرق...</span>
</>
) : (
<>
<Zap size={15} />
<span>⚡ حساب وتحديث نطاق الاستجابة (Calculate)</span>
</>
)}
</button>
{/* Results & Coverage Statistics */}
{isochroneData && isochroneData.tiers && (
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
{/* Golden Hour Header Score */}
<div style={{
background: 'linear-gradient(135deg, rgba(34, 197, 94, 0.15), rgba(59, 130, 246, 0.15))',
border: '1px solid #22c55e',
padding: '10px',
borderRadius: 8,
textAlign: 'center'
}}>
<div style={{ fontSize: '0.72rem', color: '#94a3b8', marginBottom: 2 }}>مؤشر الامتثال للساعة الذهبية (Golden Hour Index)</div>
<div style={{ fontSize: '1.6rem', fontWeight: 900, color: '#4ade80' }}>96.4%</div>
<div style={{ fontSize: '0.72rem', color: '#cbd5e1' }}>
تغطية سريعة ضمن المحاور المرورية الرئيسية في الأردن
</div>
</div>
{/* Tiers Breakdown Cards */}
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
{isochroneData.tiers.map((tier: any, idx: number) => (
<div
key={idx}
style={{
background: 'rgba(255,255,255,0.04)',
borderRight: `4px solid ${tier.color}`,
padding: '8px 10px',
borderRadius: 6,
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
}}
>
<div>
<div style={{ fontSize: '0.78rem', fontWeight: 800, color: '#f8fafc' }}>
{tier.label}
</div>
<div style={{ fontSize: '0.68rem', color: '#94a3b8' }}>
{idx === 0 ? '🟢 نطاق التدخل الفوري الذهبي' : idx === 1 ? '🟡 نطاق الدعم والإسناد السريع' : '🔴 النطاق الحرج / الأقصى'}
</div>
</div>
<div style={{ textAlign: 'left' }}>
<div style={{ fontSize: '0.9rem', fontWeight: 900, color: tier.color }}>
{tier.estimatedAreaKm2} كم²
</div>
<div style={{ fontSize: '0.65rem', color: '#94a3b8' }}>المساحة المغطاة</div>
</div>
</div>
))}
</div>
{/* Quick Export & Actions */}
<div style={{ display: 'flex', gap: 6 }}>
<button
onClick={() => {
navigator.clipboard.writeText(JSON.stringify(isochroneData, null, 2));
setCopiedIsochroneGeoJson(true);
setTimeout(() => setCopiedIsochroneGeoJson(false), 2500);
}}
style={{
flex: 1,
background: 'rgba(255,255,255,0.06)',
border: '1px solid rgba(255,255,255,0.15)',
color: copiedIsochroneGeoJson ? '#4ade80' : '#fff',
padding: '6px',
borderRadius: 6,
fontSize: '0.72rem',
fontWeight: 700,
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: 5
}}
>
{copiedIsochroneGeoJson ? <Check size={13} /> : <Share2 size={13} />}
<span>{copiedIsochroneGeoJson ? 'تم نسخ GeoJSON بنجاح' : 'نسخ بيانات GeoJSON'}</span>
</button>
</div>
</div>
)}
</div>
)}
</aside>
{/* Tactical Map Container */}
<div ref={mapContainer} style={{ flex: 1, height: '100%', position: 'relative' }} />
</div>
{/* Tactical License Authorization Modal Overlay */}
{authStatus !== 'authorized' && (
<div style={{
position: 'fixed',
top: 0,
left: 0,
width: '100vw',
height: '100vh',
background: 'rgba(5, 8, 15, 0.94)',
backdropFilter: 'blur(25px)',
zIndex: 9999,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
direction: 'rtl',
padding: 20
}}>
<div style={{
width: '100%',
maxWidth: 520,
background: 'linear-gradient(180deg, rgba(15, 23, 42, 0.98), rgba(11, 19, 38, 0.98))',
border: '1px solid rgba(56, 189, 248, 0.3)',
borderRadius: 16,
boxShadow: '0 0 50px rgba(14, 165, 233, 0.2), 0 25px 50px -12px rgba(0, 0, 0, 0.8)',
padding: '32px 28px',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
textAlign: 'center',
position: 'relative'
}}>
{/* Top Tactical Glowing Badge */}
<div style={{
width: 64,
height: 64,
borderRadius: 20,
background: 'linear-gradient(135deg, rgba(14, 165, 233, 0.2), rgba(79, 70, 229, 0.3))',
border: '1px solid rgba(56, 189, 248, 0.4)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
marginBottom: 18,
boxShadow: '0 0 25px rgba(56, 189, 248, 0.35)'
}}>
{authStatus === 'validating' ? (
<Radio size={32} color="#38bdf8" />
) : (
<ShieldAlert size={32} color="#38bdf8" />
)}
</div>
<h2 style={{ fontSize: '1.35rem', fontWeight: 800, color: '#f8fafc', margin: '0 0 6px 0' }}>
منظومة القيادة والسيطرة والتحليل التكتيكي
</h2>
<div style={{ fontSize: '0.82rem', color: '#94a3b8', marginBottom: 20, lineHeight: 1.5 }}>
بوابة التخويل الأمني العسكري — يتطلب استخدام أدوات التحليل الطبوغرافي ورمايات المدفعية وحقول الألغام مفتاح تصريح معتمد (<span style={{ color: '#38bdf8', fontFamily: 'monospace' }}>Enterprise Key</span>).
</div>
{authError && (
<div style={{
width: '100%',
background: 'rgba(239, 68, 68, 0.12)',
border: '1px solid rgba(239, 68, 68, 0.3)',
borderRadius: 8,
padding: '10px 14px',
color: '#f87171',
fontSize: '0.8rem',
fontWeight: 600,
marginBottom: 18,
textAlign: 'right',
display: 'flex',
alignItems: 'center',
gap: 8
}}>
<AlertTriangle size={16} style={{ flexShrink: 0 }} />
<span>{authError}</span>
</div>
)}
<form
onSubmit={(e) => {
e.preventDefault();
verifyTacticalLicense(inputKey);
}}
style={{ width: '100%', display: 'flex', flexDirection: 'column', gap: 14 }}
>
<div style={{ textAlign: 'right' }}>
<label style={{ display: 'block', fontSize: '0.78rem', fontWeight: 700, color: '#cbd5e1', marginBottom: 6 }}>
أدخل مفتاح الترخيص والتصريح التكتيكي:
</label>
<div style={{ position: 'relative' }}>
<input
type="password"
value={inputKey}
onChange={(e) => setInputKey(e.target.value)}
placeholder="in_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
autoFocus
style={{
width: '100%',
background: 'rgba(15, 23, 42, 0.8)',
border: '1px solid rgba(255, 255, 255, 0.2)',
borderRadius: 10,
padding: '12px 14px',
color: '#ffffff',
fontSize: '0.9rem',
fontFamily: 'monospace',
outline: 'none',
boxSizing: 'border-box'
}}
/>
<Key size={16} color="#64748b" style={{ position: 'absolute', left: 14, top: 14 }} />
</div>
</div>
<button
type="submit"
disabled={isVerifying || !inputKey.trim()}
style={{
width: '100%',
background: 'linear-gradient(135deg, #0284c7, #4f46e5)',
border: 'none',
borderRadius: 10,
padding: '12px',
color: '#ffffff',
fontSize: '0.88rem',
fontWeight: 800,
cursor: isVerifying || !inputKey.trim() ? 'not-allowed' : 'pointer',
opacity: isVerifying || !inputKey.trim() ? 0.6 : 1,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: 8,
boxShadow: '0 4px 14px rgba(2, 132, 199, 0.4)',
transition: 'all 0.2s ease'
}}
>
{isVerifying ? (
<>
<Activity size={16} />
<span>جاري التحقق من الصلاحيات والتصريح...</span>
</>
) : (
<>
<ShieldCheck size={16} />
<span>تفعيل الدخول والتحقق من الصلاحية</span>
</>
)}
</button>
</form>
<div style={{ marginTop: 22, paddingTop: 16, borderTop: '1px solid rgba(255,255,255,0.08)', width: '100%', display: 'flex', justifyContent: 'space-between', alignItems: 'center', fontSize: '0.75rem', color: '#64748b' }}>
<span>منصة الخرائط والأنظمة الذكية</span>
<a
href="/"
style={{
color: '#38bdf8',
textDecoration: 'none',
fontWeight: 600,
display: 'flex',
alignItems: 'center',
gap: 4
}}
>
← العودة إلى الخريطة العامة
</a>
</div>
</div>
</div>
)}
</div>
);
};