feat(tactical): add comprehensive Terrain Study (دراسة الأرض) with natural/manmade obstacles and OAKOC intelligence report

This commit is contained in:
Hamza-Ayed
2026-08-18 10:42:04 +03:00
parent 91c36d99c9
commit 6bd0bc0ce5
2 changed files with 695 additions and 13 deletions
+422 -13
View File
@@ -23,20 +23,28 @@ import {
MapPin,
Target,
LocateFixed,
Move
Move,
Building,
TreePine,
Printer,
FileText,
Compass,
Check
} from 'lucide-react';
import {
calculateLineOfSight,
calculateRadialViewshed,
calculateMinefieldAnalysis,
calculateTerrainStudy,
LineOfSightResult,
Viewshed360Result,
MinefieldAnalysisResult,
TerrainStudyResult,
calculateDistance,
calculateAzimuth
} from '../utils/elevationService';
type TacticalMode = 'los' | 'viewshed' | 'artillery' | 'minefield' | 'hlz' | 'symbols' | 'study';
type TacticalMode = 'terrain' | 'los' | 'viewshed' | 'artillery' | 'minefield' | 'hlz' | 'symbols' | 'study';
interface TacticalSymbolItem {
id: string;
@@ -67,11 +75,18 @@ export const TacticalDefenseView: React.FC = () => {
const mapContainer = useRef<HTMLDivElement>(null);
const map = useRef<maplibregl.Map | null>(null);
const [mode, setMode] = useState<TacticalMode>('los');
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);
// Line of Sight State
// 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);
// 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);
@@ -79,14 +94,14 @@ export const TacticalDefenseView: React.FC = () => {
const [losResult, setLosResult] = useState<LineOfSightResult | null>(null);
const [losLoading, setLosLoading] = useState<boolean>(false);
// Viewshed State
// 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);
// Artillery Fire Mission State
// 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)');
@@ -94,19 +109,19 @@ export const TacticalDefenseView: React.FC = () => {
const [artilleryResult, setArtilleryResult] = useState<any | null>(null);
const [artilleryLoading, setArtilleryLoading] = useState<boolean>(false);
// Minefield Analysis State
// 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);
// HLZ State
// 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);
// Tactical Symbols State
// 7. Tactical Symbols State
const [symbols, setSymbols] = useState<TacticalSymbolItem[]>(DEFAULT_SYMBOLS);
const [selectedSymbolType, setSelectedSymbolType] = useState<string>('friendly');
const [newSymbolName, setNewSymbolName] = useState<string>('');
@@ -120,6 +135,9 @@ export const TacticalDefenseView: React.FC = () => {
const activePlacementRef = useRef(activePlacement);
activePlacementRef.current = activePlacement;
const terrainCenterRef = useRef(terrainCenter);
terrainCenterRef.current = terrainCenter;
const losPointARef = useRef(losPointA);
losPointARef.current = losPointA;
@@ -203,6 +221,8 @@ export const TacticalDefenseView: React.FC = () => {
// Sync Draggable Markers to Map
useEffect(() => {
if (!map.current) return;
// Terrain Study Center
updateMarker('terrain-c', terrainCenter, '⛰️ مركز دراسة الأرض', '#6366f1', (pos) => setTerrainCenter(pos));
// LOS
updateMarker('los-a', losPointA, '📍 راصد A', '#0284c7', (pos) => setLosPointA(pos));
updateMarker('los-b', losPointB, '🎯 هدف B', '#d97706', (pos) => setLosPointB(pos));
@@ -216,7 +236,7 @@ export const TacticalDefenseView: React.FC = () => {
updateMarker('mine-e', mineEnd, '⛔ نهاية حقل الألغام', '#991b1b', (pos) => setMineEnd(pos));
// HLZ
updateMarker('hlz-c', hlzCenter, '🚁 مركز HLZ', '#059669', (pos) => setHlzCenter(pos));
}, [losPointA, losPointB, viewshedCenter, gunPos, targetPos, mineStart, mineEnd, hlzCenter]);
}, [terrainCenter, losPointA, losPointB, viewshedCenter, gunPos, targetPos, mineStart, mineEnd, hlzCenter]);
// Map Initialization
useEffect(() => {
@@ -256,6 +276,11 @@ export const TacticalDefenseView: React.FC = () => {
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);
@@ -298,7 +323,9 @@ export const TacticalDefenseView: React.FC = () => {
}
// 2. Default Context-Aware Click by Active Mode
if (currentMode === 'los') {
if (currentMode === 'terrain') {
setTerrainCenter([clickedLat, clickedLng]);
} else if (currentMode === 'los') {
if (!losPointARef.current) {
setLosPointA([clickedLat, clickedLng]);
} else if (!losPointBRef.current) {
@@ -352,6 +379,33 @@ export const TacticalDefenseView: React.FC = () => {
});
initialMap.on('load', () => {
// 0. Terrain Study Circle Layer
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.15
}
});
initialMap.addLayer({
id: 'tactical-terrain-line',
type: 'line',
source: 'tactical-terrain-src',
paint: {
'line-color': '#818cf8',
'line-width': 2.5,
'line-dasharray': [3, 2]
}
});
// 1. Line of Sight Layer
initialMap.addSource('tactical-los-src', {
type: 'geojson',
@@ -493,6 +547,50 @@ export const TacticalDefenseView: React.FC = () => {
};
}, []);
// Run Terrain Tactical Study
const runTerrainStudy = () => {
if (!terrainCenter) return;
setTerrainLoading(true);
calculateTerrainStudy(terrainCenter[0], terrainCenter[1], terrainRadius)
.then((res) => {
setTerrainResult(res);
setTerrainLoading(false);
// 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));
};
// Auto-run initial terrain study on mount
useEffect(() => {
runTerrainStudy();
}, [terrainCenter, terrainRadius]);
// Update Line of Sight Calculations & Map Layer
useEffect(() => {
if (!losPointA || !losPointB) {
@@ -700,10 +798,10 @@ export const TacticalDefenseView: React.FC = () => {
essential: true
});
// Dynamically re-anchor operational points into this region so the user is not stuck with old coordinates
const lat = sector.lat;
const lng = sector.lng;
setTerrainCenter([Number(lat.toFixed(5)), Number(lng.toFixed(5))]);
setLosPointA([Number((lat - 0.015).toFixed(5)), Number((lng - 0.015).toFixed(5))]);
setLosPointB([Number((lat + 0.025).toFixed(5)), Number((lng + 0.025).toFixed(5))]);
setViewshedCenter([Number(lat.toFixed(5)), Number(lng.toFixed(5))]);
@@ -714,6 +812,38 @@ export const TacticalDefenseView: React.FC = () => {
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.minElevation}م | الفارق التضاريسي ${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',
@@ -767,6 +897,7 @@ export const TacticalDefenseView: React.FC = () => {
{/* Tactical Modes Selector */}
<div style={{ display: 'flex', gap: 4, background: 'rgba(255, 255, 255, 0.05)', padding: 3, borderRadius: 10, border: '1px solid rgba(255,255,255,0.1)' }}>
{[
{ id: 'terrain', label: 'دراسة الأرض', icon: <Mountain size={13} /> },
{ id: 'los', label: 'تبادل الرؤية (LOS)', icon: <Eye size={13} /> },
{ id: 'viewshed', label: 'كشف 360°', icon: <Radio size={13} /> },
{ id: 'artillery', label: 'موقع المدفعية', icon: <Flame size={13} /> },
@@ -853,7 +984,7 @@ export const TacticalDefenseView: React.FC = () => {
{/* Left Side Tactical Control Deck */}
<aside style={{
width: 410,
width: 440,
background: 'rgba(15, 23, 42, 0.96)',
backdropFilter: 'blur(20px)',
borderLeft: '1px solid rgba(255, 255, 255, 0.12)',
@@ -909,7 +1040,273 @@ export const TacticalDefenseView: React.FC = () => {
<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>
{/* 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>
{/* Elevation & Slope Metrics Grid */}
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 6 }}>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 7, borderRadius: 6, textAlign: 'center' }}>
<div style={{ fontSize: '0.62rem', color: '#94a3b8' }}>أعلى قمة</div>
<div style={{ fontSize: '0.9rem', fontWeight: 800, color: '#38bdf8' }}>{terrainResult.maxElevation}م</div>
</div>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 7, borderRadius: 6, textAlign: 'center' }}>
<div style={{ fontSize: '0.62rem', color: '#94a3b8' }}>أخفض نقطة</div>
<div style={{ fontSize: '0.9rem', fontWeight: 800, color: '#38bdf8' }}>{terrainResult.minElevation}م</div>
</div>
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 7, borderRadius: 6, textAlign: 'center' }}>
<div style={{ fontSize: '0.62rem', color: '#94a3b8' }}>الفارق التضاريسي</div>
<div style={{ fontSize: '0.9rem', fontWeight: 800, color: '#fb923c' }}>{terrainResult.reliefMeters}م</div>
</div>
</div>
{/* Slope Distribution Bar */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8 }}>
<div style={{ fontSize: '0.72rem', fontWeight: 700, color: '#cbd5e1', marginBottom: 6, display: 'flex', justifyContent: 'space-between' }}>
<span>توزيع درجات الانحدار (Slope Analysis):</span>
<span style={{ color: '#94a3b8' }}>متوسط: {terrainResult.avgSlopeDegrees}°</span>
</div>
<div style={{ height: 10, width: '100%', display: 'flex', borderRadius: 5, overflow: 'hidden', marginBottom: 6 }}>
<div style={{ width: `${terrainResult.slopeDistribution.flatPercentage}%`, background: '#22c55e' }} title={`منبسط: ${terrainResult.slopeDistribution.flatPercentage}%`} />
<div style={{ width: `${terrainResult.slopeDistribution.moderatePercentage}%`, background: '#eab308' }} title={`متوسط: ${terrainResult.slopeDistribution.moderatePercentage}%`} />
<div style={{ width: `${terrainResult.slopeDistribution.steepPercentage}%`, background: '#ef4444' }} title={`شديد الوعورة: ${terrainResult.slopeDistribution.steepPercentage}%`} />
</div>
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: '0.68rem', color: '#94a3b8' }}>
<span style={{ color: '#4ade80' }}>■ منبسط (&lt;5°): {terrainResult.slopeDistribution.flatPercentage}%</span>
<span style={{ color: '#facc15' }}>■ متوسط (5-15°): {terrainResult.slopeDistribution.moderatePercentage}%</span>
<span style={{ color: '#f87171' }}>■ وعر (&gt;15°): {terrainResult.slopeDistribution.steepPercentage}%</span>
</div>
</div>
{/* SECTION 1: NATURAL OBSTACLES */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8, border: '1px solid rgba(239, 68, 68, 0.2)' }}>
<div style={{ fontSize: '0.78rem', fontWeight: 800, color: '#f87171', marginBottom: 6, display: 'flex', alignItems: 'center', gap: 6 }}>
<AlertOctagon size={14} /> 1. الموانع الطبيعية (Natural Obstacles):
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
{terrainResult.naturalObstacles.map((obs, idx) => (
<div key={idx} style={{ background: 'rgba(0,0,0,0.2)', padding: '6px 8px', borderRadius: 6, fontSize: '0.72rem' }}>
<div style={{ fontWeight: 700, color: '#fca5a5' }}>• {obs.name} ({obs.type})</div>
<div style={{ color: '#cbd5e1', marginTop: 2 }}>{obs.description}</div>
<div style={{ color: '#94a3b8', fontSize: '0.68rem', marginTop: 2 }}><strong>الأثر التكتيكي:</strong> {obs.impact}</div>
</div>
))}
</div>
</div>
{/* SECTION 2: MAN-MADE OBSTACLES & INFRASTRUCTURE */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8, border: '1px solid rgba(245, 158, 11, 0.2)' }}>
<div style={{ fontSize: '0.78rem', fontWeight: 800, color: '#f59e0b', marginBottom: 6, display: 'flex', alignItems: 'center', gap: 6 }}>
<Building size={14} /> 2. الموانع الصناعية والبنية التحتية (Man-Made Features):
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
{terrainResult.manMadeObstacles.map((obs, idx) => (
<div key={idx} style={{ background: 'rgba(0,0,0,0.2)', padding: '6px 8px', borderRadius: 6, fontSize: '0.72rem' }}>
<div style={{ fontWeight: 700, color: '#fde047' }}>• {obs.name}</div>
<div style={{ color: '#cbd5e1', marginTop: 2 }}>{obs.description}</div>
<div style={{ color: '#94a3b8', fontSize: '0.68rem', marginTop: 2 }}><strong>ملاحظة تكتيكية:</strong> {obs.tacticalNote}</div>
</div>
))}
</div>
</div>
{/* SECTION 3: URBAN AREAS & DEMOGRAPHICS */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8, border: '1px solid rgba(56, 189, 248, 0.2)' }}>
<div style={{ fontSize: '0.78rem', fontWeight: 800, color: '#38bdf8', marginBottom: 6, display: 'flex', alignItems: 'center', gap: 6 }}>
<Layers size={14} /> 3. المناطق المأهولة بالسكان والعمران:
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: '0.72rem', color: '#cbd5e1' }}>
<div><strong>الكثافة السكانية:</strong> {terrainResult.urbanAndDemographics.densityAr}</div>
<div><strong>التجمعات والقرى الرئيسية:</strong> {terrainResult.urbanAndDemographics.settlements.join('، ')}</div>
<div><strong>قتال المناطق المبنية (MOUT):</strong> {terrainResult.urbanAndDemographics.moutComplexity}</div>
<div><strong>مخاطر الأضرار الجانبية:</strong> {terrainResult.urbanAndDemographics.collateralDamageRisk}</div>
</div>
</div>
{/* SECTION 4: SOIL & CONCEALMENT */}
<div style={{ background: 'rgba(255,255,255,0.04)', padding: 10, borderRadius: 8, border: '1px solid rgba(16, 185, 129, 0.2)' }}>
<div style={{ fontSize: '0.78rem', fontWeight: 800, color: '#10b981', marginBottom: 6, display: 'flex', alignItems: 'center', gap: 6 }}>
<TreePine size={14} /> 4. التربة وإمكانات الإخفاء والتمويه:
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: '0.72rem', color: '#cbd5e1' }}>
<div><strong>نوع التربة:</strong> {terrainResult.landCoverAndCover.soilType}</div>
<div><strong>صلاحية السير للآليات:</strong> {terrainResult.landCoverAndCover.trafficability}</div>
<div><strong>الإخفاء من الرصد الأرضي:</strong> {terrainResult.landCoverAndCover.concealmentRating}</div>
<div><strong>الانكشاف للرصد الجوي:</strong> {terrainResult.landCoverAndCover.airObservationExposure}</div>
</div>
</div>
{/* SECTION 5: COMMANDER'S TACTICAL RECOMMENDATIONS (OAKOC) */}
<div style={{ background: 'linear-gradient(135deg, rgba(79, 70, 229, 0.15), rgba(99, 102, 241, 0.15))', padding: 12, borderRadius: 8, border: '1px solid #6366f1' }}>
<div style={{ fontSize: '0.82rem', fontWeight: 800, color: '#a5b4fc', marginBottom: 8, display: 'flex', alignItems: 'center', gap: 6 }}>
<Award size={16} /> 5. التقييم التكتيكي للقائد والتوصيات الميدانية:
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 6, fontSize: '0.72rem', color: '#f8fafc', lineHeight: 1.45 }}>
{terrainResult.tacticalRecommendations.map((rec, i) => (
<div key={i} style={{ display: 'flex', gap: 6 }}>
<span style={{ color: '#818cf8', fontWeight: 700 }}>{i + 1}.</span>
<span>{rec}</span>
</div>
))}
</div>
</div>
</div>
)}
</div>
)}
{/* ========================================================================= */}
{/* MODE 1: LINE OF SIGHT */}
{/* ========================================================================= */}
{mode === 'los' && (
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
<div style={{ paddingBottom: 8, borderBottom: '1px solid rgba(255,255,255,0.08)' }}>
@@ -1080,7 +1477,9 @@ export const TacticalDefenseView: React.FC = () => {
</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)' }}>
@@ -1198,7 +1597,9 @@ export const TacticalDefenseView: React.FC = () => {
</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)' }}>
@@ -1366,7 +1767,9 @@ export const TacticalDefenseView: React.FC = () => {
</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)' }}>
@@ -1543,7 +1946,9 @@ export const TacticalDefenseView: React.FC = () => {
</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)' }}>
@@ -1637,7 +2042,9 @@ export const TacticalDefenseView: React.FC = () => {
</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)' }}>
@@ -1787,7 +2194,9 @@ export const TacticalDefenseView: React.FC = () => {
</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)' }}>