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 =
- '
' +
- '
' +
- '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);
}
});