diff --git a/apps/web/src/pages/ExecutiveShowcase.tsx b/apps/web/src/pages/ExecutiveShowcase.tsx index 9663489..a7da39b 100644 --- a/apps/web/src/pages/ExecutiveShowcase.tsx +++ b/apps/web/src/pages/ExecutiveShowcase.tsx @@ -22,7 +22,9 @@ import { Plane, Radar, Sparkles, - ExternalLink + ExternalLink, + Flame, + ShieldAlert } from 'lucide-react'; export const ExecutiveShowcase: React.FC = () => { diff --git a/apps/web/src/pages/IntelligenceDashboard.tsx b/apps/web/src/pages/IntelligenceDashboard.tsx index f5acfc0..fcba6d5 100644 --- a/apps/web/src/pages/IntelligenceDashboard.tsx +++ b/apps/web/src/pages/IntelligenceDashboard.tsx @@ -19,17 +19,17 @@ type RefKey = | 'osm'; const REFS: Record = { - 'google-hybrid': { label: '🛰️ Google Hybrid', tiles: 'https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, - 'google-topo': { label: '🏔️ Google Topo', tiles: 'https://mt1.google.com/vt/lyrs=p&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, - 'soviet-military': { label: '🇷🇺 Soviet Military Topo', tiles: 'https://maps.marshruty.ru/ml.ashx?x={x}&y={y}&z={z}&i=1&al=1', attribution: '© Soviet Military Topo 1:100k', maxzoom: 15 }, - 'opentopo': { label: '🧭 OpenTopoMap', tiles: 'https://tile.opentopomap.org/{z}/{x}/{y}.png', attribution: '© OpenTopoMap', maxzoom: 17 }, - 'esri-clarity': { label: '✨ Esri Clarity HD', tiles: 'https://clarity.maptiles.arcgis.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri', maxzoom: 19 }, - 'google-sat': { label: '🛰️ Google Sat', tiles: 'https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, - 'google-streets': { label: '🗺️ Google Streets', tiles: 'https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, - 'esri-sat': { label: '🛰️ Esri Satellite', tiles: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri, Maxar', maxzoom: 19 }, - 'esri-topo': { label: '🏔️ Esri Topo', tiles: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri', maxzoom: 19 }, - 'esri-streets': { label: '🗺️ Esri Streets', tiles: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri', maxzoom: 19 }, - 'osm': { label: '🧭 OSM Standard', tiles: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', attribution: '© OpenStreetMap', maxzoom: 19 }, + 'google-hybrid': { label: '🛰️ Google Hybrid', tiles: 'https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, + 'google-topo': { label: '🏔️ Google Topo', tiles: 'https://mt1.google.com/vt/lyrs=p&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, + 'soviet-military': { label: '🇷🇺 Soviet Military Topo', tiles: 'https://maps.marshruty.ru/ml.ashx?x={x}&y={y}&z={z}&i=1&al=1', attribution: '© Soviet Military Topo 1:100k', maxzoom: 15 }, + 'opentopo': { label: '🧭 OpenTopoMap', tiles: 'https://tile.opentopomap.org/{z}/{x}/{y}.png', attribution: '© OpenTopoMap', maxzoom: 17 }, + 'esri-clarity': { label: '✨ Esri Clarity HD', tiles: 'https://clarity.maptiles.arcgis.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri', maxzoom: 19 }, + 'google-sat': { label: '🛰️ Google Sat', tiles: 'https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, + 'google-streets': { label: '🗺️ Google Streets', tiles: 'https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, + 'esri-sat': { label: '🛰️ Esri Satellite', tiles: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri, Maxar', maxzoom: 19 }, + 'esri-topo': { label: '🏔️ Esri Topo', tiles: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri', maxzoom: 19 }, + 'esri-streets': { label: '🗺️ Esri Streets', tiles: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri', maxzoom: 19 }, + 'osm': { label: '🧭 OSM Standard', tiles: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', attribution: '© OpenStreetMap', maxzoom: 19 }, }; const API = (import.meta as any).env.VITE_API_URL || '/api'; @@ -42,7 +42,7 @@ const rasterStyle = (ref: any): any => ({ layers: [{ id: 'ref', type: 'raster', source: 'ref' }], }); -const DEFAULT_SYSTEM_KEY = (import.meta as any).env.VITE_API_KEY || 'zP9vL5mK2nQ8xR7jT4wS1yB6hG3fV0cX'; +const DEFAULT_SYSTEM_KEY = (import.meta as any).env.VITE_API_KEY; const IntelligenceDashboard: React.FC = () => { const [stats, setStats] = useState(null); @@ -95,7 +95,7 @@ const IntelligenceDashboard: React.FC = () => { [point.x - radius, point.y - radius], [point.x + radius, point.y + radius] ]; - + try { const features = map.queryRenderedFeatures(bbox).filter(f => { const id = f.layer?.id || ''; @@ -109,22 +109,22 @@ const IntelligenceDashboard: React.FC = () => { const geom = f.geometry; if (!geom) continue; - const lines: [number, number][][] = geom.type === 'LineString' + const lines: [number, number][][] = geom.type === 'LineString' ? [geom.coordinates as [number, number][]] : geom.type === 'MultiLineString' - ? (geom.coordinates as [number, number][][]) - : []; + ? (geom.coordinates as [number, number][][]) + : []; for (const line of lines) { for (let i = 0; i < line.length - 1; i++) { const p1 = map.project(line[i] as any); const p2 = map.project(line[i + 1] as any); - + const dx = p2.x - p1.x; const dy = p2.y - p1.y; const lenSq = dx * dx + dy * dy; if (lenSq === 0) continue; - + const t = Math.max(0, Math.min(1, ((point.x - p1.x) * dx + (point.y - p1.y) * dy) / lenSq)); const closePx = { x: p1.x + t * dx, y: p1.y + t * dy }; const distPx = Math.hypot(point.x - closePx.x, point.y - closePx.y); @@ -308,14 +308,14 @@ const IntelligenceDashboard: React.FC = () => { const nameLine = p.name ? `${p.name}
` : ''; const evidence = isOvt ? `${p.uniqueDriverCount ?? 0} trace pts corroborate` : `${p.uniqueDriverCount || 1} drivers`; if (popupRef.current) popupRef.current.remove(); - + const popupDiv = document.createElement('div'); popupDiv.style.font = '12px sans-serif'; popupDiv.style.color = '#1e293b'; popupDiv.style.lineHeight = '1.6'; popupDiv.innerHTML = `${nameLine}${title}
Conf: ${Math.round(p.confidence * 100)}%
${Math.round(p.lengthMeters || 0)}m | ${evidence}
`; - + popupDiv.querySelector(`#btn-del-${p.id}`)?.addEventListener('click', () => { deleteRoad(p.id, isAppr ? 'approved' : 'candidate'); popupRef.current?.remove(); @@ -501,12 +501,12 @@ const IntelligenceDashboard: React.FC = () => { setTimeout(() => setMsg(''), 5000); }; - const cancelDrawing = () => { - pointsRef.current = []; - redrawDrawing(); + const cancelDrawing = () => { + pointsRef.current = []; + redrawDrawing(); if (leftMapRef.current) updateSnapGuide(leftMapRef.current, null); if (rightMapRef.current) updateSnapGuide(rightMapRef.current, null); - setDrawing(false); + setDrawing(false); }; if (needsAuth) { @@ -691,8 +691,8 @@ const IntelligenceDashboard: React.FC = () => { {/* Left Map: Our Data */}
-
- Our Map (Intaleq) +
+ 🗺️ خريطتنا السيادية (Intaleq Map)
@@ -700,9 +700,9 @@ const IntelligenceDashboard: React.FC = () => {
-
+
{(Object.keys(REFS) as Array).map(k => ( - ))}