import React, { useState, useEffect } from 'react'; import MapComponent from './components/MapComponent'; import { Navigation, Compass, Activity, BarChart3, MapPin } from 'lucide-react'; import { decodePolyline } from './utils/polyline'; 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 [stats, setStats] = useState(null); const [loading, setLoading] = useState(false); const [show3D, setShow3D] = useState(false); const [showPOIs, setShowPOIs] = useState(true); // Geocoding State const [searchQuery, setSearchQuery] = useState(''); const [searchResults, setSearchResults] = useState([]); const [showResults, setShowResults] = useState(false); const [newPlace, setNewPlace] = useState<{lat: number, lng: number} | null>(null); const [placeForm, setPlaceForm] = useState({ name: '', name_ar: '', category: '' }); const [currentRegion, setCurrentRegion] = useState('Jordan'); const regions = [ { name: 'Syria', name_ar: 'سوريا', center: [36.29, 33.51], zoom: 12, flag: '🇸🇾' }, { name: 'Jordan', name_ar: 'الأردن', center: [35.91, 31.95], zoom: 12, flag: '🇯🇴' }, { name: 'Egypt', name_ar: 'مصر', center: [31.23, 30.04], zoom: 11, flag: '🇪🇬' }, ]; const handleRegionSwitch = (region: any) => { setCurrentRegion(region.name); if (map) { map.flyTo({ center: region.center, zoom: region.zoom, essential: true, duration: 3000 }); } }; const handleMapClick = (lat: number, lng: number) => { setNewPlace({ lat, lng }); }; const handleSearch = async () => { if (searchQuery.length < 3) return; setLoading(true); try { const apiUrl = (import.meta as any).env.VITE_API_URL || '/api'; let queryUrl = `${apiUrl}/geocoding/search?q=${searchQuery}&radius=20000`; if (map) { const center = map.getCenter(); queryUrl += `&lat=${center.lat}&lng=${center.lng}`; } const response = await fetch(queryUrl, { headers: { 'x-api-key': 'intaleq_secret_2026' } }); const data = await response.json(); setSearchResults(data.results || []); setShowResults(true); if (data.results && data.results.length > 0 && map) { const place = data.results[0]; // API returns flat latitude/longitude fields (not a nested location object) const placeLng = parseFloat(place.longitude); const placeLat = parseFloat(place.latitude); if (!isNaN(placeLat) && !isNaN(placeLng)) { map.flyTo({ center: [placeLng, placeLat], zoom: 15 }); } } } catch (e) { console.error("Search failed", e); } finally { setLoading(false); } }; const submitNewPlace = async () => { if (!newPlace) return; try { const apiUrl = (import.meta as any).env.VITE_API_URL || '/api'; await fetch(`${apiUrl}/geocoding/places`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-key': 'intaleq_secret_2026' }, body: JSON.stringify({ latitude: newPlace.lat, longitude: newPlace.lng, name: placeForm.name, name_ar: placeForm.name_ar, category: placeForm.category, }) }); setNewPlace(null); setPlaceForm({ name: '', name_ar: '', category: '' }); alert('Place added successfully! / تم إضافة المكان بنجاح'); } catch (e) { console.error(e); alert('Failed to add place / فشل إضافة المكان'); } }; 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, using fallback UI"); } }; useEffect(() => { fetchStats(); const interval = setInterval(fetchStats, 60000); return () => clearInterval(interval); }, []); 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 calculateRoute = async () => { setLoading(true); try { // Points for Amman and Zarqa as defaults const from = [31.9539, 35.9106]; const to = [32.0608, 36.1032]; const apiUrl = (import.meta as any).env.VITE_API_URL || '/api'; const apiKey = (import.meta as any).env.VITE_API_KEY || 'intaleq_secret_2026'; const response = await fetch(`${apiUrl}/maps/route?fromLat=${from[0]}&fromLng=${from[1]}&toLat=${to[0]}&toLng=${to[1]}`, { headers: { 'x-api-key': apiKey } }); const data = await response.json(); if (data.points && map) { const coords = typeof data.points === 'string' ? decodePolyline(data.points) : data.points; setRouteData({ ...data, points: coords }); if (map.getSource('route')) { map.getSource('route').setData({ type: 'Feature', properties: {}, geometry: { type: 'LineString', coordinates: coords } }); } } } catch (error) { console.error("Error calculating route:", error); } finally { setLoading(false); } }; return (

{currentRegion} Maps SaaS

Self-Hosted Mobility Prototype

{regions.map(r => ( ))}
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 }); }} style={{ padding: '8px', 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 near you.
)}

Options / خيارات


Simulation / المحاكاة

Live Drivers: 12
{/* Add Place Modal */} {newPlace && (

Add New Place / إضافة مكان

setPlaceForm({...placeForm, name: e.target.value})} />
setPlaceForm({...placeForm, name_ar: e.target.value})} />
setPlaceForm({...placeForm, category: e.target.value})} />
)} {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;