Adição do campo de busca no mapa
This commit is contained in:
@@ -181,6 +181,7 @@ module Analytics
|
||||
endereco: r['endereco_completo'].presence || r['address'].presence,
|
||||
unidade: r['contact_name'],
|
||||
motorista: r['driver'],
|
||||
veiculo: r['vehicle'],
|
||||
checkout: data_de(r['checkout'])&.strftime('%d/%m/%Y'),
|
||||
foto: foto_url(r['foto_da_fachada'])
|
||||
}
|
||||
@@ -235,7 +236,7 @@ module Analytics
|
||||
label = conn.quote(Operacao.label(tabela))
|
||||
<<~SQL.strip
|
||||
SELECT #{label} AS operacao,
|
||||
r.reference_id, r.driver, r.status, r.observation, r.contact_name, r.address,
|
||||
r.reference_id, r.driver, r.vehicle, r.status, r.observation, r.contact_name, r.address,
|
||||
r.checkout, r.planned_date, r.foto_da_fachada,
|
||||
r.latitude, r.longitude,
|
||||
r.checkout_latitude, r.checkout_longitude,
|
||||
|
||||
@@ -4,7 +4,12 @@
|
||||
<div class="mapa-box bg-[#1a1a1a] rounded-2xl border border-white/5 p-6">
|
||||
<div class="flex flex-wrap items-center justify-between gap-3 mb-4">
|
||||
<p class="text-gray-400 text-sm font-medium">Mapa de Entregas</p>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<input type="search" class="mapa-busca w-56 max-w-full bg-[#1a1a1a] border border-white/10 rounded-lg px-3 py-1.5 text-xs text-gray-200 placeholder-gray-500 focus:outline-none focus:border-orange-500"
|
||||
placeholder="Buscar NF, paciente ou veículo…" autocomplete="off">
|
||||
<span class="mapa-busca-info text-xs text-gray-500"></span>
|
||||
</div>
|
||||
<div class="map-toggle inline-flex rounded-lg overflow-hidden border border-white/10 text-xs">
|
||||
<button type="button" data-view="heat"
|
||||
class="px-3 py-1.5 bg-orange-500 text-black font-semibold">🔥 Calor</button>
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
.flatpickr-months .flatpickr-prev-month svg, .flatpickr-months .flatpickr-next-month svg { fill: #f97316; }
|
||||
/* O mapa do Leaflet usa fundo claro — moldura escura ao redor */
|
||||
.leaflet-container { background: #1a1a1a; }
|
||||
/* Realça ruas e nomes no tile escuro (CARTO dark_all tem baixo contraste) */
|
||||
.tiles-escuro-contraste { filter: brightness(1.45) contrast(1.2) saturate(0.9); }
|
||||
</style>
|
||||
<% end %>
|
||||
|
||||
@@ -356,6 +358,7 @@
|
||||
linha('📍', p.endereco) +
|
||||
linha('🏥', p.unidade) +
|
||||
linha('🚚', p.motorista) +
|
||||
linha('🚗', p.veiculo) +
|
||||
linha('🕒', p.checkout) +
|
||||
foto +
|
||||
'</div>';
|
||||
@@ -389,7 +392,8 @@
|
||||
// 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
|
||||
attribution: '© OpenStreetMap © CARTO', subdomains: 'abcd', maxZoom: 20,
|
||||
className: 'tiles-escuro-contraste'
|
||||
});
|
||||
const claro = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© OpenStreetMap', maxZoom: 19
|
||||
@@ -412,21 +416,26 @@
|
||||
// O popup é uma FUNÇÃO, então o HTML da foto só é montado/baixado quando
|
||||
// o balão é clicado — nada de imagem é requisitado ao carregar o mapa.
|
||||
let marcadores = null;
|
||||
let entradas = []; // [{ marker, p }] — para a busca filtrar sem recriar
|
||||
const resultadosBusca = L.layerGroup();
|
||||
function construirMarcadores() {
|
||||
if (marcadores) return marcadores;
|
||||
marcadores = L.layerGroup();
|
||||
pontos.forEach(function (p) {
|
||||
L.marker([p.lat, p.lng], { icon: ICONE })
|
||||
.bindPopup(function () { return popupHtml(p); }, { maxWidth: 260 })
|
||||
.addTo(marcadores);
|
||||
const marker = L.marker([p.lat, p.lng], { icon: ICONE })
|
||||
.bindPopup(function () { return popupHtml(p); }, { maxWidth: 260 });
|
||||
marker.addTo(marcadores);
|
||||
entradas.push({ marker: marker, p: p });
|
||||
});
|
||||
return marcadores;
|
||||
}
|
||||
|
||||
// Começa no Calor (cai para Pontos se não houver calor)
|
||||
let vistaAtual = heatLayer ? 'heat' : 'pontos';
|
||||
(heatLayer || construirMarcadores()).addTo(mapa);
|
||||
|
||||
function mostrar(view) {
|
||||
vistaAtual = view;
|
||||
if (heatLayer) mapa.removeLayer(heatLayer);
|
||||
if (marcadores) mapa.removeLayer(marcadores);
|
||||
if (view === 'pontos') { construirMarcadores().addTo(mapa); }
|
||||
@@ -441,8 +450,61 @@
|
||||
}
|
||||
|
||||
box.querySelectorAll('.map-toggle button').forEach(function (b) {
|
||||
b.addEventListener('click', function () { mostrar(b.dataset.view); });
|
||||
b.addEventListener('click', function () {
|
||||
const inputBusca = box.querySelector('.mapa-busca');
|
||||
if (inputBusca && inputBusca.value) { inputBusca.value = ''; }
|
||||
limparBusca();
|
||||
mostrar(b.dataset.view);
|
||||
});
|
||||
});
|
||||
|
||||
// ── Busca por NF / paciente / veículo ──
|
||||
const inputBusca = box.querySelector('.mapa-busca');
|
||||
const infoBusca = box.querySelector('.mapa-busca-info');
|
||||
|
||||
function limparBusca() {
|
||||
mapa.removeLayer(resultadosBusca);
|
||||
resultadosBusca.clearLayers();
|
||||
if (infoBusca) infoBusca.textContent = '';
|
||||
}
|
||||
|
||||
function aplicarBusca(termo) {
|
||||
const q = String(termo || '').trim().toLowerCase();
|
||||
if (!q) {
|
||||
limparBusca();
|
||||
mostrar(vistaAtual); // restaura a visão padrão (Calor ou Pontos)
|
||||
return;
|
||||
}
|
||||
construirMarcadores();
|
||||
if (heatLayer) mapa.removeLayer(heatLayer);
|
||||
if (marcadores) mapa.removeLayer(marcadores);
|
||||
resultadosBusca.clearLayers();
|
||||
|
||||
const casa = function (v) { return String(v == null ? '' : v).toLowerCase().indexOf(q) !== -1; };
|
||||
const achados = entradas.filter(function (e) {
|
||||
return casa(e.p.nf) || casa(e.p.nome) || casa(e.p.veiculo);
|
||||
});
|
||||
achados.forEach(function (e) { e.marker.addTo(resultadosBusca); });
|
||||
resultadosBusca.addTo(mapa);
|
||||
|
||||
if (achados.length) {
|
||||
const grupo = L.featureGroup(achados.map(function (e) { return e.marker; }));
|
||||
mapa.fitBounds(grupo.getBounds(), { padding: [40, 40], maxZoom: 16 });
|
||||
if (achados.length === 1) { achados[0].marker.openPopup(); }
|
||||
if (infoBusca) infoBusca.textContent = achados.length + (achados.length === 1 ? ' resultado' : ' resultados');
|
||||
} else {
|
||||
if (infoBusca) infoBusca.textContent = 'Nenhum resultado';
|
||||
}
|
||||
}
|
||||
|
||||
if (inputBusca) {
|
||||
let debounce = null;
|
||||
inputBusca.addEventListener('input', function () {
|
||||
clearTimeout(debounce);
|
||||
const valor = inputBusca.value;
|
||||
debounce = setTimeout(function () { aplicarBusca(valor); }, 200);
|
||||
});
|
||||
}
|
||||
|
||||
// Tela cheia (Fullscreen API sobre o container do mapa)
|
||||
const btnFs = box.querySelector('.map-fullscreen');
|
||||
|
||||
Reference in New Issue
Block a user