Alteração do Mapa com os pontos da onde foi entreque

This commit is contained in:
2026-06-29 21:16:22 -03:00
parent 7d6c79f878
commit 4c035d4a4c
2 changed files with 51 additions and 11 deletions

View File

@@ -181,7 +181,8 @@ module Analytics
endereco: r['endereco_completo'].presence || r['address'].presence,
unidade: r['contact_name'],
motorista: r['driver'],
checkout: data_de(r['checkout'])&.strftime('%d/%m/%Y')
checkout: data_de(r['checkout'])&.strftime('%d/%m/%Y'),
foto: foto_url(r['foto_da_fachada'])
}
end.first(MAX_PONTOS)
end
@@ -218,6 +219,12 @@ module Analytics
nil
end
# Só aceita URL http(s) — evita injetar lixo no <img src> do popup.
def foto_url(valor)
url = valor.to_s.strip
url.match?(%r{\Ahttps?://}i) ? url : nil
end
def carregar
return [] if @tabelas.empty?
@@ -229,7 +236,7 @@ module Analytics
<<~SQL.strip
SELECT #{label} AS operacao,
r.reference_id, r.driver, r.status, r.observation, r.contact_name, r.address,
r.checkout, r.planned_date,
r.checkout, r.planned_date, r.foto_da_fachada,
r.latitude, r.longitude,
r.checkout_latitude, r.checkout_longitude,
#{selects_gade(conn, tabela)}

View File

@@ -341,8 +341,12 @@
function popupHtml(p) {
const linha = (icone, val) => val ? ('<div>' + icone + ' ' + escapeHtml(val) + '</div>') : '';
const foto = p.foto
? '<img src="' + escapeHtml(p.foto) + '" alt="Fachada" loading="lazy" ' +
'style="margin-top:8px;width:100%;max-height:150px;object-fit:cover;border-radius:6px">'
: '';
return '' +
'<div style="min-width:200px">' +
'<div style="min-width:210px;max-width:240px">' +
'<div style="font-weight:700;color:#f97316">NF ' + escapeHtml(p.nf) + '</div>' +
'<div style="font-weight:600;margin-bottom:4px">' + escapeHtml(p.nome) + '</div>' +
'<div style="color:#16a34a;font-size:11px;margin-bottom:6px">✔ Entrega realizada</div>' +
@@ -350,11 +354,27 @@
linha('🏥', p.unidade) +
linha('🚚', p.motorista) +
linha('🕒', p.checkout) +
foto +
'</div>';
}
// Ícone de localização (balão) — laranja da marca, sem depender de imagem externa.
function pinIcon() {
return L.divIcon({
className: 'pin-entrega',
html: '<svg width="28" height="40" viewBox="0 0 28 40" xmlns="http://www.w3.org/2000/svg">' +
'<path d="M14 0C6.8 0 1 5.8 1 13c0 9.7 13 27 13 27s13-17.3 13-27C27 5.8 21.2 0 14 0z" ' +
'fill="#f97316" stroke="#0a0a0a" stroke-width="1.5"/>' +
'<circle cx="14" cy="13" r="5" fill="#0a0a0a"/></svg>',
iconSize: [28, 40],
iconAnchor: [14, 40],
popupAnchor: [0, -36]
});
}
function initMapas() {
if (typeof L === 'undefined') return;
const ICONE = pinIcon();
document.querySelectorAll('.mapa-box').forEach(function (box) {
const el = box.querySelector('.mapa-calor');
if (!el || el._leaflet_id) return;
@@ -362,22 +382,35 @@
const heat = JSON.parse(el.dataset.heat || '[]');
const pontos = JSON.parse(el.dataset.pontos || '[]');
const centro = JSON.parse(el.dataset.centro || '[-23.55,-46.63]');
const mapa = L.map(el).setView(centro, 11);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
// Camadas base (todas grátis, sem chave): escuro (padrão, combina com o
// tema), claro e satélite.
const escuro = L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
attribution: '© OpenStreetMap © CARTO', subdomains: 'abcd', maxZoom: 20
});
const claro = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap', maxZoom: 19
}).addTo(mapa);
});
const satelite = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
attribution: 'Tiles © Esri', maxZoom: 19
});
const mapa = L.map(el, { layers: [escuro] }).setView(centro, 11);
L.control.layers(
{ '🌙 Escuro': escuro, '🗺️ Claro': claro, '🛰️ Satélite': satelite },
{}, { position: 'topright', collapsed: true }
).addTo(mapa);
// Camada de calor
const heatLayer = (heat.length && L.heatLayer)
? L.heatLayer(heat, { radius: 18, blur: 22, maxZoom: 14 }) : null;
// Camada de pontos (marcadores com popup)
// Camada de pontos (marcadores em balão com popup)
const marcadores = L.layerGroup();
pontos.forEach(function (p) {
L.circleMarker([p.lat, p.lng], {
radius: 6, color: '#0a0a0a', weight: 1,
fillColor: '#f97316', fillOpacity: 0.85
}).bindPopup(popupHtml(p)).addTo(marcadores);
L.marker([p.lat, p.lng], { icon: ICONE })
.bindPopup(popupHtml(p))
.addTo(marcadores);
});
// Começa no Calor (cai para Pontos se não houver calor)