import React, { useState, useEffect } from 'react'; import MapComponent from './components/MapComponent'; import { Navigation, Compass, Activity, BarChart3, MapPin, Eye, Shield, Gauge, Clock, AlertTriangle } from 'lucide-react'; import { decodePolyline } from './utils/polyline'; import WeatherPanel from './components/WeatherPanel'; import { LineOfSightTool } from './components/LineOfSightTool'; const DEFAULT_API_KEY = (import.meta as any).env.VITE_API_KEY || localStorage.getItem('intaleq_api_key') || ''; function App() { const [map, setMap] = useState(null); const [debug, setDebug] = useState({ zoom: 12, center: [35.91, 31.95], bounds: '' }); const [routeData, setRouteData] = useState(null); const [routeSummary, setRouteSummary] = useState(null); const [stats, setStats] = useState(null); const [loading, setLoading] = useState(false); const [routeLoading, setRouteLoading] = useState(false); const [routeError, setRouteError] = useState(''); // Layer Toggles const [show3D, setShow3D] = useState(false); const [showPOIs, setShowPOIs] = useState(true); const [showTerrain, setShowTerrain] = useState(true); const [showContours, setShowContours] = useState(true); const [showAdminBoundaries, setShowAdminBoundaries] = useState(true); const [showWeather, setShowWeather] = useState(false); const [showLOS, setShowLOS] = useState(false); const [losPointA, setLosPointA] = useState<[number, number] | null>(null); const [losPointB, setLosPointB] = useState<[number, number] | null>(null); const [weatherCity, setWeatherCity] = useState(null); const [weatherAlerts, setWeatherAlerts] = useState([]); // Geocoding State const [searchQuery, setSearchQuery] = useState(''); const [searchResults, setSearchResults] = useState([]); const [showResults, setShowResults] = useState(false); const [currentRegion, setCurrentRegion] = useState('Jordan'); // Route Form State const [originText, setOriginText] = useState('31.9539, 35.9106'); const [destText, setDestText] = useState('32.0608, 36.1032'); const regions = [ { name: 'Syria', name_ar: 'سوريا', center: [36.29, 33.51], zoom: 11, flag: '🇸🇾', defaultOrigin: '33.5138, 36.2765', // Damascus defaultDest: '36.2021, 37.1343' // Aleppo }, { name: 'Jordan', name_ar: 'الأردن', center: [35.91, 31.95], zoom: 11, flag: '🇯🇴', defaultOrigin: '31.9539, 35.9106', // Amman defaultDest: '32.0608, 36.1032' // Zarqa }, { name: 'Egypt', name_ar: 'مصر', center: [31.23, 30.04], zoom: 10, flag: '🇪🇬', defaultOrigin: '30.0444, 31.2357', // Cairo defaultDest: '31.2001, 29.9187' // Alexandria }, ]; const handleRegionSwitch = (region: any) => { setCurrentRegion(region.name); setOriginText(region.defaultOrigin); setDestText(region.defaultDest); setRouteData(null); setWeatherCity(null); // Clear existing route line if drawn if (map && map.getSource('route')) { map.getSource('route').setData({ type: 'Feature', properties: {}, geometry: { type: 'LineString', coordinates: [] } }); } if (map) { map.flyTo({ center: region.center, zoom: region.zoom, essential: true, duration: 2500 }); } }; const handleSearch = async () => { if (searchQuery.trim().length < 2) return; setLoading(true); try { const apiUrl = (import.meta as any).env.VITE_API_URL || '/api'; let queryUrl = `${apiUrl}/geocoding/search?q=${encodeURIComponent(searchQuery)}&radius=50000`; if (map) { const center = map.getCenter(); queryUrl += `&lat=${center.lat}&lng=${center.lng}`; } const response = await fetch(queryUrl, { headers: { 'x-api-key': DEFAULT_API_KEY } }); const data = await response.json(); setSearchResults(data.results || []); setShowResults(true); if (data.results && data.results.length > 0 && map) { const place = data.results[0]; const placeLng = parseFloat(place.longitude); const placeLat = parseFloat(place.latitude); if (!isNaN(placeLat) && !isNaN(placeLng)) { map.flyTo({ center: [placeLng, placeLat], zoom: 15, duration: 2000 }); } } } catch (e) { console.error("Search failed", e); } finally { setLoading(false); } }; const fetchStats = async () => { try { const apiUrl = (import.meta as any).env.VITE_API_URL || '/api'; const response = await fetch(`${apiUrl}/map-refinement/roads/summary`); if (!response.ok) throw new Error('Stats fetch failed'); const data = await response.json(); setStats(data); } catch (error) { console.warn("Stats fetch failed"); } }; useEffect(() => { fetchStats(); const interval = setInterval(fetchStats, 60000); return () => clearInterval(interval); }, []); useEffect(() => { if (!showWeather) return; const fetchAlerts = async () => { try { const apiUrl = (import.meta as any).env.VITE_API_URL || '/api'; const regionParam = currentRegion ? `?region=${encodeURIComponent(currentRegion)}` : ''; const res = await fetch(`${apiUrl}/weather/alerts${regionParam}`, { headers: { 'x-api-key': DEFAULT_API_KEY } }); const data = await res.json(); setWeatherAlerts(Array.isArray(data) ? data : []); } catch (e) { console.warn('Weather alerts fetch failed', e); setWeatherAlerts([]); } }; fetchAlerts(); }, [showWeather, currentRegion]); const handleMapLoad = (initializedMap: any) => { setMap(initializedMap); initializedMap.on('move', () => { const center = initializedMap.getCenter(); setDebug({ zoom: initializedMap.getZoom().toFixed(2), center: [center.lng.toFixed(4), center.lat.toFixed(4)], bounds: initializedMap.getBounds().toString() }); }); }; const parseCoordinates = (input: string): [number, number] | null => { const parts = input.split(',').map(p => parseFloat(p.trim())); if (parts.length === 2 && !isNaN(parts[0]) && !isNaN(parts[1])) { return [parts[0], parts[1]]; // [lat, lng] } return null; }; const calculateRoute = async () => { setRouteError(''); const originCoords = parseCoordinates(originText); const destCoords = parseCoordinates(destText); if (!originCoords || !destCoords) { setRouteError('Please enter valid coordinates format: lat, lng'); return; } setRouteLoading(true); try { const apiUrl = (import.meta as any).env.VITE_API_URL || '/api'; const url = `${apiUrl}/maps/route?fromLat=${originCoords[0]}&fromLng=${originCoords[1]}&toLat=${destCoords[0]}&toLng=${destCoords[1]}&profile=car&steps=true`; const response = await fetch(url, { headers: { 'x-api-key': DEFAULT_API_KEY } }); const data = await response.json(); if (data && data.points) { const coords = typeof data.points === 'string' ? decodePolyline(data.points) : data.points; setRouteData({ ...data, points: coords }); if (map && map.getSource('route')) { map.getSource('route').setData({ type: 'Feature', properties: {}, geometry: { type: 'LineString', coordinates: coords } }); // Fit map to route bounds if (coords.length > 0) { let minLng = coords[0][0], maxLng = coords[0][0]; let minLat = coords[0][1], maxLat = coords[0][1]; for (const pt of coords) { minLng = Math.min(minLng, pt[0]); maxLng = Math.max(maxLng, pt[0]); minLat = Math.min(minLat, pt[1]); maxLat = Math.max(maxLat, pt[1]); } map.fitBounds([[minLng, minLat], [maxLng, maxLat]], { padding: { top: 70, bottom: 70, left: 360, right: 70 }, duration: 2000 }); } } } else { setRouteError(data.message || 'No route found between selected points.'); } } catch (error) { console.error("Error calculating route:", error); setRouteError('Failed to calculate route. Please try again.'); } finally { setRouteLoading(false); } }; const handleMapClick = (lat: number, lng: number) => { if (showLOS) { if (!losPointA) { setLosPointA([lat, lng]); } else if (!losPointB) { setLosPointB([lat, lng]); } else { // Reset and set point A setLosPointA([lat, lng]); setLosPointB(null); } } }; const handleClearLOS = () => { setLosPointA(null); setLosPointB(null); }; const handleCloseLOS = () => { setShowLOS(false); setLosPointA(null); setLosPointB(null); }; return (

{currentRegion} Maps SaaS

Intaleq Mobility & Maps Cloud

{/* Region Selector */}
{regions.map(r => ( ))}
{/* Search Bar */}
setSearchQuery(e.target.value)} onKeyDown={e => e.key === 'Enter' && handleSearch()} />
{showResults && searchResults.length > 0 && (
{searchResults.map((res) => (
{ const rLng = parseFloat(res.longitude); const rLat = parseFloat(res.latitude); if (!isNaN(rLat) && !isNaN(rLng)) { map?.flyTo({ center: [rLng, rLat], zoom: 16, duration: 1500 }); } }} style={{ padding: '10px', borderBottom: '1px solid var(--glass-border)', cursor: 'pointer', fontSize: '0.85rem' }} className="search-result-item" >
{res.name_ar || res.name}
{res.address &&
{res.address}
}
{res.distance ? (Number(res.distance) / 1000).toFixed(1) + ' km away' : ''} | {(res.source || '').replace('_', ' ')}
))}
)} {showResults && searchResults.length === 0 && (
No results found / لم يتم العثور على نتائج.
)}
{/* Route Calculation */}
setOriginText(e.target.value)} />
setDestText(e.target.value)} />
{routeError && (
{routeError}
)} {/* Route Summary Card */} {routeData && (

{routeData.routeName || 'تفاصيل المسار'} {routeData.tags ? routeData.tags[0] : 'FASTEST'}

{(Number(routeData.distance || 0) / 1000).toFixed(1)} كم {Math.round(Number(routeData.duration || 0) / 60)} دقيقة
{/* Elevation & Slope Summary */} {routeData.elevationSummary && (
📈 إجمالي الصعود: +{routeData.elevationSummary.totalAscentMeters}م 📉 إجمالي الهبوط: -{routeData.elevationSummary.totalDescentMeters}م
{(routeData.elevationSummary.maxInclinePercent > 0 || routeData.elevationSummary.maxDeclinePercent < 0) && (
أقصى صعود: +{routeData.elevationSummary.maxInclinePercent}% أقصى انحدار: {routeData.elevationSummary.maxDeclinePercent}%
)}
)} {/* Steep Slope Warnings Alert */} {routeData.elevationSummary?.steepWarnings && routeData.elevationSummary.steepWarnings.length > 0 && (
تحذير تضاريسي للمركبات الثقيلة:
{routeData.elevationSummary.steepWarnings.slice(0, 2).map((w: any, idx: number) => (
• {w.street ? `شارع ${w.street}: ` : ''}{w.slopePercent > 0 ? `صعود حاد (+${w.slopePercent}%)` : `منحدر جبلي شديد (${w.slopePercent}%)`}
))}
)} {/* Turn by turn expandable instructions */} {routeData.instructions && routeData.instructions.length > 0 && (
عرض خطوات المسار بالتفصيل ({routeData.instructions.length} خطوة)
{routeData.instructions.map((inst: any, idx: number) => (
{idx + 1}. {inst.text}
{inst.distance ? `${Math.round(inst.distance)}م` : ''} {inst.slopePercent ? `| انحدار: ${inst.slopePercent}%` : ''}
))}
)}
)}
{/* Layers & Map Options */}

Layers & Map Options / خيارات الخريطة

{/* Tactical Line of Sight (LOS) Toggle */}
Military

Simulation / المحاكاة

Live Drivers: 12
setWeatherCity(cityData)} losActive={showLOS} losPointA={losPointA} losPointB={losPointB} /> {/* Tactical Line of Sight Tool Overlay */} {stats && stats.telemetry && (

Map Insights / التحليل

Total GPS Points / إجمالي النقاط {stats.telemetry?.totalPoints?.toLocaleString()}
Last 24h / آخر 24 ساعة +{stats.telemetry?.last24h?.toLocaleString()}
Updated Roads / شوارع محدثة {stats.roadSegments?.analyzed}
Candidate Roads / شـوارع مرشحة {stats.candidateRoads?.total}
)}
Zoom: {debug.zoom}
Center: {debug.center[0]}, {debug.center[1]}
); } export default App;