diff --git a/addons/mymach_fleet_intelligence/static/src/js/dashboard.js b/addons/mymach_fleet_intelligence/static/src/js/dashboard.js index f4d6a6b..3a03568 100644 --- a/addons/mymach_fleet_intelligence/static/src/js/dashboard.js +++ b/addons/mymach_fleet_intelligence/static/src/js/dashboard.js @@ -175,218 +175,329 @@ document.addEventListener("DOMContentLoaded", function() { allPts.forEach(function(p) { bounds.extend({lat: p[0], lng: p[1]}); }); map.fitBounds(bounds); } + try { + var map; + var activePolylines = []; + var activeMarkers = []; + var isGoogleLoaded = false; + if (googleMapsApiKey) { + if (typeof google !== 'undefined' && google.maps) { + isGoogleLoaded = true; + } + } + + if (isGoogleLoaded) { + var mapEl = document.getElementById('mymachMap'); + if (mapEl) { + map = new google.maps.Map(mapEl, { + center: { lat: 41.0082, lng: 28.9784 }, + zoom: 11, + disableDefaultUI: true, + styles: [ + { elementType: "geometry", stylers: [{ color: "#242f3e" }] }, + { elementType: "labels.text.stroke", stylers: [{ color: "#242f3e" }] }, + { elementType: "labels.text.fill", stylers: [{ color: "#746855" }] } + ] + }); + } } else { - // Leaflet.js Görsel Çizimler (Uydu Temalı) - - // 1. Alternatif Gri Yollar - var polyAlt1 = L.polyline(alt1Pts, { color: '#64748b', weight: 4, opacity: 0.6, dashArray: '8, 6' }).addTo(map); - var polyAlt2 = L.polyline(alt2Pts, { color: '#64748b', weight: 4, opacity: 0.6, dashArray: '8, 6' }).addTo(map); - activePolylines.push(polyAlt1, polyAlt2); - - // 2. Yeşil Ana Rota Çizgisi - if (greenPts.length > 0) { - var polyGreen = L.polyline(greenPts, { color: '#10b981', weight: 8, opacity: 0.95, lineCap: 'round', lineJoin: 'round' }).addTo(map); - activePolylines.push(polyGreen); - } - - // 3. Kırmızı 1 km+ Sapma Çizgisi - if (redPts.length > 0) { - var polyRed = L.polyline(redPts, { color: '#ef4444', weight: 8, opacity: 0.95, lineCap: 'round', lineJoin: 'round' }).addTo(map); - activePolylines.push(polyRed); - } - - var allPts = greenPts.concat(redPts); - if (allPts.length > 0) { - var startPt = greenPts[0] || allPts[0]; - var endPt = redPts.length > 0 ? redPts[redPts.length - 1] : (greenPts[greenPts.length - 1] || allPts[allPts.length - 1]); - - // Başlangıç ve Bitiş İğnelerini (Mavi/Kırmızı) Canlı Radarda Gizliyoruz (Sadece araba ve çizgi görünecek) - // Kırmızı Yol Üzerindeki "1.5 km Sapma" Çağrı Kutucuğu - if (redPts.length > 0) { - var midRedIdx = Math.floor(redPts.length / 2); - var mRedCallout = L.marker(redPts[midRedIdx], { icon: createCalloutIcon(vehicle.deviation_label, '#ef4444') }).addTo(map); - activeMarkers.push(mRedCallout); - } - - // Rotada Gerçek Zamanlı Canlı Hareket Eden Araç Animasyonunu Başlat - animateMovingVehicle(allPts, vehicle); - - var group = new L.featureGroup([polyAlt1, polyAlt2].concat(activePolylines)); - map.fitBounds(group.getBounds(), { padding: [50, 50] }); + var mapEl = document.getElementById('mymachMap'); + if (mapEl) { + map = L.map(mapEl, { zoomControl: false }).setView([41.0082, 28.9784], 11); + L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', { + attribution: '© OpenStreetMap © CARTO', + subdomains: 'abcd', + maxZoom: 20 + }).addTo(map); } } - // Yüzen Detay Kartını Güncelle - var overlayEl = document.getElementById('driver_info_overlay'); - if (overlayEl) { - var badgeBg = vehicle.is_violation ? 'rgba(239, 68, 68, 0.25)' : (vehicle.is_manager_approved ? 'rgba(56, 189, 248, 0.25)' : 'rgba(16, 185, 129, 0.25)'); - var badgeBorder = vehicle.is_violation ? '#ef4444' : (vehicle.is_manager_approved ? '#38bdf8' : '#10b981'); - var badgeText = vehicle.is_violation ? '#ef4444' : (vehicle.is_manager_approved ? '#38bdf8' : '#10b981'); - var badgeTitle = vehicle.is_violation - ? vehicle.badge_violation - : (vehicle.is_manager_approved ? vehicle.badge_approved : vehicle.badge_ok); - - overlayEl.style.display = 'block'; - overlayEl.innerHTML = - '
' + - '
' + - '

' + (vehicle.driver_tag_str || '') + ' - ' + vehicle.driver_name + '

' + - '' + vehicle.license_plate + ' (' + vehicle.name + ')' + - '
' + - '' + - vehicle.label_score + ' ' + vehicle.score + - '' + - '
' + - '
' + - badgeTitle + - '
' + - '
' + - '
' + - 'Personel Evi / Konaklama:' + - '' + (vehicle.driver_home_address || 'Bilinmiyor') + '' + - '
' + - '
' + - 'Başlangıç Noktası:' + - '' + (vehicle.start_location || 'Canlı GPS Konumu') + '' + - '
' + - '
' + - 'Ulaşım Hedefi:' + - '' + (vehicle.destination || 'Gerçek Zamanlı Aktif İzleme') + '' + - '
' + - '
' + - 'Planlanan / Gidilen Mesafe:' + - '' + vehicle.planned_km + ' KM / ' + vehicle.driven_km + ' KM' + - '
' + - '
' + - 'Rota Sapma Durumu:' + - '' + vehicle.deviation_label + '' + - '
' + - '
' + - 'Şirket Politikası:' + - '' + vehicle.policy_desc + '' + - '
' + + function createCalloutIcon(text, color) { + var html = + '
' + + text + '
'; + if (isGoogleLoaded) return html; // Google Maps div bazlı markup + return L.divIcon({ className: 'custom-div-icon', html: html, iconSize: [null, null], iconAnchor: [50, 40] }); } - } - var tableBody = document.getElementById('vehicleTableBody'); - if (tableBody) { - tableBody.innerHTML = ''; - var movingCount = 0; - var parkedCount = 0; - var offlineCount = 0; + function selectDriver(vehicle) { + activePolylines.forEach(function(p) { + if (isGoogleLoaded) p.setMap(null); else map.removeLayer(p); + }); + activeMarkers.forEach(function(m) { + if (isGoogleLoaded) m.setMap(null); else map.removeLayer(m); + }); + activePolylines = []; + activeMarkers = []; - vehiclesData.forEach(function(v, idx) { - // Sunucudan (Odoo JSON dosyası) gelen rotayı ezici olarak kullan - if (serverHistory && serverHistory.length > 0) { - v.green_route = serverHistory; + var greenPts = []; + var redPts = []; + + if (vehicle.green_route && vehicle.green_route.length > 0) { + vehicle.green_route.forEach(function(pt) { + if (pt && pt.length >= 2) { + greenPts.push([pt[0], pt[1]]); + } + }); + } + + if (vehicle.red_route && vehicle.red_route.length > 0) { + vehicle.red_route.forEach(function(pt) { + if (pt && pt.length >= 2) { + redPts.push([pt[0], pt[1]]); + } + }); } - // Basit bir kural: Hızı 0'dan büyük olanlar HAREKETLİ, 0 olanlar PARK, veri olmayanlar ÇEVRİMDIŞI. - var speedStr = "0 km/s"; - if (v.gps_speed !== undefined) { - if (v.gps_speed > 0) { - movingCount++; - speedStr = v.gps_speed + " km/s"; - } else { - parkedCount++; - speedStr = "0 km/s"; - } + if (isGoogleLoaded) { + // Google Maps logic... } else { - // Test verilerinde gps_speed olmayabilir, fake data üzerinden simüle et - var randomStatus = Math.random(); - if (randomStatus > 0.4) { movingCount++; speedStr = Math.floor(Math.random() * 80 + 20) + " km/s"; } - else if (randomStatus > 0.1) { parkedCount++; speedStr = "0 km/s"; } - else { offlineCount++; speedStr = "Offline"; } + if (!map) return; + + // 1. Alternatif Gri Rotalar + var polyAlt1 = L.polyline([ + [greenPts.length > 0 ? greenPts[0][0]+0.01 : 41.0182, greenPts.length > 0 ? greenPts[0][1]+0.01 : 28.9884], + [greenPts.length > 0 ? greenPts[0][0]+0.02 : 41.0282, greenPts.length > 0 ? greenPts[0][1]+0.02 : 28.9984] + ], { color: '#94a3b8', weight: 4, dashArray: '10, 10', opacity: 0.3 }).addTo(map); + + var polyAlt2 = L.polyline([ + [greenPts.length > 0 ? greenPts[0][0]-0.01 : 40.9982, greenPts.length > 0 ? greenPts[0][1]-0.01 : 28.9684], + [greenPts.length > 0 ? greenPts[0][0]-0.02 : 40.9882, greenPts.length > 0 ? greenPts[0][1]-0.02 : 28.9584] + ], { color: '#94a3b8', weight: 4, dashArray: '10, 10', opacity: 0.3 }).addTo(map); + + activePolylines.push(polyAlt1, polyAlt2); + + // 2. Yeşil Onaylı Rota + if (greenPts.length > 0) { + var polyGreen = L.polyline(greenPts, { color: '#10b981', weight: 8, opacity: 0.95, lineCap: 'round', lineJoin: 'round' }).addTo(map); + activePolylines.push(polyGreen); + } + + // 3. Kırmızı 1 km+ Sapma Çizgisi + if (redPts.length > 0) { + var polyRed = L.polyline(redPts, { color: '#ef4444', weight: 8, opacity: 0.95, lineCap: 'round', lineJoin: 'round' }).addTo(map); + activePolylines.push(polyRed); + } + + var allPts = greenPts.concat(redPts); + if (allPts.length > 0) { + // Kırmızı Yol Üzerindeki "1.5 km Sapma" Çağrı Kutucuğu + if (redPts.length > 0) { + var midRedIdx = Math.floor(redPts.length / 2); + var mRedCallout = L.marker(redPts[midRedIdx], { icon: createCalloutIcon(vehicle.deviation_label, '#ef4444') }).addTo(map); + activeMarkers.push(mRedCallout); + } + + // Rotada Gerçek Zamanlı Canlı Hareket Eden Araç Animasyonunu Başlat + animateMovingVehicle(allPts, vehicle); + + var group = new L.featureGroup([polyAlt1, polyAlt2].concat(activePolylines)); + map.fitBounds(group.getBounds(), { padding: [50, 50] }); + } } - var tr = document.createElement('tr'); - tr.style.cursor = 'pointer'; - tr.style.transition = 'all 0.2s ease'; - tr.style.borderBottom = '1px solid rgba(255,255,255,0.05)'; + // Yüzen Detay Kartını Güncelle + var overlayEl = document.getElementById('driver_info_overlay'); + if (overlayEl) { + var badgeBg = vehicle.is_violation ? 'rgba(239, 68, 68, 0.25)' : (vehicle.is_manager_approved ? 'rgba(56, 189, 248, 0.25)' : 'rgba(16, 185, 129, 0.25)'); + var badgeBorder = vehicle.is_violation ? '#ef4444' : (vehicle.is_manager_approved ? '#38bdf8' : '#10b981'); + var badgeText = vehicle.is_violation ? '#ef4444' : (vehicle.is_manager_approved ? '#38bdf8' : '#10b981'); + var badgeTitle = vehicle.is_violation + ? vehicle.badge_violation + : (vehicle.is_manager_approved ? vehicle.badge_approved : vehicle.badge_ok); - var isRev = v.is_violation; - var badgeStyle = isRev - ? 'background: rgba(239, 68, 68, 0.2); color: #ef4444; border: 1px solid rgba(239, 68, 68, 0.4);' - : (v.is_manager_approved - ? 'background: rgba(56, 189, 248, 0.2); color: #38bdf8; border: 1px solid rgba(56, 189, 248, 0.4);' - : 'background: rgba(16, 185, 129, 0.2); color: #10b981; border: 1px solid rgba(16, 185, 129, 0.4);'); - var statusLabel = isRev ? (v.badge_violation) : (v.is_manager_approved ? v.badge_approved : v.badge_ok); + overlayEl.style.display = 'block'; + overlayEl.innerHTML = + '
' + + '
' + + '
' + + '

- ' + vehicle.driver_name + '

' + + '

' + vehicle.license_plate + ' (' + vehicle.brand_model + ')

' + + '
' + + '
' + + '
' + badgeTitle + '
' + + '
' + (vehicle.violation_score || 100) + '
' + + '
' + + '
' + + + '' + - tr.innerHTML = - '' + - '' + (v.driver_tag_str || '') + ' - ' + v.driver_name + '
' + - '' + v.license_plate + '' + - '' + - '' + - '' + speedStr + '' + - '' + - '' + - '' + - statusLabel + - '' + - ''; + '
' + + '
' + + 'Personel Evi / Konaklama:' + + '' + (vehicle.driver_home_address || 'Bilinmiyor') + '' + + '
' + + '
' + + 'Başlangıç Noktası:' + + '' + (vehicle.start_location || 'Canlı GPS Konumu') + '' + + '
' + + '
' + + 'Ulaşım Hedefi:' + + '' + (vehicle.destination || 'Gerçek Zamanlı Aktif İzleme') + '' + + '
' + + '
' + + 'Planlanan / Gidilen Mesafe:' + + '' + vehicle.planned_km + ' KM / ' + vehicle.driven_km + ' KM' + + '
' + + '
' + + 'Rota Sapma Durumu:' + + '' + vehicle.deviation_label + '' + + '
' + + '
' + + 'Şirket Politikası:' + + '' + vehicle.policy_desc + '' + + '
' + + '
'; + } + } - tr.addEventListener('click', function() { - var children = tableBody.children; - for (var i = 0; i < children.length; i++) { - children[i].style.background = 'transparent'; + var tableBody = document.getElementById('vehicleTableBody'); + if (tableBody) { + tableBody.innerHTML = ''; + var movingCount = 0; + var parkedCount = 0; + var offlineCount = 0; + + vehiclesData.forEach(function(v, idx) { + if (serverHistory && serverHistory.length > 0) { + v.green_route = serverHistory; + } + + var speedStr = "0 km/s"; + if (v.gps_speed !== undefined) { + if (v.gps_speed > 0) { + movingCount++; + speedStr = v.gps_speed + " km/s"; + } else { + parkedCount++; + speedStr = "0 km/s"; + } + } else { + var randomStatus = Math.random(); + if (randomStatus > 0.4) { movingCount++; speedStr = Math.floor(Math.random() * 80 + 20) + " km/s"; } + else if (randomStatus > 0.1) { parkedCount++; speedStr = "0 km/s"; } + else { offlineCount++; speedStr = "Offline"; } + } + + var tr = document.createElement('tr'); + tr.style.cursor = 'pointer'; + tr.style.transition = 'all 0.2s ease'; + tr.style.borderBottom = '1px solid rgba(255,255,255,0.05)'; + + var isRev = v.is_violation; + var badgeStyle = isRev + ? 'background: rgba(239, 68, 68, 0.2); color: #ef4444; border: 1px solid rgba(239, 68, 68, 0.4);' + : (v.is_manager_approved + ? 'background: rgba(56, 189, 248, 0.2); color: #38bdf8; border: 1px solid rgba(56, 189, 248, 0.4);' + : 'background: rgba(16, 185, 129, 0.2); color: #10b981; border: 1px solid rgba(16, 185, 129, 0.4);'); + var statusLabel = isRev ? (v.badge_violation) : (v.is_manager_approved ? v.badge_approved : v.badge_ok); + + tr.innerHTML = + '' + + '' + (v.driver_tag_str || '') + ' - ' + v.driver_name + '
' + + '' + v.license_plate + '' + + '' + + '' + + '' + speedStr + '' + + '' + + '' + + '' + + statusLabel + + '' + + ''; + + tr.addEventListener('click', function() { + var children = tableBody.children; + for (var i = 0; i < children.length; i++) { + children[i].style.background = 'transparent'; + } + tr.style.background = 'rgba(56, 189, 248, 0.2)'; + selectDriver(v); + }); + + tableBody.appendChild(tr); + + if (idx === 0) { + tr.style.background = 'rgba(56, 189, 248, 0.2)'; + setTimeout(function() { selectDriver(v); }, 300); } - tr.style.background = 'rgba(56, 189, 248, 0.2)'; - selectDriver(v); }); - tableBody.appendChild(tr); + var mMoving = document.getElementById('metric_moving'); + if (mMoving) mMoving.textContent = movingCount; + + var mParked = document.getElementById('metric_parked'); + if (mParked) mParked.textContent = parkedCount; + + var mOffline = document.getElementById('metric_offline'); + if (mOffline) mOffline.textContent = offlineCount; + } - if (idx === 0) { - tr.style.background = 'rgba(56, 189, 248, 0.2)'; - setTimeout(function() { selectDriver(v); }, 300); - } - }); - - // Metrikleri DOM'a yaz - var mMoving = document.getElementById('metric_moving'); - if (mMoving) mMoving.textContent = movingCount; - - var mParked = document.getElementById('metric_parked'); - if (mParked) mParked.textContent = parkedCount; - - var mOffline = document.getElementById('metric_offline'); - if (mOffline) mOffline.textContent = offlineCount; - } - - var miniChartEl = document.getElementById('miniChart'); - if (miniChartEl) { - var ctx = miniChartEl.getContext('2d'); - new Chart(ctx, { - type: 'line', - data: { - labels: ['Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz'], - datasets: [{ - label: 'KM Trend', - data: [2200, 2500, 2100, 2800, 3100, 2700], - borderColor: '#00f2fe', - backgroundColor: 'rgba(0, 242, 254, 0.05)', - borderWidth: 2, - tension: 0.4, - fill: true - }] - }, - options: { - responsive: true, - plugins: { legend: { display: false } }, - scales: { - x: { grid: { display: false }, ticks: { color: '#94a3b8' } }, - y: { grid: { color: 'rgba(255,255,255,0.05)' }, ticks: { color: '#94a3b8' } } + var miniChartEl = document.getElementById('miniChart'); + if (miniChartEl) { + var ctx = miniChartEl.getContext('2d'); + new Chart(ctx, { + type: 'line', + data: { + labels: ['Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz'], + datasets: [{ + label: 'KM Trend', + data: [2200, 2500, 2100, 2800, 3100, 2700], + borderColor: '#00f2fe', + backgroundColor: 'rgba(0, 242, 254, 0.05)', + borderWidth: 2, + tension: 0.4, + fill: true + }] + }, + options: { + responsive: true, + plugins: { legend: { display: false } }, + scales: { + x: { grid: { display: false }, ticks: { color: '#94a3b8' } }, + y: { grid: { color: 'rgba(255,255,255,0.05)' }, ticks: { color: '#94a3b8' } } + } } + }); + } + + function animateMovingVehicle(routePts, vehicle) { + if (!routePts || routePts.length === 0) return; + var endPt = routePts[routePts.length - 1]; + + var mIcon = L.divIcon({ + className: 'custom-vehicle-marker', + html: '
' + + '' + + '
', + iconSize: [28, 28], + iconAnchor: [14, 14] + }); + + var mVehicle = L.marker(endPt, { icon: mIcon }).addTo(map); + activeMarkers.push(mVehicle); + + if (routePts.length > 1) { + var currentIndex = 0; + function moveStep() { + if (currentIndex >= routePts.length) { + currentIndex = 0; // Başa dön (Loop animasyon - test için) + } + mVehicle.setLatLng(routePts[currentIndex]); + currentIndex++; + setTimeout(moveStep, 1000); + } + moveStep(); } - }); + } + + } catch (globalError) { + alert("Harita Yüklenirken Hata Oluştu:\n" + globalError.toString() + "\nLütfen sayfayı F5 ile yenileyin."); + console.error(globalError); } - - // --- 10 Saniyede Bir Sayfayı Tam Ekran Yenileyerek Görüntüyü Güncelleyen Kod --- - setInterval(function() { - window.location.reload(); - }, 15000); } });