feat(tactical): add tactical line of sight API, elevation engine, dynamic step resolution, weather module and executive showcase

This commit is contained in:
Hamza-Ayed
2026-08-17 19:25:39 +03:00
parent 03cdc6fe9c
commit a0a5a09135
29 changed files with 3896 additions and 190 deletions
+32 -21
View File
@@ -4,6 +4,7 @@ import maplibregl from 'maplibre-gl'
import App from './App.tsx'
import CompareView from './pages/CompareView'
import IntelligenceDashboard from './pages/IntelligenceDashboard'
import { ExecutiveShowcase } from './pages/ExecutiveShowcase'
import './index.css'
maplibregl.setRTLTextPlugin(
@@ -12,49 +13,53 @@ maplibregl.setRTLTextPlugin(
true
);
// Lightweight hash router (no dependency). '#compare' and '#review' are additive
// views; anything else falls back to the existing map app, untouched.
// Lightweight hash router
const NAV = [
{ hash: '#map', label: 'Map', icon: '🗺️' },
{ hash: '#review', label: 'Review', icon: '🔍' },
{ hash: '#compare', label: 'Compare', icon: '⚖️' },
{ hash: '#executive', label: 'العرض الاستراتيجي', icon: '🎖️' },
{ hash: '#map', label: 'الخريطة الحية', icon: '🗺️' },
{ hash: '#review', label: 'تدقيق الطرق', icon: '🔍' },
{ hash: '#compare', label: 'المقارنة', icon: '⚖️' },
]
function ViewNav({ hash }: { hash: string }) {
const isMap = hash !== '#compare' && hash !== '#review'
const currentHash = hash || '#map'
return (
<div style={{
position: 'fixed',
top: 10,
right: 16,
top: 12,
left: '50%',
transform: 'translateX(-50%)',
zIndex: 9999,
display: 'flex',
alignItems: 'center',
gap: 4,
background: 'rgba(15, 23, 42, 0.88)',
backdropFilter: 'blur(12px)',
border: '1px solid rgba(255, 255, 255, 0.12)',
boxShadow: '0 4px 20px rgba(0, 0, 0, 0.45)',
background: 'rgba(15, 23, 42, 0.92)',
backdropFilter: 'blur(20px)',
border: '1px solid rgba(255, 255, 255, 0.18)',
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.55)',
borderRadius: 999,
padding: '3px 4px',
fontFamily: 'Inter, system-ui, sans-serif'
padding: '4px 6px',
fontFamily: "'IBM Plex Sans Arabic', Inter, system-ui, sans-serif",
direction: 'rtl',
maxWidth: 'calc(100vw - 24px)',
overflowX: 'auto'
}}>
{NAV.map(n => {
const active = n.hash === '#map' ? isMap : hash === n.hash
const active = currentHash === n.hash
return (
<a key={n.hash} href={n.hash}
style={{
textDecoration: 'none',
fontSize: 12,
fontWeight: 600,
fontWeight: 700,
color: active ? '#fff' : '#94a3b8',
background: active ? 'linear-gradient(135deg, #6366f1, #4f46e5)' : 'transparent',
boxShadow: active ? '0 2px 8px rgba(99, 102, 241, 0.4)' : 'none',
padding: '5px 13px',
padding: '6px 14px',
borderRadius: 999,
display: 'flex',
alignItems: 'center',
gap: 5,
gap: 6,
transition: 'all 0.15s ease'
}}>
<span>{n.icon}</span>
@@ -67,13 +72,19 @@ function ViewNav({ hash }: { hash: string }) {
}
function Root() {
const [hash, setHash] = useState(window.location.hash)
const [hash, setHash] = useState(window.location.hash || '#map')
useEffect(() => {
const on = () => setHash(window.location.hash)
const on = () => setHash(window.location.hash || '#map')
window.addEventListener('hashchange', on)
return () => window.removeEventListener('hashchange', on)
}, [])
const view = hash === '#compare' ? <CompareView /> : hash === '#review' ? <IntelligenceDashboard /> : <App />
const view =
hash === '#executive' || hash === '#pitch' ? <ExecutiveShowcase /> :
hash === '#compare' ? <CompareView /> :
hash === '#review' ? <IntelligenceDashboard /> :
<App />
return (
<>
<ViewNav hash={hash} />