chore: add build artifacts, map assets, and routing data
This commit is contained in:
+194
-78
@@ -6,7 +6,6 @@ import CompareView from './pages/CompareView'
|
||||
import IntelligenceDashboard from './pages/IntelligenceDashboard'
|
||||
import { ExecutiveShowcase } from './pages/ExecutiveShowcase'
|
||||
import { TacticalDefenseView } from './pages/TacticalDefenseView'
|
||||
import { SiroStrategicReport } from './pages/SiroStrategicReport'
|
||||
import './index.css'
|
||||
|
||||
maplibregl.setRTLTextPlugin(
|
||||
@@ -15,62 +14,175 @@ maplibregl.setRTLTextPlugin(
|
||||
true
|
||||
);
|
||||
|
||||
// Lightweight hash router
|
||||
// Lightweight hash router with Sovereign Executive Styling
|
||||
const NAV = [
|
||||
{ hash: '#siro', label: 'تقرير المركز الجغرافي (سيرو)', icon: '🏛️' },
|
||||
{ hash: '#tactical', label: 'المنظومة التكتيكية العسكرية', icon: '🎖️' },
|
||||
{ hash: '#map', label: 'الخريطة والطقس والملاحة', icon: '🗺️' },
|
||||
{ hash: '#compare', label: 'مقارنة العمالقة والأسعار', icon: '⚖️' },
|
||||
{ hash: '#review', label: 'تدقيق الطرق الذكي', icon: '🔍' },
|
||||
]
|
||||
|
||||
function ViewNav({ hash }: { hash: string }) {
|
||||
const currentHash = hash || '#siro'
|
||||
function MasterHeader({ hash }: { hash: string }) {
|
||||
const currentHash = hash || '#tactical'
|
||||
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||
|
||||
const toggleFullscreen = () => {
|
||||
if (!document.fullscreenElement) {
|
||||
document.documentElement.requestFullscreen().catch(() => {});
|
||||
setIsFullscreen(true);
|
||||
} else {
|
||||
document.exitFullscreen().catch(() => {});
|
||||
setIsFullscreen(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
<header style={{
|
||||
position: 'fixed',
|
||||
top: 12,
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: 52,
|
||||
zIndex: 9999,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 4,
|
||||
background: 'rgba(255, 255, 255, 0.88)',
|
||||
backdropFilter: 'blur(20px)',
|
||||
border: '1px solid rgba(0, 0, 0, 0.1)',
|
||||
boxShadow: '0 4px 20px rgba(0, 0, 0, 0.08)',
|
||||
borderRadius: 999,
|
||||
padding: '4px 6px',
|
||||
justifyContent: 'space-between',
|
||||
padding: '0 16px',
|
||||
background: 'rgba(9, 14, 26, 0.94)',
|
||||
backdropFilter: 'blur(24px)',
|
||||
WebkitBackdropFilter: 'blur(24px)',
|
||||
borderBottom: '1px solid rgba(255, 255, 255, 0.08)',
|
||||
boxShadow: '0 4px 20px rgba(0, 0, 0, 0.4)',
|
||||
fontFamily: "'IBM Plex Sans Arabic', Inter, system-ui, sans-serif",
|
||||
direction: 'rtl',
|
||||
maxWidth: 'calc(100vw - 24px)',
|
||||
overflowX: 'auto'
|
||||
userSelect: 'none'
|
||||
}}>
|
||||
{NAV.map(n => {
|
||||
const active = currentHash === n.hash
|
||||
return (
|
||||
<a key={n.hash} href={n.hash}
|
||||
style={{
|
||||
textDecoration: 'none',
|
||||
fontSize: 12,
|
||||
fontWeight: 700,
|
||||
color: active ? '#fff' : '#64748b',
|
||||
background: active ? 'linear-gradient(135deg, #0071e3, #4f46e5)' : 'transparent',
|
||||
boxShadow: active ? '0 2px 8px rgba(0, 113, 227, 0.35)' : 'none',
|
||||
padding: '6px 14px',
|
||||
borderRadius: 999,
|
||||
{/* Right Section: Brand & Sovereign Security Clearance */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||
<a href="#tactical" style={{ textDecoration: 'none', display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
<div style={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: 9,
|
||||
background: 'linear-gradient(135deg, #0071e3, #6366f1)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
boxShadow: '0 0 14px rgba(0, 113, 227, 0.5)'
|
||||
}}>
|
||||
<span style={{ fontSize: 16 }}>⚡</span>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{
|
||||
color: '#ffffff',
|
||||
fontSize: '0.85rem',
|
||||
fontWeight: 800,
|
||||
letterSpacing: '-0.02em',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 6,
|
||||
transition: 'all 0.15s ease'
|
||||
gap: 6
|
||||
}}>
|
||||
<span>{n.icon}</span>
|
||||
<span>{n.label}</span>
|
||||
</a>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<span>منظومة انطلاق المكانية</span>
|
||||
<span style={{
|
||||
fontSize: '9.5px',
|
||||
background: 'rgba(0, 113, 227, 0.25)',
|
||||
border: '1px solid rgba(0, 113, 227, 0.5)',
|
||||
color: '#38bdf8',
|
||||
padding: '1px 5px',
|
||||
borderRadius: 4,
|
||||
fontWeight: 700
|
||||
}}>
|
||||
Sovereign Cloud v3.2
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Center Section: Master Navigation Segmented Controller */}
|
||||
<nav style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 3,
|
||||
background: 'rgba(255, 255, 255, 0.05)',
|
||||
border: '1px solid rgba(255, 255, 255, 0.1)',
|
||||
padding: '3px 4px',
|
||||
borderRadius: 12,
|
||||
boxShadow: 'inset 0 1px 4px rgba(0, 0, 0, 0.3)'
|
||||
}}>
|
||||
{NAV.map(n => {
|
||||
const active = currentHash === n.hash
|
||||
return (
|
||||
<a key={n.hash} href={n.hash}
|
||||
style={{
|
||||
textDecoration: 'none',
|
||||
fontSize: 12,
|
||||
fontWeight: active ? 800 : 600,
|
||||
color: active ? '#ffffff' : '#94a3b8',
|
||||
background: active
|
||||
? 'linear-gradient(135deg, #0071e3, #4f46e5)'
|
||||
: 'transparent',
|
||||
boxShadow: active ? '0 2px 10px rgba(0, 113, 227, 0.45)' : 'none',
|
||||
padding: '6px 12px',
|
||||
borderRadius: 9,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 6,
|
||||
transition: 'all 0.15s ease'
|
||||
}}>
|
||||
<span style={{ fontSize: 13 }}>{n.icon}</span>
|
||||
<span>{n.label}</span>
|
||||
</a>
|
||||
)
|
||||
})}
|
||||
</nav>
|
||||
|
||||
{/* Left Section: Live Sovereign Telemetry & Fullscreen */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 6,
|
||||
background: 'rgba(34, 197, 94, 0.12)',
|
||||
border: '1px solid rgba(34, 197, 94, 0.3)',
|
||||
color: '#4ade80',
|
||||
padding: '4px 10px',
|
||||
borderRadius: 8,
|
||||
fontSize: '0.72rem',
|
||||
fontWeight: 700
|
||||
}}>
|
||||
<span style={{
|
||||
display: 'inline-block',
|
||||
width: 7,
|
||||
height: 7,
|
||||
borderRadius: '50%',
|
||||
background: '#22c55e',
|
||||
boxShadow: '0 0 8px #22c55e'
|
||||
}} />
|
||||
<span>السحابة السيادية متصلة</span>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={toggleFullscreen}
|
||||
title="ملء الشاشة"
|
||||
style={{
|
||||
background: 'rgba(255, 255, 255, 0.06)',
|
||||
border: '1px solid rgba(255, 255, 255, 0.12)',
|
||||
color: '#cbd5e1',
|
||||
padding: '5px 9px',
|
||||
borderRadius: 8,
|
||||
fontSize: '0.75rem',
|
||||
cursor: 'pointer',
|
||||
fontWeight: 600,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 4
|
||||
}}
|
||||
>
|
||||
<span>{isFullscreen ? '🗗' : '🗖'}</span>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -97,8 +209,8 @@ class ErrorBoundary extends React.Component<{ children: React.ReactNode }, { has
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
height: '100vh',
|
||||
background: '#f8fafc',
|
||||
color: '#1d1d1f',
|
||||
background: '#090e1a',
|
||||
color: '#ffffff',
|
||||
fontFamily: 'system-ui, sans-serif',
|
||||
direction: 'rtl',
|
||||
padding: 24,
|
||||
@@ -106,7 +218,7 @@ class ErrorBoundary extends React.Component<{ children: React.ReactNode }, { has
|
||||
}}>
|
||||
<div style={{ fontSize: '3rem', marginBottom: 16 }}>⚠️</div>
|
||||
<h2 style={{ fontSize: '1.4rem', fontWeight: 700, marginBottom: 8 }}>حدث خطأ أثناء تحميل الواجهة</h2>
|
||||
<p style={{ color: '#64748b', fontSize: '0.9rem', maxWidth: 450, marginBottom: 20 }}>
|
||||
<p style={{ color: '#94a3b8', fontSize: '0.9rem', maxWidth: 450, marginBottom: 20 }}>
|
||||
{this.state.error?.message || 'تعذر تحميل أحد مكونات الخريطة'}
|
||||
</p>
|
||||
<button
|
||||
@@ -132,11 +244,11 @@ class ErrorBoundary extends React.Component<{ children: React.ReactNode }, { has
|
||||
}
|
||||
|
||||
function Root() {
|
||||
const [hash, setHash] = useState(window.location.hash || '#executive');
|
||||
const [hash, setHash] = useState(window.location.hash || '#tactical');
|
||||
|
||||
useEffect(() => {
|
||||
const on = () => {
|
||||
const h = window.location.hash || '#executive';
|
||||
const h = window.location.hash || '#tactical';
|
||||
setHash(h);
|
||||
setTimeout(() => {
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
@@ -146,8 +258,7 @@ function Root() {
|
||||
return () => window.removeEventListener('hashchange', on);
|
||||
}, []);
|
||||
|
||||
const isSiro = hash === '#siro' || hash === '#report' || hash === '' || hash === '#executive';
|
||||
const isTactical = hash === '#tactical' || hash === '#military';
|
||||
const isTactical = hash === '#tactical' || hash === '#military' || hash === '' || hash === '#siro';
|
||||
const isExecutive = hash === '#showcase' || hash === '#pitch';
|
||||
const isCompare = hash === '#compare';
|
||||
const isReview = hash === '#review';
|
||||
@@ -155,43 +266,48 @@ function Root() {
|
||||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<ViewNav hash={hash} />
|
||||
<MasterHeader hash={hash} />
|
||||
|
||||
{isSiro && (
|
||||
<div style={{ width: '100vw', height: '100vh', position: 'absolute', top: 0, left: 0, overflowY: 'auto', overflowX: 'hidden' }}>
|
||||
<SiroStrategicReport />
|
||||
</div>
|
||||
)}
|
||||
<main style={{
|
||||
position: 'absolute',
|
||||
top: 52,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
width: '100vw',
|
||||
height: 'calc(100vh - 52px)',
|
||||
overflow: 'hidden'
|
||||
}}>
|
||||
{isTactical && (
|
||||
<div style={{ width: '100%', height: '100%' }}>
|
||||
<TacticalDefenseView />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isTactical && (
|
||||
<div style={{ width: '100vw', height: '100vh', position: 'absolute', top: 0, left: 0 }}>
|
||||
<TacticalDefenseView />
|
||||
</div>
|
||||
)}
|
||||
{isExecutive && (
|
||||
<div style={{ width: '100%', height: '100%', overflowY: 'auto', overflowX: 'hidden' }}>
|
||||
<ExecutiveShowcase />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isExecutive && (
|
||||
<div style={{ width: '100vw', height: '100vh', position: 'absolute', top: 0, left: 0, overflowY: 'auto', overflowX: 'hidden' }}>
|
||||
<ExecutiveShowcase />
|
||||
</div>
|
||||
)}
|
||||
{isCompare && (
|
||||
<div style={{ width: '100%', height: '100%' }}>
|
||||
<CompareView />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isCompare && (
|
||||
<div style={{ width: '100vw', height: '100vh', position: 'absolute', top: 0, left: 0 }}>
|
||||
<CompareView />
|
||||
</div>
|
||||
)}
|
||||
{isReview && (
|
||||
<div style={{ width: '100%', height: '100%' }}>
|
||||
<IntelligenceDashboard />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isReview && (
|
||||
<div style={{ width: '100vw', height: '100vh', position: 'absolute', top: 0, left: 0 }}>
|
||||
<IntelligenceDashboard />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isMap && (
|
||||
<div style={{ width: '100vw', height: '100vh', position: 'absolute', top: 0, left: 0 }}>
|
||||
<App />
|
||||
</div>
|
||||
)}
|
||||
{isMap && (
|
||||
<div style={{ width: '100%', height: '100%' }}>
|
||||
<App />
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user